113 lines
3.9 KiB
TypeScript
113 lines
3.9 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'
|
|
import { openSourceControlForWorktree } from './helpers/worktree-registration'
|
|
|
|
type E2eWorktree = {
|
|
branchName: string
|
|
worktreePath: string
|
|
}
|
|
|
|
function createWorktreeWithStagedChange(repoPath: string): E2eWorktree {
|
|
const branchName = `e2e-commit-draft-${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'),
|
|
'# Commit Draft Persistence E2E\n\nPreserve this draft across remounts.\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 {
|
|
try {
|
|
rmSync(worktreePath, { recursive: true, force: true })
|
|
execFileSync('git', ['worktree', 'prune'], { cwd: repoPath, stdio: 'pipe' })
|
|
} catch {
|
|
// Best effort: the branch delete below is still worth attempting.
|
|
}
|
|
}
|
|
try {
|
|
execFileSync('git', ['branch', '-D', branchName], { cwd: repoPath, stdio: 'pipe' })
|
|
} catch {
|
|
// The branch may already be gone when git prunes it with the worktree.
|
|
}
|
|
}
|
|
|
|
test.describe('Source Control commit draft persistence', () => {
|
|
test('preserves a typed draft when the sidebar tab remounts', async ({
|
|
orcaPage,
|
|
testRepoPath
|
|
}) => {
|
|
let firstWorktree: E2eWorktree | null = null
|
|
let secondWorktree: E2eWorktree | null = null
|
|
|
|
try {
|
|
firstWorktree = createWorktreeWithStagedChange(testRepoPath)
|
|
secondWorktree = createWorktreeWithStagedChange(testRepoPath)
|
|
await waitForSessionReady(orcaPage)
|
|
await openSourceControlForWorktree(orcaPage, testRepoPath, firstWorktree.worktreePath)
|
|
|
|
const textarea = orcaPage.getByRole('textbox', { name: 'Commit message' })
|
|
await expect(textarea).toBeVisible({ timeout: 10_000 })
|
|
|
|
const draft = 'fix: keep draft after leaving Source Control'
|
|
await textarea.fill(draft)
|
|
await expect(textarea).toHaveValue(draft)
|
|
|
|
await orcaPage.evaluate(() => {
|
|
const state = window.__store?.getState()
|
|
state?.setRightSidebarTab('explorer')
|
|
})
|
|
await expect
|
|
.poll(
|
|
async () => orcaPage.evaluate(() => window.__store?.getState().rightSidebarTab ?? null),
|
|
{ timeout: 5_000 }
|
|
)
|
|
.toBe('explorer')
|
|
await expect(textarea).toBeHidden()
|
|
|
|
await orcaPage.evaluate(() => {
|
|
const state = window.__store?.getState()
|
|
state?.setRightSidebarTab('source-control')
|
|
})
|
|
await expect
|
|
.poll(
|
|
async () => orcaPage.evaluate(() => window.__store?.getState().rightSidebarTab ?? null),
|
|
{ timeout: 5_000 }
|
|
)
|
|
.toBe('source-control')
|
|
|
|
await expect(textarea).toBeVisible({ timeout: 10_000 })
|
|
await expect(textarea).toHaveValue(draft)
|
|
|
|
await openSourceControlForWorktree(orcaPage, testRepoPath, secondWorktree.worktreePath)
|
|
await expect(textarea).toBeVisible({ timeout: 10_000 })
|
|
await expect(textarea).toHaveValue('')
|
|
|
|
await openSourceControlForWorktree(orcaPage, testRepoPath, firstWorktree.worktreePath)
|
|
await expect(textarea).toBeVisible({ timeout: 10_000 })
|
|
await expect(textarea).toHaveValue(draft)
|
|
} finally {
|
|
for (const worktree of [firstWorktree, secondWorktree]) {
|
|
if (worktree) {
|
|
cleanupWorktree(testRepoPath, worktree.worktreePath, worktree.branchName)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
})
|