mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 16:03:17 +00:00
The scheduled E2E suite has been red for 3+ weeks with ~19 deterministic failures across 9/10 shards. All are test-side issues (stale assertions, CI-timing races, over-strict perf thresholds, and fixture gaps); no product regressions were found. Two small app changes are test-support only: a stable data-testid on the GitHub item detail surface, and honoring prefers-reduced-motion in the sidebar reveal scroll (also an a11y win). Fixes: - github-cli-stall / pr-comments / onboarding: update stale assertions to current UI (inline GitHub detail, removed 'Open' badge #7338, error-state recovery #6473, Host-selector Add Project UI). - source-control / workspace-space-git-status: poll worktrees.list past the 5s detection-scan cache; match git-reported store paths (not realpath'd). - terminal-column-desync / combined-diff: poll to convergence instead of a fixed wait; ignore virtualizer remeasurement in the scroll-jump metric. - terminal-tui-wheel-reports/-drain: space notches past the burst window; reduce dense CDP stream + test.slow to fit the 120s budget. - settings-display-name-ime: commit the IME composition (persist-on-commit since #6238). onboarding: broaden step predicate for auto-skipped steps. - terminal-shortcuts: guard the split before Cmd/Ctrl+W and confirm the 'Stop and Close' dialog. tab-close: drain late startup terminals. - artificial-opencode: tolerate a single scheduler spike in the drift gate. - worktree: resolve create base to the local HEAD branch; assert URL-resolve reuse via the lookup count. Co-authored-by: Orca <help@stably.ai>
146 lines
5.5 KiB
TypeScript
146 lines
5.5 KiB
TypeScript
import { execFileSync } from 'node:child_process'
|
|
import { rmSync, writeFileSync } from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
import { test, expect } from './helpers/orca-app'
|
|
import { waitForSessionReady } from './helpers/store'
|
|
|
|
function createWorktreeWithStagedChange(repoPath: string): {
|
|
branchName: string
|
|
worktreePath: string
|
|
} {
|
|
const branchName = `e2e-ai-commit-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
const worktreePath = path.join(os.tmpdir(), branchName)
|
|
execFileSync('git', ['worktree', 'add', worktreePath, '-b', branchName], {
|
|
cwd: repoPath,
|
|
stdio: 'pipe'
|
|
})
|
|
writeFileSync(
|
|
path.join(worktreePath, 'README.md'),
|
|
'# AI Commit Message E2E\n\nGenerated flow.\n'
|
|
)
|
|
execFileSync('git', ['add', 'README.md'], { cwd: worktreePath, stdio: 'pipe' })
|
|
return { branchName, worktreePath }
|
|
}
|
|
|
|
function cleanupWorktree(repoPath: string, worktreePath: string, branchName: string): void {
|
|
try {
|
|
execFileSync('git', ['worktree', 'remove', '--force', worktreePath], {
|
|
cwd: repoPath,
|
|
stdio: 'pipe'
|
|
})
|
|
} catch {
|
|
rmSync(worktreePath, { recursive: true, force: true })
|
|
}
|
|
try {
|
|
execFileSync('git', ['branch', '-D', branchName], { cwd: repoPath, stdio: 'pipe' })
|
|
} catch {
|
|
// The branch is already gone when git prunes it with the worktree.
|
|
}
|
|
}
|
|
|
|
test.describe('Source Control AI commit messages', () => {
|
|
test('generates a commit message from staged changes through the Source Control UI', async ({
|
|
orcaPage,
|
|
testRepoPath
|
|
}) => {
|
|
const { branchName, worktreePath } = createWorktreeWithStagedChange(testRepoPath)
|
|
const agentCommand =
|
|
'node -e "setTimeout(() => process.stdout.write(\'Add generated E2E message\'), 250)"'
|
|
|
|
try {
|
|
await waitForSessionReady(orcaPage)
|
|
await orcaPage.evaluate(
|
|
async ({ repoPath, worktreePath: targetWorktreePath, agentCommand: command }) => {
|
|
const store = window.__store
|
|
if (!store) {
|
|
throw new Error('window.__store is not available')
|
|
}
|
|
const state = store.getState()
|
|
await state.fetchRepos()
|
|
const repo = store.getState().repos.find((entry) => entry.path === repoPath)
|
|
if (!repo) {
|
|
throw new Error(`Seeded E2E repo was not registered: ${repoPath}`)
|
|
}
|
|
const normalizeMacTmpPath = (value: string): string =>
|
|
value.startsWith('/private/var/') ? value.slice('/private'.length) : value
|
|
|
|
// Why: worktrees:list serves a 5s detection-scan cache that a raw
|
|
// `git worktree add` (done outside Orca) does not invalidate, so the
|
|
// freshly-created worktree can be briefly absent from the boot scan.
|
|
// Poll until the cache refreshes and surfaces it.
|
|
const deadline = Date.now() + 10_000
|
|
let listedWorktrees = await window.api.worktrees.list({ repoId: repo.id })
|
|
let worktree = listedWorktrees.find(
|
|
(entry) => normalizeMacTmpPath(entry.path) === normalizeMacTmpPath(targetWorktreePath)
|
|
)
|
|
while (!worktree && Date.now() < deadline) {
|
|
await new Promise((resolve) => setTimeout(resolve, 250))
|
|
listedWorktrees = await window.api.worktrees.list({ repoId: repo.id })
|
|
worktree = listedWorktrees.find(
|
|
(entry) => normalizeMacTmpPath(entry.path) === normalizeMacTmpPath(targetWorktreePath)
|
|
)
|
|
}
|
|
store.setState((current) => ({
|
|
worktreesByRepo: {
|
|
...current.worktreesByRepo,
|
|
[repo.id]: listedWorktrees
|
|
}
|
|
}))
|
|
if (!worktree) {
|
|
throw new Error(
|
|
`E2E worktree was not loaded: ${targetWorktreePath}; listed=${listedWorktrees
|
|
.map((entry) => entry.path)
|
|
.join(', ')}`
|
|
)
|
|
}
|
|
|
|
store.getState().setActiveWorktree(worktree.id)
|
|
await store.getState().updateSettings({
|
|
commitMessageAi: {
|
|
enabled: true,
|
|
agentId: 'custom',
|
|
selectedModelByAgent: {},
|
|
selectedThinkingByModel: {},
|
|
customPrompt: '',
|
|
customAgentCommand: command
|
|
}
|
|
})
|
|
const status = await window.api.git.status({ worktreePath: worktree.path })
|
|
store.getState().setGitStatus(worktree.id, status)
|
|
store.getState().setRightSidebarTab('source-control')
|
|
store.getState().setRightSidebarOpen(true)
|
|
},
|
|
{ repoPath: testRepoPath, worktreePath, agentCommand }
|
|
)
|
|
|
|
await expect
|
|
.poll(
|
|
async () =>
|
|
orcaPage.evaluate(() => {
|
|
const state = window.__store?.getState()
|
|
return Boolean(state?.rightSidebarOpen && state?.rightSidebarTab === 'source-control')
|
|
}),
|
|
{ timeout: 5_000 }
|
|
)
|
|
.toBe(true)
|
|
|
|
const textarea = orcaPage.getByRole('textbox', { name: 'Commit message' })
|
|
await expect(textarea).toBeVisible({ timeout: 10_000 })
|
|
await expect(textarea).toHaveValue('')
|
|
|
|
const generate = orcaPage.getByRole('button', { name: 'Generate commit message with AI' })
|
|
await expect(generate).toBeVisible()
|
|
await expect(generate).toBeEnabled()
|
|
await generate.click()
|
|
|
|
await expect(
|
|
orcaPage.getByRole('button', { name: 'Stop generating commit message' })
|
|
).toBeVisible()
|
|
await expect(textarea).toHaveValue('Add generated E2E message', { timeout: 10_000 })
|
|
} finally {
|
|
cleanupWorktree(testRepoPath, worktreePath, branchName)
|
|
}
|
|
})
|
|
})
|