import { readFile, rm } from "node:fs/promises"; import { expect, test, type Page } from "./fixtures"; import { gotoAppShell, openSettings } from "./helpers/app"; import { injectDesktopBridge } from "./helpers/desktop-updates"; import { clickSettingsBackToWorkspace } from "./helpers/settings"; interface EditorOpenRecord { editorId: string; workspacePath: string; filePath?: string; line?: number; column?: number; } function requireE2EEnv(name: string): string { const value = process.env[name]?.trim(); if (!value) { throw new Error(`${name} is not set.`); } return value; } async function readEditorOpenRecords(recordPath: string): Promise { try { const contents = await readFile(recordPath, "utf8"); return contents .split("\n") .filter((line) => line.trim().length > 0) .map((line) => JSON.parse(line) as EditorOpenRecord); } catch (error) { if (error instanceof Error && "code" in error && error.code === "ENOENT") { return []; } throw error; } } async function chooseEditorTarget(page: Page, targetId: "vscode"): Promise { await expect(page.getByTestId("workspace-open-in-editor-primary")).toBeVisible({ timeout: 30_000, }); await page.getByTestId("workspace-open-in-editor-caret").click(); await expect(page.getByTestId("workspace-open-in-editor-menu")).toBeVisible(); await page.getByTestId(`workspace-open-in-editor-item-${targetId}`).click(); } async function expectEditorOpened(input: { recordPath: string; editorId: string; path: string; afterCount: number; }): Promise { await expect .poll( async () => { const records = await readEditorOpenRecords(input.recordPath); return records .slice(input.afterCount) .some( (record) => record.editorId === input.editorId && record.workspacePath === input.path, ); }, { timeout: 30_000 }, ) .toBe(true); } test.describe("Workspace open in editor", () => { test("keeps the selected editor target after leaving and returning to the workspace", async ({ page, withWorkspace, }) => { test.setTimeout(90_000); const serverId = requireE2EEnv("E2E_SERVER_ID"); const recordPath = requireE2EEnv("E2E_EDITOR_RECORD_PATH"); await rm(recordPath, { force: true }); await injectDesktopBridge(page, { serverId, editorTargets: [ { id: "cursor", label: "Cursor", kind: "editor", icon: { kind: "symbol", name: "terminal" }, }, { id: "vscode", label: "VS Code", kind: "editor", icon: { kind: "symbol", name: "terminal" }, }, ], editorRecordPath: recordPath, }); const workspace = await withWorkspace({ prefix: "workspace-editor-target-" }); await workspace.navigateTo(); await chooseEditorTarget(page, "vscode"); await expectEditorOpened({ recordPath, editorId: "vscode", path: workspace.repoPath, afterCount: 0, }); const recordsAfterSelection = (await readEditorOpenRecords(recordPath)).length; await openSettings(page); await clickSettingsBackToWorkspace(page); await expect(page).toHaveURL(/\/workspace\//, { timeout: 30_000 }); await page.getByTestId("workspace-open-in-editor-primary").click(); await expectEditorOpened({ recordPath, editorId: "vscode", path: workspace.repoPath, afterCount: recordsAfterSelection, }); const recordsAfterReturnOpen = (await readEditorOpenRecords(recordPath)).length; await gotoAppShell(page); await workspace.navigateTo(); await page.getByTestId("workspace-open-in-editor-primary").click(); await expectEditorOpened({ recordPath, editorId: "vscode", path: workspace.repoPath, afterCount: recordsAfterReturnOpen, }); }); });