# 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: accessibility.spec.ts >> fresh installed-VSIX accessibility tree satisfies the interaction contract
- Location: src/e2e-ui/__tests__/accessibility.spec.ts:123:5

# Error details

```
TimeoutError: locator.click: Timeout 30000ms exceeded.
Call log:
  - waiting for locator('iframe.webview[src*="extensionId=commandcode"]').first().contentFrame().locator('iframe#active-frame').contentFrame().locator('.cc-prompt').getByRole('button', { name: /allow options/i })
    - locator resolved to <button type="button" aria-haspopup="menu" aria-expanded="false" title="Allow options" aria-label="Allow options" class="cc-btn cc-btn-primary cc-perm-caret">…</button>
  - attempting click action
    2 × waiting for element to be visible, enabled and stable
      - element is not stable
    - retrying click action
    - waiting 20ms
    2 × waiting for element to be visible, enabled and stable
      - element is not stable
    - retrying click action
      - waiting 100ms
    54 × waiting for element to be visible, enabled and stable
       - element is visible, enabled and stable
       - scrolling into view if needed
       - done scrolling
       - <div class="ccm-picker-backdrop"></div> intercepts pointer events
     - retrying click action
       - waiting 500ms

```

# Page snapshot

```yaml
- application [ref=f1e2]:
  - generic [ref=f1e6]:
    - generic [ref=f1e8]:
      - toolbar [ref=f1e16]:
        - button "Go Back (⌃-)" [disabled] [ref=f1e17]: 
        - button "Go Forward (⌃⇧-)" [disabled] [ref=f1e18]: 
        - toolbar "Agent Status" [ref=f1e19] [cursor=pointer]:
          - generic [ref=f1e20]:
            - button "Open Quick Access" [ref=f1e21]:
              - generic [ref=f1e22]: workspace
            - generic [ref=f1e23]:
              - button [ref=f1e24]:
                - button "Toggle Chat" [ref=f1e25]: 
              - button "More Actions" [ref=f1e29]: 
      - generic [ref=f1e30]:
        - toolbar [ref=f1e34]:
          - button "Open in Agents Window (⇧⌘A)" [ref=f1e35] [cursor=pointer]:
            - generic: Open in Agents
          - button "Sign In" [ref=f1e37] [cursor=pointer]
        - toolbar "Title actions" [ref=f1e43]:
          - button "Customize Layout..." [ref=f1e44] [cursor=pointer]: 
          - button "Toggle Primary Side Bar (⌘B)" [pressed] [ref=f1e45] [cursor=pointer]: 
          - button "Toggle Panel (⌘J)" [ref=f1e46] [cursor=pointer]: 
          - button "Toggle Secondary Side Bar (⌥⌘B)" [pressed] [ref=f1e47] [cursor=pointer]: 
    - generic [ref=f1e53]:
      - generic [ref=f1e55]:
        - tablist "Active View Switcher" [ref=f1e58]:
          - tab "Explorer (⇧⌘E)" [expanded] [selected] [ref=f1e59] [cursor=pointer]:
            - generic "Explorer (⇧⌘E)" [ref=f1e60]: 
          - tab "Search (⇧⌘F)" [ref=f1e61] [cursor=pointer]:
            - generic "Search (⇧⌘F)" [ref=f1e62]: 
          - tab "Source Control (⌃⇧G)" [ref=f1e63] [cursor=pointer]:
            - generic "Source Control (⌃⇧G)" [ref=f1e64]: 
          - tab "Run and Debug (⇧⌘D)" [ref=f1e65] [cursor=pointer]:
            - generic "Run and Debug (⇧⌘D)" [ref=f1e66]: 
          - tab "Extensions (⇧⌘X)" [ref=f1e67] [cursor=pointer]:
            - generic "Extensions (⇧⌘X)" [ref=f1e68]: 
          - tab "Command Code" [ref=f1e69] [cursor=pointer]
        - toolbar "Manage" [ref=f1e73]:
          - button "Accounts" [ref=f1e74] [cursor=pointer]:
            - generic "Accounts" [ref=f1e75]: 
          - button "Manage" [ref=f1e76] [cursor=pointer]:
            - generic "Manage" [ref=f1e77]: 
      - generic [ref=f1e78]:
        - generic [ref=f1e79]:
          - heading "Explorer" [level=2] [ref=f1e81]
          - toolbar "Explorer actions" [ref=f1e85]:
            - button "Views and More Actions..." [ref=f1e88] [cursor=pointer]: 
          - generic:
            - generic:
              - generic:
                - toolbar
        - generic [ref=f1e93]:
          - generic [ref=f1e95]:
            - 'button "Explorer Section: workspace" [expanded] [ref=f1e96] [cursor=pointer]':
              - generic [ref=f1e97]: 
              - text: 
              - 'heading "Explorer Section: workspace" [level=3] [ref=f1e98]': workspace
              - text:    
            - tree "Files Explorer" [ref=f1e101]:
              - treeitem "README.md" [level=1] [ref=f1e103] [cursor=pointer]:
                - generic "/var/folders/1_/1g2yy6dn1cx7lbb4hrp8qqdw0000gn/T/cc-e2e-ZJuSjR/workspace/README.md" [ref=f1e106]:
                  - text: 
                  - generic [ref=f1e107]: README.md
          - button "Outline Section" [ref=f1e112] [cursor=pointer]:
            - generic [ref=f1e113]: 
            - text: 
            - heading "Outline" [level=3] [ref=f1e114]
            - text:  
          - button "Timeline Section" [ref=f1e117] [cursor=pointer]:
            - generic [ref=f1e118]: 
            - text: 
            - heading "Timeline" [level=3] [ref=f1e119]
            - text:    
      - main [ref=f1e128]:
        - generic "Editor Group 1 (empty)" [ref=f1e136]:
          - generic [ref=f1e137]:
            - generic [ref=f1e138]:
              - generic:
                - generic:
                  - toolbar
            - generic [ref=f1e143]:
              - generic [ref=f1e144]:
                - term [ref=f1e145]: Open Chat
                - definition [ref=f1e146]:
                  - generic "Control+Command+I" [ref=f1e147]:
                    - generic [ref=f1e148]: ⌃
                    - generic [ref=f1e149]: ⌘
                    - generic [ref=f1e150]: I
              - generic [ref=f1e151]:
                - term [ref=f1e152]: Show All Commands
                - definition [ref=f1e153]:
                  - generic "Shift+Command+P" [ref=f1e154]:
                    - generic [ref=f1e155]: ⇧
                    - generic [ref=f1e156]: ⌘
                    - generic [ref=f1e157]: P
              - generic [ref=f1e158]:
                - term [ref=f1e159]: Start Debugging
                - definition [ref=f1e160]:
                  - generic "F5" [ref=f1e161]
      - generic [ref=f1e163]:
        - generic [ref=f1e164]:
          - tablist "Active View Switcher" [ref=f1e168]:
            - tab "Chat (⌃⌘I)" [ref=f1e169] [cursor=pointer]:
              - generic "Chat (⌃⌘I)" [ref=f1e170]: Chat
            - tab "Command Code" [expanded] [selected] [ref=f1e171] [cursor=pointer]
          - generic [ref=f1e173]:
            - generic:
              - generic:
                - list "Command Code actions"
          - toolbar [ref=f1e177]:
            - button "Maximize Secondary Side Bar" [ref=f1e178] [cursor=pointer]: 
            - button "Hide Secondary Side Bar (⌥⌘B)" [ref=f1e179] [cursor=pointer]: 
        - generic [ref=f1e186]:
          - text: 
          - document [ref=f1e187]
    - status [ref=f1e189]:
      - generic [ref=f1e190]:
        - generic "remote" [ref=f1e191]:
          - button "remote" [ref=f1e192] [cursor=pointer]:
            - generic [ref=f1e193]: 
        - generic "No Problems" [ref=f1e194]:
          - button "No Problems" [ref=f1e195] [cursor=pointer]:
            - generic [ref=f1e196]: 
            - text: "0"
            - generic [ref=f1e197]: 
            - text: "0"
      - generic [ref=f1e198]:
        - generic "Notifications" [ref=f1e199]:
          - button "Notifications" [ref=f1e200] [cursor=pointer]:
            - generic [ref=f1e201]: 
        - generic "Command Code, Open Command Code" [ref=f1e202]:
          - button "Command Code, Open Command Code" [ref=f1e203] [cursor=pointer]: Command Code
        - generic "Copilot status" [ref=f1e204]:
          - button "Copilot status" [ref=f1e205] [cursor=pointer]:
            - generic [ref=f1e206]: 
  - generic:
    - alert
    - alert
  - iframe [active] [ref=f1e208]:
    - document [ref=f2e1]:
      - iframe [ref=f2e2]:
        - document [ref=f3e1]:
          - generic [ref=f3e3]:
            - generic [ref=f3e4]:
              - generic [ref=f3e5]: New Session
              - generic [ref=f3e10]:
                - button "Sessions" [ref=f3e11] [cursor=pointer]:
                  - generic [ref=f3e12]: 
                - button "Settings" [ref=f3e13] [cursor=pointer]:
                  - generic [ref=f3e14]: 
                - button "New Session" [ref=f3e15] [cursor=pointer]:
                  - generic [ref=f3e16]: 
            - generic [ref=f3e18]:
              - img "Command Code" [ref=f3e19]
              - generic [ref=f3e33]: What should we work on?
            - group "Edit README.md" [ref=f3e34]:
              - status [ref=f3e35]: "Permission needed: Edit README.md"
              - generic [ref=f3e36]:
                - generic [ref=f3e42]:
                  - generic [ref=f3e43]: Edit
                  - generic [ref=f3e44]: README.md
                - button "Dismiss" [ref=f3e47] [cursor=pointer]:
                  - generic [ref=f3e48]: 
              - paragraph [ref=f3e49]: This file will be changed.
              - generic [ref=f3e50]:
                - button "Deny" [ref=f3e51] [cursor=pointer]
                - generic [ref=f3e52]:
                  - button "Allow once" [ref=f3e53] [cursor=pointer]
                  - button "Allow options" [ref=f3e54] [cursor=pointer]
            - generic [ref=f3e57]:
              - status [ref=f3e58]
              - generic [ref=f3e61]:
                - combobox "Message Command Code" [ref=f3e62]
                - generic [ref=f3e63]:
                  - generic [ref=f3e64]:
                    - button "Add context" [ref=f3e65] [cursor=pointer]
                    - 'button "Permissions: Ask for approval (Shift+Tab cycles Ask, Auto-accept edits, Plan, and Full access)" [ref=f3e70] [cursor=pointer]'
                  - generic [ref=f3e74]:
                    - generic [ref=f3e76]:
                      - 'button "Model: Accessibility Model" [ref=f3e77] [cursor=pointer]':
                        - generic [ref=f3e78]: AM
                      - 'button "Reasoning effort: medium" [expanded] [ref=f3e81] [cursor=pointer]'
                    - button "Send" [disabled] [ref=f3e87]
          - listbox "Reasoning effort" [ref=f3e91]:
            - option "Default" [ref=f3e92]
            - option "Low" [ref=f3e97]
            - option "Medium" [selected] [ref=f3e102]:
              - generic [ref=f3e103] [cursor=pointer]:
                - generic [ref=f3e104]: Medium
                - generic [ref=f3e106]: 
            - option "High" [ref=f3e108]
```

# Test source

```ts
  145 | 		'cc-composer-menu-list',
  146 | 	);
  147 | 	await expect(frame().locator('#cc-composer-menu-list')).toHaveAttribute(
  148 | 		'role',
  149 | 		'listbox',
  150 | 	);
  151 | 	const slashMenu = frame().locator('#cc-composer-menu');
  152 | 	await expect(slashMenu).toBeVisible();
  153 | 	await expect(composer).toBeFocused();
  154 | 	await expect(slashMenu).toContainText('model');
  155 | 	// The vendored slash menu is a flat, scored list of command rows (no
  156 | 	// "Commands" group heading), each a button whose name leads with the command.
  157 | 	snapshots.slashMenu = await captureTree('slash-menu', slashMenu, [
  158 | 		'/model',
  159 | 	]);
  160 | 
  161 | 	await composer.fill('@READ');
  162 | 	await expect(composer).toHaveAttribute('aria-expanded', 'true');
  163 | 	await expect(composer).toHaveAttribute(
  164 | 		'aria-controls',
  165 | 		'cc-composer-menu-list',
  166 | 	);
  167 | 	const mentionMenu = frame().locator('#cc-composer-menu');
  168 | 	await expect(mentionMenu).toBeVisible({timeout: 10_000});
  169 | 	await expect(mentionMenu).toContainText('README', {timeout: 10_000});
  170 | 	await expect(composer).toBeFocused();
  171 | 	snapshots.mentionMenu = await captureTree('mention-menu', mentionMenu, [
  172 | 		'README',
  173 | 	]);
  174 | 	await composer.fill('');
  175 | 
  176 | 	// The model picker is the vendored ModelEffortSelector in the toolbar slot:
  177 | 	// its trigger is the "Model: …" button and its menu is the portalled dropdown.
  178 | 	// Its a11y contract is the deliberately-weakened vendored one (a search
  179 | 	// textbox + grouped model/effort buttons, not the old listbox/option rows).
  180 | 	await frame()
  181 | 		.getByRole('button', {name: /^Model:/})
  182 | 		.click();
  183 | 	const modelPicker = frame().getByRole('dialog', {name: 'Models'});
  184 | 	await expect(modelPicker).toBeVisible();
  185 | 	// Assert the picker's STABLE a11y structure (search combobox + grouped Models
  186 | 	// listbox with option rows), not a specific model name: the host-owned picker
  187 | 	// renders the LIVE model catalog, so the real host's list races and supersedes
  188 | 	// the forged "Accessibility Model" option — asserting it would flake.
  189 | 	snapshots.modelPicker = await captureTree('model-picker', modelPicker, [
  190 | 		'combobox "Filter Models"',
  191 | 		'listbox "Models"',
  192 | 		'option "',
  193 | 	]);
  194 | 	await frame()
  195 | 		.getByRole('combobox', {name: 'Filter Models'})
  196 | 		.press('Escape');
  197 | 
  198 | 	await composer.fill('/effort');
  199 | 	await composer.press('Enter');
  200 | 	const effortPicker = frame().getByRole('listbox', {
  201 | 		name: 'Reasoning effort',
  202 | 	});
  203 | 	await expect(effortPicker).toBeVisible();
  204 | 	snapshots.effortPicker = await captureTree('effort-picker', effortPicker, [
  205 | 		'listbox "Reasoning effort"',
  206 | 		'option',
  207 | 	]);
  208 | 	await effortPicker.press('Escape');
  209 | 
  210 | 	await postHostMessage({
  211 | 		type: 'permission',
  212 | 		request: {
  213 | 			requestId: 'accessibility-permission',
  214 | 			kind: 'file',
  215 | 			toolName: 'EDIT',
  216 | 			subject: 'README.md',
  217 | 			choices: [
  218 | 				{label: 'Allow once', value: 'yes'},
  219 | 				{label: 'Allow for this session', value: 'yes-session'},
  220 | 				{label: 'Allow for this project', value: 'yes-project'},
  221 | 				{label: 'Deny', value: 'no'},
  222 | 			],
  223 | 		},
  224 | 	});
  225 | 	const permissionPrompt = frame().locator('.cc-prompt');
  226 | 	await expect(permissionPrompt).toBeVisible();
  227 | 	// The card is now the host-owned forked permission card (role=group named for
  228 | 	// the EXACT action — verb + subject, e.g. "Edit README.md" — with the subject
  229 | 	// in a code element, and Deny / Allow once / the "Allow options" caret). Its
  230 | 	// scoped-grant menu is a portalled menu, not a child of the card, so capture
  231 | 	// the two trees separately.
  232 | 	snapshots.permissionPrompt = await captureTree(
  233 | 		'permission-prompt',
  234 | 		permissionPrompt,
  235 | 		[
  236 | 			'group "Edit README.md"',
  237 | 			'button "Deny"',
  238 | 			'button "Allow once"',
  239 | 			'button "Allow options"',
  240 | 		],
  241 | 	);
  242 | 	const moreAllowOptions = permissionPrompt.getByRole('button', {
  243 | 		name: /allow options/i,
  244 | 	});
> 245 | 	await moreAllowOptions.click();
      |                         ^ TimeoutError: locator.click: Timeout 30000ms exceeded.
  246 | 	// The forked card's scoped-grant menu (.cc-perm-menu, role=menu) has no
  247 | 	// accessible name of its own — it is opened from the "Allow options" caret.
  248 | 	const permissionMenu = frame().locator('.cc-perm-menu');
  249 | 	await expect(permissionMenu).toBeVisible();
  250 | 	snapshots.permissionMenu = await captureTree(
  251 | 		'permission-menu',
  252 | 		permissionMenu,
  253 | 		[
  254 | 			'menuitem "Allow for this session"',
  255 | 			'menuitem "Allow for this project"',
  256 | 		],
  257 | 	);
  258 | 	// Keyboard reaches the scoped grants: the forked card's menu highlights an
  259 | 	// item with `cc-selected`. It opens with the first scope highlighted, and
  260 | 	// ArrowDown moves the highlight to the next — a keyboard user can pick a scope
  261 | 	// without a pointer.
  262 | 	await expect(
  263 | 		permissionMenu.getByRole('menuitem', {name: 'Allow for this session'}),
  264 | 	).toHaveClass(/cc-selected/);
  265 | 	await permissionMenu.press('ArrowDown');
  266 | 	await expect(
  267 | 		permissionMenu.getByRole('menuitem', {name: 'Allow for this project'}),
  268 | 	).toHaveClass(/cc-selected/);
  269 | 
  270 | 	mkdirSync(CAPTURE_DIRECTORY, {recursive: true});
  271 | 	writeFileSync(
  272 | 		join(CAPTURE_DIRECTORY, 'fresh-accessibility-tree.json'),
  273 | 		`${JSON.stringify(snapshots, null, 2)}\n`,
  274 | 	);
  275 | });
  276 | 
```