Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions extension/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ module.exports = {
// below the all-files summary number.
coverageThreshold: {
global: {
statements: 62,
branches: 58,
functions: 58,
lines: 61,
statements: 65,
branches: 60,
functions: 61,
lines: 65,
},
"./src/rules/": {
statements: 90,
Expand Down
201 changes: 197 additions & 4 deletions extension/src/lib/__tests__/rule-engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,60 @@ jest.mock("../enforcement", () => ({
ENFORCEMENT_ENABLED_DEFAULT: true,
}));

// Mock availability so tests can drive availability-flip reconciliation
// directly. The real module subscribes to user-api-key changes underneath,
// which the engine doesn't need to know about for unit coverage.
jest.mock("../availability", () => ({
getRuleAvailabilityStates: jest.fn(),
subscribeRuleAvailability: jest.fn(() => () => undefined),
}));

// Mock revealAll so we can assert it fires when a rule is disabled, without
// the real implementation walking the DOM for placeholder elements that
// don't exist in these unit tests.
jest.mock("../placeholder", () => {
// biome-ignore lint/suspicious/noExplicitAny: jest.requireActual returns any
const actual = jest.requireActual<Record<string, unknown>>("../placeholder");
return {
...actual,
revealAll: jest.fn(),
};
});

import type { RuleAvailabilityStates } from "../availability";
import {
getRuleAvailabilityStates,
subscribeRuleAvailability,
} from "../availability";
import { subscribeEnforcementEnabled } from "../enforcement";
import { isTopFrame } from "../frame";
import { revealAll } from "../placeholder";
import { start } from "../rule-engine";
import { getRuleStates } from "../storage";
import { getRuleStates, subscribe } from "../storage";

const getRuleStatesMock = getRuleStates as jest.MockedFunction<
typeof getRuleStates
>;
const isTopFrameMock = isTopFrame as jest.MockedFunction<typeof isTopFrame>;
const getAvailabilityMock = getRuleAvailabilityStates as jest.MockedFunction<
typeof getRuleAvailabilityStates
>;
const subscribeStorageMock = subscribe as jest.MockedFunction<typeof subscribe>;
const subscribeEnforcementMock =
subscribeEnforcementEnabled as jest.MockedFunction<
typeof subscribeEnforcementEnabled
>;
const subscribeAvailabilityMock =
subscribeRuleAvailability as jest.MockedFunction<
typeof subscribeRuleAvailability
>;
const revealAllMock = revealAll as jest.MockedFunction<typeof revealAll>;

const ALL_AVAILABLE: RuleAvailabilityStates = {
"all-frame-rule": { available: true },
"top-only-rule": { available: true },
"unavailable-rule": { available: false, reason: "unavailable" },
};

const allEnabled = {
"all-frame-rule": true,
Expand All @@ -75,11 +121,21 @@ function setFrame(isTop: boolean): void {
}

beforeEach(() => {
// Tests in "missing document.body" remove the body element; restore it so
// subsequent tests can mutate innerHTML. TS lib types `document.body` as
// non-null, but jsdom honors the removal we did and returns null here.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!document.body) {
document.documentElement.append(document.createElement("body"));
}
document.body.innerHTML = "<main></main>";
(allFrameRule.apply as jest.Mock).mockClear();
(topOnlyRule.apply as jest.Mock).mockClear();
(unavailableRule.apply as jest.Mock).mockClear();
for (const rule of FAKE_RULES) {
(rule.apply as jest.Mock).mockClear();
(rule.teardown as jest.Mock).mockClear();
}
getRuleStatesMock.mockResolvedValue(allEnabled);
getAvailabilityMock.mockResolvedValue(ALL_AVAILABLE);
revealAllMock.mockClear();
});

describe("rule engine — frame gating", () => {
Expand All @@ -103,3 +159,140 @@ describe("rule engine — frame gating", () => {
expect(unavailableRule.apply).not.toHaveBeenCalled();
});
});

describe("rule engine — reconciliation", () => {
// Subscriptions are wired inside start(); pull the listeners back out of the
// mock so each test can drive them with new snapshots.
async function startAndCaptureListeners(): Promise<{
onStorageChange: (next: typeof allEnabled) => void;
onEnforcementChange: (enabled: boolean) => void;
onAvailabilityChange: (next: typeof ALL_AVAILABLE) => void;
}> {
setFrame(true);
await start();
const storageCall = subscribeStorageMock.mock.calls.at(-1);
const enforcementCall = subscribeEnforcementMock.mock.calls.at(-1);
const availabilityCall = subscribeAvailabilityMock.mock.calls.at(-1);
if (!storageCall || !enforcementCall || !availabilityCall) {
throw new Error("start() did not subscribe");
}
return {
onStorageChange: storageCall[0],
onEnforcementChange: enforcementCall[0],
onAvailabilityChange: availabilityCall[0],
};
}

it("applies a rule when storage flips it on", async () => {
getRuleStatesMock.mockResolvedValue({
...allEnabled,
"all-frame-rule": false,
});
const { onStorageChange } = await startAndCaptureListeners();
expect(allFrameRule.apply).not.toHaveBeenCalled();

onStorageChange(allEnabled);

expect(allFrameRule.apply).toHaveBeenCalledTimes(1);
expect(allFrameRule.teardown).not.toHaveBeenCalled();
});

it("tears down and reveals a rule when storage flips it off", async () => {
const { onStorageChange } = await startAndCaptureListeners();
expect(allFrameRule.apply).toHaveBeenCalledTimes(1);

onStorageChange({ ...allEnabled, "all-frame-rule": false });

expect(revealAllMock).toHaveBeenCalledWith("all-frame-rule");
expect(allFrameRule.teardown).toHaveBeenCalledTimes(1);
});

it("does nothing for a rule whose effective state didn't change", async () => {
const { onStorageChange } = await startAndCaptureListeners();
(allFrameRule.apply as jest.Mock).mockClear();

// Re-emit the same states — reconcile should be a no-op for every rule.
onStorageChange({ ...allEnabled });

expect(allFrameRule.apply).not.toHaveBeenCalled();
expect(allFrameRule.teardown).not.toHaveBeenCalled();
});

it("tears down all rules when enforcement is disabled", async () => {
const { onEnforcementChange } = await startAndCaptureListeners();
expect(allFrameRule.apply).toHaveBeenCalledTimes(1);
expect(topOnlyRule.apply).toHaveBeenCalledTimes(1);

onEnforcementChange(false);

expect(allFrameRule.teardown).toHaveBeenCalledTimes(1);
expect(topOnlyRule.teardown).toHaveBeenCalledTimes(1);
});

it("re-applies rules when enforcement flips back on", async () => {
const { onEnforcementChange } = await startAndCaptureListeners();
onEnforcementChange(false);
(allFrameRule.apply as jest.Mock).mockClear();
(topOnlyRule.apply as jest.Mock).mockClear();

onEnforcementChange(true);

expect(allFrameRule.apply).toHaveBeenCalledTimes(1);
expect(topOnlyRule.apply).toHaveBeenCalledTimes(1);
});

it("tears down a rule when its availability flips to false", async () => {
const { onAvailabilityChange } = await startAndCaptureListeners();
expect(allFrameRule.apply).toHaveBeenCalledTimes(1);

onAvailabilityChange({
...ALL_AVAILABLE,
"all-frame-rule": { available: false, reason: "now unavailable" },
});

expect(allFrameRule.teardown).toHaveBeenCalledTimes(1);
});

it("applies a rule when its availability flips to true", async () => {
const { onAvailabilityChange } = await startAndCaptureListeners();
// unavailable-rule started unavailable → never applied at start().
expect(unavailableRule.apply).not.toHaveBeenCalled();

onAvailabilityChange({
...ALL_AVAILABLE,
"unavailable-rule": { available: true },
});

expect(unavailableRule.apply).toHaveBeenCalledTimes(1);
});
});

describe("rule engine — missing document.body", () => {
it("skips initial application when document.body is absent", async () => {
// Some about:blank / about:srcdoc iframes hit document_idle without a
// body. The engine logs and returns; rules must not be invoked with null.
document.body.remove();
setFrame(true);

await expect(start()).resolves.toBeUndefined();
expect(allFrameRule.apply).not.toHaveBeenCalled();
expect(topOnlyRule.apply).not.toHaveBeenCalled();
});

it("skips reconciliation when document.body is absent on a state change", async () => {
setFrame(true);
await start();
const onStorageChange = subscribeStorageMock.mock.calls.at(-1)?.[0];
if (!onStorageChange) {
throw new Error("no storage listener");
}
(allFrameRule.apply as jest.Mock).mockClear();
(allFrameRule.teardown as jest.Mock).mockClear();

document.body.remove();
onStorageChange({ ...allEnabled, "all-frame-rule": false });

expect(allFrameRule.apply).not.toHaveBeenCalled();
expect(allFrameRule.teardown).not.toHaveBeenCalled();
});
});
Loading