1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
import assert from "node:assert/strict";
function tags(frame) {
return frame.styleRows.flatMap((cells, y) => y > 0 && cells.some((cell) => cell[2] & 128) ? [y] : []);
}
function pane(frame, name) {
const rows = tags(frame);
const top = rows.find((row) => frame.lines[row].includes(name));
assert.notEqual(top, undefined, `missing pane ${name}`);
const bottom = rows.find((row) => row > top) ?? frame.rows;
return { top, bottom, lines: frame.lines.slice(top + 1, bottom) };
}
function styled(frame) {
const source = pane(frame, "touch-fixture.zig");
for (const [line, word, fg, bold] of [
["pub fn choose", "fn", 0xcf87e8, true],
["pub fn choose", "choose", 0xbdbdbd, false],
["if (value", "if", 0xcf87e8, true],
["if (value", "42", 0xe3c78a, false],
["if (value", '"touch fixture"', 0xe3c78a, false],
]) {
const row = source.lines.findIndex((text) => text.includes(line));
assert(row >= 0, `missing source line ${line}`);
const col = source.lines[row].indexOf(word);
for (let x = col; x < col + word.length; x++) {
const style = frame.styleRows[source.top + 1 + row][x];
assert.equal(style[0], fg, `${word} cell ${x - col} foreground`);
assert.equal(Boolean(style[2] & 1), bold, `${word} cell ${x - col} bold`);
}
}
for (const [row, line] of source.lines.entries()) {
const number = /\b(\d+),/.exec(line);
if (!number) continue;
for (let x = number.index; x < number.index + number[1].length; x++) {
assert.equal(frame.styleRows[source.top + 1 + row][x][0], 0xe3c78a, `row ${row} number foreground`);
}
}
}
export default async function ({ grid, touch, wait, stable, delay }) {
await stable(100, 5000);
let frame = await grid();
assert.equal(tags(frame).length, 1);
styled(frame);
const link = "/virtual/pane/1/body";
const row = frame.lines.findIndex((text) => text.includes(link));
const col = frame.lines[row].indexOf(link) + 5;
let finger = await touch("down", 1, col, row, null);
finger = await touch("move", 1, col + 0.2, row + 0.1, finger);
await touch("up", 1, col + 0.2, row + 0.1, finger);
await wait(async () => tags(await grid()).length === 2, 5000, "Look opening the reflected body");
await stable(100, 5000);
frame = await grid();
styled(frame);
const reflected = pane(frame, link);
assert(reflected.lines.some((text) => text.includes("pub fn choose")));
const original = pane(frame, "touch-fixture.zig");
const sharedRows = Math.min(reflected.lines.length, original.lines.length);
assert(sharedRows >= 6);
assert.deepEqual(reflected.lines.slice(0, sharedRows), original.lines.slice(0, sharedRows), "Look must read the source pane body");
const center = Math.floor(frame.cols / 2);
const separator = Math.max(original.top, reflected.top) - 1;
const distance = original.top < reflected.top ? 4 : -4;
finger = await touch("down", 2, center, separator, null);
for (let step = 1; step <= 8; step++) {
finger = await touch("move", 2, center, separator + distance * step / 8, finger);
await delay(20);
}
await touch("up", 2, center, separator + distance, finger);
await stable(100, 5000);
frame = await grid();
assert.equal(tags(frame).length, 2);
const resized = pane(frame, "touch-fixture.zig");
assert(resized.lines.length >= original.lines.length + 3, "separator drag must resize, not scroll the body");
assert.equal(resized.lines[0], original.lines[0], "resizing must preserve the source viewport");
styled(frame);
const beforeScroll = resized.lines[0];
const reflectedBeforeScroll = pane(frame, link).lines;
const from = resized.bottom - 3;
const to = Math.max(resized.top + 2, from - 7);
finger = await touch("down", 3, center, from, null);
for (let step = 1; step <= 12; step++) {
finger = await touch("move", 3, center, from + (to - from) * step / 12, finger);
await delay(20);
}
await touch("up", 3, center, to, finger);
await stable(100, 5000);
frame = await grid();
assert.equal(tags(frame).length, 2, "scroll must not also Look");
const scrolled = pane(frame, "touch-fixture.zig");
assert.equal(scrolled.top, resized.top, "body scrolling must not resize");
assert.notEqual(scrolled.lines[0], beforeScroll, "source must scroll");
assert(Number(scrolled.lines[0].trim().split(/\s+/)[0]) > 1, "scroll advances source line numbers");
assert.deepEqual(pane(frame, link).lines, reflectedBeforeScroll, "scrolling the source must leave its neighbor alone");
}
|