# 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: 20-schedule-browser.spec.ts >> owner creates assigned/open drafts, publishes, edits, copies and switches tenants using real UI
- Location: e2e-scheduling-real/20-schedule-browser.spec.ts:3:5

# Error details

```
TimeoutError: locator.selectOption: Timeout 20000ms exceeded.
Call log:
  - waiting for getByRole('dialog').getByLabel('Employee', { exact: true })

```

# Page snapshot

```yaml
- generic [active] [ref=e1]:
  - button "Open Next.js Dev Tools" [ref=e7] [cursor=pointer]:
    - img [ref=e8]
  - alert [ref=e11]
  - generic [ref=e12]:
    - complementary [ref=e13]:
      - link "Alqove" [ref=e14] [cursor=pointer]:
        - /url: /seller
        - img "Alqove" [ref=e15]
      - paragraph [ref=e16]: Seller Dashboard
      - generic [ref=e17]:
        - text: Store
        - combobox "Store" [ref=e18]:
          - option "E2E-01f484404ecc4c4d Store A" [selected]
          - option "E2E-01f484404ecc4c4d Store B"
      - navigation [ref=e19]:
        - link "Dashboard" [ref=e20] [cursor=pointer]:
          - /url: /seller
        - link "Inbox" [ref=e21] [cursor=pointer]:
          - /url: /seller/inbox
        - link "Listings" [ref=e22] [cursor=pointer]:
          - /url: /seller/listings
        - link "Orders" [ref=e23] [cursor=pointer]:
          - /url: /seller/orders
        - link "Returns" [ref=e24] [cursor=pointer]:
          - /url: /seller/returns
        - link "Payouts" [ref=e25] [cursor=pointer]:
          - /url: /seller/payouts
        - link "Statements" [ref=e26] [cursor=pointer]:
          - /url: /seller/statements
        - link "Reviews" [ref=e27] [cursor=pointer]:
          - /url: /seller/reviews
        - link "Team" [ref=e28] [cursor=pointer]:
          - /url: /seller/team
        - link "Schedule" [ref=e29] [cursor=pointer]:
          - /url: /seller/schedule
        - link "Timesheets" [ref=e30] [cursor=pointer]:
          - /url: /seller/timesheets
        - link "Payroll" [ref=e31] [cursor=pointer]:
          - /url: /seller/payroll
        - link "Schedule settings" [ref=e32] [cursor=pointer]:
          - /url: /seller/settings/schedule
        - link "Settings" [ref=e33] [cursor=pointer]:
          - /url: /seller/settings
    - generic [ref=e34]:
      - banner [ref=e35]:
        - generic [ref=e36]: E2E-01f484404ecc4c4d Store A
        - button "Notifications" [ref=e39]: 🔔
      - main [ref=e40]:
        - main [ref=e41]:
          - heading "Schedule" [level=1] [ref=e42]
          - generic [ref=e43]:
            - button "Previous week" [ref=e44]
            - generic [ref=e45]:
              - text: Week containing
              - textbox "Week containing" [ref=e46]: 2026-08-31
            - button "Next week" [ref=e47]
            - button "Reload week" [ref=e48]
          - button "Add shift" [ref=e49]
          - paragraph [ref=e50]:
            - text: "All dates and times:"
            - strong [ref=e51]: America/Los_Angeles
          - generic [ref=e52]:
            - button "Copy week" [ref=e53]
            - button "Publish week" [ref=e54]
            - link "Schedule settings" [ref=e55] [cursor=pointer]:
              - /url: /seller/settings/schedule
          - paragraph [ref=e56]: 0 draft shifts · 0 published shifts
          - generic "Weekly schedule scroll area" [ref=e57]:
            - table "Weekly schedule" [ref=e58]:
              - row "Employee Mon, Aug 31 Tue, Sep 1 Wed, Sep 2 Thu, Sep 3 Fri, Sep 4 Sat, Sep 5 Sun, Sep 6" [ref=e59]:
                - columnheader "Employee" [ref=e60]
                - columnheader "Mon, Aug 31" [ref=e61]
                - columnheader "Tue, Sep 1" [ref=e62]
                - columnheader "Wed, Sep 2" [ref=e63]
                - columnheader "Thu, Sep 3" [ref=e64]
                - columnheader "Fri, Sep 4" [ref=e65]
                - columnheader "Sat, Sep 5" [ref=e66]
                - columnheader "Sun, Sep 6" [ref=e67]
              - row "Open shifts Open shifts, 2026-08-31 Open shifts, 2026-09-01 Open shifts, 2026-09-02 Open shifts, 2026-09-03 Open shifts, 2026-09-04 Open shifts, 2026-09-05 Open shifts, 2026-09-06" [ref=e68]:
                - rowheader "Open shifts" [ref=e69]
                - cell "Open shifts, 2026-08-31" [ref=e70]
                - cell "Open shifts, 2026-09-01" [ref=e71]
                - cell "Open shifts, 2026-09-02" [ref=e72]
                - cell "Open shifts, 2026-09-03" [ref=e73]
                - cell "Open shifts, 2026-09-04" [ref=e74]
                - cell "Open shifts, 2026-09-05" [ref=e75]
                - cell "Open shifts, 2026-09-06" [ref=e76]
              - row "E2E-01f484404ecc4c4d Owner E2E-01f484404ecc4c4d Owner, 2026-08-31 E2E-01f484404ecc4c4d Owner, 2026-09-01 E2E-01f484404ecc4c4d Owner, 2026-09-02 E2E-01f484404ecc4c4d Owner, 2026-09-03 E2E-01f484404ecc4c4d Owner, 2026-09-04 E2E-01f484404ecc4c4d Owner, 2026-09-05 E2E-01f484404ecc4c4d Owner, 2026-09-06" [ref=e77]:
                - rowheader "E2E-01f484404ecc4c4d Owner" [ref=e78]
                - cell "E2E-01f484404ecc4c4d Owner, 2026-08-31" [ref=e79]
                - cell "E2E-01f484404ecc4c4d Owner, 2026-09-01" [ref=e80]
                - cell "E2E-01f484404ecc4c4d Owner, 2026-09-02" [ref=e81]
                - cell "E2E-01f484404ecc4c4d Owner, 2026-09-03" [ref=e82]
                - cell "E2E-01f484404ecc4c4d Owner, 2026-09-04" [ref=e83]
                - cell "E2E-01f484404ecc4c4d Owner, 2026-09-05" [ref=e84]
                - cell "E2E-01f484404ecc4c4d Owner, 2026-09-06" [ref=e85]
              - row "E2E-01f484404ecc4c4d Manager E2E-01f484404ecc4c4d Manager, 2026-08-31 E2E-01f484404ecc4c4d Manager, 2026-09-01 E2E-01f484404ecc4c4d Manager, 2026-09-02 E2E-01f484404ecc4c4d Manager, 2026-09-03 E2E-01f484404ecc4c4d Manager, 2026-09-04 E2E-01f484404ecc4c4d Manager, 2026-09-05 E2E-01f484404ecc4c4d Manager, 2026-09-06" [ref=e86]:
                - rowheader "E2E-01f484404ecc4c4d Manager" [ref=e87]
                - cell "E2E-01f484404ecc4c4d Manager, 2026-08-31" [ref=e88]
                - cell "E2E-01f484404ecc4c4d Manager, 2026-09-01" [ref=e89]
                - cell "E2E-01f484404ecc4c4d Manager, 2026-09-02" [ref=e90]
                - cell "E2E-01f484404ecc4c4d Manager, 2026-09-03" [ref=e91]
                - cell "E2E-01f484404ecc4c4d Manager, 2026-09-04" [ref=e92]
                - cell "E2E-01f484404ecc4c4d Manager, 2026-09-05" [ref=e93]
                - cell "E2E-01f484404ecc4c4d Manager, 2026-09-06" [ref=e94]
              - row "E2E-01f484404ecc4c4d Staff E2E-01f484404ecc4c4d Staff, 2026-08-31 E2E-01f484404ecc4c4d Staff, 2026-09-01 E2E-01f484404ecc4c4d Staff, 2026-09-02 E2E-01f484404ecc4c4d Staff, 2026-09-03 E2E-01f484404ecc4c4d Staff, 2026-09-04 E2E-01f484404ecc4c4d Staff, 2026-09-05 E2E-01f484404ecc4c4d Staff, 2026-09-06" [ref=e95]:
                - rowheader "E2E-01f484404ecc4c4d Staff" [ref=e96]
                - cell "E2E-01f484404ecc4c4d Staff, 2026-08-31" [ref=e97]
                - cell "E2E-01f484404ecc4c4d Staff, 2026-09-01" [ref=e98]
                - cell "E2E-01f484404ecc4c4d Staff, 2026-09-02" [ref=e99]
                - cell "E2E-01f484404ecc4c4d Staff, 2026-09-03" [ref=e100]
                - cell "E2E-01f484404ecc4c4d Staff, 2026-09-04" [ref=e101]
                - cell "E2E-01f484404ecc4c4d Staff, 2026-09-05" [ref=e102]
                - cell "E2E-01f484404ecc4c4d Staff, 2026-09-06" [ref=e103]
            - paragraph [ref=e104]: No shifts this week.
```

# Test source

```ts
  1  | import { test, expect, fixture, login, api, dbRows, evidence } from './helpers';
  2  | 
  3  | test('owner creates assigned/open drafts, publishes, edits, copies and switches tenants using real UI', async ({ page, request }) => {
  4  |   const week = '2026-10-05';
  5  |   const assignedNote = fixture.marker + ' assigned';
  6  |   const openNote = fixture.marker + ' open';
  7  |   await login(page, 'owner', '/seller/schedule');
  8  |   await page.getByLabel('Week containing', { exact: true }).fill(week);
  9  |   await expect(page.getByRole('table', { name: /Weekly schedule/i })).toBeVisible();
  10 |   for (const [membership, notes, day] of [[fixture.actors.staff.memberships.a, assignedNote, week], ['', openNote, '2026-10-06']]) {
  11 |     await page.getByRole('button', { name: 'Add shift', exact: true }).click();
  12 |     const dialog = page.getByRole('dialog');
> 13 |     await dialog.getByLabel('Employee', { exact: true }).selectOption(membership);
     |                                                          ^ TimeoutError: locator.selectOption: Timeout 20000ms exceeded.
  14 |     await dialog.getByLabel('Starts (store time)', { exact: true }).fill(`${day}T09:00`);
  15 |     await dialog.getByLabel('Ends (store time)', { exact: true }).fill(`${day}T17:00`);
  16 |     await dialog.getByLabel('Notes', { exact: true }).fill(notes);
  17 |     await dialog.getByRole('button', { name: 'Save shift', exact: true }).click();
  18 |     await expect(dialog).toHaveCount(0);
  19 |   }
  20 |   const drafts = await api(request, 'owner', 'GET', `/schedule/week?week_start=${week}`);
  21 |   expect(drafts.shifts).toHaveLength(2);
  22 |   expect(drafts.shifts.every((s: any) => s.published_at === null)).toBe(true);
  23 |   expect(drafts.shifts.filter((s: any) => s.store_membership_id === null)).toHaveLength(1);
  24 |   const assigned = drafts.shifts.find((s: any) => s.notes === assignedNote);
  25 |   expect(dbRows('shifts').find((s: any) => s.id === assigned.id).published_at).toBeNull();
  26 |   expect((await api(request, 'staff', 'GET', `/my/schedule?week_start=${week}`)).shifts).toHaveLength(0);
  27 |   expect((await api(request, 'staff', 'GET', `/schedule/week?week_start=${week}`)).shifts).toHaveLength(0);
  28 |   await page.getByRole('button', { name: 'Publish week', exact: true }).click();
  29 |   await page.getByRole('button', { name: 'Confirm publish', exact: true }).click();
  30 |   await expect(page.getByRole('dialog')).toHaveCount(0);
  31 |   expect((await api(request, 'staff', 'GET', `/my/schedule?week_start=${week}`)).shifts).toHaveLength(1);
  32 |   expect((await api(request, 'staff', 'GET', `/schedule/week?week_start=${week}`)).shifts).toHaveLength(2);
  33 |   await page.getByRole('button', { name: new RegExp(fixture.actors.staff.name + '.*09:00') }).click();
  34 |   const dialog = page.getByRole('dialog');
  35 |   await dialog.getByLabel('Notes', { exact: true }).fill(assignedNote + ' revised');
  36 |   await dialog.getByRole('button', { name: 'Save shift', exact: true }).click();
  37 |   await expect(dialog).toHaveCount(0);
  38 |   const after = await api(request, 'owner', 'GET', `/schedule/week?week_start=${week}`);
  39 |   expect(after.shifts.find((s: any) => s.id === assigned.id).published_at).toBeNull();
  40 |   expect((await api(request, 'staff', 'GET', `/my/schedule?week_start=${week}`)).shifts).toHaveLength(0);
  41 |   await page.getByRole('button', { name: 'Copy week', exact: true }).click();
  42 |   await dialog.getByLabel('Target week').fill('2026-10-12');
  43 |   const beforeCopy = dbRows('shifts').length;
  44 |   await dialog.getByRole('button', { name: 'Preview copy', exact: true }).click();
  45 |   await expect(dialog.getByRole('region', { name: 'Copy preview' })).toBeVisible();
  46 |   expect(dbRows('shifts')).toHaveLength(beforeCopy);
  47 |   await dialog.getByRole('button', { name: 'Commit copy', exact: true }).click();
  48 |   await expect(dialog).toHaveCount(0);
  49 |   const copy = await api(request, 'owner', 'GET', '/schedule/week?week_start=2026-10-12');
  50 |   expect(copy.shifts).toHaveLength(2);
  51 |   expect(copy.shifts.every((s: any) => s.published_at === null)).toBe(true);
  52 |   await page.getByRole('combobox', { name: 'Store', exact: true }).selectOption(fixture.stores.b.id);
  53 |   await page.getByLabel('Week containing', { exact: true }).fill(week);
  54 |   await expect(page.getByText(openNote, { exact: true })).toHaveCount(0);
  55 |   await expect(page.getByText(assignedNote + ' revised', { exact: true })).toHaveCount(0);
  56 |   expect((await api(request, 'owner', 'GET', `/schedule/week?week_start=${week}`, undefined, 200, 'b')).shifts).toHaveLength(0);
  57 |   await evidence('schedule-browser-draft-publish-edit-copy-switch', { drafts, after, copy, sql: dbRows('shifts') });
  58 | });
  59 | 
  60 | test('staff browser excludes drafts, refreshes published own/team view and switches stores', async ({ page, request }) => {
  61 |   const week = '2026-11-02', notes = fixture.marker + ' staff browser';
  62 |   await api(request, 'owner', 'POST', '/schedule/shifts', { store_membership_id: fixture.actors.staff.memberships.a, starts_at: week + 'T17:00:00Z', ends_at: week + 'T19:00:00Z', notes }, 201);
  63 |   await login(page, 'staff', '/staff/schedule');
  64 |   await page.getByLabel('Week containing', { exact: true }).fill(week);
  65 |   await expect(page.getByText('No published shifts this week.', { exact: true })).toBeVisible();
  66 |   await expect(page.getByText(notes, { exact: true })).toHaveCount(0);
  67 |   await api(request, 'owner', 'POST', '/schedule/publish', { week_start: week });
  68 |   await page.getByRole('button', { name: 'Reload week', exact: true }).click();
  69 |   await expect(page.getByText(notes, { exact: true })).toBeVisible();
  70 |   await page.getByRole('button', { name: 'Team Schedule', exact: true }).click();
  71 |   await expect(page.getByRole('table', { name: 'Weekly schedule', exact: true })).toBeVisible();
  72 |   await expect(page.getByText(notes, { exact: true })).toBeVisible();
  73 |   await page.getByRole('combobox', { name: 'Store', exact: true }).selectOption(fixture.stores.b.id);
  74 |   await page.getByLabel('Week containing', { exact: true }).fill(week);
  75 |   await expect(page.getByText(notes, { exact: true })).toHaveCount(0);
  76 |   await evidence('staff-browser-visibility-switch', { week, notes, store: fixture.stores.b.id });
  77 | });
  78 | 
```