Files
paseo/packages/app/src/terminal/native-renderer/terminal-resize-policy.test.ts
T
Mohamed BoudraandGitHub 04fe30a3e2 Improve mobile terminal input, selection, and resizing (#1607)
* Improve mobile terminal typing and selection

* Address mobile terminal review feedback

* Remove dead native terminal input branch

* Replay bracketed paste in terminal preamble

* Stop per-key terminal resize claims

* Stabilize terminal resize browser test

* Use tracked terminal mode for native paste

* Restore terminal toolbar focus handling

* Adapt native terminal to active visibility state

* Stabilize native mobile terminal interactions

* Add native terminal fallback boundary

* test(app): expect explicit terminal resize reclaim

* fix(app): stabilize terminal buffer coordinates

* fix(app): restrict terminal resize ownership

* fix(terminal): gate restored input modes

* fix(app): recover stalled terminal WebViews

* chore(app): remove obsolete terminal harnesses

* fix(app): make terminal resize claims explicit

* test(app): stabilize native terminal device flows

* test(app): track absolute scrollback coordinates

* fix(app): isolate terminal WebView streams

* fix(terminal): arbitrate size ownership across clients

The daemon now distinguishes explicit ownership claims from passive geometry updates. This keeps the active client authoritative while allowing its split, viewport, and keyboard changes to resize the PTY without idle clients stealing it.

* test(server): make worker resize assertion portable

Query the worker's authoritative terminal snapshot instead of invoking a Unix-only shell command, so the resize regression exercises the same boundary on Windows and Unix.
2026-08-03 19:01:34 +08:00

293 lines
8.3 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { createTerminalResizePolicy, updateTerminalResizePolicy } from "./terminal-resize-policy";
describe("terminal resize policy", () => {
test("passive first layout records measured size without claiming terminal size", () => {
const initial = createTerminalResizePolicy({ rows: 24, cols: 80 });
const result = updateTerminalResizePolicy(initial, {
source: "measure",
size: { rows: 41, cols: 48 },
});
expect(result).toEqual({
state: {
measuredSize: { rows: 41, cols: 48 },
claimedSize: null,
lastClaimToken: null,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 41, cols: 48 },
resizeClaim: null,
measuredSizeChanged: true,
});
});
test("focus or explicit ownership claims the measured size", () => {
const measured = updateTerminalResizePolicy(
createTerminalResizePolicy({ rows: 24, cols: 80 }),
{
source: "measure",
size: { rows: 41, cols: 48 },
},
);
const result = updateTerminalResizePolicy(measured.state, {
source: "claim",
size: { rows: 41, cols: 48 },
});
expect(result).toEqual({
state: {
measuredSize: { rows: 41, cols: 48 },
claimedSize: { rows: 41, cols: 48 },
lastClaimToken: null,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 41, cols: 48 },
resizeClaim: { rows: 41, cols: 48, force: false },
measuredSizeChanged: false,
});
});
test("focus or explicit ownership before measurement claims the next measured size", () => {
const pendingClaim = updateTerminalResizePolicy(
createTerminalResizePolicy({ rows: 24, cols: 80 }),
{
source: "claim",
size: null,
},
);
const result = updateTerminalResizePolicy(pendingClaim.state, {
source: "measure",
size: { rows: 41, cols: 48 },
});
expect(pendingClaim).toEqual({
state: {
measuredSize: { rows: 24, cols: 80 },
claimedSize: null,
lastClaimToken: null,
pendingClaim: true,
pendingForceClaim: false,
},
measuredSize: { rows: 24, cols: 80 },
resizeClaim: null,
measuredSizeChanged: false,
});
expect(result).toEqual({
state: {
measuredSize: { rows: 41, cols: 48 },
claimedSize: { rows: 41, cols: 48 },
lastClaimToken: null,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 41, cols: 48 },
resizeClaim: { rows: 41, cols: 48, force: false },
measuredSizeChanged: true,
});
});
test("duplicate claimed sizes are deduped", () => {
const firstClaim = updateTerminalResizePolicy(
createTerminalResizePolicy({ rows: 24, cols: 80 }),
{
source: "claim",
size: { rows: 41, cols: 48 },
claimToken: 1,
},
);
const result = updateTerminalResizePolicy(firstClaim.state, {
source: "claim",
size: { rows: 41, cols: 48 },
claimToken: 1,
});
expect(result).toEqual({
state: {
measuredSize: { rows: 41, cols: 48 },
claimedSize: { rows: 41, cols: 48 },
lastClaimToken: 1,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 41, cols: 48 },
resizeClaim: null,
measuredSizeChanged: false,
});
});
test("explicit interaction can reclaim the same measured size", () => {
const firstClaim = updateTerminalResizePolicy(
createTerminalResizePolicy({ rows: 24, cols: 80 }),
{
source: "claim",
size: { rows: 21, cols: 55 },
claimToken: 1,
},
);
const result = updateTerminalResizePolicy(firstClaim.state, {
source: "claim",
size: { rows: 21, cols: 55 },
claimToken: 2,
});
expect(result).toEqual({
state: {
measuredSize: { rows: 21, cols: 55 },
claimedSize: { rows: 21, cols: 55 },
lastClaimToken: 2,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 21, cols: 55 },
resizeClaim: { rows: 21, cols: 55, force: true },
measuredSizeChanged: false,
});
});
test("explicit interaction can reclaim after policy reset loses the previous claimed size", () => {
const resetPolicy = createTerminalResizePolicy({ rows: 21, cols: 55 });
const result = updateTerminalResizePolicy(resetPolicy, {
source: "claim",
size: { rows: 21, cols: 55 },
claimToken: 2,
});
expect(result).toEqual({
state: {
measuredSize: { rows: 21, cols: 55 },
claimedSize: { rows: 21, cols: 55 },
lastClaimToken: 2,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 21, cols: 55 },
resizeClaim: { rows: 21, cols: 55, force: true },
measuredSizeChanged: false,
});
});
test("explicit interaction before measurement forces the next measured reclaim after policy reset", () => {
const pendingClaim = updateTerminalResizePolicy(
createTerminalResizePolicy({ rows: 21, cols: 55 }),
{
source: "claim",
size: null,
claimToken: 2,
},
);
const result = updateTerminalResizePolicy(pendingClaim.state, {
source: "measure",
size: { rows: 21, cols: 55 },
});
expect(result).toEqual({
state: {
measuredSize: { rows: 21, cols: 55 },
claimedSize: { rows: 21, cols: 55 },
lastClaimToken: 2,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 21, cols: 55 },
resizeClaim: { rows: 21, cols: 55, force: true },
measuredSizeChanged: false,
});
});
test("passive measurement of the same size still dedupes after a claim", () => {
const firstClaim = updateTerminalResizePolicy(
createTerminalResizePolicy({ rows: 24, cols: 80 }),
{
source: "claim",
size: { rows: 21, cols: 55 },
claimToken: 1,
},
);
const result = updateTerminalResizePolicy(firstClaim.state, {
source: "measure",
size: { rows: 21, cols: 55 },
});
expect(result).toEqual({
state: {
measuredSize: { rows: 21, cols: 55 },
claimedSize: { rows: 21, cols: 55 },
lastClaimToken: 1,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 21, cols: 55 },
resizeClaim: null,
measuredSizeChanged: false,
});
});
test("passive layout changes never reuse an earlier explicit claim", () => {
const firstClaim = updateTerminalResizePolicy(
createTerminalResizePolicy({ rows: 24, cols: 80 }),
{
source: "claim",
size: { rows: 41, cols: 48 },
},
);
const duplicateLayout = updateTerminalResizePolicy(firstClaim.state, {
source: "measure",
size: { rows: 41, cols: 48 },
});
const changedLayout = updateTerminalResizePolicy(duplicateLayout.state, {
source: "measure",
size: { rows: 43, cols: 50 },
});
expect(duplicateLayout.resizeClaim).toEqual(null);
expect(changedLayout.resizeClaim).toEqual(null);
expect(changedLayout.measuredSize).toEqual({ rows: 43, cols: 50 });
});
test("keyboard measurement stays passive until the interaction explicitly reclaims", () => {
const focused = updateTerminalResizePolicy(createTerminalResizePolicy({ rows: 24, cols: 80 }), {
source: "claim",
size: { rows: 41, cols: 53 },
});
const keyboardOpen = updateTerminalResizePolicy(focused.state, {
source: "measure",
size: { rows: 26, cols: 53 },
});
expect(keyboardOpen).toEqual({
state: {
measuredSize: { rows: 26, cols: 53 },
claimedSize: { rows: 41, cols: 53 },
lastClaimToken: null,
pendingClaim: false,
pendingForceClaim: false,
},
measuredSize: { rows: 26, cols: 53 },
resizeClaim: null,
measuredSizeChanged: true,
});
const settledKeyboardClaim = updateTerminalResizePolicy(keyboardOpen.state, {
source: "claim",
size: { rows: 26, cols: 53 },
claimToken: 1,
});
expect(settledKeyboardClaim.resizeClaim).toEqual({ rows: 26, cols: 53, force: true });
});
});