Files
openidea/e2e/run.spec.ts
T
l0ng-ai aa33d2eeab test(e2e): cover the core editing, code intelligence, VCS, run and debug flows (#1277)
Adds a `stubDesktop` fixture that switches the browser-mode workbench to
desktop mode over a stubbed IPC layer: tests answer the commands they need,
push Channel messages and IdeEvents, and assert on the commands invoked.
Unstubbed commands fail as unavailable; documents keep the in-memory backend.

New flows covered:
- editing: modified marker and ⌘S save, ⌘R Replace All with one undo step
- code intelligence: completion, ⌘B to another file, Find Usages, in-place
  rename, diagnostics with an ⌥⏎ quick fix
- VCS: ⌘K commit with message validation and default inclusion
- run: ⌃R output streaming and exit, ⌘F2 stop
- debug: breakpoint, stop with frames and variables, step over, resume
- search: Find in Files results opening at the match line; the Search
  Everywhere file case no longer needs fixme
- terminal: typed command reaches the shell and its output renders
- project: Close Project to Welcome, trust prompt, open a recent project
2026-11-04 22:52:48 +08:00

83 lines
3.6 KiB
TypeScript

/**
* Running a configuration: output streams into the Run tool window, the exit shows, and Stop ends
* the process. Processes are stubbed at the IPC layer; `crates/ide-run` covers spawning them.
*/
import type { Page } from "@playwright/test";
import { expect, ipcCalls, stubDesktop, test } from "./fixtures";
const RUN_ID = 7;
/** One saved `npm run dev` configuration; `run_start` prints `output` and exits with `exit` unless null. */
async function stubRun(page: Page, output: { kind: string; text: string }[], exit: number | null) {
await stubDesktop(page);
await page.evaluate(
async ({ output, exit, runId }) => {
const config = {
id: "npm:dev",
name: "dev",
kind: "npm",
command: "npm",
args: ["run", "dev"],
cwd: "/dev/acme-api",
env: {},
detected: false,
isTest: false,
debugAdapter: null,
};
const h = window.__ipc.handlers;
h.run_configs = () => [config];
h.run_apply_settings = (args) => args.configs;
h.run_start = (args) => {
setTimeout(() => {
for (const chunk of output) window.__ipc.send(args.onOutput, chunk);
if (exit !== null) setTimeout(() => window.__ipc.emit({ type: "runExited", run: runId, code: exit }), 50);
}, 50);
return { id: runId, configId: config.id, name: config.name, pid: 4242, running: true, pty: false };
};
h.run_stop = () => {
setTimeout(() => window.__ipc.emit({ type: "runExited", run: runId, code: 143 }), 50);
return null;
};
const runModule = "/src/features/run/store.ts";
const { loadConfigs, selectConfig } = await import(/* @vite-ignore */ runModule);
await loadConfigs();
selectConfig(config.id);
},
{ output, exit, runId: RUN_ID },
);
}
test("⌃R runs the selected configuration and shows its output and exit", async ({ workbench: page }) => {
await stubRun(
page,
[
{ kind: "stdout", text: "> acme-api@1.0.0 dev\n" },
{ kind: "stdout", text: "Server listening on http://localhost:8080\n" },
{ kind: "stderr", text: "warning: config/dev.json not found, using defaults\n" },
],
0,
);
await page.keyboard.press("Control+r");
const run = page.getByRole("region", { name: "Run", exact: true });
await expect(run).toBeVisible();
await expect(run.getByText("Server listening on http://localhost:8080")).toBeVisible();
await expect(run.getByText("warning: config/dev.json not found, using defaults")).toBeVisible();
// Once the process exits, Stop is off and the session's tab tells the exit code.
await expect(run.getByRole("button", { name: "Stop" })).toBeDisabled();
await expect(run.getByRole("tab", { name: "dev" })).toHaveAttribute("data-tooltip", /exit code 0$/);
const [started] = await ipcCalls(page, "run_start");
expect(started).toMatchObject({ config: { id: "npm:dev", command: "npm", args: ["run", "dev"] } });
});
test("⌘F2 stops a running process", async ({ workbench: page }) => {
await stubRun(page, [{ kind: "stdout", text: "Watching for changes…\n" }], null);
await page.keyboard.press("Control+r");
const run = page.getByRole("region", { name: "Run", exact: true });
await expect(run.getByText("Watching for changes…")).toBeVisible();
await expect(run.getByRole("button", { name: "Stop" })).toBeEnabled();
await page.keyboard.press("Meta+F2");
await expect.poll(() => ipcCalls(page, "run_stop")).toEqual([{ id: RUN_ID }]);
await expect(run.getByRole("button", { name: "Stop" })).toBeDisabled();
await expect(run.getByRole("tab", { name: "dev" })).toHaveAttribute("data-tooltip", /exit code 143$/);
});