# 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: 3486/dev/kpd-mid-hidden-fields.spec.ts >> [3486][dev] [QA] PG 한국결제데이터 MID 생성/수정 입력박스 미노출 검증 >> [ADMIN] role >> PG MID 관리 >> PG 한국결제데이터 MID 수정 화면에 PG MID 입력박스가 보이지 않는지 확인
- Location: src/qa/scenarios/kpdMidHiddenFields.ts:153:9

# Error details

```
Error: 한국결제데이터 MID 수정 화면에 PG MID 입력박스가 보이면 안 됩니다.

expect(received).toEqual(expected) // deep equality

- Expected  - 1
+ Received  + 4

- Array []
+ Array [
+   "input[type=text] / id=mid / label=MID MID / parent=MID",
+   "input[type=text] / id=mid / label=MID MID / parent=MID",
+ ]
```

# Test source

```ts
  126 |   const metadata = loadIssueMetadata(options.issueId);
  127 |   const checklist = loadChecklist(options.issueId, options.environment);
  128 |   const pageDefinition = findPageDefinition(checklist.pages, "PG MID 관리");
  129 |   const runtimeEnv = getRuntimeQaEnv(options.environment, pageDefinition.application ?? "admin", ADMIN_ROLE);
  130 |   const blockReason = getRuntimeBlockReason(runtimeEnv);
  131 |   const items = checklist.checklist;
  132 |
  133 |   test.describe(`[${options.issueId}][${options.environment}] ${metadata.subject}`, () => {
  134 |     test.describe(`[${ADMIN_ROLE}] role`, () => {
  135 |       test.describe(pageDefinition.name, () => {
  136 |         test.skip(!!blockReason, blockReason);
  137 |
  138 |         test(items[0], async ({ browser }, testInfo) => {
  139 |           test.setTimeout(75_000);
  140 |           await verifyHiddenFieldOnCreate(browser, runtimeEnv, pageDefinition, testInfo, HIDDEN_FIELDS.pgMid);
  141 |         });
  142 |
  143 |         test(items[1], async ({ browser }, testInfo) => {
  144 |           test.setTimeout(75_000);
  145 |           await verifyHiddenFieldOnCreate(browser, runtimeEnv, pageDefinition, testInfo, HIDDEN_FIELDS.publicKey);
  146 |         });
  147 |
  148 |         test(items[2], async ({ browser }, testInfo) => {
  149 |           test.setTimeout(75_000);
  150 |           await verifyHiddenFieldOnCreate(browser, runtimeEnv, pageDefinition, testInfo, HIDDEN_FIELDS.appId);
  151 |         });
  152 |
  153 |         test(items[3], async ({ browser }, testInfo) => {
  154 |           test.setTimeout(75_000);
  155 |           await verifyHiddenFieldOnUpdate(browser, runtimeEnv, pageDefinition, testInfo, HIDDEN_FIELDS.pgMid);
  156 |         });
  157 |
  158 |         test(items[4], async ({ browser }, testInfo) => {
  159 |           test.setTimeout(75_000);
  160 |           await verifyHiddenFieldOnUpdate(browser, runtimeEnv, pageDefinition, testInfo, HIDDEN_FIELDS.publicKey);
  161 |         });
  162 |
  163 |         test(items[5], async ({ browser }, testInfo) => {
  164 |           test.setTimeout(75_000);
  165 |           await verifyHiddenFieldOnUpdate(browser, runtimeEnv, pageDefinition, testInfo, HIDDEN_FIELDS.appId);
  166 |         });
  167 |       });
  168 |     });
  169 |   });
  170 | }
  171 |
  172 | async function verifyHiddenFieldOnCreate(
  173 |   browser: Browser,
  174 |   runtimeEnv: RuntimeQaEnv,
  175 |   pageDefinition: QaPageDefinition,
  176 |   testInfo: TestInfo,
  177 |   field: HiddenFieldDefinition
  178 | ): Promise<void> {
  179 |   await withRolePage(browser, runtimeEnv, async (page) => {
  180 |     const target = await openKpdCreatePage(page, runtimeEnv);
  181 |     await verifyHiddenField(page, pageDefinition, testInfo, field, target, "create");
  182 |   });
  183 | }
  184 |
  185 | async function verifyHiddenFieldOnUpdate(
  186 |   browser: Browser,
  187 |   runtimeEnv: RuntimeQaEnv,
  188 |   pageDefinition: QaPageDefinition,
  189 |   testInfo: TestInfo,
  190 |   field: HiddenFieldDefinition
  191 | ): Promise<void> {
  192 |   await withRolePage(browser, runtimeEnv, async (page) => {
  193 |     const target = await openKpdUpdatePage(page, runtimeEnv);
  194 |     await verifyHiddenField(page, pageDefinition, testInfo, field, target, "update");
  195 |   });
  196 | }
  197 |
  198 | async function verifyHiddenField(
  199 |   page: Page,
  200 |   pageDefinition: QaPageDefinition,
  201 |   testInfo: TestInfo,
  202 |   field: HiddenFieldDefinition,
  203 |   target: KpdTarget,
  204 |   surface: "create" | "update"
  205 | ): Promise<void> {
  206 |   const controls = await collectVisibleControls(page);
  207 |   const matches = controls.filter((control) => matchesHiddenField(control, field));
  208 |   const surfaceLabel = surface === "create" ? "생성" : "수정";
  209 |   const slug = `${surface}-${safeFilename(field.label).toLowerCase()}`;
  210 |
  211 |   await attachEvidenceLog(testInfo, pageDefinition, ADMIN_ROLE, slug, [
  212 |     `화면: 한국결제데이터 MID ${surfaceLabel} 화면`,
  213 |     `PG 계약: ${target.contractName} (${target.contractId})`,
  214 |     `PG: ${target.providerName} (${target.providerCode})`,
  215 |     `MID 타입: ${target.midType}`,
  216 |     `수정 대상 MID: ${surface === "update" ? `${target.mid} (${target.midId})` : "생성 화면 검증"}`,
  217 |     `검증 입력박스: ${field.label}`,
  218 |     `매칭된 visible 입력박스 수: ${matches.length}`,
  219 |     ...matches.map((control, index) => `${index + 1}. ${formatControl(control)}`)
  220 |   ]);
  221 |   await attachPageScreenshot(page, testInfo, pageDefinition, ADMIN_ROLE, slug);
  222 |
  223 |   expect(
  224 |     matches.map(formatControl),
  225 |     `한국결제데이터 MID ${surfaceLabel} 화면에 ${field.label} 입력박스가 보이면 안 됩니다.`
> 226 |   ).toEqual([]);
      |     ^ Error: 한국결제데이터 MID 수정 화면에 PG MID 입력박스가 보이면 안 됩니다.
  227 | }
  228 |
  229 | function findPageDefinition(pages: QaPageDefinition[], name: string): QaPageDefinition {
  230 |   const exact = pages.find((pageDefinition) => pageDefinition.name === name);
  231 |   if (exact) {
  232 |     return exact;
  233 |   }
  234 |   const partial = pages.find((pageDefinition) => pageDefinition.name.includes(name));
  235 |   if (partial) {
  236 |     return partial;
  237 |   }
  238 |   throw new Error(`checklist.json pages에 '${name}' 페이지 정의가 필요합니다.`);
  239 | }
  240 |
  241 | function getRuntimeBlockReason(runtimeEnv: RuntimeQaEnv): string | undefined {
  242 |   const appKey = runtimeEnv.application.toUpperCase();
  243 |   const envKey = runtimeEnv.environment.toUpperCase();
  244 |
  245 |   if (!runtimeEnv.baseUrl) {
  246 |     return `BIX_${appKey}_${envKey}_BASE_URL 또는 BIX_${appKey}_${envKey}_LOGIN_URL이 필요합니다.`;
  247 |   }
  248 |   if (!runtimeEnv.tenantId) {
  249 |     return `BIX_${appKey}_${envKey}_TENANT_ID가 필요합니다.`;
  250 |   }
  251 |   if (!runtimeEnv.storageState && (!runtimeEnv.username || !runtimeEnv.password || !runtimeEnv.loginUrl)) {
  252 |     return `${runtimeEnv.application}/${runtimeEnv.environment}/${runtimeEnv.role} role의 storageState 또는 로그인 계정 정보가 필요합니다.`;
  253 |   }
  254 |
  255 |   return undefined;
  256 | }
  257 |
  258 | async function withRolePage(
  259 |   browser: Browser,
  260 |   runtimeEnv: RuntimeQaEnv,
  261 |   callback: (page: Page) => Promise<void>
  262 | ): Promise<void> {
  263 |   const context = await browser.newContext(
  264 |     runtimeEnv.storageState
  265 |       ? { storageState: runtimeEnv.storageState, acceptDownloads: true }
  266 |       : { acceptDownloads: true }
  267 |   );
  268 |   const page = await context.newPage();
  269 |
  270 |   try {
  271 |     await callback(page);
  272 |   } finally {
  273 |     await context.close();
  274 |   }
  275 | }
  276 |
  277 | async function ensureAuthenticated(page: Page, runtimeEnv: RuntimeQaEnv): Promise<void> {
  278 |   const url = buildAdminUrl(runtimeEnv, "/");
  279 |   await openAuthenticatedUrl(page, url.toString(), runtimeEnv, "전산 홈");
  280 | }
  281 |
  282 | async function openAuthenticatedUrl(
  283 |   page: Page,
  284 |   targetUrl: string,
  285 |   runtimeEnv: RuntimeQaEnv,
  286 |   pageName: string
  287 | ): Promise<void> {
  288 |   if (!runtimeEnv.storageState) {
  289 |     await loginWithCredentials(page, runtimeEnv);
  290 |   }
  291 |
  292 |   await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 60_000 });
  293 |   await waitForNavigationToSettle(page);
  294 |
  295 |   if (await isLoginPage(page, runtimeEnv)) {
  296 |     await loginWithCredentials(page, runtimeEnv);
  297 |     await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 60_000 });
  298 |     await waitForNavigationToSettle(page);
  299 |   }
  300 |
  301 |   expect(await isLoginPage(page, runtimeEnv), `${pageName} 업무 화면 진입 전 로그인 화면을 벗어나야 합니다.`).toBe(false);
  302 | }
  303 |
  304 | async function waitForNavigationToSettle(page: Page): Promise<void> {
  305 |   await page.waitForLoadState("domcontentloaded");
  306 |   await page.waitForLoadState("networkidle", { timeout: 5_000 }).catch(() => undefined);
  307 |   await page.waitForTimeout(500);
  308 | }
  309 |
  310 | async function openKpdCreatePage(page: Page, runtimeEnv: RuntimeQaEnv): Promise<KpdTarget> {
  311 |   await ensureAuthenticated(page, runtimeEnv);
  312 |   const target = await resolveKpdTarget(page, runtimeEnv);
  313 |   const url = buildAdminUrl(runtimeEnv, "/business/pg-mid-manage/create");
  314 |   url.searchParams.set("midType", target.midType);
  315 |   await openAuthenticatedUrl(page, url.toString(), runtimeEnv, "PG 한국결제데이터 MID 생성");
  316 |   await expect(page.locator("body"), "PG MID 생성/등록 화면이 표시되어야 합니다.").toContainText(/PG\s*MID\s*관리|PG\s*MID.*(생성|등록)|MID\s*타입/);
  317 |   await selectContract(page, target.contractName);
  318 |   await expect(page.locator("body"), "한국결제데이터 계약명이 선택된 상태로 보여야 합니다.").toContainText(target.contractName);
  319 |   await page.waitForTimeout(800);
  320 |   return target;
  321 | }
  322 |
  323 | async function openKpdUpdatePage(page: Page, runtimeEnv: RuntimeQaEnv): Promise<KpdTarget> {
  324 |   await ensureAuthenticated(page, runtimeEnv);
  325 |   const target = await resolveKpdTarget(page, runtimeEnv);
  326 |   const url = buildAdminUrl(runtimeEnv, `/business/pg-mid-manage/update/${target.midId}`);
```