refactor!: replace snapshot with reader fallback, collapse commands
Remove the snapshot command and enhance reader to try Firefox Reader View first, falling back to raw Turndown conversion of document.body when Reader View fails or is skipped via --no-reader. - reader always returns markdown by default (--format=json for structured) - JSON output includes method: 'reader' | 'raw' to signal extraction path - --no-reader skips Reader View (stays on loaded page, preserving JS mutations) - Add @ts-nocheck to test/smoke.js and exclude test/ from tsconfig - Update all tests from snapshot to reader with --no-reader for data URIs - Update AGENTS.md and help text BREAKING CHANGE: snapshot subcommand removed; use reader instead.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// @ts-nocheck
|
||||
|
||||
import {
|
||||
mkdtempSync,
|
||||
@@ -73,7 +74,7 @@ test("no args prints help", ["help", "cli"], () => {
|
||||
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.match(result.stdout, /Usage: glimpse <command> <url> \[options\]/);
|
||||
assert.match(result.stdout, /snapshot <url>/);
|
||||
assert.match(result.stdout, /reader <url>/);
|
||||
assert.equal(result.stderr, "");
|
||||
});
|
||||
|
||||
@@ -86,48 +87,50 @@ test("help flag prints help", ["help", "cli"], () => {
|
||||
assert.equal(result.stderr, "");
|
||||
});
|
||||
|
||||
test("snapshot returns page metadata and content", ["snapshot"], () => {
|
||||
const output = expectSuccess([
|
||||
"snapshot",
|
||||
test("reader extracts page content as markdown", ["reader"], () => {
|
||||
const result = runCli([
|
||||
"reader",
|
||||
dataHtml(
|
||||
'<title>Hello</title><h1>Main</h1><a href="/x">X</a><button>Go</button>',
|
||||
'<title>Hello</title><h1>Main</h1><p>Some text</p><a href="https://example.com">Link</a>',
|
||||
),
|
||||
"--no-reader",
|
||||
]);
|
||||
|
||||
assert.equal(output.ok, true);
|
||||
assert.equal(output.title, "Hello");
|
||||
assert.equal(typeof output.elapsedMs, "number");
|
||||
assert.deepEqual(output.result.headings, [{ level: 1, text: "Main" }]);
|
||||
assert.deepEqual(output.result.links, [{ href: "/x", text: "X" }]);
|
||||
assert.equal(output.result.buttons[0].text, "Go");
|
||||
assert.match(output.result.text, /Main/);
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const output = result.stdout.trim();
|
||||
assert.match(output, /# Main/);
|
||||
assert.match(output, /Some text/);
|
||||
assert.match(output, /\[Link\]\(https:\/\/example\.com\/??\)/);
|
||||
});
|
||||
|
||||
test("snapshot extracts aria headings", ["snapshot"], () => {
|
||||
test("reader returns json format with method field", ["reader"], () => {
|
||||
const output = expectSuccess([
|
||||
"snapshot",
|
||||
dataHtml(
|
||||
'<title>Hello</title><div role="heading" aria-level="2">ARIA</div>',
|
||||
),
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title><h1>Main</h1><p>World</p>"),
|
||||
"--no-reader",
|
||||
"--format=json",
|
||||
]);
|
||||
|
||||
assert.equal(output.ok, true);
|
||||
assert.deepEqual(output.result.headings, [{ level: 2, text: "ARIA" }]);
|
||||
assert.equal(output.title, "Hello");
|
||||
assert.equal(output.method, "raw");
|
||||
assert.equal(typeof output.markdown, "string");
|
||||
assert.match(output.markdown, /# Main/);
|
||||
assert.match(output.text, /Main/);
|
||||
});
|
||||
|
||||
test(
|
||||
"snapshot runs top-level javascript before extraction",
|
||||
["snapshot", "js"],
|
||||
"reader runs top-level javascript before extraction",
|
||||
["reader", "js"],
|
||||
() => {
|
||||
const output = expectSuccess([
|
||||
"snapshot",
|
||||
const result = runCli([
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title><h1>Old</h1>"),
|
||||
"--no-reader",
|
||||
"--js=document.querySelector('h1').textContent = 'New'",
|
||||
]);
|
||||
|
||||
assert.equal(output.ok, true);
|
||||
assert.deepEqual(output.result.headings, [{ level: 1, text: "New" }]);
|
||||
assert.equal(output.result.text, "New");
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.match(result.stdout, /# New/);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -182,8 +185,9 @@ test(
|
||||
writeFileSync(configPath, "not json");
|
||||
|
||||
const output = expectFailure([
|
||||
"snapshot",
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title>"),
|
||||
"--no-reader",
|
||||
`--config=${configPath}`,
|
||||
]);
|
||||
|
||||
@@ -201,8 +205,9 @@ test(
|
||||
writeFileSync(configPath, JSON.stringify({ search: { provider: 42 } }));
|
||||
|
||||
const output = expectFailure([
|
||||
"snapshot",
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title>"),
|
||||
"--no-reader",
|
||||
`--config=${configPath}`,
|
||||
]);
|
||||
|
||||
@@ -218,17 +223,17 @@ test("empty home config is accepted", ["config"], () => {
|
||||
mkdirSync(configDir, { recursive: true });
|
||||
writeFileSync(join(configDir, "config.json"), "{}");
|
||||
|
||||
const output = expectSuccess(
|
||||
["snapshot", dataHtml("<title>Hello</title><h1>Main</h1>")],
|
||||
const result = runCli(
|
||||
["reader", dataHtml("<title>Hello</title><h1>Main</h1>"), "--no-reader"],
|
||||
{ env: { ...process.env, XDG_CONFIG_HOME: configHome } },
|
||||
);
|
||||
|
||||
assert.equal(output.ok, true);
|
||||
assert.equal(output.title, "Hello");
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.match(result.stdout, /# Main/);
|
||||
});
|
||||
|
||||
test("unknown command returns structured error", ["errors", "cli"], () => {
|
||||
const output = expectFailure(["nope", dataHtml("<title>Hello</title>")]);
|
||||
const output = expectFailure(["nope", dataHtml("<title>Hello</title>"), "--no-reader"]);
|
||||
|
||||
assert.equal(output.ok, false);
|
||||
assert.equal(output.error.code, "UNKNOWN_COMMAND");
|
||||
@@ -241,8 +246,9 @@ test(
|
||||
["errors", "timeout"],
|
||||
() => {
|
||||
const output = expectFailure([
|
||||
"snapshot",
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title>"),
|
||||
"--no-reader",
|
||||
"--timeout=abc",
|
||||
]);
|
||||
|
||||
@@ -255,8 +261,9 @@ test(
|
||||
|
||||
test("invalid wait-until returns invalid option", ["errors", "wait"], () => {
|
||||
const output = expectFailure([
|
||||
"snapshot",
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title>"),
|
||||
"--no-reader",
|
||||
"--wait-until=loaded",
|
||||
]);
|
||||
|
||||
@@ -266,20 +273,22 @@ test("invalid wait-until returns invalid option", ["errors", "wait"], () => {
|
||||
});
|
||||
|
||||
test("wait-js succeeds when condition is true", ["wait"], () => {
|
||||
const output = expectSuccess([
|
||||
"snapshot",
|
||||
dataHtml("<title>Hello</title>"),
|
||||
const result = runCli([
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title><h1>Main</h1>"),
|
||||
"--no-reader",
|
||||
'--wait-js=return document.title === "Hello"',
|
||||
]);
|
||||
|
||||
assert.equal(output.ok, true);
|
||||
assert.equal(output.title, "Hello");
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.match(result.stdout, /# Main/);
|
||||
});
|
||||
|
||||
test("wait-js timeout returns wait timeout", ["wait", "errors"], () => {
|
||||
const output = expectFailure([
|
||||
"snapshot",
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title>"),
|
||||
"--no-reader",
|
||||
"--wait-js=return false",
|
||||
"--timeout=1",
|
||||
]);
|
||||
@@ -296,8 +305,9 @@ test(
|
||||
["wait", "errors", "js"],
|
||||
() => {
|
||||
const output = expectFailure([
|
||||
"snapshot",
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title>"),
|
||||
"--no-reader",
|
||||
'--wait-js=throw new Error("boom")',
|
||||
]);
|
||||
|
||||
@@ -313,8 +323,9 @@ test(
|
||||
["errors", "js"],
|
||||
() => {
|
||||
const output = expectFailure([
|
||||
"snapshot",
|
||||
"reader",
|
||||
dataHtml("<title>Hello</title>"),
|
||||
"--no-reader",
|
||||
'--js=throw new Error("boom")',
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user