import { expect, it } from "vitest";
import {
HTML_INLINE_SCRIPT_LOCATION_LIMIT,
htmlArtifactReferences,
} from "./htmlArtifactReferences.js";
it.each([
['', []],
['', []],
[``, []],
["", ["actual.js"]],
['', ["actual.js"]],
['', ["actual.js"]],
["", ["actual.js"]],
['', ["actual.js"]],
['', ["actual.js"]],
['', []],
['', ["actual.js"]],
['', []],
['', []],
['', []],
[``, []],
[
'',
[],
],
['', []],
[
'',
["actual.js"],
],
['', ["actual.js"]],
['', ["actual.js"]],
['', ["actual.js"]],
['', ["actual.js"]],
['', ["actual.js"]],
['', ["actual.js"]],
['', ["actual.js"]],
])("extracts document references from %s", (text, expected) => {
expect(
htmlArtifactReferences(text).scripts.map(({ scriptPath }) => scriptPath),
).toEqual(expected);
});
it("retains exact opening-tag coordinates rather than the script body", () => {
const opening = '`;
expect(htmlArtifactReferences(text)).toEqual({
baseHref: null,
inlineScripts: [],
inlineScriptCount: 0,
scripts: [
{
scriptPath: "actual.js",
scriptType: "classic",
location: {
start: { line: 3, column: 0 },
end: { line: 4, column: ' title=">" src=actual.js>'.length },
},
},
],
});
});
it.each([
['', "actual/"],
['', "actual/"],
['', ""],
['', "actual/"],
['', "actual/"],
])("selects the first real base href from %s", (text, expected) => {
expect(htmlArtifactReferences(text).baseHref).toBe(expected);
});
it("traverses deep valid document content without recursive stack growth", () => {
const depth = 12_000;
const text = `${"
".repeat(depth)}${"
".repeat(depth)}`;
expect(
htmlArtifactReferences(text).scripts.map(({ scriptPath }) => scriptPath),
).toEqual(["actual.js"]);
});
it.each([
["", ["classic"]],
['', ["classic"]],
['', ["classic"]],
['', ["classic"]],
['', ["classic"]],
['', ["classic"]],
['', ["module"]],
// Only the JavaScript MIME check strips whitespace; `module` must match.
['', []],
['', []],
["", ["classic"]],
// A src attribute, even an empty one, means the browser ignores the body.
['', []],
['', []],
["", []],
['', []],
['', []],
['', []],
['', []],
['', []],
['', []],
["", []],
["", []],
["", ["classic"]],
])("classifies inline JavaScript in %s", (text, expected) => {
expect(
htmlArtifactReferences(text).inlineScripts.map(
({ scriptType }) => scriptType,
),
).toEqual(expected);
});
it.each([
['src="ignored.js"', "classic"],
['language="vbscript"', "classic"],
['xlink:type="simple"', "classic"],
['type=" Text/JavaScript "', "classic"],
['type="MODULE"', "module"],
['type=" module "', null],
['type="application/json"', null],
['type="text/javascript; charset=utf-8"', null],
['href="external.js"', null],
['href=""', null],
['xlink:href="external.js"', null],
] as const)("classifies SVG inline bodies using %s", (attributes, expected) => {
const result = htmlArtifactReferences(
``,
);
expect(result.inlineScripts.map(({ scriptType }) => scriptType)).toEqual(
expected === null ? [] : [expected],
);
expect(result.inlineScriptCount).toBe(expected === null ? 0 : 1);
});
it.each([
['href="external.js"', ["external.js"]],
['xlink:href="external.js"', ["external.js"]],
['href="first.js" xlink:href="second.js"', ["first.js"]],
['xlink:href="second.js" href="first.js"', ["first.js"]],
['href="" xlink:href="ignored.js"', []],
['other:href="ignored.js"', []],
['src="ignored.js"', []],
])("extracts SVG script URL from %s", (attributes, paths) => {
const result = htmlArtifactReferences(
``,
);
expect(result.scripts.map(({ scriptPath }) => scriptPath)).toEqual(paths);
expect(result.inlineScriptCount).toBe(0);
});
it("shares precise body coordinates and location limits across HTML and SVG", () => {
const prefix = '\n`).inlineScripts,
).toEqual([
{
scriptType: "classic",
location: { start: { line: 2, column: 30 }, end: { line: 4, column: 0 } },
},
]);
const repeated = "".repeat(60);
const result = htmlArtifactReferences(repeated);
expect(result.inlineScriptCount).toBe(120);
expect(result.inlineScripts).toHaveLength(HTML_INLINE_SCRIPT_LOCATION_LIMIT);
expect(
htmlArtifactReferences("")
.inlineScriptCount,
).toBe(0);
});
it("retains exact inline script body coordinates, including unclosed scripts", () => {
const prefix = "\nafter
`;
expect(htmlArtifactReferences(closed).inlineScripts).toEqual([
{
scriptType: "classic",
location: { start: { line: 2, column: 8 }, end: { line: 4, column: 0 } },
},
]);
const unclosed = `${prefix}${body}`;
expect(htmlArtifactReferences(unclosed).inlineScripts).toEqual([
{
scriptType: "classic",
location: { start: { line: 2, column: 8 }, end: { line: 4, column: 0 } },
},
]);
});
it("counts every inline body but retains bounded locations", () => {
const count = 150_000;
const text = "".repeat(count);
const { inlineScripts, inlineScriptCount } = htmlArtifactReferences(text);
expect(inlineScriptCount).toBe(count);
expect(inlineScripts).toHaveLength(HTML_INLINE_SCRIPT_LOCATION_LIMIT);
const column = 18 * (HTML_INLINE_SCRIPT_LOCATION_LIMIT - 1) + 8;
expect(inlineScripts.at(-1)).toEqual({
scriptType: "classic",
location: {
start: { line: 1, column },
end: { line: 1, column: column + 1 },
},
});
});
it.each([
['type=" Text/JavaScript "', "classic"],
['type="MODULE"', "module"],
['type="application/json"', null],
['type="importmap"', null],
['type="speculationrules"', null],
['language="vbscript"', null],
['type=" module "', null],
])(
"retains external src with parsed JavaScript type for %s",
(attributes, scriptType) => {
expect(
htmlArtifactReferences(``)
.scripts,
).toMatchObject([{ scriptPath: "source.js", scriptType }]);
},
);