← Files Biological Sequence & Alignment ViewerARCHIVED FILE
e2e/persistent-native-recovery.spec.ts
6.07 KB · Sep 30, 2026 · 23:01 UTC
import { expect, test } from "@playwright/test";
test("restores the exact installed Sequence viewer after its real MCP process exits", async ({
page,
request,
}) => {
const loadedViewerBundles: Array<string> = [];
page.on("request", (browserRequest) => {
const pathname = new URL(browserRequest.url()).pathname;
if (
pathname === "/e2e/generated-viewer.html" ||
pathname === "/dist/views/app.js.gz" ||
pathname === "/dist/views/styles.css"
) {
loadedViewerBundles.push(pathname);
}
});
await page.goto("/e2e/installed.html?persistent-native=1&fixture=dna-single.fasta");
const viewer = page.frameLocator("#viewer");
await expect(viewer.getByLabel("Wrapped sequence view")).toBeVisible();
const runningResponse = await request.get("/__sequence-viewer-persistent/mcp-status");
expect(runningResponse.ok()).toBe(true);
const running = (await runningResponse.json()) as McpProcessStatus;
expect(running.running).toBe(true);
expect(running.pid).toBeGreaterThan(0);
expect(running.spawnCount).toBeGreaterThanOrEqual(1);
expect(running.bridgeRequests).toMatchObject({
resourceReads: 1,
toolCalls: 1,
});
await viewer.getByLabel("Search sequence").fill("CGTA");
await viewer.getByRole("button", { name: "Display", exact: true }).click();
await viewer.getByRole("button", { name: "Show reverse complement" }).click();
await viewer.getByRole("button", { name: "Split", exact: true }).click();
await expect
.poll(() =>
page.evaluate(() => {
const checkpoint = window.readPersistentNativeCheckpoint();
return {
layout: checkpoint?.sequence?.view?.layout,
orientation: checkpoint?.sequence?.view?.orientation,
query: checkpoint?.sequence?.query,
};
}),
)
.toEqual({
layout: "split",
orientation: "reverse-complement",
query: "CGTA",
});
const beforeDeath = await page.evaluate(() => ({
bundle: window.__persistentNativeBundle,
nativeRequests: window.__persistentNativeRequests.length,
realMcpRequests: window.__realMcpRequests.length,
sourceEditRequests: window.__persistentNativeRequests.filter(({ name }) =>
name.includes("/source_edit/"),
).length,
}));
expect(beforeDeath.bundle).toMatchObject({
byteLength: expect.any(Number),
mountCount: 1,
sha256: expect.stringMatching(/^[a-f0-9]{64}$/u),
});
expect(beforeDeath.sourceEditRequests).toBe(0);
expect(beforeDeath.realMcpRequests).toBe(2);
const terminateResponse = await request.post("/__sequence-viewer-persistent/reset-mcp");
expect(terminateResponse.ok()).toBe(true);
const terminated = (await terminateResponse.json()) as McpTermination;
expect(terminated.before).toMatchObject({
pid: running.pid,
running: true,
});
expect(terminated.terminatedPid).toBe(running.pid);
expect(terminated.terminatedPidRunning).toBe(false);
expect(terminated.after).toMatchObject({
bridgeRequests: running.bridgeRequests,
pid: null,
running: false,
spawnCount: running.spawnCount,
});
await page.evaluate(() => window.remountPersistentNativeViewer());
await expect(viewer.getByLabel("Wrapped sequence view")).toBeVisible();
await expect(viewer.getByLabel("Search sequence")).toHaveValue("CGTA");
await viewer.getByRole("button", { name: "Display", exact: true }).click();
await expect(viewer.getByRole("button", { name: "Show forward strand" })).toBeVisible();
await expect(viewer.getByRole("button", { name: "Split", exact: true })).toHaveAttribute(
"aria-pressed",
"true",
);
const recovery = await page.evaluate(
(nativeRequestCount) => ({
bundle: window.__persistentNativeBundle,
checkpoint: window.readPersistentNativeCheckpoint(),
nativeRequests: window.__persistentNativeRequests
.slice(nativeRequestCount)
.map(({ name }) => name),
realMcpRequests: window.__realMcpRequests,
sourceEditRequests: window.__persistentNativeRequests.filter(({ name }) =>
name.includes("/source_edit/"),
).length,
}),
beforeDeath.nativeRequests,
);
expect(recovery.bundle).toEqual({
...beforeDeath.bundle,
mountCount: 2,
});
expect(recovery.checkpoint).toMatchObject({
family: "sequence",
mode: "sequence",
sequence: {
query: "CGTA",
view: {
layout: "split",
orientation: "reverse-complement",
},
},
sourceRevision: "installed-sequence-native-source-v1",
version: 1,
});
expect(recovery.nativeRequests).toEqual(
expect.arrayContaining([
"ui/scientific/sequence/records",
"ui/scientific/sequence/restore_checkpoint",
"ui/scientific/sequence/window",
]),
);
expect(recovery.realMcpRequests).toHaveLength(beforeDeath.realMcpRequests);
expect(recovery.sourceEditRequests).toBe(0);
expect(loadedViewerBundles).toEqual([]);
const finalResponse = await request.get("/__sequence-viewer-persistent/mcp-status");
expect(finalResponse.ok()).toBe(true);
expect((await finalResponse.json()) as McpProcessStatus).toEqual(terminated.after);
});
type McpProcessStatus = {
bridgeRequests: { resourceReads: number; toolCalls: number };
pid: number | null;
running: boolean;
spawnCount: number;
};
type McpTermination = {
after: McpProcessStatus;
before: McpProcessStatus;
terminatedPid: number | null;
terminatedPidRunning: boolean;
};
declare global {
interface Window {
__persistentNativeBundle:
| { byteLength: number; mountCount: number; sha256: string }
| undefined;
__persistentNativeRequests: Array<{
checkpointBytes?: number;
name: string;
revision: number;
}>;
readPersistentNativeCheckpoint: () =>
| {
family: "sequence";
mode: "alignment" | "sequence";
sequence?: {
query: string;
view: {
layout: "circular" | "linear" | "split";
orientation: "forward" | "reverse-complement";
};
};
sourceRevision: string;
version: 1;
}
| undefined;
remountPersistentNativeViewer: () => void;
}
}
SHA-256: 6d56a0467bdb1bcd117a2c1965908ba220c8a00aa3c3cd8bc29b2fe8fb6a80ec