From 8dea573cf98847beeadea55e6138daa320f65dad Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 3 Sep 2026 11:35:23 -0700 Subject: [PATCH] Extract add-project navigation into reusable helper with UI transition f MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sidebar navigation is transitioning to a new composer-based entry point. Extract openAddProjectDialog() helper to support both legacy and new patterns: - Legacy: direct "Add Project" button click - New: "New workspace" → "Add project" menu flow Updates all test files that open the add project dialog to use the helper, ensuring tests continue working through the UI transition. Also fixes the agents sidebar button selector to target the activity toggle button instead of a radio. --- tests/e2e/activity-agent-pane-isolation.spec.ts | 2 +- tests/e2e/add-project-default-checkout.spec.ts | 6 ++---- tests/e2e/folder-setup-shallow-priority.spec.ts | 11 +++-------- tests/e2e/folder-setup.spec.ts | 11 +++-------- tests/e2e/golden-core-flows.spec.ts | 6 ++---- tests/e2e/helpers/sidebar-navigation.ts | 16 ++++++++++++++++ tests/e2e/helpers/ssh-config-host-picker.ts | 6 ++---- .../multi-client-navigation-isolation.spec.ts | 6 ++---- ...ired-web-add-project-unavailable-host.spec.ts | 6 ++---- tests/e2e/pr11346-selected-runtime-add.spec.ts | 6 ++---- 10 files changed, 35 insertions(+), 41 deletions(-) create mode 100644 tests/e2e/helpers/sidebar-navigation.ts diff --git a/tests/e2e/activity-agent-pane-isolation.spec.ts b/tests/e2e/activity-agent-pane-isolation.spec.ts index 12ac2f68dc6..54152407c0d 100644 --- a/tests/e2e/activity-agent-pane-isolation.spec.ts +++ b/tests/e2e/activity-agent-pane-isolation.spec.ts @@ -32,7 +32,7 @@ type SplitGroupTerminal = { } function agentsSidebarButton(page: Page) { - return page.getByRole('radio', { name: /^Agents$/ }).first() + return page.getByRole('button', { name: /View activity|Turn off activity view/i }).first() } async function seedActivityThread( diff --git a/tests/e2e/add-project-default-checkout.spec.ts b/tests/e2e/add-project-default-checkout.spec.ts index 0077fa6ff34..71c167697fc 100644 --- a/tests/e2e/add-project-default-checkout.spec.ts +++ b/tests/e2e/add-project-default-checkout.spec.ts @@ -4,6 +4,7 @@ import { mkdtemp } from 'node:fs/promises' import os from 'node:os' import path from 'node:path' import { test, expect } from './helpers/orca-app' +import { openAddProjectDialog } from './helpers/sidebar-navigation' import { waitForSessionReady } from './helpers/store' const tempRoots: string[] = [] @@ -86,10 +87,7 @@ test.describe('Add project default checkout', () => { await waitForSessionReady(orcaPage) const fixture = await createCloneFixture() - await orcaPage - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(orcaPage) const addDialog = orcaPage.getByRole('dialog', { name: /Add a project/i }) await expect(addDialog).toBeVisible() await addDialog.getByRole('button', { name: /Clone from URL/i }).click() diff --git a/tests/e2e/folder-setup-shallow-priority.spec.ts b/tests/e2e/folder-setup-shallow-priority.spec.ts index 15bccef63ee..abd0853b153 100644 --- a/tests/e2e/folder-setup-shallow-priority.spec.ts +++ b/tests/e2e/folder-setup-shallow-priority.spec.ts @@ -5,6 +5,7 @@ import os from 'node:os' import path from 'node:path' import type { ElectronApplication, Locator } from '@stablyai/playwright-test' import { test, expect } from './helpers/orca-app' +import { openAddProjectDialog } from './helpers/sidebar-navigation' import { waitForSessionReady } from './helpers/store' const tempRoots: string[] = [] @@ -166,10 +167,7 @@ test('prioritizes shallow sibling repositories in a bounded nested scan', async const fixture = await createShallowPriorityTruncationFixture() await chooseFolderInNativeDialog(electronApp, fixture.parentPath) - await orcaPage - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(orcaPage) const dialog = orcaPage.getByRole('dialog', { name: /Add a project/i }) await expect(dialog).toBeVisible() await dialog.getByRole('button', { name: /Browse folder/i }).click() @@ -256,10 +254,7 @@ test('can stop a nested repo scan and import repositories found so far', async ( }) await chooseFolderInNativeDialog(electronApp, fixture.parentPath) - await orcaPage - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(orcaPage) const dialog = orcaPage.getByRole('dialog', { name: /Add a project/i }) await dialog.getByRole('button', { name: /Browse folder/i }).click() diff --git a/tests/e2e/folder-setup.spec.ts b/tests/e2e/folder-setup.spec.ts index fc0f27824cc..8524b108165 100644 --- a/tests/e2e/folder-setup.spec.ts +++ b/tests/e2e/folder-setup.spec.ts @@ -4,6 +4,7 @@ import { mkdtemp } from 'node:fs/promises' import os from 'node:os' import path from 'node:path' import { test, expect } from './helpers/orca-app' +import { openAddProjectDialog } from './helpers/sidebar-navigation' import { waitForSessionReady } from './helpers/store' import type { ElectronApplication, Locator } from '@stablyai/playwright-test' @@ -122,10 +123,7 @@ test.describe('Folder setup', () => { const fixture = await createNestedRepoFixture() await chooseFolderInNativeDialog(electronApp, fixture.parentPath) - await orcaPage - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(orcaPage) const dialog = orcaPage.getByRole('dialog', { name: /Add a project/i }) await expect(dialog).toBeVisible() await dialog.getByRole('button', { name: /Browse folder/i }).click() @@ -190,10 +188,7 @@ test.describe('Folder setup', () => { const fixture = await createLargeNestedRepoFixture() await chooseFolderInNativeDialog(electronApp, fixture.parentPath) - await orcaPage - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(orcaPage) const dialog = orcaPage.getByRole('dialog', { name: /Add a project/i }) await expect(dialog).toBeVisible() await dialog.getByRole('button', { name: /Browse folder/i }).click() diff --git a/tests/e2e/golden-core-flows.spec.ts b/tests/e2e/golden-core-flows.spec.ts index 96863251081..4ab85d7644d 100644 --- a/tests/e2e/golden-core-flows.spec.ts +++ b/tests/e2e/golden-core-flows.spec.ts @@ -5,6 +5,7 @@ import os from 'node:os' import path from 'node:path' import type { ElectronApplication, Page } from '@stablyai/playwright-test' import { test, expect } from './helpers/orca-app' +import { openAddProjectDialog } from './helpers/sidebar-navigation' import { ensureTerminalVisible, waitForActiveWorktree, waitForSessionReady } from './helpers/store' import { countVisibleTerminalPanes, @@ -211,10 +212,7 @@ async function addProjectFromSidebar( repoPath: string ): Promise { await chooseFolderInNativeDialog(electronApp, repoPath) - await page - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(page) const addDialog = page.getByRole('dialog', { name: /Add a project/i }) await expect(addDialog).toBeVisible() await addDialog.getByRole('button', { name: /Browse folder/i }).click() diff --git a/tests/e2e/helpers/sidebar-navigation.ts b/tests/e2e/helpers/sidebar-navigation.ts new file mode 100644 index 00000000000..7f3965dde2a --- /dev/null +++ b/tests/e2e/helpers/sidebar-navigation.ts @@ -0,0 +1,16 @@ +import { expect, type Page } from '@stablyai/playwright-test' + +/** Open Add Project through the current sidebar/composer entry point. */ +export async function openAddProjectDialog(page: Page): Promise { + const legacyButton = page.getByRole('button', { name: /Add Project/i }).first() + if (await legacyButton.isVisible().catch(() => false)) { + await legacyButton.click() + } else { + await page + .getByRole('button', { name: /New workspace/i }) + .first() + .click() + await page.getByRole('button', { name: 'Add project', exact: true }).click() + } + await expect(page.getByRole('dialog', { name: /Add a project/i })).toBeVisible() +} diff --git a/tests/e2e/helpers/ssh-config-host-picker.ts b/tests/e2e/helpers/ssh-config-host-picker.ts index 482eb9ed84e..59142937359 100644 --- a/tests/e2e/helpers/ssh-config-host-picker.ts +++ b/tests/e2e/helpers/ssh-config-host-picker.ts @@ -7,6 +7,7 @@ import { mkdirSync, writeFileSync } from 'node:fs' import path from 'node:path' import type { ElectronApplication, Locator, Page } from '@stablyai/playwright-test' import { expect } from '@stablyai/playwright-test' +import { openAddProjectDialog } from './sidebar-navigation' export function makeSshConfigHostPrefix(): string { return `e2e-ssh-cfg-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}` @@ -101,10 +102,7 @@ export async function returnToAppShell(page: Page): Promise { /** Add Project → Host → Add remote host → Add SSH host → form dialog. */ export async function openAddSshHostDialog(page: Page): Promise { await returnToAppShell(page) - await page - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(page) const addProjectDialog = page.getByRole('dialog', { name: /Add a project/i }) await expect(addProjectDialog).toBeVisible({ timeout: 10_000 }) diff --git a/tests/e2e/multi-client-navigation-isolation.spec.ts b/tests/e2e/multi-client-navigation-isolation.spec.ts index c8a01eff636..39732835c65 100644 --- a/tests/e2e/multi-client-navigation-isolation.spec.ts +++ b/tests/e2e/multi-client-navigation-isolation.spec.ts @@ -5,6 +5,7 @@ import os from 'node:os' import path from 'node:path' import type { ElectronApplication, Page } from '@stablyai/playwright-test' import { test, expect } from './helpers/orca-app' +import { openAddProjectDialog } from './helpers/sidebar-navigation' import { worktreeRow, worktreeRowSurface } from './worktree-row-locators' type RuntimePairingOffer = { @@ -343,10 +344,7 @@ test('routes Add Project folder browsing through the paired host', async ({ const offer = await createPairingOffer(orcaPage) const client = await openPairedClient(electronApp, offer, visibleWorktreeId) try { - await client - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(client) const addDialog = client.getByRole('dialog', { name: /Add a project/i }) await expect(addDialog).toBeVisible() await expect(addDialog).not.toContainText('Local Mac') diff --git a/tests/e2e/paired-web-add-project-unavailable-host.spec.ts b/tests/e2e/paired-web-add-project-unavailable-host.spec.ts index 771feffdde7..20d569c0c9f 100644 --- a/tests/e2e/paired-web-add-project-unavailable-host.spec.ts +++ b/tests/e2e/paired-web-add-project-unavailable-host.spec.ts @@ -1,5 +1,6 @@ import type { ElectronApplication, Page, TestInfo } from '@stablyai/playwright-test' import { expect, test } from './helpers/orca-app' +import { openAddProjectDialog } from './helpers/sidebar-navigation' import { launchHeadlessPairedRuntimeHost, type HeadlessPairedRuntimeHost @@ -61,10 +62,7 @@ async function assertCreationActionsDisabled(args: { testInfo: TestInfo topology: 'headed' | 'headless' }): Promise { - await args.page - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(args.page) const dialog = args.page.getByRole('dialog', { name: /Add a project/i }) await expect(dialog).toBeVisible() const hostPicker = dialog.getByRole('combobox') diff --git a/tests/e2e/pr11346-selected-runtime-add.spec.ts b/tests/e2e/pr11346-selected-runtime-add.spec.ts index 93e49d4c4f2..8648589f499 100644 --- a/tests/e2e/pr11346-selected-runtime-add.spec.ts +++ b/tests/e2e/pr11346-selected-runtime-add.spec.ts @@ -6,6 +6,7 @@ import type { FolderWorkspace } from '../../src/shared/folder-workspace-types' import type { ProjectGroup } from '../../src/shared/project-group-types' import type { Repo } from '../../src/shared/repo-types' import { expect, test } from './helpers/orca-app' +import { openAddProjectDialog } from './helpers/sidebar-navigation' import { revealPairedClientWindow } from './helpers/paired-client-window-reveal' import { forwardRendererConsole } from './helpers/renderer-console-forwarding' import { @@ -22,10 +23,7 @@ import { } from './pr11346-selected-runtime-identity-oracle' async function selectRuntimeHost(page: Page, runtimeName: string): Promise { - await page - .getByRole('button', { name: /Add Project/i }) - .first() - .click() + await openAddProjectDialog(page) const dialog = page.getByRole('dialog', { name: /Add a project/i }) await expect(dialog).toBeVisible() const hostPicker = dialog.getByRole('combobox')