Own decoded node columns, attributes and parent validity once for web, Electron and SVG interpretation. Reject dangling, self and cyclic parent facts; preserve source positions and independently unknown fields. Remove fallback conversions and duplicated string/attribute interpretation. Validation: baseline public CLI controls failed for four malformed cases with valid control passing; final 103 focused checks, 5 final built CLI controls, 1 late-detach cancellation control (20 skipped), check:fast and docs:check passed. Fake CDP producers and generated local files; no real browser/Electron or Windows qualification. Refs #4709
127 lines
4.0 KiB
TypeScript
127 lines
4.0 KiB
TypeScript
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { expect } from "vitest";
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { electronPageInspectionSchema } from "../../../src/domain/javascript/electronObservation.js";
|
|
import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
const cases: ReadonlyArray<{
|
|
name: string;
|
|
parents: readonly number[];
|
|
names?: readonly number[];
|
|
secondDocument?: boolean;
|
|
indices: readonly number[];
|
|
invalid: number;
|
|
}> = [
|
|
{ name: "valid parent", parents: [-1, 0, 1], indices: [0, 1, 2], invalid: 0 },
|
|
{
|
|
name: "out-of-range parent",
|
|
parents: [-1, 0, 99999],
|
|
indices: [0, 1],
|
|
invalid: 1,
|
|
},
|
|
{ name: "self parent", parents: [-1, 0, 2], indices: [0, 1], invalid: 1 },
|
|
{ name: "parent cycle", parents: [-1, 2, 1], indices: [0], invalid: 2 },
|
|
{
|
|
name: "unknown name before a second document",
|
|
parents: [-1, 0, 1],
|
|
names: [1, 999, 3],
|
|
secondDocument: true,
|
|
indices: [0, 2, 3, 4, 5],
|
|
invalid: 1,
|
|
},
|
|
];
|
|
|
|
for (const scenario of cases)
|
|
cliTest(
|
|
`Electron DOM admission preserves source identities: ${scenario.name}`,
|
|
async ({ cli, onTestFinished }) => {
|
|
const root = await createTestTempDirectory("rea-electron-dom-admission-");
|
|
const main = join(root, "index.html");
|
|
await writeFile(main, "<html/>");
|
|
const url = pathToFileURL(main).href;
|
|
const document = (
|
|
parents: readonly number[],
|
|
names: readonly number[],
|
|
) => ({
|
|
documentURL: 0,
|
|
nodes: {
|
|
nodeType: [9, 1, 1],
|
|
nodeName: names,
|
|
nodeValue: [-1, -1, 4],
|
|
parentIndex: parents,
|
|
attributes: [[], [], []],
|
|
},
|
|
});
|
|
const browser = await startFakeCdpBrowser({
|
|
electronFileUrl: url,
|
|
commandResult: ({ method }) =>
|
|
method === "DOMSnapshot.captureSnapshot"
|
|
? {
|
|
strings: [url, "#document", "DIV", "SPAN", "😀"],
|
|
documents: [
|
|
document(scenario.parents, scenario.names ?? [1, 2, 3]),
|
|
...(scenario.secondDocument
|
|
? [document([-1, 0, 1], [1, 2, 3])]
|
|
: []),
|
|
],
|
|
}
|
|
: undefined,
|
|
});
|
|
onTestFinished(() => browser.close());
|
|
const output = await cli.run({
|
|
arguments: [
|
|
"inspect-electron-page",
|
|
browser.endpoint,
|
|
"electron-page",
|
|
"--observation-ms",
|
|
"0",
|
|
"--json",
|
|
],
|
|
});
|
|
expect(output.exitCode, output.stderr).toBe(0);
|
|
const inspection = electronPageInspectionSchema.parse(
|
|
parseEvidence(output.json).normalized_result,
|
|
);
|
|
expect(inspection.dom.total_nodes).toBe(scenario.secondDocument ? 6 : 3);
|
|
expect(inspection.dom.nodes.map(({ index }) => index)).toEqual(
|
|
scenario.indices,
|
|
);
|
|
const excluded = inspection.completeness.excluded.filter(
|
|
({ section, reason }) =>
|
|
section === "dom" && reason === "invalid_protocol_value",
|
|
);
|
|
expect(excluded).toEqual(
|
|
scenario.invalid === 0
|
|
? []
|
|
: [
|
|
{
|
|
section: "dom",
|
|
reason: "invalid_protocol_value",
|
|
count: scenario.invalid,
|
|
},
|
|
],
|
|
);
|
|
if (scenario.secondDocument)
|
|
expect(inspection.dom.nodes.at(-1)).toMatchObject({
|
|
index: 5,
|
|
parent_index: 4,
|
|
node_value_length: 4,
|
|
});
|
|
if (scenario.invalid === 0)
|
|
expect(
|
|
inspection.dom.nodes.map(({ parent_index, node_value_length }) => [
|
|
parent_index,
|
|
node_value_length,
|
|
]),
|
|
).toEqual([
|
|
[-1, 0],
|
|
[0, 0],
|
|
[1, 4],
|
|
]);
|
|
},
|
|
);
|