# 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: commerce.spec.ts >> Phase 50/60: authenticated customer completes payment and observes operator shipment
- Location: e2e/specs/a-commerce/commerce.spec.ts:348:1

# Error details

```
Error: expect(received).toEqual(expected) // deep equality

Expected: Any<String>
Received: undefined
```

# Test source

```ts
  1   | import type { APIRequestContext, Browser, Locator, Page, TestInfo } from '@playwright/test';
  2   | 
  3   | import { expect, test } from '@playwright/test';
  4   | 
  5   | const privateMarkers = ['cart', 'address', 'order', 'coupon', 'payment'];
  6   | const addressResourceUrl =
  7   |   /\/commerce-admin\/rest\/resource\/commerce-member(?:%3A|:|%253A)address(?:[/?#]|$)/;
  8   | const addressMinePath = '/api/commerce/member/address/mine';
  9   | const addressActionPath = '/api/commerce/member/address';
  10  | const passportTestActivateCurrentPath = '/api/home/user/passportTest/activateCurrent';
  11  | 
  12  | interface IAddressFixture {
  13  |   addressLine1: string;
  14  |   city: string;
  15  |   recipientName: string;
  16  |   updatedCity: string;
  17  | }
  18  | 
  19  | function collectPageErrors(page: Page) {
  20  |   const errors: Error[] = [];
  21  |   page.on('pageerror', error => {
  22  |     errors.push(error);
  23  |   });
  24  |   return errors;
  25  | }
  26  | 
  27  | function waitForApiResponse(page: Page, method: string, path: string | RegExp) {
  28  |   return page.waitForResponse(response => {
  29  |     const url = new URL(response.url());
  30  |     return (
  31  |       response.request().method() === method &&
  32  |       (typeof path === 'string' ? url.pathname === path : path.test(url.pathname)) &&
  33  |       !response.request().headers()['x-vona-openapi-schema']
  34  |     );
  35  |   });
  36  | }
  37  | 
  38  | function waitForAddressResponse(page: Page, method: string, path: string | RegExp) {
  39  |   return waitForApiResponse(page, method, path);
  40  | }
  41  | 
  42  | function waitForAddressMine(page: Page) {
  43  |   return waitForAddressResponse(page, 'GET', addressMinePath);
  44  | }
  45  | 
  46  | async function getAdminOrderRow(page: Page, orderId: number): Promise<Locator> {
  47  |   const row = page.locator(`tr:has(td:first-child:text-is("${orderId}"))`);
  48  |   await expect(row).toHaveCount(1);
  49  |   return row;
  50  | }
  51  | 
  52  | async function login(
  53  |   page: Page,
  54  |   path: string,
  55  |   username: string,
  56  |   password: string,
  57  |   hydratedSite: string,
  58  | ) {
  59  |   await page.goto(path, { waitUntil: 'load' });
  60  |   await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', hydratedSite);
  61  |   if (!page.url().includes('/login')) {
  62  |     await page.waitForURL(/\/login(?:\?|$)/);
  63  |   }
  64  |   if (page.url().includes('/login')) {
  65  |     const usernameInput = page.getByPlaceholder('Your Username');
  66  |     const passwordInput = page.getByPlaceholder('Your Password');
  67  |     await usernameInput.fill(username);
  68  |     await passwordInput.fill(password);
  69  |     await expect(usernameInput).toHaveValue(username);
  70  |     await expect(passwordInput).toHaveValue(password);
  71  |     await expect(page.getByPlaceholder('Please input captcha')).not.toHaveValue('');
  72  |     const loginResponse = waitForApiResponse(page, 'POST', '/api/home/user/passport/login');
  73  |     await page.getByRole('button', { name: 'Login', exact: true }).click();
  74  |     const loginResponseValue = await loginResponse;
  75  |     if (!loginResponseValue.ok()) {
  76  |       throw new Error(
  77  |         `login failed: ${loginResponseValue.status()} ${await loginResponseValue.text()}`,
  78  |       );
  79  |     }
  80  |     await expect(page).not.toHaveURL(/\/login(?:\?|$)/);
  81  |   }
  82  | }
  83  | 
  84  | async function registerCustomer(
  85  |   request: APIRequestContext,
  86  |   testInfo: TestInfo,
  87  | ): Promise<{ password: string; username: string }> {
  88  |   const id = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
  89  |   const username = `e2e-address-${id}`;
  90  |   const password = 'address-e2e-password';
  91  |   const captchaResponse = await request.post('/api/captcha/create', {
  92  |     data: { scene: 'captcha-simple:simple' },
  93  |   });
  94  |   expect(captchaResponse.ok()).toBeTruthy();
  95  |   const captcha = (await captchaResponse.json()).data;
  96  |   expect(captcha?.id).toEqual(expect.any(String));
> 97  |   expect(captcha?.token).toEqual(expect.any(String));
      |                          ^ Error: expect(received).toEqual(expected) // deep equality
  98  | 
  99  |   const registerResponse = await request.post('/api/home/user/passport/register', {
  100 |     data: {
  101 |       username,
  102 |       email: `${username}@example.test`,
  103 |       password,
  104 |       passwordConfirm: password,
  105 |       captcha: { id: captcha.id, token: captcha.token },
  106 |     },
  107 |   });
  108 |   expect(registerResponse.ok()).toBeTruthy();
  109 |   const registration = (await registerResponse.json()).data;
  110 |   expect(registration?.jwt?.accessToken).toEqual(expect.any(String));
  111 |   const activateResponse = await request.post(passportTestActivateCurrentPath, {
  112 |     headers: { Authorization: `Bearer ${registration.jwt.accessToken}` },
  113 |   });
  114 |   expect(activateResponse.ok()).toBeTruthy();
  115 |   return { username, password };
  116 | }
  117 | 
  118 | function createAddressFixture(testInfo: TestInfo): IAddressFixture {
  119 |   const id = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
  120 |   return {
  121 |     recipientName: `E2E Address ${id}`,
  122 |     addressLine1: `1 Test Street ${id}`,
  123 |     city: `Original City ${id}`,
  124 |     updatedCity: `Updated City ${id}`,
  125 |   };
  126 | }
  127 | 
  128 | async function fillAddressForm(page: Page, fixture: IAddressFixture) {
  129 |   await page.getByPlaceholder('Recipient name').fill(fixture.recipientName);
  130 |   await page.getByPlaceholder('Phone').fill('15555550123');
  131 |   await page.getByPlaceholder('Country code').fill('US');
  132 |   await page.getByPlaceholder('Region').fill('California');
  133 |   await page.getByPlaceholder('City').fill(fixture.city);
  134 |   await page.getByPlaceholder('Postal code').fill('94105');
  135 |   await page.getByPlaceholder('Address line 1').fill(fixture.addressLine1);
  136 |   await page.getByPlaceholder('Address line 2').fill('Suite E2E');
  137 | }
  138 | 
  139 | async function createAddressThroughCustomerPage(
  140 |   browser: Browser,
  141 |   request: APIRequestContext,
  142 |   testInfo: TestInfo,
  143 | ) {
  144 |   const customer = await registerCustomer(request, testInfo);
  145 |   const fixture = createAddressFixture(testInfo);
  146 |   const context = await browser.newContext();
  147 |   const page = await context.newPage();
  148 |   const pageErrors = collectPageErrors(page);
  149 | 
  150 |   await login(page, '/commerce/address', customer.username, customer.password, 'commerce');
  151 |   await expect(page).toHaveURL(/\/commerce\/address(?:\?|$)/);
  152 |   await expect(page.getByRole('heading', { name: 'Addresses' })).toBeVisible();
  153 |   await expect(page.getByText('No addresses yet.')).toBeVisible();
  154 | 
  155 |   await fillAddressForm(page, fixture);
  156 |   const createResponse = waitForAddressResponse(page, 'POST', `${addressActionPath}/createMine`);
  157 |   const mineResponse = waitForAddressMine(page);
  158 |   await page.getByRole('button', { name: 'Save address', exact: true }).click();
  159 |   await createResponse;
  160 |   await mineResponse;
  161 |   await expect(page.getByRole('heading', { name: fixture.recipientName })).toBeVisible();
  162 | 
  163 |   return { context, fixture, page, pageErrors };
  164 | }
  165 | 
  166 | async function deleteAddressThroughCustomerPage(page: Page, fixture: IAddressFixture) {
  167 |   const card = page
  168 |     .locator('article')
  169 |     .filter({ has: page.getByRole('heading', { name: fixture.recipientName }) });
  170 |   const deleteResponse = waitForAddressResponse(page, 'DELETE', /\/deleteMine\/[^/]+$/);
  171 |   const mineResponse = waitForAddressMine(page);
  172 |   await card.getByRole('button', { name: 'Delete', exact: true }).click();
  173 |   await deleteResponse;
  174 |   await mineResponse;
  175 |   await expect(page.getByRole('heading', { name: fixture.recipientName })).toHaveCount(0);
  176 |   await expect(page.getByText('No addresses yet.')).toBeVisible();
  177 | }
  178 | 
  179 | test(
  180 |   'ATP-SSR-01: anonymous Commerce HTML is public and hydration completes',
  181 |   { tag: ['@web', '@smoke'] },
  182 |   async ({ page, request }) => {
  183 |     const response = await request.get('/commerce');
  184 |     expect(response.ok()).toBeTruthy();
  185 |     const html = await response.text();
  186 |     const normalizedHtml = html.toLowerCase();
  187 |     expect(normalizedHtml).not.toContain('data-zova-hydrated');
  188 | 
  189 |     for (const marker of privateMarkers) {
  190 |       expect(normalizedHtml).not.toContain(`"${marker}"`);
  191 |     }
  192 | 
  193 |     const pageErrors = collectPageErrors(page);
  194 |     const documentResponse = await page.goto('/commerce', { waitUntil: 'load' });
  195 |     expect(documentResponse?.ok()).toBeTruthy();
  196 |     await expect(page).toHaveURL(/\/commerce(?:\/|$)/);
  197 |     await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'commerce');
```