test(app/e2e): picker keyboard interaction tests (Cluster G8) (#744)
* 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 <div role="button"> 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.
This commit is contained in:
@@ -202,12 +202,45 @@ export async function expectStartingRefPickerTriggerPr(
|
|||||||
page: Page,
|
page: Page,
|
||||||
input: { number: number; title: string; headRef: string },
|
input: { number: number; title: string; headRef: string },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
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.number}`);
|
||||||
await expect(trigger).toContainText(input.title);
|
await expect(trigger).toContainText(input.title);
|
||||||
await expect(trigger).not.toContainText(input.headRef);
|
await expect(trigger).not.toContainText(input.headRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function openBranchPicker(page: Page): Promise<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function expectPickerOpen(page: Page): Promise<void> {
|
||||||
|
await expect(page.getByTestId("combobox-desktop-container")).toBeVisible({ timeout: 30_000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function expectPickerClosed(page: Page): Promise<void> {
|
||||||
|
await expect(page.getByTestId("combobox-desktop-container")).not.toBeVisible({
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function expectPickerSelected(page: Page, label: string): Promise<void> {
|
||||||
|
const trigger = page.getByRole("button", { name: "Starting ref" });
|
||||||
|
await expect(trigger).toContainText(label);
|
||||||
|
}
|
||||||
|
|
||||||
export async function expectComposerGithubAttachmentPill(
|
export async function expectComposerGithubAttachmentPill(
|
||||||
page: Page,
|
page: Page,
|
||||||
input: { number: number; title: string },
|
input: { number: number; title: string },
|
||||||
|
|||||||
@@ -8,16 +8,22 @@ import {
|
|||||||
archiveLocalWorkspaceFromDaemon,
|
archiveLocalWorkspaceFromDaemon,
|
||||||
assertNewWorkspaceSidebarAndHeader,
|
assertNewWorkspaceSidebarAndHeader,
|
||||||
clickNewWorkspaceButton,
|
clickNewWorkspaceButton,
|
||||||
|
closeBranchPicker,
|
||||||
connectNewWorkspaceDaemonClient,
|
connectNewWorkspaceDaemonClient,
|
||||||
createWorktreeViaDaemon,
|
createWorktreeViaDaemon,
|
||||||
delayBrowserAgentCreatedStatus,
|
delayBrowserAgentCreatedStatus,
|
||||||
expectComposerGithubAttachmentPill,
|
expectComposerGithubAttachmentPill,
|
||||||
|
expectPickerClosed,
|
||||||
|
expectPickerOpen,
|
||||||
|
expectPickerSelected,
|
||||||
expectStartingRefPickerTriggerPr,
|
expectStartingRefPickerTriggerPr,
|
||||||
|
openBranchPicker,
|
||||||
openNewWorkspaceComposer,
|
openNewWorkspaceComposer,
|
||||||
openStartingRefPicker,
|
|
||||||
openProjectViaDaemon,
|
openProjectViaDaemon,
|
||||||
|
openStartingRefPicker,
|
||||||
selectBranchInPicker,
|
selectBranchInPicker,
|
||||||
selectGitHubPrInPicker,
|
selectGitHubPrInPicker,
|
||||||
|
selectPickerOptionByKeyboard,
|
||||||
} from "./helpers/new-workspace";
|
} from "./helpers/new-workspace";
|
||||||
import { createTempGitRepo, readWorktreeBranchInfo } from "./helpers/workspace";
|
import { createTempGitRepo, readWorktreeBranchInfo } from "./helpers/workspace";
|
||||||
import {
|
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 }) => {
|
test("selected GitHub PR shows PR context in the trigger and composer", async ({ page }) => {
|
||||||
const tempRepo = await createTempGitRepo("new-workspace-pr-ref-");
|
const tempRepo = await createTempGitRepo("new-workspace-pr-ref-");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user