Files
remoteconn-gitea/apps/miniprogram/pages/terminal/codexCaptureReplay.test.ts
2026-03-21 18:57:10 +08:00

122 lines
3.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from "vitest";
const { decodeCodexTtyCapture20260311 } = require("./codexCaptureFixture.js");
const {
consumeTerminalSyncUpdateFrames,
createTerminalSyncUpdateState
} = require("./vtParser.js");
const {
getActiveTerminalBuffer,
rebuildTerminalBufferStateFromReplayText
} = require("./terminalBufferState.js");
const { buildTerminalViewportState } = require("./terminalViewportModel.js");
function splitTextIntoChunks(text: string, chunkSize: number) {
const chunks = [];
for (let index = 0; index < text.length; index += chunkSize) {
chunks.push(text.slice(index, index + chunkSize));
}
return chunks;
}
function serializeViewportLines(replayText: string) {
const state = rebuildTerminalBufferStateFromReplayText(replayText, {
bufferCols: 80,
bufferRows: 24
});
const active = getActiveTerminalBuffer(state);
const viewport = buildTerminalViewportState({
bufferRows: active.cells,
cursorRow: active.cursorRow,
activeBufferName: state.activeBufferName,
visibleRows: 24,
lineHeight: 20
});
return viewport.renderRows
.map((line) =>
(Array.isArray(line) ? line : [])
.filter((cell) => cell && !cell.continuation)
.map((cell) => cell.text || " ")
.join("")
.replace(/\s+$/, "")
)
.filter(Boolean);
}
describe("codexCaptureReplay", () => {
it("真实 Codex 抓包回放时,会在交互进行中同时保留 Working 行和底部 footer", () => {
const sample = decodeCodexTtyCapture20260311();
expect(sample).toContain("\u001b[?2026h");
expect(sample).toContain("Working");
expect(sample).toContain("gpt-5.4 xhigh");
let syncState = createTerminalSyncUpdateState();
let replayText = "";
let matchedLines: string[] | null = null;
splitTextIntoChunks(sample, 97).forEach((chunk) => {
if (matchedLines) {
return;
}
const result = consumeTerminalSyncUpdateFrames(chunk, syncState);
syncState = result.state;
if (!result.text) {
return;
}
replayText += result.text;
const lines = serializeViewportLines(replayText);
const hasWorking = lines.some((line) => line.includes("Working"));
const hasFooter = lines.some(
(line) => line.includes("gpt-5.4 xhigh") && line.includes("~/remoteconn")
);
const hasConversation = lines.some(
(line) =>
line.includes("Reply with the single word OK and then stop.") ||
line.includes("Summarize recent commits")
);
if (hasWorking && hasFooter && hasConversation) {
matchedLines = lines;
}
});
expect(matchedLines).not.toBeNull();
expect(matchedLines).toEqual(
expect.arrayContaining([
expect.stringContaining("Working"),
expect.stringContaining("gpt-5.4 xhigh"),
expect.stringContaining("Summarize recent commits")
])
);
});
it("真实 Codex 抓包完整回放后normal buffer viewport 仍会保留底部 footer", () => {
let syncState = createTerminalSyncUpdateState();
let replayText = "";
splitTextIntoChunks(decodeCodexTtyCapture20260311(), 97).forEach((chunk) => {
const result = consumeTerminalSyncUpdateFrames(chunk, syncState);
syncState = result.state;
replayText += result.text;
});
expect(syncState).toEqual({
depth: 0,
carryText: "",
bufferedText: ""
});
const lines = serializeViewportLines(replayText);
expect(
lines.some((line) => line.includes("gpt-5.4 xhigh") && line.includes("~/remoteconn"))
).toBe(true);
expect(lines).toEqual(
expect.arrayContaining([
expect.stringContaining("Conversation interrupted"),
expect.stringContaining("Summarize recent commits")
])
);
});
});