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
13 changes: 13 additions & 0 deletions extension/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions extension/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ module.exports = {
moduleNameMapper: {
"^webext-storage$": "<rootDir>/src/__test-mocks__/webext-storage.ts",
"^abort-utils$": "<rootDir>/src/__test-mocks__/abort-utils.ts",
// webext-messenger is ESM-only too; the stub keeps the real `lib/messenger`
// loadable in jsdom (its wrappers go inert). Tests that assert on messaging
// mock `lib/messenger` directly.
"^webext-messenger$": "<rootDir>/src/__test-mocks__/webext-messenger.ts",
},
// - jest-webextension-mock: installs globalThis.chrome + browser with
// jest.fn() stubs for MV2/MV3 APIs the extension uses (runtime, storage,
Expand Down Expand Up @@ -71,6 +75,12 @@ module.exports = {
"!src/lib/*.tsx",
"!src/lib/options-badge.ts",
"!src/lib/options-button-toggle.ts",
// Transport glue over `webext-messenger`: the typed method contract plus
// thin getMethod/getNotifier wrappers. Exercising it requires the loaded
// extension (real cross-context messaging); unit-testing it would just test
// the library. The runtime validation it gates lives in `message-schemas.ts`,
// which IS covered. Same posture as `effective-enforcement.ts` below.
"!src/lib/messenger.ts",
"!src/lib/use-chrome-storage-value.ts",
"!src/lib/use-transient-status.ts",
"!src/lib/placeholder-count.ts",
Expand Down
1 change: 1 addition & 0 deletions extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"react-dom": "19.2.6",
"tldts": "^7.4.2",
"urlpattern-polyfill": "10.1.0",
"webext-messenger": "^0.35.0",
"webext-storage": "^3.1.0"
},
"devDependencies": {
Expand Down
46 changes: 24 additions & 22 deletions extension/src/__test-mocks__/debug-trace-stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@
// Shared setup for tests that exercise the debug-trace recorder, the
// segment tracker, or anything downstream of `recordRuleApplication`.
//
// Installs a jest.fn() over `chrome.runtime.sendMessage`, resets the
// recorder module state, and gives callers a typed accessor for the
// captured trace entries. Use in beforeEach:
// The recorder now emits via the typed `lib/messenger` wrapper
// `reportDebugTraceEvent` instead of raw `chrome.runtime.sendMessage`. Tests
// that use this stub MUST mock that module so the import below resolves to a
// jest mock:
//
// jest.mock("../../lib/messenger", () => ({
// reportDebugTraceEvent: jest.fn(),
// // ...any other messenger exports the test's code-under-test uses
// }));
//
// Usage:
//
// let stub: DebugTraceStub;
// beforeEach(() => { stub = installDebugTraceStub(); });
Expand All @@ -16,36 +24,30 @@ import {
__resetDebugTraceForTesting,
__setDebugTraceEnabledForTesting,
} from "../lib/debug-trace";
import type {
DebugTraceEntry,
DebugTraceEventMessage,
} from "../lib/detection-messages";
import type { DebugTraceEntry } from "../lib/detection-messages";
import { reportDebugTraceEvent } from "../lib/messenger";

export interface DebugTraceStub {
sendMessage: jest.Mock;
setEnabled: (value: boolean) => void;
// Every entry sent via the `debug-trace-event` message, in call order.
// The mocked `reportDebugTraceEvent` — assert call counts directly.
events: jest.Mock;
// Every entry passed to `reportDebugTraceEvent`, in call order.
sentEntries: () => DebugTraceEntry[];
reset: () => void;
}

export function installDebugTraceStub(): DebugTraceStub {
__resetDebugTraceForTesting();
const sendMessage = jest.fn().mockResolvedValue(undefined);
(globalThis as { chrome: unknown }).chrome = {
runtime: { sendMessage },
};
const events = reportDebugTraceEvent as jest.Mock;
events.mockClear();
return {
sendMessage,
setEnabled: __setDebugTraceEnabledForTesting,
events,
sentEntries: () =>
sendMessage.mock.calls
.map(([message]) => message as { type: string })
.filter(
(message): message is DebugTraceEventMessage =>
message.type === "debug-trace-event",
)
.map((message) => message.entry),
reset: __resetDebugTraceForTesting,
events.mock.calls.map(([entry]) => entry as DebugTraceEntry),
reset: () => {
__resetDebugTraceForTesting();
events.mockClear();
},
};
}
34 changes: 34 additions & 0 deletions extension/src/__test-mocks__/webext-messenger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2026 PixieBrix, Inc.
// Licensed under PolyForm Shield 1.0.0 — see LICENSE.

// Test mock for the ESM-only `webext-messenger` package. ts-jest emits
// CommonJS, so importing the real package trips
// `SyntaxError: Unexpected token 'export'` — the same treatment `webext-storage`
// and `abort-utils` get (see jest.config.cjs moduleNameMapper).
//
// None of our unit tests exercise real cross-context messaging. Tests that
// assert on a specific messenger call mock `lib/messenger` directly; every
// other test only loads `lib/messenger` transitively (e.g. via `debug-trace`)
// and just needs its getMethod/getNotifier wrappers to be constructible and
// inert. So the senders here are no-op stubs and `registerMethods` does
// nothing.

export const backgroundTarget = { page: "background" };

const inertMethod = (): Promise<unknown> => Promise.resolve(undefined);

const inertNotifier = (): void => {
// noop — notifications are fire-and-forget
};

export function getMethod(): () => Promise<unknown> {
return inertMethod;
}

export function getNotifier(): () => void {
return inertNotifier;
}

export function registerMethods(): void {
// noop — no real receiver in the unit-test world
}
Loading
Loading