mirror of
https://github.com/morluto/rea.git
synced 2026-10-22 13:52:32 -05:00
* test(javascript): reproduce omitted NodeNext artifact sources Tests only: qualify current .mts/.cts artifact selection through real directory and ASAR reconstruction and the built public CLI. Signed-off-by: Rudy Celekli <rudy@gradiahq.com> * fix(javascript): retain NodeNext artifact source facts Read .mts and .cts artifact files through the existing inert TypeScript parser, preserving selected file identities and analysis facts in directory and ASAR inputs. Document source selection and format the existing regression assertion. Signed-off-by: Rudy Celekli <rudy@gradiahq.com> --------- Signed-off-by: Rudy Celekli <rudy@gradiahq.com> Co-authored-by: N0zoM1z0 <1705004398@qq.com>
150 lines
5.2 KiB
TypeScript
150 lines
5.2 KiB
TypeScript
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { expect } from "vitest";
|
|
import { z } from "zod";
|
|
|
|
import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascript/javascriptApplicationAnalysis.js";
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { analyzeJavaScriptApplication } from "../../../src/application/javascript/JavaScriptApplicationService.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
const progressLine = z.object({
|
|
rea_progress: z.object({
|
|
phase: z.string(),
|
|
completed: z.number(),
|
|
total: z.number().nullable(),
|
|
message: z.string(),
|
|
sequence: z.number(),
|
|
terminal: z.boolean().optional(),
|
|
}),
|
|
});
|
|
|
|
for (const command of ["analyze-javascript-application", "analyze"]) {
|
|
cliTest(
|
|
`${command} reports real analysis phases on stderr and preserves stdout Evidence`,
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-js-cli-progress-");
|
|
await writeFile(join(root, "main.js"), "fetch('');\n");
|
|
const direct = await analyzeJavaScriptApplication({ input_path: root });
|
|
if (!direct.ok) throw direct.error;
|
|
|
|
const output = await cli.run({ arguments: [command, root, "--json"] });
|
|
expect(output.exitCode).toBe(0);
|
|
expect(parseEvidence(output.json).evidence_id).toBe(
|
|
direct.value.evidence_id,
|
|
);
|
|
const updates = output.stderr
|
|
.trim()
|
|
.split("\n")
|
|
.map((line) => progressLine.parse(JSON.parse(line)).rea_progress);
|
|
expect(updates[0]?.completed).toBe(0);
|
|
expect(
|
|
updates.some(({ phase }) => phase === "parse_javascript_sources"),
|
|
).toBe(true);
|
|
expect(updates.at(-1)).toMatchObject({ completed: 1, terminal: true });
|
|
expect(
|
|
updates.slice(0, -1).every(({ terminal }) => terminal !== true),
|
|
).toBe(true);
|
|
},
|
|
);
|
|
}
|
|
|
|
cliTest(
|
|
"keeps a failed analysis distinct from a completed result",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-js-cli-progress-failure-");
|
|
const output = await cli.run({
|
|
arguments: [
|
|
"analyze-javascript-application",
|
|
join(root, "absent"),
|
|
"--json",
|
|
],
|
|
});
|
|
expect(output.json).toMatchObject({ code: "artifact_operation_failed" });
|
|
const updates = output.stderr
|
|
.trim()
|
|
.split("\n")
|
|
.map((line) => progressLine.parse(JSON.parse(line)).rea_progress);
|
|
expect(updates[0]?.completed).toBe(0);
|
|
expect(updates.every(({ terminal }) => terminal !== true)).toBe(true);
|
|
},
|
|
);
|
|
|
|
for (const extension of ["mts", "cts"]) {
|
|
for (const command of ["analyze-javascript-application", "analyze"]) {
|
|
cliTest(
|
|
`${command} returns facts for a selected ${extension} source`,
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-nodenext-cli-");
|
|
const path = `selected.${extension}`;
|
|
await writeFile(
|
|
join(root, path),
|
|
"export function selected_feature(value: string) { return { value }; }",
|
|
);
|
|
const output = await cli.run({ arguments: [command, root, "--json"] });
|
|
expect(output.exitCode).toBe(0);
|
|
const evidence = parseEvidence(output.json);
|
|
const result = javascriptApplicationAnalysisResultSchema.parse(
|
|
evidence.normalized_result,
|
|
);
|
|
expect(result.statistics).toMatchObject({
|
|
relevant_files: 1,
|
|
parsed_javascript_files: 1,
|
|
parse_failures: 0,
|
|
});
|
|
expect(
|
|
result.graph.nodes.some(
|
|
({ kind, observations }) =>
|
|
kind === "javascript-asset" &&
|
|
observations.some(({ properties }) => properties.path === path),
|
|
),
|
|
).toBe(true);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
for (const command of ["analyze-javascript-application", "analyze"]) {
|
|
cliTest(
|
|
`${command} retains HTML source ranges after a bare carriage return`,
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-html-coordinate-cli-");
|
|
const opening = '<script src="actual.js">';
|
|
await Promise.all([
|
|
writeFile(
|
|
join(root, "index.html"),
|
|
`<main>context</main>\r${opening}</script>`,
|
|
),
|
|
writeFile(join(root, "actual.js"), "export const actual = true;"),
|
|
]);
|
|
const output = await cli.run({ arguments: [command, root, "--json"] });
|
|
expect(output.exitCode).toBe(0);
|
|
const evidence = parseEvidence(output.json);
|
|
const result = javascriptApplicationAnalysisResultSchema.parse(
|
|
evidence.normalized_result,
|
|
);
|
|
const loads = result.graph.edges.filter(
|
|
({ relation, properties }) =>
|
|
relation === "loads" && properties.script_path === "actual.js",
|
|
);
|
|
expect(loads).toHaveLength(1);
|
|
expect(loads[0]).toMatchObject({
|
|
properties: { resolved_path: "actual.js" },
|
|
evidence: {
|
|
location: {
|
|
available: true,
|
|
value: {
|
|
kind: "source-range",
|
|
source: "index.html",
|
|
start: { line: 2, column: 0 },
|
|
end: { line: 2, column: opening.length },
|
|
},
|
|
},
|
|
},
|
|
});
|
|
},
|
|
);
|
|
}
|