mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-28 20:22:45 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f9982b488 |
@@ -1,11 +0,0 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { runInNewContext } from "node:vm";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
it("loads the native terminal dependency with the initial React Native navigator", () => {
|
||||
const require = createRequire(import.meta.url);
|
||||
const source = readFileSync(require.resolve("@xterm/headless"), "utf8");
|
||||
// React Native exposes navigator before the app's layout initializes its polyfill.
|
||||
expect(() => runInNewContext(source, { navigator: {}, module: { exports: {} } })).not.toThrow();
|
||||
});
|
||||
@@ -47,33 +47,3 @@ describe("renderTerminalSnapshotToAnsi", () => {
|
||||
expect(ansi).toContain("ABCDEFGHIJ\r\nKLMNOP");
|
||||
});
|
||||
});
|
||||
|
||||
describe("renderTerminalSnapshotToAnsi wide chars", () => {
|
||||
it("skips wide-char continuation cells so the glyph keeps its two columns", () => {
|
||||
// The daemon captures "통" as the glyph followed by an empty continuation
|
||||
// cell. Emitting anything for that cell pushes the rest of the row right.
|
||||
const state: TerminalState = {
|
||||
rows: 1,
|
||||
cols: 10,
|
||||
scrollback: [],
|
||||
grid: [[{ char: "통" }, { char: "" }, { char: "합" }, { char: "" }, { char: "!" }]],
|
||||
cursor: { row: 0, col: 5 },
|
||||
};
|
||||
|
||||
expect(renderTerminalSnapshotToAnsi(state)).toContain("통합!");
|
||||
});
|
||||
|
||||
it("replays snapshots from an old daemon verbatim, including its continuation spaces", () => {
|
||||
// Old daemons already collapsed the continuation to " ". The serializer
|
||||
// cannot tell that apart from a real space, so it must not guess.
|
||||
const state: TerminalState = {
|
||||
rows: 1,
|
||||
cols: 10,
|
||||
scrollback: [],
|
||||
grid: [cells("통 합 !")],
|
||||
cursor: { row: 0, col: 6 },
|
||||
};
|
||||
|
||||
expect(renderTerminalSnapshotToAnsi(state)).toContain("통 합 !");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,12 +87,6 @@ function renderTerminalRow(row: TerminalCell[], padToCols?: number): string {
|
||||
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
const cell = row[index] ?? { char: " " };
|
||||
// An empty char marks a wide-char continuation cell. The glyph in the
|
||||
// previous cell already advances the cursor two columns, so emitting
|
||||
// anything here would shift the rest of the row one column right.
|
||||
if (cell.char === "") {
|
||||
continue;
|
||||
}
|
||||
const nextStyle = getTerminalStyle(cell);
|
||||
if (!terminalStylesEqual(previousStyle, nextStyle)) {
|
||||
output.push(styleToAnsi(nextStyle));
|
||||
|
||||
@@ -298,6 +298,35 @@ describe("createGitLabService", () => {
|
||||
expect(calls[1]).toEqual(["mr", "view", "14", "-F", "json"]);
|
||||
});
|
||||
|
||||
it.each(["list", "view"])(
|
||||
"resolves the current merge request when %s returns legacy null references",
|
||||
async (legacyCommand) => {
|
||||
const legacyMr = {
|
||||
...OPEN_MR,
|
||||
reference: "!14",
|
||||
references: null,
|
||||
web_url: "https://gitlab.example.com/example-group/example-project/merge_requests/14",
|
||||
};
|
||||
const { service } = makeService((args) => {
|
||||
const mr = args[1] === legacyCommand ? legacyMr : OPEN_MR;
|
||||
if (args[0] === "mr" && args[1] === "list") return ok(JSON.stringify([mr]));
|
||||
if (args[0] === "mr" && args[1] === "view") return ok(JSON.stringify(mr));
|
||||
if (args[0] === "api") return ok("{}");
|
||||
throw new Error(`unexpected call: ${args.join(" ")}`);
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.getCurrentPullRequestStatus({ cwd: "/repo", headRef: "release/v0.4.0" }),
|
||||
).resolves.toMatchObject({
|
||||
number: 14,
|
||||
title: OPEN_MR.title,
|
||||
state: "open",
|
||||
headRefName: "release/v0.4.0",
|
||||
url: legacyCommand === "view" ? legacyMr.web_url : OPEN_MR.web_url,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("reports a conflicting merge request as CONFLICTING", async () => {
|
||||
const conflicting = {
|
||||
...OPEN_MR,
|
||||
|
||||
@@ -168,7 +168,7 @@ const GitLabMergeRequestSchema = z
|
||||
description: z.string().nullable().optional(),
|
||||
labels: z.array(z.string()).optional(),
|
||||
updated_at: z.string().optional(),
|
||||
references: z.object({ full: z.string().optional() }).passthrough().optional(),
|
||||
references: z.object({ full: z.string().optional() }).passthrough().nullable().optional(),
|
||||
head_pipeline: GitLabPipelineSchema.nullable().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
@@ -24,8 +24,6 @@ import {
|
||||
} from "node:fs";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import * as pty from "node-pty";
|
||||
import xterm from "@xterm/headless";
|
||||
import { renderTerminalSnapshotToAnsi } from "@getpaseo/protocol/terminal-snapshot";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { setImmediate as waitForImmediate, setTimeout as delay } from "node:timers/promises";
|
||||
@@ -52,10 +50,6 @@ function getLines(state: ReturnType<TerminalSession["getState"]>): string[] {
|
||||
return state.grid.map(rowToText);
|
||||
}
|
||||
|
||||
function rowHasChar(row: TerminalRow, char: string): boolean {
|
||||
return row.some((cell) => cell.char === char);
|
||||
}
|
||||
|
||||
// Wait for terminal state to match expected lines
|
||||
async function waitForLines(
|
||||
session: TerminalSession,
|
||||
@@ -529,44 +523,6 @@ describe("createTerminal", () => {
|
||||
expect(withoutFlags.scrollbackWrapped).toBeUndefined();
|
||||
});
|
||||
|
||||
it("restores CJK text without injected spaces when a snapshot round-trips through the client", async () => {
|
||||
// Each Hangul syllable is a wide char: xterm stores it as a 2-cell pair
|
||||
// whose second cell has no chars. Capturing that continuation as " " made
|
||||
// every restored syllable three columns wide (paseo#3434).
|
||||
const text = "통합 구현은 전체 테스트를 통과했습니다";
|
||||
const session = trackSession(
|
||||
await createTerminal({
|
||||
workspaceId: "ws-test",
|
||||
cwd: realpathSync(tmpdir()),
|
||||
cols: 80,
|
||||
rows: 10,
|
||||
command: process.execPath,
|
||||
args: [
|
||||
"-e",
|
||||
`process.stdout.write(${JSON.stringify(text)}); setInterval(() => {}, 100000);`,
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const state = await waitForState(session, (current) => rowHasChar(current.grid[0], "다"));
|
||||
|
||||
// Replay the snapshot the way the client does on tab switch and read the
|
||||
// row back the way a selection copy would.
|
||||
const restored = new xterm.Terminal({ cols: 80, rows: 10, allowProposedApi: true });
|
||||
try {
|
||||
await new Promise<void>((resolve) =>
|
||||
restored.write(renderTerminalSnapshotToAnsi(state), resolve),
|
||||
);
|
||||
expect(restored.buffer.active.getLine(0)?.translateToString(true)).toBe(text);
|
||||
} finally {
|
||||
restored.dispose();
|
||||
}
|
||||
|
||||
// The continuation cell must stay empty so the restore path can skip it.
|
||||
expect(state.grid[0][0].char).toBe("통");
|
||||
expect(state.grid[0][1].char).toBe("");
|
||||
});
|
||||
|
||||
it("captures exit diagnostics from the terminal buffer", async () => {
|
||||
const session = trackSession(
|
||||
await createTerminal({
|
||||
|
||||
@@ -580,9 +580,7 @@ function extractCell(terminal: TerminalType, row: number, col: number): Terminal
|
||||
const bg = bgMode !== 0 ? cell.getBgColor() : undefined;
|
||||
|
||||
return {
|
||||
// Wide chars (CJK) span two cells; xterm returns "" for the zero-width
|
||||
// continuation. Collapsing it to " " injects a real space on restore.
|
||||
char: cell.getWidth() === 0 ? "" : cell.getChars() || " ",
|
||||
char: cell.getChars() || " ",
|
||||
fg,
|
||||
bg,
|
||||
fgMode: fgMode !== 0 ? fgMode : undefined,
|
||||
@@ -641,9 +639,7 @@ function extractScrollback(
|
||||
const fg = fgMode !== 0 ? cell.getFgColor() : undefined;
|
||||
const bg = bgMode !== 0 ? cell.getBgColor() : undefined;
|
||||
rowCells.push({
|
||||
// Wide chars (CJK) span two cells; xterm returns "" for the zero-width
|
||||
// continuation. Collapsing it to " " injects a real space on restore.
|
||||
char: cell.getWidth() === 0 ? "" : cell.getChars() || " ",
|
||||
char: cell.getChars() || " ",
|
||||
fg,
|
||||
bg,
|
||||
fgMode: fgMode !== 0 ? fgMode : undefined,
|
||||
|
||||
Reference in New Issue
Block a user