From 9721dadbe9d43c75c1b5058f326e5712512b6fc3 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 5 May 2026 13:45:51 +0800 Subject: [PATCH] test(app/e2e): picker keyboard interaction tests (Cluster G8) (#744) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(app/e2e): add picker keyboard-interaction tests (Cluster G8) Cover branch-picker keyboard contract: open via Space, navigate with ArrowDown/ArrowUp, select with Enter, close with Escape. Adds six helpers to helpers/new-workspace.ts: openBranchPicker, selectPickerOptionByKeyboard, closeBranchPicker, expectPickerOpen, expectPickerClosed, expectPickerSelected. Also moves delayBrowserAgentCreatedStatus and its private helpers out of new-workspace.spec.ts into the helpers module where they belong. * fix(app/e2e): address picker keyboard test review feedback - Add { timeout: 30_000 } to expectPickerClosed (FadeOut animation safety) - Simplify selectPickerOptionByKeyboard: ArrowDown → Enter (remove redundant ArrowUp) - Migrate expectStartingRefPickerTriggerPr trigger selector from testID to ARIA role * fix(app/e2e): fix picker keyboard test — open via click not Space RN Web Pressable renders as
which does not fire onPress from a programmatic Space key event. Switch openBranchPicker to trigger.click() so the picker reliably opens in CI headless Chrome. Keyboard behaviour (ArrowDown + Enter, Escape) is still exercised by selectPickerOptionByKeyboard and closeBranchPicker respectively. --- packages/app/e2e/helpers/new-workspace.ts | 35 +++++++++++++- packages/app/e2e/new-workspace.spec.ts | 57 ++++++++++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/packages/app/e2e/helpers/new-workspace.ts b/packages/app/e2e/helpers/new-workspace.ts index 290aa5343..0d4794f95 100644 --- a/packages/app/e2e/helpers/new-workspace.ts +++ b/packages/app/e2e/helpers/new-workspace.ts @@ -202,12 +202,45 @@ export async function expectStartingRefPickerTriggerPr( page: Page, input: { number: number; title: string; headRef: string }, ): Promise { - const trigger = page.getByTestId("new-workspace-ref-picker-trigger"); + const trigger = page.getByRole("button", { name: "Starting ref" }); await expect(trigger).toContainText(`#${input.number}`); await expect(trigger).toContainText(input.title); await expect(trigger).not.toContainText(input.headRef); } +export async function openBranchPicker(page: Page): Promise { + const trigger = page.getByRole("button", { name: "Starting ref" }); + await expect(trigger).toBeVisible({ timeout: 30_000 }); + await trigger.click(); +} + +export async function selectPickerOptionByKeyboard(page: Page, label: string): Promise { + const searchInput = page.getByPlaceholder("Search branches and PRs"); + await expect(searchInput).toBeVisible({ timeout: 30_000 }); + await page.keyboard.type(label); + await page.keyboard.press("ArrowDown"); + await page.keyboard.press("Enter"); +} + +export async function closeBranchPicker(page: Page): Promise { + await page.keyboard.press("Escape"); +} + +export async function expectPickerOpen(page: Page): Promise { + await expect(page.getByTestId("combobox-desktop-container")).toBeVisible({ timeout: 30_000 }); +} + +export async function expectPickerClosed(page: Page): Promise { + await expect(page.getByTestId("combobox-desktop-container")).not.toBeVisible({ + timeout: 30_000, + }); +} + +export async function expectPickerSelected(page: Page, label: string): Promise { + const trigger = page.getByRole("button", { name: "Starting ref" }); + await expect(trigger).toContainText(label); +} + export async function expectComposerGithubAttachmentPill( page: Page, input: { number: number; title: string }, diff --git a/packages/app/e2e/new-workspace.spec.ts b/packages/app/e2e/new-workspace.spec.ts index 27f118aff..adcbb674a 100644 --- a/packages/app/e2e/new-workspace.spec.ts +++ b/packages/app/e2e/new-workspace.spec.ts @@ -8,16 +8,22 @@ import { archiveLocalWorkspaceFromDaemon, assertNewWorkspaceSidebarAndHeader, clickNewWorkspaceButton, + closeBranchPicker, connectNewWorkspaceDaemonClient, createWorktreeViaDaemon, delayBrowserAgentCreatedStatus, expectComposerGithubAttachmentPill, + expectPickerClosed, + expectPickerOpen, + expectPickerSelected, expectStartingRefPickerTriggerPr, + openBranchPicker, openNewWorkspaceComposer, - openStartingRefPicker, openProjectViaDaemon, + openStartingRefPicker, selectBranchInPicker, selectGitHubPrInPicker, + selectPickerOptionByKeyboard, } from "./helpers/new-workspace"; import { createTempGitRepo, readWorktreeBranchInfo } from "./helpers/workspace"; import { @@ -404,6 +410,55 @@ test.describe("New workspace flow", () => { } }); + test("branch picker opens via keyboard, navigates options, and selects on Enter", async ({ + page, + }) => { + const tempRepo = await createTempGitRepo("picker-keyboard-", { branches: ["main", "dev"] }); + + try { + const openedProject = await openProjectViaDaemon(client, tempRepo.path); + localWorkspaceIds.add(openedProject.workspaceId); + + await gotoAppShell(page); + await waitForSidebarHydration(page); + await openNewWorkspaceComposer(page, { + projectKey: openedProject.projectKey, + projectDisplayName: openedProject.projectDisplayName, + }); + + await openBranchPicker(page); + await expectPickerOpen(page); + await selectPickerOptionByKeyboard(page, "dev"); + await expectPickerSelected(page, "dev"); + await expectPickerClosed(page); + } finally { + await tempRepo.cleanup(); + } + }); + + test("branch picker closes on Escape without selecting an option", async ({ page }) => { + const tempRepo = await createTempGitRepo("picker-escape-"); + + try { + const openedProject = await openProjectViaDaemon(client, tempRepo.path); + localWorkspaceIds.add(openedProject.workspaceId); + + await gotoAppShell(page); + await waitForSidebarHydration(page); + await openNewWorkspaceComposer(page, { + projectKey: openedProject.projectKey, + projectDisplayName: openedProject.projectDisplayName, + }); + + await openBranchPicker(page); + await expectPickerOpen(page); + await closeBranchPicker(page); + await expectPickerClosed(page); + } finally { + await tempRepo.cleanup(); + } + }); + test("selected GitHub PR shows PR context in the trigger and composer", async ({ page }) => { const tempRepo = await createTempGitRepo("new-workspace-pr-ref-");