# 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:
  - button "Open Next.js Dev Tools" [ref=e6] [cursor=pointer]:
    - img [ref=e7]
  - alert
  - generic:
    - complementary:
      - link:
        - /url: /seller
        - img
      - paragraph: Seller Dashboard
      - generic:
        - text: Store
        - combobox
      - navigation:
        - link:
          - /url: /seller
          - text: Dashboard
        - link:
          - /url: /seller/inbox
          - text: Inbox
        - link:
          - /url: /seller/listings
          - text: Listings
        - link:
          - /url: /seller/orders
          - text: Orders
        - link:
          - /url: /seller/returns
          - text: Returns
        - link:
          - /url: /seller/payouts
          - text: Payouts
        - link:
          - /url: /seller/statements
          - text: Statements
        - link:
          - /url: /seller/reviews
          - text: Reviews
        - link:
          - /url: /seller/team
          - text: Team
        - link:
          - /url: /seller/schedule
          - text: Schedule
        - link:
          - /url: /seller/timesheets
          - text: Timesheets
        - link:
          - /url: /seller/payroll
          - text: Payroll
        - link:
          - /url: /seller/settings/schedule
          - text: Schedule settings
        - link:
          - /url: /seller/settings
          - text: Settings
    - generic:
      - banner:
        - generic: E2E-bb6168a707df86d4 Store A
        - generic:
          - generic:
            - button: 🔔
      - main:
        - main:
          - heading [level=1]: Schedule
          - generic:
            - button: Previous week
            - generic:
              - text: Week containing
              - textbox: 2026-10-05
            - button: Next week
            - button: Reload week
          - button: Add shift
          - paragraph:
            - text: "All dates and times:"
            - strong: America/Los_Angeles
          - generic:
            - button: Copy week
            - button: Publish week
            - link:
              - /url: /seller/settings/schedule
              - text: Schedule settings
          - paragraph: 0 draft shifts · 0 published shifts
          - generic:
            - table:
              - row:
                - columnheader: Employee
                - columnheader: Mon, Oct 5
                - columnheader: Tue, Oct 6
                - columnheader: Wed, Oct 7
                - columnheader: Thu, Oct 8
                - columnheader: Fri, Oct 9
                - columnheader: Sat, Oct 10
                - columnheader: Sun, Oct 11
              - row:
                - rowheader: Open shifts
                - cell
                - cell
                - cell
                - cell
                - cell
                - cell
                - cell
              - row:
                - rowheader: E2E-bb6168a707df86d4 Owner
                - cell
                - cell
                - cell
                - cell
                - cell
                - cell
                - cell
              - row:
                - rowheader: E2E-bb6168a707df86d4 Manager
                - cell
                - cell
                - cell
                - cell
                - cell
                - cell
                - cell
              - row:
                - rowheader: E2E-bb6168a707df86d4 Staff
                - cell
                - cell
                - cell
                - cell
                - cell
                - cell
                - cell
            - paragraph: No shifts this week.
  - dialog "Add shift" [ref=e11]:
    - heading "Add shift" [level=2] [ref=e12]
    - paragraph [ref=e13]: All times use America/Los_Angeles. Saving makes this shift a draft; publish the week to share it. Clock-change gaps and ambiguous times must be avoided.
    - generic [ref=e14]:
      - group [ref=e15]:
        - generic [ref=e16]:
          - text: Employee
          - combobox "Employee" [active] [ref=e17]:
            - option "Open shift" [selected]
            - option "E2E-bb6168a707df86d4 Owner"
            - option "E2E-bb6168a707df86d4 Manager"
            - option "E2E-bb6168a707df86d4 Staff"
        - generic [ref=e18]:
          - text: Position
          - combobox "Position" [ref=e19]:
            - option "No position" [selected]
        - generic [ref=e20]:
          - text: Starts (store time)
          - textbox "Starts (store time)" [ref=e21]: 2026-10-05T09:00
        - generic [ref=e22]:
          - text: Ends (store time)
          - textbox "Ends (store time)" [ref=e23]: 2026-10-05T17:00
        - generic [ref=e24]:
          - text: Notes
          - textbox "Notes" [ref=e25]
      - generic [ref=e26]:
        - button "Save shift" [ref=e27]
        - button "Cancel" [ref=e28]
    - button "Close" [ref=e29]:
      - img [ref=e30]
      - generic [ref=e33]: Close
```

# 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 | 
```