|
| 1 | +/** |
| 2 | + * Feature assertion: the keyboard shortcut that opens the composer's thinking |
| 3 | + * (reasoning effort) menu. |
| 4 | + * |
| 5 | + * Mirrors Claude Code Desktop's effort-menu shortcut (⌘⇧E). Drives the real |
| 6 | + * built app over CDP: |
| 7 | + * composer renders a thinking-level trigger with its menu CLOSED → dispatch |
| 8 | + * the Cmd/Ctrl+Shift+E keydown at the window level → the thinking menu OPENS |
| 9 | + * (lists all six levels). |
| 10 | + * |
| 11 | + * Asserting the menu is closed *before* the keypress and open *after* it proves |
| 12 | + * the shortcut actually opens the menu — not merely that the menu can render. |
| 13 | + */ |
| 14 | + |
| 15 | +import type { Assertion } from '../runner'; |
| 16 | + |
| 17 | +const TRIGGER = '[data-testid="thinking-level-trigger"]'; |
| 18 | +const ITEM = '[data-testid="thinking-level-item"]'; |
| 19 | + |
| 20 | +/** The six thinking levels are the single source of truth for the count. */ |
| 21 | +const EXPECTED_LEVEL_COUNT = 6; |
| 22 | + |
| 23 | +/** Menu items that are actually visible (Radix renders them in a portal). */ |
| 24 | +const VISIBLE_ITEMS_EXPR = `[...document.querySelectorAll(${JSON.stringify( |
| 25 | + ITEM, |
| 26 | +)})].filter((el) => el.offsetParent !== null)`; |
| 27 | + |
| 28 | +/** |
| 29 | + * Dispatch the effort-menu shortcut at the window level, where the action |
| 30 | + * registry's capture-phase keydown listener lives. `mod` resolves to Cmd on |
| 31 | + * macOS and Ctrl elsewhere; set both `metaKey` and `ctrlKey` so the same event |
| 32 | + * matches on either platform (the registry only checks the platform's mod key). |
| 33 | + */ |
| 34 | +const PRESS_SHORTCUT_EXPR = `(() => { |
| 35 | + window.dispatchEvent(new KeyboardEvent('keydown', { |
| 36 | + key: 'e', |
| 37 | + code: 'KeyE', |
| 38 | + ctrlKey: true, |
| 39 | + metaKey: true, |
| 40 | + shiftKey: true, |
| 41 | + bubbles: true, |
| 42 | + cancelable: true, |
| 43 | + })); |
| 44 | + return true; |
| 45 | +})()`; |
| 46 | + |
| 47 | +const assertion: Assertion = { |
| 48 | + name: 'Cmd/Ctrl+Shift+E opens the composer thinking-level menu', |
| 49 | + async run(app) { |
| 50 | + const { session } = app; |
| 51 | + |
| 52 | + // App fully mounted. |
| 53 | + await session.waitForFunction( |
| 54 | + '!document.getElementById("_loader") && (document.getElementById("root")?.childElementCount ?? 0) > 0', |
| 55 | + { timeoutMs: 30000, message: 'React UI did not mount' }, |
| 56 | + ); |
| 57 | + |
| 58 | + // Reach the ready AppShell (not onboarding / workspace picker) — the same |
| 59 | + // stable, non-localized anchor the other composer assertions wait on. |
| 60 | + await session.waitForSelector('[aria-label="Craft menu"]', { |
| 61 | + timeoutMs: 30000, |
| 62 | + message: 'app did not reach the ready AppShell state', |
| 63 | + }); |
| 64 | + |
| 65 | + // The composer renders a thinking-level trigger. |
| 66 | + await session.waitForSelector(TRIGGER, { |
| 67 | + timeoutMs: 20000, |
| 68 | + message: 'thinking-level picker trigger did not render in the composer', |
| 69 | + }); |
| 70 | + |
| 71 | + // Precondition: the menu is closed (no visible items). |
| 72 | + if ((await session.evaluate<number>(`${VISIBLE_ITEMS_EXPR}.length`)) !== 0) { |
| 73 | + throw new Error('thinking-level menu was already open before pressing the shortcut'); |
| 74 | + } |
| 75 | + |
| 76 | + // Press the shortcut at the window level. |
| 77 | + if (!(await session.evaluate<boolean>(PRESS_SHORTCUT_EXPR))) { |
| 78 | + throw new Error('failed to dispatch the Cmd/Ctrl+Shift+E keydown'); |
| 79 | + } |
| 80 | + |
| 81 | + // The menu opens and lists all six thinking levels — proving the shortcut |
| 82 | + // opened it. |
| 83 | + await session.waitForFunction( |
| 84 | + `${VISIBLE_ITEMS_EXPR}.length === ${EXPECTED_LEVEL_COUNT}`, |
| 85 | + { |
| 86 | + timeoutMs: 8000, |
| 87 | + message: `thinking-level menu did not open (expected ${EXPECTED_LEVEL_COUNT} levels) after pressing Cmd/Ctrl+Shift+E`, |
| 88 | + }, |
| 89 | + ); |
| 90 | + }, |
| 91 | +}; |
| 92 | + |
| 93 | +export default assertion; |
0 commit comments