mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
429 lines
16 KiB
TypeScript
429 lines
16 KiB
TypeScript
import { openSidebarWorkspaceComposer } from './helpers/sidebar-project-dialog'
|
||
// Drive the real Create Workspace composer: old direct store creation missed #1186’s React #31 render crash after #710’s modal replacement.
|
||
// #1191 removed StartFromField, but console/pageerror checks still reject equivalent render crashes; see tests/e2e/AGENTS.md.
|
||
|
||
import type { ConsoleMessage } from '@stablyai/playwright-test'
|
||
import { test, expect } from './helpers/orca-app'
|
||
import {
|
||
waitForSessionReady,
|
||
waitForActiveWorktree,
|
||
getActiveWorktreeId,
|
||
ensureTerminalVisible,
|
||
worktreeExists
|
||
} from './helpers/store'
|
||
|
||
test.describe('Create Workspace', () => {
|
||
test.beforeEach(async ({ orcaPage }) => {
|
||
await waitForSessionReady(orcaPage)
|
||
await waitForActiveWorktree(orcaPage)
|
||
})
|
||
|
||
test('creates a worktree through the composer UI and activates it', async ({ orcaPage }) => {
|
||
const worktreeIdBefore = await getActiveWorktreeId(orcaPage)
|
||
|
||
// Capture both console’s invalid-React-child message and pageerror #31 for dev/production render failures.
|
||
const pageErrors: Error[] = []
|
||
orcaPage.on('pageerror', (err) => {
|
||
pageErrors.push(err)
|
||
})
|
||
const consoleErrors: string[] = []
|
||
const onConsole = (msg: ConsoleMessage): void => {
|
||
if (msg.type() === 'error') {
|
||
consoleErrors.push(msg.text())
|
||
}
|
||
}
|
||
orcaPage.on('console', onConsole)
|
||
|
||
const workspaceName = `e2e-create-${Date.now()}`
|
||
|
||
try {
|
||
// Open via visible affordance to cover lazy modal mount.
|
||
await openSidebarWorkspaceComposer(orcaPage)
|
||
|
||
const dialog = orcaPage.getByRole('dialog', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(dialog).toBeVisible()
|
||
|
||
// Wait for agent probe/name focus/hydration/setup hooks before clicking through Radix FocusScope reparenting.
|
||
await expect(dialog.locator('[data-workspace-name-input="true"]')).toBeVisible()
|
||
|
||
// Round-trip getBaseRefDefault so envelope consumers can trigger #1186-class render errors after #1191 removed StartFromField.
|
||
await orcaPage.evaluate(async () => {
|
||
const repoId = Object.values(window.__store!.getState().worktreesByRepo).flat()[0]?.repoId
|
||
if (!repoId) {
|
||
return
|
||
}
|
||
await window.api.repos.getBaseRefDefault({ repoId })
|
||
})
|
||
await orcaPage.waitForTimeout(100)
|
||
|
||
// Plain text in default smart mode submits as workspace name.
|
||
const nameInput = dialog.getByPlaceholder(/Type a name/i)
|
||
await expect(nameInput).toBeVisible()
|
||
await nameInput.fill(workspaceName)
|
||
|
||
const createButton = dialog.getByRole('button', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(createButton).toBeEnabled()
|
||
await createButton.click()
|
||
|
||
await expect(dialog).toBeHidden({ timeout: 15_000 })
|
||
|
||
await expect
|
||
.poll(async () => worktreeExists(orcaPage, workspaceName), {
|
||
timeout: 10_000,
|
||
message: `Worktree "${workspaceName}" did not appear in the store`
|
||
})
|
||
.toBe(true)
|
||
|
||
await expect
|
||
.poll(
|
||
async () => {
|
||
const id = await getActiveWorktreeId(orcaPage)
|
||
return id !== null && id !== worktreeIdBefore
|
||
},
|
||
{ timeout: 10_000, message: 'New worktree did not become the active worktree' }
|
||
)
|
||
.toBe(true)
|
||
|
||
// Require auto-created terminal to prove activateAndRevealWorktree ran beyond creating the store row.
|
||
await ensureTerminalVisible(orcaPage)
|
||
|
||
expect(pageErrors, `pageerror fired: ${pageErrors.map((e) => e.message).join(', ')}`).toEqual(
|
||
[]
|
||
)
|
||
const reactChildErrors = consoleErrors.filter((text) =>
|
||
/Objects are not valid as a React child|Minified React error #31/i.test(text)
|
||
)
|
||
expect(reactChildErrors, `React render error: ${reactChildErrors.join(', ')}`).toEqual([])
|
||
} finally {
|
||
orcaPage.off('console', onConsole)
|
||
// Best-effort close if the test failed mid-flow and left the modal open.
|
||
await orcaPage
|
||
.evaluate(() => {
|
||
window.__store?.getState().closeModal()
|
||
})
|
||
.catch(() => {
|
||
/* page may already be torn down */
|
||
})
|
||
}
|
||
})
|
||
|
||
test('creates an emoji-named worktree with a safe git branch', async ({ orcaPage }) => {
|
||
const workspaceName = '🚀🧪✨'
|
||
|
||
try {
|
||
await openSidebarWorkspaceComposer(orcaPage)
|
||
|
||
const dialog = orcaPage.getByRole('dialog', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(dialog).toBeVisible()
|
||
await expect(dialog.locator('[data-workspace-name-input="true"]')).toBeVisible()
|
||
|
||
const nameInput = dialog.getByPlaceholder(/Type a name/i)
|
||
await nameInput.fill(workspaceName)
|
||
|
||
const createButton = dialog.getByRole('button', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(createButton).toBeEnabled()
|
||
await createButton.click()
|
||
|
||
await expect(dialog).toBeHidden({ timeout: 15_000 })
|
||
await expect(orcaPage.getByRole('option', { name: new RegExp(workspaceName) })).toBeVisible({
|
||
timeout: 10_000
|
||
})
|
||
|
||
const branch = await orcaPage.evaluate((displayName) => {
|
||
const worktrees = Object.values(window.__store!.getState().worktreesByRepo).flat()
|
||
return worktrees.find((worktree) => worktree.displayName === displayName)?.branch ?? null
|
||
}, workspaceName)
|
||
expect(branch).toBe('refs/heads/rocket-test-tube-sparkles')
|
||
} finally {
|
||
await orcaPage
|
||
.evaluate(() => {
|
||
window.__store?.getState().closeModal()
|
||
})
|
||
.catch(() => {
|
||
/* page may already be torn down */
|
||
})
|
||
}
|
||
})
|
||
|
||
test('enters the Korean flag with the flag_kr shortcode suggestion', async ({ orcaPage }) => {
|
||
try {
|
||
await openSidebarWorkspaceComposer(orcaPage)
|
||
|
||
const dialog = orcaPage.getByRole('dialog', { name: /Create (Workspace|Worktree)/i })
|
||
const nameInput = dialog.getByPlaceholder(/Type a name/i)
|
||
await expect(nameInput).toBeVisible()
|
||
|
||
await nameInput.pressSequentially('Launch :flag_kr', { delay: 100 })
|
||
const emojiSuggestions = orcaPage.locator('[data-workspace-emoji-suggestions="true"]')
|
||
const sourceSuggestions = orcaPage.locator('[data-workspace-source-suggestions="true"]')
|
||
await expect(emojiSuggestions).toBeVisible()
|
||
await expect(emojiSuggestions.getByRole('option', { name: ':flag_kr:' })).toBeVisible()
|
||
await expect(emojiSuggestions).toHaveAttribute('data-side', 'top')
|
||
await expect(sourceSuggestions).toBeVisible()
|
||
await expect(sourceSuggestions).toHaveAttribute('data-side', 'bottom')
|
||
// Keep both independently positioned suggestion surfaces visible in proof recordings.
|
||
await orcaPage.waitForTimeout(750)
|
||
|
||
await nameInput.pressSequentially(':')
|
||
await expect(nameInput).toHaveValue('Launch 🇰🇷')
|
||
await expect(orcaPage.getByRole('option', { name: /:flag_kr:/i })).toHaveCount(0)
|
||
await nameInput.pressSequentially(' experiment')
|
||
await expect(nameInput).toHaveValue('Launch 🇰🇷 experiment')
|
||
// Keep the asserted result visible in retained proof recordings.
|
||
await orcaPage.waitForTimeout(750)
|
||
} finally {
|
||
await orcaPage
|
||
.evaluate(() => {
|
||
window.__store?.getState().closeModal()
|
||
})
|
||
.catch(() => {
|
||
/* page may already be torn down */
|
||
})
|
||
}
|
||
})
|
||
|
||
test('shows a failed workspace entry when worktree creation fails', async ({ orcaPage }) => {
|
||
await orcaPage.evaluate(() => {
|
||
const store = window.__store
|
||
if (!store) {
|
||
throw new Error('window.__store is not available')
|
||
}
|
||
const originalCreateWorktree = store.getState().createWorktree
|
||
;(
|
||
window as unknown as {
|
||
__restoreCreateWorktree?: () => void
|
||
}
|
||
).__restoreCreateWorktree = () => {
|
||
store.setState({ createWorktree: originalCreateWorktree })
|
||
}
|
||
store.setState({
|
||
createWorktree: async () => {
|
||
throw new Error('could not resolve a default base ref for the E2E fixture')
|
||
}
|
||
})
|
||
})
|
||
|
||
try {
|
||
const workspaceName = `e2e-create-failure-${Date.now()}`
|
||
|
||
await openSidebarWorkspaceComposer(orcaPage)
|
||
|
||
const dialog = orcaPage.getByRole('dialog', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(dialog).toBeVisible()
|
||
await expect(dialog.locator('[data-workspace-name-input="true"]')).toBeVisible()
|
||
|
||
const nameInput = dialog.getByPlaceholder(/Type a name/i)
|
||
await expect(nameInput).toBeVisible()
|
||
await nameInput.fill(workspaceName)
|
||
|
||
const createButton = dialog.getByRole('button', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(createButton).toBeEnabled()
|
||
await createButton.click()
|
||
|
||
await expect(dialog).toBeHidden()
|
||
const failedWorkspace = orcaPage.getByRole('button', {
|
||
name: new RegExp(`${workspaceName} No base branch found`)
|
||
})
|
||
await expect(failedWorkspace).toBeVisible()
|
||
await expect(orcaPage.getByText('Couldn’t create worktree')).toBeVisible()
|
||
await expect(failedWorkspace).toContainText('No base branch found')
|
||
await expect(orcaPage.getByRole('button', { name: 'Retry' })).toBeVisible()
|
||
} finally {
|
||
await orcaPage
|
||
.evaluate(() => {
|
||
;(
|
||
window as unknown as {
|
||
__restoreCreateWorktree?: () => void
|
||
}
|
||
).__restoreCreateWorktree?.()
|
||
window.__store?.getState().closeModal()
|
||
})
|
||
.catch(() => {
|
||
/* page may already be torn down */
|
||
})
|
||
}
|
||
})
|
||
|
||
test('reuses a resolved pasted GitHub URL when quick create submits', async ({
|
||
electronApp,
|
||
orcaPage
|
||
}) => {
|
||
const title = `E2E smart URL resolution ${Date.now()}`
|
||
const url = 'https://github.com/stablyai/orca/pull/2049'
|
||
const linkedWorkspacePattern = new RegExp(title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
||
|
||
try {
|
||
await openSidebarWorkspaceComposer(orcaPage)
|
||
|
||
const dialog = orcaPage.getByRole('dialog', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(dialog).toBeVisible()
|
||
await expect(dialog.locator('[data-workspace-name-input="true"]')).toBeVisible()
|
||
|
||
await electronApp.evaluate(
|
||
({ ipcMain }, { title, url }) => {
|
||
const counters = globalThis as unknown as {
|
||
__smartGitHubLookupCount: number
|
||
__smartResolvePrBaseCount: number
|
||
}
|
||
counters.__smartGitHubLookupCount = 0
|
||
counters.__smartResolvePrBaseCount = 0
|
||
ipcMain.removeHandler('gh:workItemByOwnerRepo')
|
||
ipcMain.handle(
|
||
'gh:workItemByOwnerRepo',
|
||
(
|
||
_event: unknown,
|
||
args: {
|
||
number: number
|
||
repoId?: string
|
||
}
|
||
) => {
|
||
counters.__smartGitHubLookupCount += 1
|
||
return {
|
||
id: `e2e-pr-${args.number}`,
|
||
type: 'pr',
|
||
number: args.number,
|
||
title,
|
||
state: 'open',
|
||
url,
|
||
labels: [],
|
||
updatedAt: '2026-05-26T00:00:00.000Z',
|
||
author: 'e2e',
|
||
repoId: args.repoId ?? 'e2e-repo'
|
||
}
|
||
}
|
||
)
|
||
ipcMain.removeHandler('worktrees:resolvePrBase')
|
||
// Use HEAD: the remote-less fixture’s default branch depends on host init.defaultBranch.
|
||
ipcMain.handle('worktrees:resolvePrBase', () => {
|
||
counters.__smartResolvePrBaseCount += 1
|
||
return { baseBranch: 'HEAD' }
|
||
})
|
||
},
|
||
{ title, url }
|
||
)
|
||
|
||
const nameInput = dialog.getByPlaceholder(/Type a name/i)
|
||
await expect(nameInput).toBeVisible()
|
||
await nameInput.fill(url)
|
||
|
||
await expect
|
||
.poll(() =>
|
||
electronApp.evaluate(() => {
|
||
const counters = globalThis as unknown as {
|
||
__smartGitHubLookupCount?: number
|
||
__smartResolvePrBaseCount?: number
|
||
}
|
||
return {
|
||
githubLookupCount: counters.__smartGitHubLookupCount ?? -1,
|
||
resolvePrBaseCount: counters.__smartResolvePrBaseCount ?? -1
|
||
}
|
||
})
|
||
)
|
||
.toEqual({ githubLookupCount: 1, resolvePrBaseCount: 0 })
|
||
const createButton = dialog.getByRole('button', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(createButton).toBeEnabled()
|
||
await createButton.click()
|
||
|
||
await expect(dialog).toBeHidden({ timeout: 15_000 })
|
||
await expect(orcaPage.getByRole('option', { name: linkedWorkspacePattern })).toBeVisible({
|
||
timeout: 10_000
|
||
})
|
||
await expect(orcaPage.getByRole('option', { name: url })).toHaveCount(0)
|
||
await expect(orcaPage.getByText('Linked PR #2049')).toBeVisible()
|
||
// Quick create reuses typing’s GitHub lookup; #5733 resolves PR head once at submit, requiring one base resolution.
|
||
await expect
|
||
.poll(() =>
|
||
electronApp.evaluate(() => {
|
||
const counters = globalThis as unknown as {
|
||
__smartGitHubLookupCount?: number
|
||
__smartResolvePrBaseCount?: number
|
||
}
|
||
return {
|
||
githubLookupCount: counters.__smartGitHubLookupCount ?? -1,
|
||
resolvePrBaseCount: counters.__smartResolvePrBaseCount ?? -1
|
||
}
|
||
})
|
||
)
|
||
.toEqual({ githubLookupCount: 1, resolvePrBaseCount: 1 })
|
||
} finally {
|
||
await orcaPage
|
||
.evaluate(() => {
|
||
window.__store?.getState().closeModal()
|
||
})
|
||
.catch(() => {
|
||
/* page may already be torn down */
|
||
})
|
||
}
|
||
})
|
||
|
||
test('names the workspace after the PR title when the pasted URL suggestion is selected', async ({
|
||
electronApp,
|
||
orcaPage
|
||
}) => {
|
||
const title = `E2E selected URL resolution ${Date.now()}`
|
||
const url = 'https://github.com/stablyai/orca/pull/2050'
|
||
const linkedWorkspacePattern = new RegExp(title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
||
|
||
try {
|
||
await openSidebarWorkspaceComposer(orcaPage)
|
||
|
||
const dialog = orcaPage.getByRole('dialog', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(dialog).toBeVisible()
|
||
await expect(dialog.locator('[data-workspace-name-input="true"]')).toBeVisible()
|
||
|
||
await electronApp.evaluate(
|
||
({ ipcMain }, { title, url }) => {
|
||
ipcMain.removeHandler('gh:workItemByOwnerRepo')
|
||
ipcMain.handle(
|
||
'gh:workItemByOwnerRepo',
|
||
(_event: unknown, args: { number: number; repoId?: string }) => ({
|
||
id: `e2e-pr-${args.number}`,
|
||
type: 'pr',
|
||
number: args.number,
|
||
title,
|
||
state: 'open',
|
||
url,
|
||
labels: [],
|
||
updatedAt: '2026-05-26T00:00:00.000Z',
|
||
author: 'e2e',
|
||
repoId: args.repoId ?? 'e2e-repo'
|
||
})
|
||
)
|
||
ipcMain.removeHandler('worktrees:resolvePrBase')
|
||
// Use HEAD: the remote-less fixture’s default branch depends on host init.defaultBranch.
|
||
ipcMain.handle('worktrees:resolvePrBase', () => ({ baseBranch: 'HEAD' }))
|
||
},
|
||
{ title, url }
|
||
)
|
||
|
||
const nameInput = dialog.getByPlaceholder(/Type a name/i)
|
||
await expect(nameInput).toBeVisible()
|
||
await nameInput.fill(url)
|
||
|
||
// #4900: selecting resolved suggestion must replace the pasted URL as name; locate its portaled row outside the dialog.
|
||
const suggestion = orcaPage.getByRole('option', { name: linkedWorkspacePattern })
|
||
await expect(suggestion).toBeVisible()
|
||
await orcaPage.keyboard.press('Enter')
|
||
|
||
const createButton = dialog.getByRole('button', { name: /Create (Workspace|Worktree)/i })
|
||
await expect(createButton).toBeEnabled()
|
||
await createButton.click()
|
||
|
||
await expect(dialog).toBeHidden({ timeout: 15_000 })
|
||
await expect(orcaPage.getByRole('option', { name: linkedWorkspacePattern })).toBeVisible({
|
||
timeout: 10_000
|
||
})
|
||
await expect(orcaPage.getByRole('option', { name: /https-github/i })).toHaveCount(0)
|
||
await expect(orcaPage.getByText('Linked PR #2050')).toBeVisible()
|
||
} finally {
|
||
await orcaPage
|
||
.evaluate(() => {
|
||
window.__store?.getState().closeModal()
|
||
})
|
||
.catch(() => {
|
||
/* page may already be torn down */
|
||
})
|
||
}
|
||
})
|
||
})
|