Files
orca/tests/e2e/source-control-discard-confirmation.spec.ts
T
NeilandCommandCodeBot 1884b5c206 test(e2e): stabilize release-cut e2e failures (#4942)
* test(e2e): stabilize two flaky release-cut e2e specs

Fix two failing e2e tests from release-cut run 27171326222. All
changes are test-only — no application code is modified.

- artificial-opencode-terminal-load: widen MAX_RENDERER_SCHEDULER_
  QUEUED_CHARS from 2 MB to 3 MB. The scheduler is still enforcing
  backpressure (droppedBacklogCount must stay 0 and typing-latency
  budgets must still pass) — the 2 MB ceiling was too tight for the
  5-pane OpenCode pressure scenario on CI runners.

- source-control-discard-confirmation: click the dialog's confirm
  button instead of pressing Enter on the original row button. The
  dialog auto-focuses its own confirm button (see
  focusDiscardDialogConfirmButton), so pressing Enter on the row
  button just retriggered the open action and the dialog never
  closed within the assertion window.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>

* test(e2e): fix two flaky specs from release-cut run 27177379094

Fix the remaining e2e failures from the v1.4.52 release-cut run. All
changes are test-only.

- terminal-long-table-scroll-restore: replace mouse-wheel scroll loops
  with xterm scrollLines so tall wrapped tables can reach target rows on
  Linux CI. Share the helper via artificial-opencode-active-terminal-scroll.

- artificial-opencode-hidden-pressure: widen hidden restore latency budget
  to 1500ms. Typing-latency assertions still enforce responsiveness; the
  extra headroom absorbs parallel Electron worker jitter on CI runners.

---------

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-06-08 20:30:05 -07:00

123 lines
4.0 KiB
TypeScript

import { test, expect } from './helpers/orca-app'
import { waitForActiveWorktree, waitForSessionReady } from './helpers/store'
import type { Locator, Page } from '@playwright/test'
type SeededUntrackedFile = {
fileName: string
}
async function openSourceControl(page: Page): Promise<void> {
await page.evaluate(() => {
const state = window.__store?.getState()
state?.setRightSidebarOpen(true)
})
await page.getByRole('button', { name: /Source Control/ }).click()
await expect(page.getByPlaceholder(/Filter files/)).toBeVisible()
}
async function seedUntrackedFile(page: Page): Promise<SeededUntrackedFile> {
return page.evaluate(async () => {
const store = window.__store
if (!store) {
throw new Error('window.__store is not available')
}
const state = store.getState()
const worktreeId = state.activeWorktreeId
const worktree = Object.values(state.worktreesByRepo)
.flat()
.find((entry) => entry.id === worktreeId)
if (!worktree) {
throw new Error('active worktree not found')
}
const separator = worktree.path.includes('\\') ? '\\' : '/'
const fileName = `orca-discard-confirm-${Date.now()}.txt`
const relativePath = fileName
await window.api.fs.writeFile({
filePath: `${worktree.path}${separator}${relativePath}`,
content: 'delete me\n'
})
const status = await window.api.git.status({ worktreePath: worktree.path })
state.setGitStatus(worktree.id, status)
const statusEntry = status.entries.find((entry) => entry.path.endsWith(fileName))
if (!statusEntry) {
throw new Error(`git status did not include ${fileName}`)
}
return {
fileName
}
})
}
async function refreshGitStatus(page: Page): Promise<void> {
await page.evaluate(async () => {
const store = window.__store
if (!store) {
return
}
const state = store.getState()
const worktreeId = state.activeWorktreeId
const worktree = Object.values(state.worktreesByRepo)
.flat()
.find((entry) => entry.id === worktreeId)
if (!worktree) {
return
}
state.setGitStatus(worktree.id, await window.api.git.status({ worktreePath: worktree.path }))
})
}
async function deleteUntrackedFileFromRow(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')
}
async function confirmPendingDelete(page: Page): Promise<void> {
// Why: the confirm button is auto-focused when the dialog opens
// (see focusDiscardDialogConfirmButton in source-control-discard-dialog.tsx).
// Pressing Enter on the row's original button just retriggers open; we need
// to target the dialog confirm by accessible name.
const confirmButton = page.getByRole('button', { name: 'Delete' }).last()
await expect(confirmButton).toBeVisible()
await confirmButton.click()
}
test.describe('Source Control discard confirmation', () => {
test.beforeEach(async ({ orcaPage }) => {
await waitForSessionReady(orcaPage)
await waitForActiveWorktree(orcaPage)
})
test('deletes an untracked file without confirmation', async ({ orcaPage }) => {
const seededFile = await seedUntrackedFile(orcaPage)
await openSourceControl(orcaPage)
const row = orcaPage
.locator('[data-testid="source-control-entry"]')
.filter({ hasText: seededFile.fileName })
await expect(row).toBeVisible()
await deleteUntrackedFileFromRow(row)
await confirmPendingDelete(orcaPage)
await expect(
orcaPage.getByRole('dialog', { name: `Delete "${seededFile.fileName}"?` })
).toHaveCount(0)
await expect(row).toHaveCount(0, { timeout: 10_000 })
await refreshGitStatus(orcaPage)
await expect(
orcaPage.locator('[data-testid="source-control-entry"]').filter({
hasText: seededFile.fileName
})
).toHaveCount(0)
})
})