Files
paseo/packages/app/e2e/browser/agent-message-rewind.spec.ts
T
Mohamed BoudraandGitHub 5a30affd7f Skip unrelated required checks without losing their names (#2542)
* fix(ci): skip unrelated required checks cleanly

Matrix jobs had to start no-op runners to preserve interpolated required-check names. Static named jobs let GitHub report genuine skipped conclusions while keeping those names stable.

* fix(ci): gate changes to install patching script

* fix(ci): route cross-package test contracts narrowly

* fix(ci): keep routed contracts merge-blocking

* fix(server): exclude nested test utilities from builds

* fix(ci): cover app-owned native and browser domains

* refactor(ci): align tests with runtime domains

Own browser specs, desktop specs, and Electron-only app code by stable directories so PR routing does not depend on filename conventions. Keep desktop integration coverage inside the required desktop check.

* fix(ci): keep routing and desktop suites isolated

The routing regression must run before dependency installation, and desktop Vitest must not collect Playwright-owned specs.

* fix(ci): isolate desktop e2e runtime

* perf(test): isolate Playwright workers

* perf(test): parallelize Playwright within specs

* fix(test): keep Playwright file scheduling

* fix(test): share dynamic Metro fixture

* fix(test): preserve own-host restart fixtures

* fix(ci): preserve routed contracts after rebase

* Stabilize Playwright wheel-scroll assertions
2026-08-02 00:56:05 +08:00

134 lines
5.2 KiB
TypeScript

import type { Locator } from "@playwright/test";
import { expect, test, type Page } from "../support/fixtures";
import { expectAgentIdle, expectAgentReadyToInterrupt } from "../support/helpers/agent-stream";
import {
cancelAgent,
expectComposerDraft,
expectComposerVisible,
submitMessage,
} from "../support/helpers/composer";
import { openAgentRoute, seedMockAgentWorkspace } from "../support/helpers/mock-agent";
async function completeSubmittedTurn(
page: Page,
agent: Awaited<ReturnType<typeof seedMockAgentWorkspace>>,
prompt: string,
): Promise<Locator> {
await submitMessage(page, prompt);
const userMessage = page.getByTestId("user-message").filter({ hasText: prompt });
await expect(userMessage).toBeVisible();
const finish = await agent.client.waitForFinish(agent.agentId, 30_000);
expect(finish.status).toBe("idle");
await expect(page.getByText("(end of synthetic stream)", { exact: true })).toBeVisible();
await expect(userMessage).toHaveAttribute("aria-busy", "false");
return userMessage;
}
async function rewindConversation(page: Page, userMessage: Locator, prompt: string): Promise<void> {
await userMessage.getByText(prompt, { exact: true }).hover();
await userMessage.getByRole("button", { name: "Rewind to this message", exact: true }).click();
await page.getByRole("button", { name: "Rewind conversation", exact: true }).click();
}
async function expectTurnCompletesNormally(
page: Page,
agent: Awaited<ReturnType<typeof seedMockAgentWorkspace>>,
): Promise<void> {
const finish = await agent.client.waitForFinish(agent.agentId, 30_000);
expect(finish.status).toBe("idle");
await expect(page.getByText("(end of synthetic stream)", { exact: true })).toBeVisible();
await expectAgentIdle(page);
}
test.describe("Agent message rewind", () => {
test("rewinds a submitted prompt and restores it to the composer", async ({ page }) => {
test.setTimeout(90_000);
const prompt = "Restore this submitted prompt after rewind.";
const agent = await seedMockAgentWorkspace({
repoPrefix: "message-rewind-roundtrip-",
title: "Message rewind roundtrip",
model: "ten-second-stream",
});
try {
await openAgentRoute(page, agent);
await expectComposerVisible(page);
const userMessage = await completeSubmittedTurn(page, agent, prompt);
await rewindConversation(page, userMessage, prompt);
await expect(page.getByText("(end of synthetic stream)", { exact: true })).toHaveCount(0);
await expectComposerDraft(page, prompt);
} finally {
await agent.cleanup();
}
});
test("keeps a pre-acknowledgement turn running after rewind is rejected", async ({ page }) => {
test.setTimeout(90_000);
const prompt = "Delay synthetic user message by 2000ms.";
const rewindError = "Cannot rewind before the provider acknowledges the submitted prompt";
const agent = await seedMockAgentWorkspace({
repoPrefix: "message-rewind-pre-echo-",
title: "Message rewind pre-echo",
model: "ten-second-stream",
});
try {
await openAgentRoute(page, agent);
await expectComposerVisible(page);
await submitMessage(page, prompt);
const userMessage = page.getByTestId("user-message").filter({ hasText: prompt });
await expect(userMessage).toBeVisible();
await expectAgentReadyToInterrupt(page);
await rewindConversation(page, userMessage, prompt);
await expect(page.getByText(rewindError, { exact: true })).toBeVisible();
await expectAgentReadyToInterrupt(page);
await expectTurnCompletesNormally(page, agent);
} finally {
await agent.cleanup();
}
});
test("does not offer rewind for daemon-handled prompts", async ({ page }) => {
const commandPrompt = "/mock handled-command";
const providerPrompt = "Keep this provider-bound prompt rewindable.";
const agent = await seedMockAgentWorkspace({
repoPrefix: "message-rewind-daemon-command-",
title: "Message rewind daemon command",
model: "ten-second-stream",
});
try {
await openAgentRoute(page, agent);
await expectComposerVisible(page);
await expectAgentIdle(page);
await submitMessage(page, commandPrompt);
const commandMessage = page.getByTestId("user-message").filter({ hasText: commandPrompt });
await expect(commandMessage).toBeVisible();
await expect(page.getByText("Mock command handled", { exact: true })).toBeVisible();
await commandMessage.getByText(commandPrompt, { exact: true }).hover();
await expect(
commandMessage.getByRole("button", { name: "Rewind to this message", exact: true }),
).toHaveCount(0);
await submitMessage(page, providerPrompt);
const providerMessage = page.getByTestId("user-message").filter({ hasText: providerPrompt });
await expect(providerMessage).toBeVisible();
await expectAgentReadyToInterrupt(page);
await providerMessage.getByText(providerPrompt, { exact: true }).hover();
await expect(
providerMessage.getByRole("button", { name: "Rewind to this message", exact: true }),
).toBeVisible();
await cancelAgent(page);
await expectAgentIdle(page);
} finally {
await agent.cleanup();
}
});
});