Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/faster-pty-transitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
78 changes: 46 additions & 32 deletions test/pty/chrome.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,17 @@ describe("PTY chrome", () => {
const initial = await session.waitForText(/Adds bonus export\./, { timeout: 15_000 });
expect(initial).toContain("Highlights the follow-up addition for review.");

await session.click(/View/);
const viewMenu = await session.waitForText(/Themes…/, { timeout: 5_000 });
const viewMenu = await harness.clickAndWaitForText(session, /View/, /Themes…/, {
timeout: 5_000,
});
expect(viewMenu).toContain("Themes…");

await session.click(/Themes…/);
const themeSelector = await session.waitForText(/github-light-default/, { timeout: 5_000 });
const themeSelector = await harness.clickAndWaitForText(
session,
/Themes…/,
/github-light-default/,
{ timeout: 5_000 },
);
expect(themeSelector).toContain("Theme selector");

await session.click(/github-light-default/);
Expand All @@ -56,8 +61,10 @@ describe("PTY chrome", () => {
);
expect(themeSelected).toContain("Adds bonus export.");

await session.click(/Agent/, { first: true });
const agentMenu = await session.waitForText(/Next annotated file/, { timeout: 5_000 });
const agentMenu = await harness.clickAndWaitForText(session, /Agent/, /Next annotated file/, {
first: true,
timeout: 5_000,
});
expect(agentMenu).toContain("Agent notes");

await session.click(/Agent notes/);
Expand All @@ -67,15 +74,18 @@ describe("PTY chrome", () => {
5_000,
);

await session.click(/Agent/, { first: true });
await session.waitForText(/Agent notes/, { timeout: 5_000 });
await session.click(/Agent notes/);
await session.waitForText(/Adds bonus export\./, { timeout: 5_000 });
await harness.clickAndWaitForText(session, /Agent/, /Agent notes/, {
first: true,
timeout: 5_000,
});
await harness.clickAndWaitForText(session, /Agent notes/, /Adds bonus export\./, {
timeout: 5_000,
});

await session.click(/Help/);
await session.waitForText(/Controls help/, { timeout: 5_000 });
await session.click(/Controls help/);
const helpDialog = await session.waitForText(/Navigation/, { timeout: 5_000 });
await harness.clickAndWaitForText(session, /Help/, /Controls help/, { timeout: 5_000 });
const helpDialog = await harness.clickAndWaitForText(session, /Controls help/, /Navigation/, {
timeout: 5_000,
});

// The key column is rendered from the commands' resolved chords.
expect(helpDialog).toContain("g / Home");
Expand All @@ -97,8 +107,7 @@ describe("PTY chrome", () => {

try {
await session.waitForText(/View\s+Navigate\s+Agent\s+Help/, { timeout: 15_000 });
await session.press("t");
await session.waitForText(/Theme selector/, { timeout: 5_000 });
await harness.pressAndWaitForText(session, "t", /Theme selector/, { timeout: 5_000 });

// OS key repeat arrives as a rapid stream while React/OpenTUI drains each preview render.
for (let index = 0; index < 100; index += 1) {
Expand Down Expand Up @@ -134,15 +143,20 @@ describe("PTY chrome", () => {
try {
await session.waitForText(/line60/, { timeout: 15_000 });

await session.press("t");
await session.waitForText(/Theme selector/, { timeout: 5_000 });
await session.press("down");
await session.waitForText(/›\s+github-dark-dimmed/, { timeout: 5_000 });
await session.press("enter");
await harness.waitForSnapshot(session, (text) => !text.includes("Theme selector"), 5_000);
await harness.pressAndWaitForText(session, "t", /Theme selector/, { timeout: 5_000 });
await harness.pressAndWaitForText(session, "down", /›\s+github-dark-dimmed/, {
timeout: 5_000,
});
await harness.pressAndWaitForSnapshot(
session,
"enter",
(text) => !text.includes("Theme selector"),
5_000,
);

await session.press("q");
const prompt = await session.waitForText(/Save view preferences\?/, { timeout: 5_000 });
const prompt = await harness.pressAndWaitForText(session, "q", /Save view preferences\?/, {
timeout: 5_000,
});
expect(prompt).toContain('- theme = "github-dark-default"');
expect(prompt).toContain('+ theme = "github-dark-dimmed"');
expect(prompt).toContain("enter/s save");
Expand Down Expand Up @@ -266,9 +280,9 @@ describe("PTY chrome", () => {
timeout: 15_000,
});

await session.press("?");
const help = await harness.waitForSnapshot(
const help = await harness.pressAndWaitForSnapshot(
session,
"?",
(text) =>
(text.includes("Keyboard help") || text.includes("Controls help")) &&
text.includes("move through lines and notes"),
Expand Down Expand Up @@ -339,27 +353,27 @@ describe("PTY chrome", () => {

expect(initial).toMatch(/▌.*▌/);

await session.press("f10");
const fileMenu = await harness.waitForSnapshot(
const fileMenu = await harness.pressAndWaitForSnapshot(
session,
"f10",
(text) => text.includes("Toggle files/filter focus") && text.includes("Quit"),
5_000,
);

expect(fileMenu).toContain("Reload");

await session.press("right");
const viewMenu = await harness.waitForSnapshot(
const viewMenu = await harness.pressAndWaitForSnapshot(
session,
"right",
(text) => text.includes("Split view") && text.includes("Unified view"),
5_000,
);

expect(viewMenu).toContain("Auto layout");

await session.press("enter");
const unified = await harness.waitForSnapshot(
const unified = await harness.pressAndWaitForSnapshot(
session,
"enter",
(text) => !/▌.*▌/.test(text) && text.includes("1 - export const alpha = 1;"),
5_000,
);
Expand Down
111 changes: 73 additions & 38 deletions test/pty/cursor-line.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
dragMouse,
lineIndexOf,
measureKeyScroll,
pressKeyRepeat,
rowCellBackgrounds,
sleep,
} from "./harness";
Expand Down Expand Up @@ -140,8 +141,9 @@ describe("PTY current line", () => {
}

session.writeRaw(`\x1b[<0;31;${endRow + 1}m`);
await session.press("y");
await session.waitForText(/Copied selection to clipboard/, { timeout: 5_000 });
await harness.pressAndWaitForText(session, "y", /Copied selection to clipboard/, {
timeout: 5_000,
});
} finally {
session.close();
}
Expand Down Expand Up @@ -173,11 +175,16 @@ describe("PTY current line", () => {
expect(splitLines[lensIndex + 1]).toContain("export const message = 'short';");
expect(splitLines[lensIndex + 2]).toContain("this is a very long wrapped line");

await session.press("1");
await harness.waitForSnapshot(session, (text) => !text.includes("Current line"), 5_000);
await harness.pressAndWaitForSnapshot(
session,
"1",
(text) => !text.includes("Current line"),
5_000,
);

await session.press("2");
await session.waitForText(/Current line · old above, new below/, { timeout: 5_000 });
await harness.pressAndWaitForText(session, "2", /Current line · old above, new below/, {
timeout: 5_000,
});
} finally {
session.close();
}
Expand Down Expand Up @@ -267,15 +274,20 @@ describe("PTY current line", () => {

try {
await session.waitForText(/View\s+Navigate\s+Agent\s+Help/, { timeout: 15_000 });
await session.press("z");
await harness.waitForSnapshot(session, (text) => text.includes("hiddenLine01"), 5_000);
await harness.pressAndWaitForSnapshot(
session,
"z",
(text) => text.includes("hiddenLine01"),
5_000,
);
// The revealed rows reach navigation one commit after they reach the screen.
await session.waitIdle({ timeout: 500 });

await session.press("k");
await session.waitIdle({ timeout: 200 });
await session.press("c");
const draft = await session.waitForText(/Draft note/, { timeout: 5_000 });
const draft = await harness.pressAndWaitForText(session, "c", /Draft note/, {
timeout: 5_000,
});

expect(lineIndexOf(draft, "Draft note")).toBe(lineIndexOf(draft, "hiddenLine01") + 1);
} finally {
Expand All @@ -294,29 +306,48 @@ describe("PTY current line", () => {
try {
await session.waitForText(/View\s+Navigate\s+Agent\s+Help/, { timeout: 15_000 });
await session.waitIdle({ timeout: 300 });
await session.press("c");
const beforeExpand = await session.waitForText(/Draft note/, { timeout: 5_000 });
const beforeExpand = await harness.pressAndWaitForText(session, "c", /Draft note/, {
timeout: 5_000,
});
const startRow = /Draft note[^R]*R(\d+)/.exec(beforeExpand)?.[1];
expect(startRow).toBeDefined();
await session.press("escape");
await harness.waitForSnapshot(session, (text) => !text.includes("Draft note"), 5_000);

await session.press("z");
await harness.waitForSnapshot(session, (text) => text.includes("hiddenLine01"), 5_000);
await harness.pressAndWaitForSnapshot(
session,
"escape",
(text) => !text.includes("Draft note"),
5_000,
);

await harness.pressAndWaitForSnapshot(
session,
"z",
(text) => text.includes("hiddenLine01"),
5_000,
);
await session.waitIdle({ timeout: 500 });
await session.press("c");
const expanded = await session.waitForText(/Draft note/, { timeout: 5_000 });
const expanded = await harness.pressAndWaitForText(session, "c", /Draft note/, {
timeout: 5_000,
});

expect(expanded).toContain("R1 ");
expect(lineIndexOf(expanded, "Draft note")).toBe(lineIndexOf(expanded, "hiddenLine01") + 1);
await session.press("escape");
await harness.waitForSnapshot(session, (text) => !text.includes("Draft note"), 5_000);

await session.press("z");
await harness.waitForSnapshot(session, (text) => !text.includes("hiddenLine01"), 5_000);
await harness.pressAndWaitForSnapshot(
session,
"escape",
(text) => !text.includes("Draft note"),
5_000,
);

await harness.pressAndWaitForSnapshot(
session,
"z",
(text) => !text.includes("hiddenLine01"),
5_000,
);
await session.waitIdle({ timeout: 500 });
await session.press("c");
const collapsed = await session.waitForText(/Draft note/, { timeout: 5_000 });
const collapsed = await harness.pressAndWaitForText(session, "c", /Draft note/, {
timeout: 5_000,
});

expect(collapsed).toContain(`R${startRow} `);
} finally {
Expand Down Expand Up @@ -365,8 +396,9 @@ describe("PTY current line", () => {
const anchor = paged[12]?.trim() ?? "";
expect(anchor.length).toBeGreaterThan(0);

await session.press("c");
const draft = await session.waitForText(/Draft note/, { timeout: 5_000 });
const draft = await harness.pressAndWaitForText(session, "c", /Draft note/, {
timeout: 5_000,
});

expect(draft).toContain(anchor);
} finally {
Expand All @@ -387,21 +419,24 @@ describe("PTY current line", () => {
await session.waitForText(/View\s+Navigate\s+Agent\s+Help/, { timeout: 15_000 });
await session.waitIdle({ timeout: 300 });

await session.press("c");
const draftAtTop = await session.waitForText(/Draft note/, { timeout: 5_000 });
const draftAtTop = await harness.pressAndWaitForText(session, "c", /Draft note/, {
timeout: 5_000,
});
const draftRowAtTop = lineIndexOf(draftAtTop, "Draft note");
expect(draftRowAtTop).toBeGreaterThan(0);

await session.press("escape");
await harness.waitForSnapshot(session, (text) => !text.includes("Draft note"), 5_000);
await harness.pressAndWaitForSnapshot(
session,
"escape",
(text) => !text.includes("Draft note"),
5_000,
);

for (let step = 0; step < 4; step += 1) {
await session.press("j");
await session.waitIdle({ timeout: 200 });
}
await pressKeyRepeat(session, "j", 4);

await session.press("c");
const draftAtCursor = await session.waitForText(/Draft note/, { timeout: 5_000 });
const draftAtCursor = await harness.pressAndWaitForText(session, "c", /Draft note/, {
timeout: 5_000,
});

expect(lineIndexOf(draftAtCursor, "Draft note")).toBeGreaterThan(draftRowAtTop);
} finally {
Expand Down
Loading
Loading