diff --git a/lib/src/stories/RemoteControlSection.stories.tsx b/lib/src/stories/RemoteControlSection.stories.tsx index e77bf686e..a8ccdd988 100644 --- a/lib/src/stories/RemoteControlSection.stories.tsx +++ b/lib/src/stories/RemoteControlSection.stories.tsx @@ -206,6 +206,10 @@ export const SetupPhoneQr: Story = { // The one setup-panel story that settles on the QR's accessible name rather // than on text, so it cannot use {@link setupPanel}. play: async (context) => { + // The QR is a lazily-imported chunk. Fetched cold after the click, it can + // outlast `findByRole`'s 1 s on a loaded WebKit runner; loaded here first, + // the panel's own import resolves from the module cache. + await import('../components/QrCode'); const canvas = within(context.canvasElement); await userEvent.click(await canvas.findByRole('button', { name: 'Set up a phone' })); await canvas.findByRole('img', { name: 'Setup code for this machine' }); diff --git a/lib/src/stories/TerminalPaneHeader.stories.tsx b/lib/src/stories/TerminalPaneHeader.stories.tsx index 64cdf2d54..f748662f4 100644 --- a/lib/src/stories/TerminalPaneHeader.stories.tsx +++ b/lib/src/stories/TerminalPaneHeader.stories.tsx @@ -17,7 +17,7 @@ import { flattenScenario, SCENARIO_SHELL_PROMPT } from '../lib/platform'; import { removeMouseSelectionState, setMouseReporting, setOverride } from '../lib/mouse-selection'; import { addPlainNote, clearAllNotepads } from '../lib/notepad/notepad-store'; import { recordToolDirty, resetToolDirty } from '../lib/tool-dirty-store'; -import { requireElement, settleTerminals, waitForCondition, waitForPrimedState } from './settle-terminals'; +import { requireElement, settleTerminalContext, waitForCondition, waitForPrimedState } from './settle-terminals'; const SESSION_ID = 'tab-story'; @@ -230,8 +230,7 @@ async function openHeaderRightClickDialog() { clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, })); - await requireElement('[data-terminal-context]', 'terminal context'); - await settleTerminals(); + await settleTerminalContext(); } /** diff --git a/lib/src/stories/Wall.stories.tsx b/lib/src/stories/Wall.stories.tsx index 9b3fb5a5c..2aa952020 100644 --- a/lib/src/stories/Wall.stories.tsx +++ b/lib/src/stories/Wall.stories.tsx @@ -6,7 +6,7 @@ import { SCENARIO_LS_OUTPUT, } from '../lib/platform'; import type { ActivityState } from '../lib/terminal-registry'; -import { requireElement, settleTerminals, waitForCondition } from './settle-terminals'; +import { requireElement, settleTerminalContext, settleTerminals, waitForCondition } from './settle-terminals'; const meta: Meta = { title: 'App/Wall', @@ -101,8 +101,7 @@ async function minimizeFirstVisiblePane() { async function openAlertDialog() { const header = await requireElement('[data-pane-header-for]', 'pane header'); header.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true, button: 2 })); - await requireElement('[data-terminal-context]', 'terminal context'); - await settleTerminals(); + await settleTerminalContext(); } export const Default: Story = { @@ -219,11 +218,6 @@ export const TerminalContext: Story = { await settleTerminals(); const header = await requireElement('[data-pane-header-for="context-live"]', 'terminal header'); header.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true, button: 2 })); - await waitForCondition(() => !!document.querySelector('[data-helper-terminal]')); - // Hold until autorun has finished and the helper has painted, so the capture - // is never the "Waiting for shell…" or "Running …" frame on the way there. - await waitForCondition(() => - document.querySelector('[aria-label="Helper terminal status"]')?.textContent?.includes('autoran') ?? false); - await settleTerminals(); + await settleTerminalContext(); }, }; diff --git a/lib/src/stories/settle-terminals.ts b/lib/src/stories/settle-terminals.ts index 6bb1fdf08..731231a3d 100644 --- a/lib/src/stories/settle-terminals.ts +++ b/lib/src/stories/settle-terminals.ts @@ -42,6 +42,24 @@ export async function settleTerminals(opts?: { timeoutMs?: number }): Promise { + await requireElement('[data-terminal-context]', 'terminal context'); + const settled = () => { + const status = document.querySelector('[data-terminal-context] [aria-label$="terminal status"]'); + return !!status && !status.querySelector('.animate-spin'); + }; + await waitForCondition(settled, opts); + if (!settled()) throw new Error('terminal context autorun never finished'); + await settleTerminals(opts); +} + /** * Hold until the preview's primed-state decorator has applied. *