# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: 3529/dev/store-settlement-balance.spec.ts >> [3529][dev] [QA] 비스킷페이 총 정산예정금 상세 내역 확인 >> [VE] role >> 비스킷페이 정산내역 >> 비스킷페이 정산내역 화면에서 조회 기간을 2026-06-12 ~ 2026-06-19로 설정했을 때 정산완료 상태의 세금과 최종 금액이 실제 공제 내역 기준으로 보이는지 확인
- Location: src/qa/scenarios/storeSettlementBalance.ts:330:11

# Error details

```
Error: 정산완료 상태 문구가 화면에 보여야 합니다.

expect(received).toContain(expected) // indexOf

Expected substring: "정산완료"
Received string:    "빅스페이먼츠 박재민_가맹_한결데 (1/1) 지갑잔고 36,571 더보기 정산예정금 19,000 지갑내역 송금 공지사항 패치노트 링크연결테스트 26.04.17 테스트 26.04.17 총 정산예정금 19,000 오늘 26.6.12 금일매출 42,345 정산금 40,227 어제 26.6.11 전일매출 0 정산금 0 홈 지갑 결제 내역 카드 정산예정금 상세 닫기 총 정산예정금 19,000원 정산예정금 9,500원 정산대기금 9,500원 총 정산예정금은 정산예정금과 정산대기금을 합산한 금액입니다."
```

# Test source

```ts
  242 |             expect(refreshedAmount, "새로고침 후 지갑잔고 정산예정금이 유지되어야 합니다.").toBe(target.expectedTotal);
  243 |
  244 |             await attachEvidenceLog(testInfo, walletPage.name, target.role, `wallet-total-${target.role}`, [
  245 |               `${target.accountLabel}(${target.departmentName}) 계정으로 스토어 지갑잔고 화면을 확인했습니다.`,
  246 |               `지갑잔고 화면 정산예정금: ${formatKrw(snapshot.walletPendingAmount ?? 0)}`,
  247 |               `wallet API processingSettlement: ${
  248 |                 snapshot.walletProcessingSettlement === undefined ? "-" : formatKrw(snapshot.walletProcessingSettlement)
  249 |               }`,
  250 |               `새로고침 후 정산예정금: ${formatKrw(refreshedAmount)}`,
  251 |               `기준 금액: ${formatKrw(target.expectedTotal)}`,
  252 |               "결론: 지갑잔고 화면 정산예정금과 새로고침 후 금액이 기준 금액과 일치합니다."
  253 |             ]);
  254 |           });
  255 |         });
  256 |       });
  257 |     }
  258 |   });
  259 | }
  260 |
  261 | function runIssue3529Suite(
  262 |   options: ScenarioOptions,
  263 |   subject: string,
  264 |   checklistItems: string[],
  265 |   pages: QaPageDefinition[]
  266 | ): void {
  267 |   const storeTransactionsPage = findPage(pages, "비스킷페이 거래내역");
  268 |   const storeSettlementsPage = findPage(pages, "비스킷페이 정산내역");
  269 |   const storeWalletPage = findPage(pages, "비스킷페이 지갑");
  270 |   const adminSettlementsPage = findPage(pages, "비스킷링크 정산내역");
  271 |   const storeRuntime = getRuntimeQaEnv(options.environment, "store", ISSUE_3529_STORE_ENV_ROLE);
  272 |   const adminRuntime = getRuntimeQaEnv(options.environment, "admin", ADMIN_ROLE);
  273 |   let transactionSnapshot: Issue3529Snapshot | undefined;
  274 |   const settlementSnapshots = new Map<string, Issue3529Snapshot>();
  275 |   let adminSettlementSnapshot: Issue3529Snapshot | undefined;
  276 |
  277 |   test.describe(`[${options.issueId}][${options.environment}] ${subject}`, () => {
  278 |     test.describe(`[${ISSUE_3529_STORE_ROLE}] role`, () => {
  279 |       test.describe(storeTransactionsPage.name, () => {
  280 |         test(checklistItems[0] ?? "BP100060 거래 정산금액 공제 전 기준 확인", async ({ browser }, testInfo) => {
  281 |           test.setTimeout(90_000);
  282 |           transactionSnapshot = await collectIssue3529StoreSnapshot(
  283 |             browser,
  284 |             storeRuntime,
  285 |             storeTransactionsPage,
  286 |             testInfo,
  287 |             "store-transaction-merchant",
  288 |             { expectedText: /거래|정산|결제/, statusText: undefined }
  289 |           );
  290 |
  291 |           expect(transactionSnapshot.bodyText, "거래내역에서 BP100060 또는 검색 조건이 확인되어야 합니다.").toMatch(/BP100060|거래|결제/);
  292 |           expect(transactionSnapshot.bodyText, "거래내역에서 정산금액 관련 문구가 보여야 합니다.").toMatch(/정산\s*금액|정산금액|정산/);
  293 |           expect(transactionSnapshot.amounts.length, "거래내역 금액 근거가 있어야 합니다.").toBeGreaterThan(0);
  294 |
  295 |           await attachEvidenceLog(testInfo, storeTransactionsPage.name, ISSUE_3529_STORE_ROLE, "store-transaction-merchant", [
  296 |             `${ISSUE_3529_STORE_ACCOUNT_LABEL} 계정으로 스토어 거래내역을 조회했습니다.`,
  297 |             `검색 기준: 가맹점 고유번호 ${ISSUE_3529_MERCHANT_CODE}, 기간 ${ISSUE_3529_PERIOD.from} ~ ${ISSUE_3529_PERIOD.to}`,
  298 |             `화면 금액 후보: ${formatAmounts(transactionSnapshot.amounts)}`,
  299 |             `수집한 요청: ${transactionSnapshot.requestUrls.slice(0, 8).join(" / ") || "-"}`,
  300 |             "결론: BP100060 조건의 거래내역 화면에서 정산금액 표시 근거를 확인했습니다."
  301 |           ]);
  302 |         });
  303 |
  304 |         test(checklistItems[1] ?? "거래내역 합계 영역 총 정산금액과 세금 정보 확인", async ({ browser }, testInfo) => {
  305 |           test.setTimeout(90_000);
  306 |           const snapshot = await collectIssue3529StoreSnapshot(
  307 |             browser,
  308 |             storeRuntime,
  309 |             storeTransactionsPage,
  310 |             testInfo,
  311 |             "store-transaction-summary",
  312 |             { expectedText: /거래|정산|합계/ }
  313 |           );
  314 |
  315 |           expect(snapshot.bodyText, "거래내역 합계/총액 영역이 보여야 합니다.").toMatch(/합계|총\s*정산|총정산|총액|정산금액/);
  316 |           expect(snapshot.bodyText, "거래내역 세금/부가세 정보가 보여야 합니다.").toMatch(/세금|부가세|세액|VAT/i);
  317 |           expect(snapshot.amounts.length, "거래내역 합계 금액 근거가 있어야 합니다.").toBeGreaterThan(0);
  318 |
  319 |           await attachEvidenceLog(testInfo, storeTransactionsPage.name, ISSUE_3529_STORE_ROLE, "store-transaction-summary", [
  320 |             "거래내역 합계 영역에서 총 정산금액 및 세금 문구를 확인했습니다.",
  321 |             `화면 금액 후보: ${formatAmounts(snapshot.amounts)}`,
  322 |             "결론: 총 정산금액/세금 정보가 화면에서 확인 가능한 형태로 표시됩니다."
  323 |           ]);
  324 |         });
  325 |       });
  326 |
  327 |       test.describe(storeSettlementsPage.name, () => {
  328 |         for (const statusText of ["정산예정", "정산대기", "정산완료"]) {
  329 |           const checklistIndex = statusText === "정산예정" ? 2 : statusText === "정산대기" ? 3 : 4;
  330 |           test(checklistItems[checklistIndex] ?? `${statusText} 정산 금액 확인`, async ({ browser }, testInfo) => {
  331 |             test.setTimeout(100_000);
  332 |             const snapshot = await collectIssue3529StoreSnapshot(
  333 |               browser,
  334 |               storeRuntime,
  335 |               storeSettlementsPage,
  336 |               testInfo,
  337 |               `store-settlement-${statusText}`,
  338 |               { expectedText: /정산|거래|결제/, statusText }
  339 |             );
  340 |             settlementSnapshots.set(statusText, snapshot);
  341 |
> 342 |             expect(snapshot.bodyText, `${statusText} 상태 문구가 화면에 보여야 합니다.`).toContain(statusText);
      |                                                                            ^ Error: 정산완료 상태 문구가 화면에 보여야 합니다.
  343 |             expect(snapshot.amounts.length, `${statusText} 금액 근거가 있어야 합니다.`).toBeGreaterThan(0);
  344 |             if (statusText === "정산완료") {
  345 |               expect(snapshot.bodyText, "정산완료 건은 세금/공제/최종 금액 근거가 보여야 합니다.").toMatch(/세금|부가세|공제|최종|실\s*정산|실정산/);
  346 |             }
  347 |
  348 |             await attachEvidenceLog(testInfo, storeSettlementsPage.name, ISSUE_3529_STORE_ROLE, `store-settlement-${statusText}`, [
  349 |               `${statusText} 조건으로 스토어 정산내역을 조회했습니다.`,
  350 |               `조회 기간: ${ISSUE_3529_PERIOD.from} ~ ${ISSUE_3529_PERIOD.to}`,
  351 |               `화면 금액 후보: ${formatAmounts(snapshot.amounts)}`,
  352 |               `정산예정금 상세 API: ${snapshot.settlementBreakdown ? formatBreakdown(snapshot.settlementBreakdown) : "-"}`,
  353 |               `수집한 요청: ${snapshot.requestUrls.slice(0, 8).join(" / ") || "-"}`,
  354 |               `결론: ${statusText} 정산 금액 표시 근거를 확인했습니다.`
  355 |             ]);
  356 |           });
  357 |         }
  358 |       });
  359 |
  360 |       test.describe(storeWalletPage.name, () => {
  361 |         test(checklistItems[6] ?? "지갑 정산예정금/정산대기금과 정산내역 합계 일치 확인", async ({ browser }, testInfo) => {
  362 |           test.setTimeout(100_000);
  363 |           const walletSnapshot = await collectIssue3529StoreSnapshot(
  364 |             browser,
  365 |             storeRuntime,
  366 |             storeWalletPage,
  367 |             testInfo,
  368 |             "store-wallet-settlement-total",
  369 |             { expectedText: /정산예정금|정산대기금|지갑/ }
  370 |           );
  371 |
  372 |           const pending = extractOptionalAmountAfterLabel(walletSnapshot.bodyText, "정산예정금");
  373 |           const waiting = extractOptionalAmountAfterLabel(walletSnapshot.bodyText, "정산대기금");
  374 |           expect(walletSnapshot.bodyText, "지갑 화면에서 정산예정금 또는 정산대기금 문구가 보여야 합니다.").toMatch(/정산예정금|정산대기금/);
  375 |           expect(walletSnapshot.amounts.length, "지갑 화면 금액 근거가 있어야 합니다.").toBeGreaterThan(0);
  376 |
  377 |           const pendingSnapshot = settlementSnapshots.get("정산예정");
  378 |           const waitingSnapshot = settlementSnapshots.get("정산대기");
  379 |           if (pending !== undefined && pendingSnapshot?.settlementBreakdown) {
  380 |             expect(pending, "지갑 정산예정금은 정산내역 정산예정 상세 금액과 일치해야 합니다.").toBe(
  381 |               pendingSnapshot.settlementBreakdown.pendingSettlementAmount
  382 |             );
  383 |           }
  384 |           if (waiting !== undefined && waitingSnapshot?.settlementBreakdown) {
  385 |             expect(waiting, "지갑 정산대기금은 정산내역 정산대기 상세 금액과 일치해야 합니다.").toBe(
  386 |               waitingSnapshot.settlementBreakdown.settlementWaitingAmount
  387 |             );
  388 |           }
  389 |
  390 |           await attachEvidenceLog(testInfo, storeWalletPage.name, ISSUE_3529_STORE_ROLE, "store-wallet-settlement-total", [
  391 |             "스토어 지갑 화면의 정산예정금/정산대기금 영역을 확인했습니다.",
  392 |             `지갑 정산예정금: ${pending === undefined ? "-" : formatKrw(pending)}`,
  393 |             `지갑 정산대기금: ${waiting === undefined ? "-" : formatKrw(waiting)}`,
  394 |             `화면 금액 후보: ${formatAmounts(walletSnapshot.amounts)}`,
  395 |             `정산예정 상세 API: ${pendingSnapshot?.settlementBreakdown ? formatBreakdown(pendingSnapshot.settlementBreakdown) : "-"}`,
  396 |             `정산대기 상세 API: ${waitingSnapshot?.settlementBreakdown ? formatBreakdown(waitingSnapshot.settlementBreakdown) : "-"}`,
  397 |             "결론: 지갑 화면과 정산내역 상세 금액 비교 근거를 수집했습니다."
  398 |           ]);
  399 |         });
  400 |       });
  401 |     });
  402 |
  403 |     test.describe(`[${ADMIN_ROLE}] role`, () => {
  404 |       test.describe(adminSettlementsPage.name, () => {
  405 |         test(checklistItems[5] ?? "비스킷링크 정산내역과 비스킷페이 정산내역 금액 비교", async ({ browser }, testInfo) => {
  406 |           test.setTimeout(100_000);
  407 |           const storeSnapshot = settlementSnapshots.get("정산예정") ?? await collectIssue3529StoreSnapshot(
  408 |             browser,
  409 |             storeRuntime,
  410 |             storeSettlementsPage,
  411 |             testInfo,
  412 |             "store-settlement-admin-source",
  413 |             { expectedText: /정산예정|정산내역/, statusText: "정산예정", attachScreenshot: false }
  414 |           );
  415 |
  416 |           adminSettlementSnapshot = await collectIssue3529AdminSettlementSnapshot(
  417 |             browser,
  418 |             adminRuntime,
  419 |             adminSettlementsPage,
  420 |             testInfo
  421 |           );
  422 |
  423 |           expect(adminSettlementSnapshot.bodyText, "비스킷링크 정산내역 화면이 표시되어야 합니다.").toMatch(/정산|가맹|업체|금액/);
  424 |           expect(adminSettlementSnapshot.amounts.length, "비스킷링크 정산내역 금액 근거가 있어야 합니다.").toBeGreaterThan(0);
  425 |           const sharedAmounts = storeSnapshot.amounts.filter((amount) => adminSettlementSnapshot?.amounts.includes(amount));
  426 |           expect(sharedAmounts.length, "스토어 정산내역과 비스킷링크 정산내역에 공통 금액 근거가 있어야 합니다.").toBeGreaterThan(0);
  427 |
  428 |           await attachEvidenceLog(testInfo, adminSettlementsPage.name, ADMIN_ROLE, "admin-store-settlement-compare", [
  429 |             `동일 기준: ${ISSUE_3529_MERCHANT_CODE}, ${ISSUE_3529_PERIOD.from} ~ ${ISSUE_3529_PERIOD.to}`,
  430 |             `스토어 정산 금액 후보: ${formatAmounts(storeSnapshot.amounts)}`,
  431 |             `비스킷링크 정산 금액 후보: ${formatAmounts(adminSettlementSnapshot.amounts)}`,
  432 |             `공통 금액 후보: ${formatAmounts(sharedAmounts)}`,
  433 |             `비스킷링크 요청: ${adminSettlementSnapshot.requestUrls.slice(0, 8).join(" / ") || "-"}`,
  434 |             "결론: 동일 기간/가맹점 기준으로 비스킷페이와 비스킷링크 정산내역 금액 비교 근거를 확인했습니다."
  435 |           ]);
  436 |         });
  437 |       });
  438 |     });
  439 |   });
  440 | }
  441 |
  442 | function findPage(pages: QaPageDefinition[], name: string): QaPageDefinition {
```