Files
rhinelab-ui/scripts/check-archive-momentum.mjs

206 lines
6.5 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdir, writeFile } from "node:fs/promises";
import { resolve } from "node:path";
import { pathToFileURL } from "node:url";
const { chromium } = await import(
process.env.PLAYWRIGHT_MODULE
? pathToFileURL(resolve(process.env.PLAYWRIGHT_MODULE)).href
: "playwright"
);
const browser = await chromium.launch({
channel: "chrome",
headless: true,
args: ["--use-angle=d3d11", "--enable-gpu", "--ignore-gpu-blocklist"],
});
const report = [];
const stats = (page) => page.evaluate(() => window.rhine.stats());
const rest = (page) =>
page.waitForFunction(() => !window.rhine.stats().archiveMomentum, null, {
timeout: 12000,
});
try {
for (const mobile of [false, true].filter(
(mobile) =>
!process.env.REVIEW_CASES ||
process.env.REVIEW_CASES === (mobile ? "mobile" : "desktop"),
)) {
const width = mobile ? 390 : 1920,
height = mobile ? 844 : 1080;
const context = await browser.newContext({
viewport: { width, height },
hasTouch: mobile,
isMobile: mobile,
});
const page = await context.newPage(),
errors = [];
page.on("pageerror", (e) => errors.push(e.message));
await page.goto(
`${process.env.REVIEW_URL || "http://127.0.0.1:5204"}/?scene=archive`,
);
await page.waitForFunction(
() => window.rhine?.stats().extraction >= 0.399,
null,
{ timeout: 60000 },
);
await page.locator('[data-action="next"]').click();
await page.waitForTimeout(2200);
const cdp = mobile ? await context.newCDPSession(page) : null;
const x = width * 0.6,
y = height * 0.38;
const down = async () =>
mobile
? cdp.send("Input.dispatchTouchEvent", {
type: "touchStart",
touchPoints: [{ x, y, id: 1 }],
})
: (await page.mouse.move(x, y), page.mouse.down());
const move = async (px, py) =>
mobile
? cdp.send("Input.dispatchTouchEvent", {
type: "touchMove",
touchPoints: [{ x: px, y: py, id: 1 }],
})
: page.mouse.move(px, py);
const up = async () =>
mobile
? cdp.send("Input.dispatchTouchEvent", {
type: "touchEnd",
touchPoints: [],
})
: page.mouse.up();
const fling = async (delay, axis = "row") => {
await down();
const vector = (await stats(page)).dragProjection[axis];
const travel = axis === "row" ? 3.2 : 0.8;
const segments = delay < 30 ? 4 : 8;
for (let i = 1; i <= segments; i++) {
await page.waitForTimeout(delay);
await move(
x + (vector.x * travel * i) / segments,
y + (vector.y * travel * i) / segments,
);
}
await up();
};
const startSlow = await stats(page);
await fling(90);
await rest(page);
const slow = await stats(page);
const slowDistance = slow.selectedCell.row - startSlow.selectedCell.row;
await fling(8);
const released = await stats(page);
assert.equal(released.archiveMomentum?.phase, "coasting");
assert.ok(released.archiveMomentum.velocity.row > 2);
await page.waitForTimeout(450);
const moving = await stats(page);
assert.ok(
moving.selectedCell.row > released.selectedCell.row,
"Files change as the released array passes them",
);
assert.ok(
moving.rail < released.rail - 0.3,
"Released array keeps travelling",
);
assert.ok(
moving.archiveMomentum.velocity.row <
released.archiveMomentum.velocity.row,
"Speed decays continuously",
);
await rest(page);
const fast = await stats(page);
const fastDistance = fast.selectedCell.row - slow.selectedCell.row;
console.log({
mobile,
slowDistance,
fastDistance,
releaseVelocity: released.archiveMomentum.velocity.row,
});
assert.ok(
fastDistance > slowDistance,
"Fast travel goes farther than identical slow travel",
);
assert.ok(
fastDistance > 3,
"Fast scrolling is no longer capped at three files",
);
// Catch the array without moving the pointer, then take over on another axis.
await fling(8);
await page.waitForTimeout(120);
await down();
const caught = await stats(page);
assert.equal(caught.archiveMomentum, null);
assert.equal(caught.holdingArchive, true);
await page.waitForTimeout(220);
const held = await stats(page);
assert.equal(
held.rail,
caught.rail,
"Pressing catches the current rail before direction lock",
);
assert.deepEqual(held.selectedCell, caught.selectedCell);
const capturedVector = caught.dragProjection.lane;
await move(x + capturedVector.x * 0.8, y + capturedVector.y * 0.8);
await page.waitForTimeout(160);
await up();
await rest(page);
assert.equal(
(await stats(page)).selectedCell.lane,
caught.selectedCell.lane + 1,
);
await fling(8, "lane");
const lateral = await stats(page);
assert.ok(
lateral.archiveMomentum?.velocity.lane > 0,
JSON.stringify(lateral.archiveMomentum),
);
await page.waitForTimeout(250);
assert.ok((await stats(page)).columnCamera > lateral.columnCamera + 0.5);
await rest(page);
await fling(8);
await page.keyboard.press("ArrowDown");
const keyboard = await stats(page);
assert.equal(
keyboard.archiveMomentum,
null,
"Keyboard selection stops the previous coast",
);
await page.waitForTimeout(500);
assert.deepEqual((await stats(page)).selectedCell, keyboard.selectedCell);
await page.locator('[data-action="settings"]').click();
await page.locator('[data-pref="reduced"]').check({ force: true });
await page.locator('[data-action="close-modal"]').click();
await page.waitForFunction(
() => !document.querySelector(".modal-backdrop"),
);
await fling(8);
assert.equal(
(await stats(page)).archiveMomentum,
null,
"Reduced motion skips continued scrolling",
);
assert.deepEqual(errors, []);
report.push({
mobile,
slowDistance,
fastDistance,
releaseVelocity: released.archiveMomentum.velocity.row,
releaseRow: released.selectedCell.row,
movingRow: moving.selectedCell.row,
checks: "passed",
});
console.log(report.at(-1));
await context.close();
}
} finally {
await mkdir(".tools/array-input", { recursive: true });
await writeFile(
resolve(".tools/array-input/momentum.json"),
JSON.stringify(report, null, 2),
);
await browser.close();
}