Files
rea/src/domain/javascript/javascriptStaticAnalysisServiceWorkers.test.ts
T
morluto 396b89c5e2 fix(javascript): attribute service workers to intact platform paths
Check the complete navigator.serviceWorker.register identity through the shared
ambient-write owner, including replaced methods and aliased receiver mutations.
Reuse the module URL reader for intact registrations while preserving unknown
document-relative strings. Cover the unresolved review case and the CLI graph.

Refs #3841

Co-authored-by: Chandan Chaudhary <chandankkumar156@gmail.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-16 06:06:06 +08:00

77 lines
3.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
const serviceWorkers = (source: string) => {
const analysis = analyzeJavaScriptStaticSource(source);
expect(analysis.parse_error_count).toBe(0);
return analysis.references
.filter(({ kind }) => kind === "service-worker")
.map(({ specifier, expression }) => ({ specifier, expression }));
};
describe("service-worker API identity", () => {
it.each([
"navigator.serviceWorker.register",
"window.navigator.serviceWorker.register",
"self.navigator.serviceWorker.register",
"globalThis.navigator.serviceWorker.register",
"window?.navigator?.serviceWorker?.register",
'navigator["serviceWorker"]["register"]',
])("resolves a module-relative URL for %s", (callee) => {
const expression = `${callee}(new URL("./sw.js?version=2", import.meta.url))`;
expect(serviceWorkers(expression)).toEqual([
{ specifier: "./sw.js?version=2", expression: null },
]);
});
it.each([
'const navigator = vendor; navigator.serviceWorker.register("./sw.js");',
'import navigator from "vendor"; navigator.serviceWorker.register("./sw.js");',
'function register(navigator) { navigator.serviceWorker.register("./sw.js"); }',
'vendor.serviceWorker.register("./sw.js");',
'serviceWorker.register("./sw.js");',
'navigator["serviceWorker.register"]("./sw.js");',
'navigator.serviceWorker[method]("./sw.js");',
'navigator.serviceWorker.register = load; navigator.serviceWorker.register(new URL("./ordinary.js", import.meta.url));',
'const sw = navigator.serviceWorker; sw.register = load; navigator.serviceWorker.register("./sw.js");',
'Object.assign(navigator.serviceWorker, { register: load }); navigator.serviceWorker.register("./sw.js");',
'Reflect.set(navigator.serviceWorker, "register", load); navigator.serviceWorker.register("./sw.js");',
'navigator.serviceWorker = vendor; navigator.serviceWorker.register("./sw.js");',
'window.navigator = vendor; navigator.serviceWorker.register("./sw.js");',
'window = vendor; window.navigator.serviceWorker.register("./sw.js");',
])("does not attribute a replaced or unproven API: %s", (source) => {
expect(serviceWorkers(source)).toEqual([]);
});
it.each([
'"./sw.js"',
'new URL("./sw.js", document.baseURI)',
'new URL("/sw.js", import.meta.url)',
'new URL("./sw.js", import.meta.url)',
])(
"retains the expression when its module-relative URL is unknown: %s",
(argument) => {
// The last case is a replaced URL constructor, not the platform intrinsic.
const prefix =
argument === 'new URL("./sw.js", import.meta.url)'
? "const URL = vendor; "
: "";
const expression = `navigator.serviceWorker.register(${argument})`;
expect(serviceWorkers(prefix + expression)).toEqual([
{ specifier: null, expression },
]);
},
);
it("does not invalidate registration for an unrelated member write", () => {
const expression =
'navigator.serviceWorker.register(new URL("./sw.js", import.meta.url))';
expect(
serviceWorkers(
`navigator.serviceWorker.unrelated = value; ${expression}`,
),
).toEqual([{ specifier: "./sw.js", expression: null }]);
});
});