Skip to content

Commit f4cacaa

Browse files
committed
Added new unit test cases for preload link elements
1 parent 416a3c2 commit f4cacaa

1 file changed

Lines changed: 83 additions & 44 deletions

File tree

test/internal.spec.ts

Lines changed: 83 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
} from "../src/internal";
1212
// Use TS source import path (extensionless)
1313
// @ts-ignore - resolved by TS during tests
14-
export { };
14+
export {};
1515

1616
type BundleEntry = { code?: any; source?: any };
1717
type Bundle = Record<string, BundleEntry>;
@@ -64,7 +64,9 @@ describe("helpers", () => {
6464

6565
describe("getUrlAttrName", () => {
6666
it("returns src for script and href otherwise", () => {
67-
const $s = cheerioLoad('<script src="/a.js"></script>')("script").get(0);
67+
const $s = cheerioLoad('<script src="/a.js"></script>')(
68+
"script"
69+
).get(0);
6870
const $l = cheerioLoad('<link rel="stylesheet" href="/a.css">')(
6971
"link"
7072
).get(0);
@@ -95,12 +97,10 @@ describe("helpers", () => {
9597

9698
it("fetches remote when http and returns bytes", async () => {
9799
const bytes = new Uint8Array([1, 2, 3]);
98-
globalThis.fetch = vi
99-
.fn()
100-
.mockResolvedValue({
101-
ok: true,
102-
arrayBuffer: () => Promise.resolve(bytes.buffer),
103-
}) as any;
100+
globalThis.fetch = vi.fn().mockResolvedValue({
101+
ok: true,
102+
arrayBuffer: () => Promise.resolve(bytes.buffer),
103+
}) as any;
104104
const res = await loadResource(
105105
"https://example.com/x.js",
106106
{},
@@ -112,44 +112,40 @@ describe("helpers", () => {
112112
});
113113

114114
it("throws on failed fetch", async () => {
115-
globalThis.fetch = vi
116-
.fn()
117-
.mockResolvedValue({
118-
ok: false,
119-
status: 404,
120-
statusText: "Not Found",
121-
}) as any;
115+
globalThis.fetch = vi.fn().mockResolvedValue({
116+
ok: false,
117+
status: 404,
118+
statusText: "Not Found",
119+
}) as any;
122120
await expect(
123121
loadResource("https://example.com/404.js", {})
124122
).rejects.toThrow("Failed to fetch");
125123
});
126124

127125
it("supports protocol-relative URLs via https", async () => {
128126
const bytes = new Uint8Array([9, 8, 7]);
129-
const fetchSpy = vi
130-
.fn()
131-
.mockResolvedValue({
132-
ok: true,
133-
arrayBuffer: () => Promise.resolve(bytes.buffer),
134-
});
127+
const fetchSpy = vi.fn().mockResolvedValue({
128+
ok: true,
129+
arrayBuffer: () => Promise.resolve(bytes.buffer),
130+
});
135131
globalThis.fetch = fetchSpy as any;
136132
const res = await loadResource(
137133
"//cdn.example.com/lib.js",
138134
{},
139135
{ enableCache: false }
140136
);
141137
expect(res).toBeInstanceOf(Uint8Array);
142-
expect(fetchSpy.mock.calls[0][0]).toBe("https://cdn.example.com/lib.js");
138+
expect(fetchSpy.mock.calls[0][0]).toBe(
139+
"https://cdn.example.com/lib.js"
140+
);
143141
});
144142

145143
it("caches remote fetches when enabled", async () => {
146144
const bytes = new Uint8Array([5, 4, 3]);
147-
const fetchSpy = vi
148-
.fn()
149-
.mockResolvedValue({
150-
ok: true,
151-
arrayBuffer: () => Promise.resolve(bytes.buffer),
152-
});
145+
const fetchSpy = vi.fn().mockResolvedValue({
146+
ok: true,
147+
arrayBuffer: () => Promise.resolve(bytes.buffer),
148+
});
153149
globalThis.fetch = fetchSpy as any;
154150
const cache = new Map<string, Uint8Array>();
155151
const url = "https://example.com/once.js";
@@ -197,7 +193,9 @@ describe("helpers", () => {
197193
});
198194

199195
it("prefers source over code when code is missing", async () => {
200-
const bundle = mockBundle({ "a.js": { source: "console.log(42)" } });
196+
const bundle = mockBundle({
197+
"a.js": { source: "console.log(42)" },
198+
});
201199
const res = await loadResource("/a.js", bundle);
202200
expect(res).toBe("console.log(42)");
203201
});
@@ -226,7 +224,11 @@ describe("helpers", () => {
226224
it("skips when already has integrity", async () => {
227225
const $script = $("script");
228226
$script.attr("integrity", "sha256-abc");
229-
await processElement($script, mockBundle({ "a.js": "x" }), "sha256");
227+
await processElement(
228+
$script,
229+
mockBundle({ "a.js": "x" }),
230+
"sha256"
231+
);
230232
expect($script.attr("integrity")).toBe("sha256-abc");
231233
});
232234

@@ -238,12 +240,22 @@ describe("helpers", () => {
238240

239241
it("skips when element wrapper is invalid", async () => {
240242
const fakeWrapper: any = { get: () => undefined, attr: () => {} };
241-
await processElement(fakeWrapper, mockBundle({ "a.js": "x" }), "sha256");
243+
await processElement(
244+
fakeWrapper,
245+
mockBundle({ "a.js": "x" }),
246+
"sha256"
247+
);
242248
});
243249

244250
it("skips when URL attribute missing", async () => {
245-
const $noHref = cheerioLoad('<link rel="stylesheet"></link>')("link");
246-
await processElement($noHref, mockBundle({ "a.css": "x" }), "sha256");
251+
const $noHref = cheerioLoad('<link rel="stylesheet"></link>')(
252+
"link"
253+
);
254+
await processElement(
255+
$noHref,
256+
mockBundle({ "a.css": "x" }),
257+
"sha256"
258+
);
247259
expect($noHref.attr("integrity")).toBeUndefined();
248260
});
249261
});
@@ -290,38 +302,65 @@ describe("helpers", () => {
290302
const bundle = mockBundle({
291303
"dist/assets/nested/path/app.js": "console.log(1)",
292304
});
293-
const out = await addSriToHtml(html, bundle, { algorithm: "sha256" });
305+
const out = await addSriToHtml(html, bundle, {
306+
algorithm: "sha256",
307+
});
294308
expect(out).toContain('integrity="sha256-');
295309
});
296310

297311
it("resolves bundle items via basename fallback", async () => {
298312
const html = `<html><head><script src="/assets/app.js"></script></head></html>`;
299313
const bundle = mockBundle({ "dist/app.js": "console.log(2)" });
300-
const out = await addSriToHtml(html, bundle, { algorithm: "sha256" });
314+
const out = await addSriToHtml(html, bundle, {
315+
algorithm: "sha256",
316+
});
301317
expect(out).toContain('integrity="sha256-');
302318
});
303319

304320
it("warns per element when processing fails", async () => {
305321
const realFetch = globalThis.fetch;
306-
const warnLocal = vi.spyOn(console, "warn").mockImplementation(() => {});
322+
const warnLocal = vi
323+
.spyOn(console, "warn")
324+
.mockImplementation(() => {});
307325
try {
308-
globalThis.fetch = vi
309-
.fn()
310-
.mockResolvedValue({
311-
ok: false,
312-
status: 500,
313-
statusText: "ERR",
314-
}) as any;
326+
globalThis.fetch = vi.fn().mockResolvedValue({
327+
ok: false,
328+
status: 500,
329+
statusText: "ERR",
330+
}) as any;
315331
const html = `<html><head><script src=\"//cdn.example.com/a.js\"></script></head></html>`;
316332
const out = await addSriToHtml(html, mockBundle({}), {
317333
algorithm: "sha256",
318334
});
319-
expect(out).toContain('<script src="//cdn.example.com/a.js"></script>');
335+
expect(out).toContain(
336+
'<script src="//cdn.example.com/a.js"></script>'
337+
);
320338
expect(warnLocal).toHaveBeenCalled();
321339
} finally {
322340
globalThis.fetch = realFetch as any;
323341
warnLocal.mockRestore();
324342
}
325343
});
344+
345+
it("adds integrity to preload links for scripts and styles", async () => {
346+
const html = `<!doctype html><html><head>
347+
<link rel="preload" as="script" href="/entry.js">
348+
<link rel="preload" as="style" href="/style.css">
349+
</head></html>`;
350+
const out = await addSriToHtml(
351+
html,
352+
mockBundle({
353+
"entry.js": "console.log(0)",
354+
"style.css": "body{}",
355+
}),
356+
{ algorithm: "sha256" }
357+
);
358+
expect(out).toContain(
359+
'<link rel="preload" as="script" href="/entry.js" integrity="sha256-'
360+
);
361+
expect(out).toContain(
362+
'<link rel="preload" as="style" href="/style.css" integrity="sha256-'
363+
);
364+
});
326365
});
327366
});

0 commit comments

Comments
 (0)