mirror of
https://github.com/software-mansion/argent.git
synced 2026-09-14 19:27:14 +08:00
1e8e5fa309
Three independent reliability fixes for **Argent Lens** (the `propose_variant` / `await_user_selection` variant-review flow and its preview window). They touch disjoint files and were validated separately; combined here into one PR. --- ### 1. Preview window 404s on dot-path installs (nvm/fnm/volta/asdf) The Lens window showed a raw `NotFoundError` instead of the variant UI whenever argent was installed under a path containing a dot-segment (most commonly nvm, `~/.nvm/...`). `GET /preview/` and `/preview/theme.css` returned 404 while JSON routes worked. Cause: the express 4 → 5 bump (#354, shipped in v0.12.1). Express 5's `send@1` defaults to `dotfiles: "ignore"`, which 404s any file path containing a dot-segment, and the preview routes pass the absolute install path to `res.sendFile`. Proven in isolation: express 5.2.1 `sendFile` of the file → 404; express 4.22.1 → 200; `sendFile(basename, { root: dir })` → 200. Fix: serve the preview UI via `sendFile(basename, { root: dir })` (a `serveUiFile` helper) so the request path carries no dot-segment. Regression test serves the UI from a temp dir whose name starts with `.`. > Note: the underlying `send@1` dot-path behaviour is a property of the express-5 migration, not of Lens — these two routes are simply the only `sendFile` calls in the tool-server today. ### 2. Variant fails to anchor right after navigation A `propose_variant` made immediately after navigating to a screen (exactly the navigate → screenshot → propose workflow) often failed to anchor: the card floated/hid ("didn't register"), or latched onto a same-text distractor (e.g. a bottom-bar tab instead of a header). Cause: `captureElementFrame` describes the device **once** at propose time, but iOS' accessibility tree is empty for ~1.25 s after a screen appears and then fills incrementally (nav chrome before screen content) — measured on a real device. Fix: `captureElementFrame` now retries across the warm-up window and **holds out for an exact label/identifier hit** (the intended element), falling back to a substring match only after the budget. `findElementMatch` / `matchFrameInTree` rank exact matches above substring ones. Adds `match-element-frame-cold-describe.test.ts`; two existing tests updated for the new retry signature. ### 3. Off-screen choices were unreachable In the preview window, the off-screen reveal prompt (the "Show them" card and the "N hidden — show" pill) was hidden unless **every on-screen choice was already staged** (`onScreenStaged`). So a proposal whose element was off-screen showed a hidden-choice count with no affordance to surface it. Fix: show the prompt whenever any element is off-screen (`offScreenCount > 0 && !revealOffscreen && !submitting`), dropping the `onScreenStaged` gate. --- Supersedes #390, #392, #393 (same commits, combined).
51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import express from "express";
|
|
import supertest from "supertest";
|
|
|
|
import { serveUiFile } from "../src/preview";
|
|
|
|
// Regression guard for the Express 5 `send` dotfiles default (`dotfiles: "ignore"`),
|
|
// which 404s any file path containing a dot-segment. argent is routinely installed
|
|
// under such a path — nvm (`~/.nvm/...`), fnm, volta, asdf — so a bare
|
|
// `res.sendFile(absolutePath)` made the Lens preview window fail to load its UI
|
|
// (`/preview/` and `/preview/theme.css` returned NotFoundError). serveUiFile must
|
|
// serve the file regardless of where on disk it lives.
|
|
describe("serveUiFile under a dot-segment install path", () => {
|
|
let dir: string;
|
|
|
|
beforeAll(() => {
|
|
// Directory name starts with "." — the exact shape that trips send's dotfile
|
|
// filter, mirroring an install at ~/.nvm/.../preview-ui.
|
|
dir = fs.mkdtempSync(path.join(os.tmpdir(), ".argent-dotfix-"));
|
|
fs.writeFileSync(path.join(dir, "index.html"), "<!doctype html><title>lens</title>");
|
|
fs.writeFileSync(path.join(dir, "theme.css"), ":root{--accent:#0a7ea4}");
|
|
});
|
|
|
|
afterAll(() => {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
});
|
|
|
|
function appServing(file: string, contentType: string) {
|
|
const app = express();
|
|
app.get("/x", (_req, res) => serveUiFile(res, path.join(dir, file), contentType));
|
|
return app;
|
|
}
|
|
|
|
it("serves index.html from a dot-path (200, not 404)", async () => {
|
|
const res = await supertest(appServing("index.html", "text/html")).get("/x");
|
|
expect(res.status).toBe(200);
|
|
expect(res.headers["content-type"]).toMatch(/text\/html/);
|
|
expect(res.text).toContain("lens");
|
|
});
|
|
|
|
it("serves theme.css from a dot-path (200, not 404)", async () => {
|
|
const res = await supertest(appServing("theme.css", "text/css")).get("/x");
|
|
expect(res.status).toBe(200);
|
|
expect(res.headers["content-type"]).toMatch(/text\/css/);
|
|
expect(res.text).toContain("--accent");
|
|
});
|
|
});
|