test: add golden E2E tests for source control workflows (#14260)

* test: add golden E2E tests for source control workflows

- Tests core source control interactions: file edit/save, commit staging, and diff viewing
- Integrated into CI/CD pipelines for Linux, macOS, and Windows
- Includes helper utilities for test setup and worktree management

* test(e2e): verify golden commit author and fix test flakiness

- Configure git author name/email at worktree level during setup
- Verify commits are made with correct author details in assertions
- Add explicit timeouts to file visibility waits and git status polling
- Fix test ordering to seed edits after source control is open
- Simplify git status refresh logic to rely on automatic updates

* Add rollback to createGoldenWorktree on setup failure

Cleanup callbacks only register after setup succeeds. When a config
command fails, the half-built worktree and branch leak into later
test runs, causing flakiness. Now we roll back immediately and
re-throw the setup error.

* test(e2e): match explorer rows after the git status badge appears

The golden file-save spec used an exact /^README.md$/ filter. After save,
the explorer row text becomes "README.md M", so reopen clicked nothing.

* test: strengthen golden worktree setup verification

- Track working directory in git call inspection to verify correct execution context
- Verify user.name/email config applies to worktree-specific settings, not repo
- Add exhaustive setup call sequence assertions to catch setup/rollback leaks
This commit is contained in:
Jinjing
2026-08-13 09:56:30 -07:00
committed by GitHub
parent e7b85266f5
commit e84fb46eaf
8 changed files with 500 additions and 0 deletions
@@ -72,6 +72,7 @@ jobs:
fi
xvfb-run --auto-servernum env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run test:e2e:terminal-rendering-golden
xvfb-run --auto-servernum env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run --if-present test:e2e:posix-profile-index-golden
xvfb-run --auto-servernum env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run --if-present test:e2e:source-control-golden
- name: Run golden E2E tests on macOS
if: runner.os == 'macOS'
@@ -82,6 +83,7 @@ jobs:
fi
env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run test:e2e:terminal-rendering-golden
env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run --if-present test:e2e:posix-profile-index-golden
env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run --if-present test:e2e:source-control-golden
- name: Run golden E2E tests on Windows
if: runner.os == 'Windows'
@@ -93,6 +95,7 @@ jobs:
if (Test-Path tests/e2e/golden-fresh-profile-terminal.spec.ts) {
pnpm run test:e2e -- tests/e2e/golden-fresh-profile-terminal.spec.ts tests/e2e/golden-shell-command.spec.ts
}
pnpm run --if-present test:e2e:source-control-golden
- name: Upload Playwright traces
if: failure()
+9
View File
@@ -879,12 +879,20 @@ jobs:
xvfb-run --auto-servernum env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run test:e2e:terminal-rendering-golden
xvfb-run --auto-servernum env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run --if-present test:e2e:posix-profile-index-golden
- name: Run source-control golden on Linux
if: runner.os == 'Linux'
run: xvfb-run --auto-servernum env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run --if-present test:e2e:source-control-golden
- name: Run terminal rendering golden on macOS
if: runner.os == 'macOS'
run: |
env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run test:e2e:terminal-rendering-golden
env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run --if-present test:e2e:posix-profile-index-golden
- name: Run source-control golden on macOS
if: runner.os == 'macOS'
run: env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run --if-present test:e2e:source-control-golden
- name: Run fresh-startup golden on Windows
if: runner.os == 'Windows'
shell: pwsh
@@ -892,6 +900,7 @@ jobs:
$env:SKIP_BUILD = '1'
$env:ORCA_E2E_FORWARD_APP_LOGS = '1'
pnpm run --if-present test:e2e:windows-fresh-startup-golden
pnpm run --if-present test:e2e:source-control-golden
- name: Upload Playwright traces
if: failure()
+1
View File
@@ -88,6 +88,7 @@
"test:e2e:multi-client-navigation": "node config/scripts/run-multi-client-navigation-e2e.mjs",
"test:e2e:floating-mobile-emulator": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/floating-mobile-emulator-tab.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1",
"test:e2e:terminal-rendering-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/terminal-raw-emoji-table-scroll-restore.spec.ts tests/e2e/terminal-webgl-atlas-budget.spec.ts --grep @terminal-rendering-golden --config tests/playwright.config.ts --project electron-headless --workers=1",
"test:e2e:source-control-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/golden-file-open-edit-save.spec.ts tests/e2e/golden-source-control-commit.spec.ts tests/e2e/golden-source-control-open-diff.spec.ts --grep @golden --config tests/playwright.config.ts --project electron-headless --workers=1",
"test:e2e:posix-profile-index-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/golden-posix-fresh-startup.spec.ts tests/e2e/golden-posix-profile-index-fsync.spec.ts --grep @posix-profile-index-golden --config tests/playwright.config.ts --project electron-headless --workers=1",
"test:e2e:windows-fresh-startup-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/golden-windows-fresh-startup.spec.ts --grep @windows-fresh-startup-golden --config tests/playwright.config.ts --project electron-headless --workers=1",
"test:e2e:terminal-rendering-release-evidence": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/terminal-opencode-emoji-table-rendering.spec.ts tests/e2e/terminal-long-table-scroll-restore.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=2",
@@ -0,0 +1,68 @@
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { test, expect } from './helpers/orca-app'
import {
activateGoldenWorktree,
cleanupGoldenWorktree,
createGoldenWorktree
} from './helpers/golden-source-control'
import { waitForSessionReady } from './helpers/store'
const README_PATH = 'README.md'
test('@golden opens, edits, saves, and reopens a tracked file', async ({
orcaPage,
testRepoPath,
registerPostElectronShutdownCleanup
}) => {
const fixture = createGoldenWorktree(testRepoPath, 'file-save')
registerPostElectronShutdownCleanup(async () => cleanupGoldenWorktree(testRepoPath, fixture))
const sentinel = `Golden file save ${Date.now()}`
const readmePath = path.join(fixture.worktreePath, README_PATH)
await waitForSessionReady(orcaPage)
await activateGoldenWorktree(orcaPage, testRepoPath, fixture.worktreePath)
await orcaPage.evaluate(() => {
const state = window.__store?.getState()
state?.setRightSidebarTab('source-control')
state?.setRightSidebarOpen(true)
})
await orcaPage.getByRole('button', { name: 'Explorer' }).click()
const explorer = orcaPage.locator('[data-orca-explorer-shell]')
// Why: after save the row's full text is "README.md M" from the git badge.
const readmeRow = explorer.locator('[data-file-explorer-row]').filter({
has: orcaPage.locator('[data-file-explorer-row-name]').getByText(README_PATH, { exact: true })
})
await expect(readmeRow).toBeVisible({ timeout: 10_000 })
await readmeRow.click()
await expect(orcaPage.locator('.editor-header-path').first()).toContainText(README_PATH, {
timeout: 20_000
})
const editor = orcaPage.locator('.rich-markdown-editor')
await expect(editor).toBeVisible({ timeout: 25_000 })
await expect(editor).toContainText('Orca E2E Test Repo')
await editor.click()
await orcaPage.keyboard.press('ControlOrMeta+End')
await orcaPage.keyboard.press('Enter')
await orcaPage.keyboard.type(sentinel)
await orcaPage.keyboard.press('ControlOrMeta+S')
await expect.poll(() => readFileSync(readmePath, 'utf8'), { timeout: 10_000 }).toContain(sentinel)
const readmeTab = orcaPage.locator('[data-tab-id]').filter({ hasText: README_PATH }).last()
await readmeTab.getByRole('button', { name: 'Close tab' }).click()
await expect(
orcaPage.locator('.editor-header-path').filter({ hasText: README_PATH })
).toHaveCount(0)
await readmeRow.click()
await expect(orcaPage.locator('.rich-markdown-editor')).toContainText(sentinel, {
timeout: 25_000
})
await expect(explorer).toBeVisible()
await expect(readmeRow).toBeVisible()
await expect(
orcaPage.getByText(path.basename(testRepoPath), { exact: true }).first()
).toBeVisible()
})
@@ -0,0 +1,67 @@
import { execFileSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import path from 'node:path'
import { test, expect } from './helpers/orca-app'
import {
cleanupGoldenWorktree,
createGoldenWorktree,
GOLDEN_CHANGED_PATH,
GOLDEN_GIT_AUTHOR_EMAIL,
GOLDEN_GIT_AUTHOR_NAME,
installPassingNodePreCommitHook,
openGoldenSourceControl,
seedGoldenSourceEdit
} from './helpers/golden-source-control'
import { waitForSessionReady } from './helpers/store'
test('@golden stages and commits a file through Source Control', async ({
orcaPage,
testRepoPath,
registerPostElectronShutdownCleanup
}) => {
const fixture = createGoldenWorktree(testRepoPath, 'commit')
registerPostElectronShutdownCleanup(async () => cleanupGoldenWorktree(testRepoPath, fixture))
seedGoldenSourceEdit(fixture.worktreePath)
const hookMarkerPath = installPassingNodePreCommitHook(fixture)
await waitForSessionReady(orcaPage)
await openGoldenSourceControl(orcaPage, testRepoPath, fixture)
const unstagedRow = orcaPage
.locator('[data-testid="source-control-entry"][data-source-control-area="unstaged"]')
.filter({ hasText: path.basename(GOLDEN_CHANGED_PATH) })
await expect(unstagedRow).toBeVisible()
const stageButton = unstagedRow.getByRole('button', { name: 'Stage' })
await stageButton.focus()
await stageButton.press('Enter')
await expect(unstagedRow).toHaveCount(0, { timeout: 10_000 })
const stagedRow = orcaPage
.locator('[data-testid="source-control-entry"][data-source-control-area="staged"]')
.filter({ hasText: path.basename(GOLDEN_CHANGED_PATH) })
await expect(stagedRow).toBeVisible({ timeout: 10_000 })
await orcaPage.getByRole('textbox', { name: 'Commit message' }).fill('test: golden daily loop')
await orcaPage.getByRole('button', { name: 'Commit', exact: true }).click()
await expect(stagedRow).toHaveCount(0, { timeout: 20_000 })
await expect
.poll(
() =>
execFileSync('git', ['status', '--porcelain'], {
cwd: fixture.worktreePath,
encoding: 'utf8'
}),
{ timeout: 20_000, message: 'Golden commit worktree did not become clean' }
)
.toBe('')
expect(
execFileSync('git', ['show', '-s', '--format=%an%n%ae%n%s', 'HEAD'], {
cwd: fixture.worktreePath,
encoding: 'utf8'
}).trim()
).toBe(`${GOLDEN_GIT_AUTHOR_NAME}\n${GOLDEN_GIT_AUTHOR_EMAIL}\ntest: golden daily loop`)
await expect.poll(() => existsSync(hookMarkerPath), { timeout: 20_000 }).toBe(true)
await expect(
orcaPage.locator('[data-sonner-toast]').filter({ hasText: /node|command not found|cmd\.exe/i })
).toHaveCount(0)
})
@@ -0,0 +1,52 @@
import { realpathSync } from 'node:fs'
import path from 'node:path'
import { test, expect } from './helpers/orca-app'
import {
cleanupGoldenWorktree,
createGoldenWorktree,
GOLDEN_ADDED_LINE,
GOLDEN_CHANGED_PATH,
GOLDEN_REMOVED_LINE,
openGoldenSourceControl,
seedGoldenSourceEdit
} from './helpers/golden-source-control'
import { waitForSessionReady } from './helpers/store'
test('@golden opens an unstaged file diff from Source Control', async ({
orcaPage,
testRepoPath,
registerPostElectronShutdownCleanup
}) => {
const fixture = createGoldenWorktree(testRepoPath, 'open-diff')
registerPostElectronShutdownCleanup(async () => cleanupGoldenWorktree(testRepoPath, fixture))
await waitForSessionReady(orcaPage)
await openGoldenSourceControl(orcaPage, testRepoPath, fixture)
seedGoldenSourceEdit(fixture.worktreePath)
const changedFile = orcaPage
.locator('[data-testid="source-control-entry"]')
.filter({ hasText: path.basename(GOLDEN_CHANGED_PATH) })
await expect(changedFile).toBeVisible({ timeout: 15_000 })
await changedFile.click()
await expect(orcaPage.locator('.monaco-diff-editor')).toBeVisible({ timeout: 20_000 })
await expect(
orcaPage
.locator('.original-in-monaco-diff-editor .view-line')
.filter({ hasText: GOLDEN_REMOVED_LINE })
).toBeVisible()
await expect(
orcaPage
.locator('.modified-in-monaco-diff-editor .view-line')
.filter({ hasText: GOLDEN_ADDED_LINE })
).toBeVisible()
await expect(orcaPage.locator('.editor-header-path').first()).toHaveAttribute(
'title',
`${realpathSync(path.join(fixture.worktreePath, GOLDEN_CHANGED_PATH))} (diff)`
)
const probe = orcaPage.getByRole('button', { name: /Source Control/ })
await probe.focus()
await expect(probe).toBeFocused()
})
+183
View File
@@ -0,0 +1,183 @@
import { execFileSync } from 'node:child_process'
import { chmodSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import type { Page } from '@stablyai/playwright-test'
import { expect } from './orca-app'
export const GOLDEN_CHANGED_PATH = 'src/index.ts'
export const GOLDEN_REMOVED_LINE = 'export const hello = "world"'
export const GOLDEN_ADDED_LINE = 'export const hello = "golden daily loop"'
export const GOLDEN_GIT_AUTHOR_NAME = 'Orca E2E'
export const GOLDEN_GIT_AUTHOR_EMAIL = 'orca-e2e@example.invalid'
const GOLDEN_PRE_COMMIT_MARKER = '.e2e-pre-commit-ran'
export type GoldenWorktree = {
branchName: string
hooksPath?: string
worktreePath: string
}
export function createGoldenWorktree(repoPath: string, label: string): GoldenWorktree {
const suffix = `${Date.now()}-${Math.random().toString(16).slice(2)}`
const branchName = `e2e-golden-${label}-${suffix}`
const worktreePath = path.join(os.tmpdir(), branchName)
execFileSync('git', ['worktree', 'add', worktreePath, '-b', branchName], {
cwd: repoPath,
stdio: 'pipe'
})
const fixture: GoldenWorktree = { branchName, worktreePath }
// Callers only register cleanup once this returns, so roll back here or the
// half-built worktree and branch leak into every later run.
try {
execFileSync('git', ['config', 'extensions.worktreeConfig', 'true'], {
cwd: worktreePath,
stdio: 'pipe'
})
execFileSync('git', ['config', '--worktree', 'user.name', GOLDEN_GIT_AUTHOR_NAME], {
cwd: worktreePath,
stdio: 'pipe'
})
execFileSync('git', ['config', '--worktree', 'user.email', GOLDEN_GIT_AUTHOR_EMAIL], {
cwd: worktreePath,
stdio: 'pipe'
})
} catch (setupError) {
try {
cleanupGoldenWorktree(repoPath, fixture)
} catch {
// Keep the setup failure as the reported cause.
}
throw setupError
}
return fixture
}
export function cleanupGoldenWorktree(repoPath: string, fixture: GoldenWorktree): void {
try {
execFileSync('git', ['config', '--worktree', '--unset-all', 'core.hooksPath'], {
cwd: fixture.worktreePath,
stdio: 'pipe'
})
} catch {
// The hook setting may not have been installed before setup failed.
}
if (fixture.hooksPath) {
rmSync(fixture.hooksPath, { recursive: true, force: true })
}
try {
execFileSync('git', ['worktree', 'remove', '--force', fixture.worktreePath], {
cwd: repoPath,
stdio: 'pipe'
})
} catch {
rmSync(fixture.worktreePath, { recursive: true, force: true })
execFileSync('git', ['worktree', 'prune'], { cwd: repoPath, stdio: 'pipe' })
}
execFileSync('git', ['branch', '-D', fixture.branchName], { cwd: repoPath, stdio: 'pipe' })
}
export function seedGoldenSourceEdit(worktreePath: string): void {
const changedPath = path.join(worktreePath, GOLDEN_CHANGED_PATH)
const original = readFileSync(changedPath, 'utf8')
if (!original.includes(GOLDEN_REMOVED_LINE)) {
throw new Error(`Golden source fixture is missing: ${GOLDEN_REMOVED_LINE}`)
}
writeFileSync(changedPath, original.replace(GOLDEN_REMOVED_LINE, GOLDEN_ADDED_LINE))
}
export function installPassingNodePreCommitHook(fixture: GoldenWorktree): string {
const hooksPath = mkdtempSync(path.join(os.tmpdir(), 'orca-e2e-git-hooks-'))
const hookPath = path.join(hooksPath, 'pre-commit')
const markerPath = path.join(hooksPath, GOLDEN_PRE_COMMIT_MARKER)
fixture.hooksPath = hooksPath
execFileSync('git', ['config', '--worktree', 'core.hooksPath', hooksPath], {
cwd: fixture.worktreePath,
stdio: 'pipe'
})
writeFileSync(
hookPath,
`#!/bin/sh\nnode -e "const fs = require('node:fs'); const path = require('node:path'); fs.writeFileSync(path.join(path.dirname(process.argv[1]), '${GOLDEN_PRE_COMMIT_MARKER}'), 'ran')" "$0"\n`
)
chmodSync(hookPath, 0o755)
return markerPath
}
export async function activateGoldenWorktree(
page: Page,
repoPath: string,
worktreePath: string
): Promise<void> {
await expect
.poll(
() =>
page.evaluate(
async ({ registeredRepoPath, targetWorktreePath }) => {
const store = window.__store
if (!store) {
throw new Error('window.__store is not available')
}
await store.getState().fetchRepos()
const normalize = (value: string): string => {
const normalized = value
.replace(/\\/g, '/')
.replace(/^\/private(?=\/var\/)/, '')
.replace(/\/+$/, '')
return navigator.userAgent.includes('Windows') ? normalized.toLowerCase() : normalized
}
const repo = store
.getState()
.repos.find((entry) => normalize(entry.path) === normalize(registeredRepoPath))
if (!repo) {
return false
}
await store.getState().fetchWorktrees(repo.id)
const worktree = (store.getState().worktreesByRepo[repo.id] ?? []).find(
(entry) => normalize(entry.path) === normalize(targetWorktreePath)
)
if (!worktree) {
return false
}
store.getState().setActiveRepo(repo.id)
store.getState().setActiveWorktree(worktree.id)
return true
},
{ registeredRepoPath: repoPath, targetWorktreePath: worktreePath }
),
{ timeout: 10_000, message: `Golden worktree did not load: ${worktreePath}` }
)
.toBe(true)
}
export async function openGoldenSourceControl(
page: Page,
repoPath: string,
fixture: GoldenWorktree
): Promise<void> {
await activateGoldenWorktree(page, repoPath, fixture.worktreePath)
await page.evaluate(() => {
const store = window.__store
if (!store) {
throw new Error('window.__store is not available')
}
const state = store.getState()
state.setRightSidebarTab('explorer')
state.setRightSidebarOpen(true)
})
await page.getByRole('button', { name: /Source Control/ }).click()
await expect(page.getByRole('textbox', { name: 'Commit message' })).toBeVisible({
timeout: 10_000
})
await expect
.poll(
() =>
page.evaluate(() => {
const state = window.__store?.getState()
return state?.activeWorktreeId
? Object.hasOwn(state.gitStatusByWorktree, state.activeWorktreeId)
: false
}),
{ timeout: 10_000, message: 'Automatic Git status refresh did not complete' }
)
.toBe(true)
}
@@ -0,0 +1,117 @@
import { execFileSync } from 'node:child_process'
import os from 'node:os'
import path from 'node:path'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
createGoldenWorktree,
GOLDEN_GIT_AUTHOR_EMAIL,
GOLDEN_GIT_AUTHOR_NAME
} from './golden-source-control'
vi.mock('node:child_process', () => ({ execFileSync: vi.fn() }))
const execFileSyncMock = vi.mocked(execFileSync)
type GitCall = { args: string[]; cwd?: string }
/** Setup runs in the new worktree while rollback runs in the repo, so cwd is part of the contract. */
const gitCallsFor = (): GitCall[] =>
execFileSyncMock.mock.calls.map(([, args, options]) => ({
args: (args ?? []) as string[],
cwd: (options as { cwd?: string } | undefined)?.cwd
}))
/** Args-only view for lookups that locate a call rather than assert its full shape. */
const gitArgsFor = (): string[][] => gitCallsFor().map((call) => call.args)
/** Returns the thrown value itself; `toThrow` only compares messages, not identity. */
const captureThrow = (run: () => void): unknown => {
try {
run()
return undefined
} catch (error) {
return error
}
}
/** The worktree path and branch name are randomly suffixed, so read them back off the add call. */
const worktreeAddTargets = (): { branchName: string; worktreePath: string } => {
const addArgs = gitArgsFor().find((args) => args[0] === 'worktree' && args[1] === 'add')
expect(addArgs).toBeDefined()
const [, , worktreePath, , branchName] = addArgs as string[]
return { branchName, worktreePath }
}
describe('createGoldenWorktree', () => {
beforeEach(() => {
execFileSyncMock.mockReset()
})
/** A half-built worktree leaks into later runs unless both the worktree and the branch go away. */
it('rolls back the worktree and branch when a configuration command fails', () => {
const setupError = new Error('git config --worktree unsupported')
execFileSyncMock.mockImplementation(((_file: string, args: string[]) => {
if (args[0] === 'config' && args[1] === 'extensions.worktreeConfig') {
throw setupError
}
return ''
}) as unknown as typeof execFileSync)
expect(captureThrow(() => createGoldenWorktree('/repo', 'rollback'))).toBe(setupError)
const { branchName, worktreePath } = worktreeAddTargets()
expect(branchName).toMatch(/^e2e-golden-rollback-/)
expect(gitCallsFor()).toContainEqual({
args: ['worktree', 'remove', '--force', worktreePath],
cwd: '/repo'
})
expect(gitCallsFor()).toContainEqual({ args: ['branch', '-D', branchName], cwd: '/repo' })
})
/** Rollback must still be attempted in full, and its own failure must not mask the setup cause. */
it('keeps the setup error when rollback itself fails', () => {
const setupError = new Error('git config --worktree unsupported')
execFileSyncMock.mockImplementation(((_file: string, args: string[]) => {
if (args[0] === 'config' && args[1] === 'extensions.worktreeConfig') {
throw setupError
}
if (args[0] === 'branch') {
throw new Error('branch is still checked out')
}
return ''
}) as unknown as typeof execFileSync)
expect(captureThrow(() => createGoldenWorktree('/repo', 'rollback-fails'))).toBe(setupError)
const { branchName, worktreePath } = worktreeAddTargets()
expect(branchName).toMatch(/^e2e-golden-rollback-fails-/)
expect(gitCallsFor()).toContainEqual({
args: ['worktree', 'remove', '--force', worktreePath],
cwd: '/repo'
})
expect(gitCallsFor()).toContainEqual({ args: ['branch', '-D', branchName], cwd: '/repo' })
})
/** The identity config must land on the worktree, not the repo, or commits pick up the host author. */
it('returns the fixture when every setup command succeeds', () => {
execFileSyncMock.mockReturnValue('')
const fixture = createGoldenWorktree('/repo', 'happy')
expect(fixture.branchName).toMatch(/^e2e-golden-happy-/)
expect(fixture.worktreePath).toBe(path.join(os.tmpdir(), fixture.branchName))
// Exhaustive: also proves no rollback ran.
expect(gitCallsFor()).toEqual([
{ args: ['worktree', 'add', fixture.worktreePath, '-b', fixture.branchName], cwd: '/repo' },
{ args: ['config', 'extensions.worktreeConfig', 'true'], cwd: fixture.worktreePath },
{
args: ['config', '--worktree', 'user.name', GOLDEN_GIT_AUTHOR_NAME],
cwd: fixture.worktreePath
},
{
args: ['config', '--worktree', 'user.email', GOLDEN_GIT_AUTHOR_EMAIL],
cwd: fixture.worktreePath
}
])
})
})