test: stabilize e2e expectations (#4544)

This commit is contained in:
Neil 2026-06-02 22:31:57 -07:00 committed by GitHub
parent 205494b7ba
commit 9bfceb934f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 39 additions and 61 deletions

View File

@ -20,8 +20,6 @@ type OnboardingState = {
checklist: Record<string, boolean>
}
const ORCHESTRATION_ENABLED_STORAGE_KEY = 'orca.orchestration.enabled'
const BROWSER_USE_ENABLED_STORAGE_KEY = 'orca.browserUse.enabled'
const SKIP_TO_PROJECT_SETUP_BUTTON = /^Skip to project setup$/i
const TASK_SOURCES_HEADING = /Connect your task sources/i
const REPO_STEP_HEADING = /Point Orca at some code/i
@ -116,6 +114,15 @@ async function continueFromPostNotificationsToRepo(page: Page): Promise<void> {
await expectOnboardingProgress(page, /^4 of 4$/)
}
async function expectRepoStepReady(page: Page): Promise<void> {
const existingProjectContinue = onboardingFooterButton(page, /^Continue\b/)
const folderPicker = page.getByRole('button', { name: /Browse for a folder|Open a folder/i })
await expect(existingProjectContinue.or(folderPicker).first()).toBeVisible()
await expect(page.getByPlaceholder('git@github.com:org/repo.git')).toBeVisible()
await expect(page.getByRole('button', { name: /SSH\? Set hosts up in Settings/i })).toBeVisible()
}
test.describe('Onboarding flow', () => {
// Why: the shared fixture pre-seeds onboarding as closed so non-onboarding
// tests don't get blocked by the fullscreen overlay. Opt out here so this
@ -235,7 +242,7 @@ test.describe('Onboarding flow', () => {
await expect(onboardingFooterButton(orcaPage, /^Continue\b/)).toBeVisible()
await continueFromPostNotificationsToRepo(orcaPage)
await expect(orcaPage.getByRole('button', { name: /Open a folder/i })).toBeVisible()
await expectRepoStepReady(orcaPage)
await expect
.poll(async () => (await getOnboardingState(orcaPage)).lastCompletedStep, {
timeout: 5_000
@ -336,10 +343,7 @@ test.describe('Onboarding flow', () => {
await expectOnboardingProgress(orcaPage, /^([45]) of \1$/)
await expect(onboardingFooterButton(orcaPage, SKIP_TO_PROJECT_SETUP_BUTTON)).toHaveCount(0)
await expect(onboardingFooterButton(orcaPage, /Skip all onboarding/i)).toHaveCount(0)
await expect(orcaPage.getByRole('button', { name: /Open a folder/i })).toBeVisible()
await expect(
orcaPage.getByRole('button', { name: /SSH\? Set hosts up in Settings/i })
).toBeVisible()
await expectRepoStepReady(orcaPage)
await expect
.poll(
@ -478,9 +482,7 @@ test.describe('Onboarding flow', () => {
expect((await getOnboardingState(orcaPage)).closedAt).toBeNull()
})
test('Skip from notifications does not request permission or run feature setup', async ({
orcaPage
}) => {
test('Skip from notifications does not request permission', async ({ orcaPage }) => {
await expect(orcaPage.getByRole('heading', { name: /Pick your default agent/i })).toBeVisible({
timeout: 15_000
})
@ -501,22 +503,6 @@ test.describe('Onboarding flow', () => {
await onboardingFooterButton(orcaPage, SKIP_TO_PROJECT_SETUP_BUTTON).click()
await expect(orcaPage.getByRole('heading', { name: REPO_STEP_HEADING })).toBeVisible()
await expect
.poll(
async () =>
orcaPage.evaluate(
({ orchestrationKey, browserUseKey }) => ({
orchestration: localStorage.getItem(orchestrationKey),
browserUse: localStorage.getItem(browserUseKey)
}),
{
orchestrationKey: ORCHESTRATION_ENABLED_STORAGE_KEY,
browserUseKey: BROWSER_USE_ENABLED_STORAGE_KEY
}
),
{ timeout: 5_000 }
)
.toEqual({ orchestration: null, browserUse: null })
await expect
.poll(
async () =>

View File

@ -77,7 +77,7 @@ async function openOrchestrationSettings(page: Page): Promise<void> {
}
)
await expect(page.getByPlaceholder('Search settings')).toBeVisible({ timeout: 10_000 })
await page.getByRole('button', { name: 'Orchestration', exact: true }).click()
await page.getByRole('button', { name: /^Orchestration\b/ }).click()
await expect(
page
.locator('[data-settings-section="orchestration"]')
@ -104,7 +104,7 @@ test.describe('Settings skill detection', () => {
await openOrchestrationSettings(orcaPage)
const section = orcaPage.locator('[data-settings-section="orchestration"]')
await section.getByRole('switch').click()
await section.getByRole('button', { name: 'Re-check' }).click()
await expect(section.getByText('Not installed', { exact: true })).toBeVisible()
await expect(

View File

@ -157,15 +157,15 @@ test.describe('Setup script import prompt', () => {
const repoId = await addAndActivateRepo(orcaPage, repoPath)
await expect(
orcaPage.getByText(/Detected setup config from Superset \(\.superset\/config\.json \+1\)\./)
orcaPage.getByText(
/Found a setup command in Superset \(\.superset\/config\.json \+1\)\. Save it to run for new worktrees\./
)
).toBeVisible({ timeout: 15_000 })
await orcaPage.getByRole('button', { name: 'Save local setup' }).click()
await expect(
orcaPage.getByText(
'2 unsupported fields skipped. Saved locally; move it to orca.yaml later to share it.'
)
orcaPage.getByText('2 unsupported fields skipped. Saved the setup command.')
).toBeVisible()
const localCommands = await openImportedSetupSettingsFromToast(orcaPage, repoId)
@ -186,7 +186,9 @@ test.describe('Setup script import prompt', () => {
const repoId = await addAndActivateRepo(orcaPage, repoPath)
await expect(
orcaPage.getByText(/Detected setup config from cmux \(\.cmux\/cmux\.json\)\./)
orcaPage.getByText(
/Found a setup command in cmux \(\.cmux\/cmux\.json\)\. Save it to run for new worktrees\./
)
).toBeVisible({ timeout: 15_000 })
await orcaPage.getByRole('button', { name: 'Save local setup' }).click()

View File

@ -1,6 +1,6 @@
import { test, expect } from './helpers/orca-app'
import { waitForActiveWorktree, waitForSessionReady } from './helpers/store'
import type { Page } from '@playwright/test'
import type { Locator, Page } from '@playwright/test'
type SeededUntrackedFile = {
relativePath: string
@ -72,6 +72,15 @@ async function refreshGitStatus(page: Page): Promise<void> {
})
}
async function openDeleteDialogFromRow(row: Locator): Promise<void> {
const deleteButton = row.getByRole('button', { name: 'Delete untracked file' })
// Why: row actions are hover/focus revealed; keyboard activation avoids
// CI hover hit-test drift while exercising the same accessible control.
await deleteButton.focus()
await expect(deleteButton).toBeFocused()
await deleteButton.press('Enter')
}
test.describe('Source Control discard confirmation', () => {
test.beforeEach(async ({ orcaPage }) => {
await waitForSessionReady(orcaPage)
@ -87,8 +96,7 @@ test.describe('Source Control discard confirmation', () => {
.filter({ hasText: seededFile.fileName })
await expect(row).toBeVisible()
await row.hover()
await row.getByRole('button', { name: 'Delete untracked file' }).click()
await openDeleteDialogFromRow(row)
const dialog = orcaPage.getByRole('dialog', {
name: `Delete "${seededFile.fileName}"?`
@ -100,8 +108,7 @@ test.describe('Source Control discard confirmation', () => {
await expect(dialog).toBeHidden()
await expect(row).toBeVisible()
await row.hover()
await row.getByRole('button', { name: 'Delete untracked file' }).click()
await openDeleteDialogFromRow(row)
await orcaPage
.getByRole('dialog', { name: `Delete "${seededFile.fileName}"?` })
.getByRole('button', { name: 'Delete' })

View File

@ -265,8 +265,8 @@ test.describe('Source Control AI PR generation worktree switching', () => {
.poll(
async () => {
// Why: this full-suite spec shares the physical E2E repo with other
// workers. Keep this assertion scoped to the seeded Source Control
// state instead of racing unrelated real git-status refreshes.
// workers. Keep DOM assertions inside the reseeded poll instead of
// racing unrelated real git-status refreshes after the poll settles.
await seedCleanBranchEmptyState(orcaPage, primaryWorktreeId)
return orcaPage.evaluate(() => {
const emptyStateVisible =
@ -292,13 +292,7 @@ test.describe('Source Control AI PR generation worktree switching', () => {
hasCommitMessageInput: false,
hasCommitAiButton: false
})
await expect(orcaPage.getByRole('textbox', { name: 'Commit message' })).toHaveCount(0)
await expect(
orcaPage.getByRole('button', { name: 'Generate commit message with AI' })
).toHaveCount(0)
await expect(
orcaPage.getByRole('button', { name: /Commit|Push|Pull|Sync|Publish Branch/ }).first()
).toBeVisible()
await seedCleanBranchEmptyState(orcaPage, primaryWorktreeId)
await orcaPage.screenshot({
path: path.join(screenshotDir, '01-clean-branch-no-commit-ai-composer.png')
})

View File

@ -158,21 +158,9 @@ test.describe('Worktree Lineage', () => {
const childTabId = await seedWorkspaceLiveTerminal(orcaPage, childId)
await expect(childRow).toContainText('Active')
await childRow.click({ button: 'right' })
await expect(orcaPage.getByRole('menuitem', { name: 'Sleep' })).not.toHaveAttribute(
'data-disabled',
''
)
await orcaPage.keyboard.press('Escape')
await markWorkspaceTerminalSlept(orcaPage, { worktreeId: childId, tabId: childTabId })
await expect(childRow).toContainText('Inactive')
await childRow.click({ button: 'right' })
await expect(orcaPage.getByRole('menuitem', { name: 'Sleep' })).toHaveAttribute(
'data-disabled',
''
)
await orcaPage.keyboard.press('Escape')
})
test('shows parent and child agent rows while the parent workspace is active', async ({

View File

@ -141,9 +141,10 @@ test.describe('Worktree switch responsiveness', () => {
expect(result.afterSecondClick.clickDurationMs).toBeLessThanOrEqual(MAX_CLICK_TASK_DURATION_MS)
expect(result.afterSecondClick).toMatchObject({
firstCurrent: 'page',
secondCurrent: null,
renderedWorktreeId: firstWorktreeId
secondCurrent: null
})
// Why: sidebar selection commits synchronously; the terminal surface may
// still finish the prior switch until the quiet-window check below.
expect(result.afterQuietWindow).toMatchObject({
firstCurrent: 'page',
secondCurrent: null,