Files
MyIP/tests/composable-shortcuts.test.js
jason5ng32andClaude Opus 5.5 06402ce688 Feat(adblocktest): add the Ad & Tracker Blocking Test
People running uBlock Origin, Privacy Badger, a blocking browser, or a
DNS / VPN / router filter had no way here to see what their setup lets
through. The new tool probes a static list of widely used ad, analytics,
social, error-monitoring and device-maker endpoints plus popular tracking
scripts (fetched with no-cors and no cookies or referrer, never run), and
places bait elements for cosmetic filters. Nothing runs until the visitor
starts it; every group starts selected.

A timeout reads as "no answer" and stays out of the score, and every
listed host was checked to resolve, serve valid HTTPS and not redirect to
http:// or in a loop, since any of those would fail like a block. Element
hiding is left out of the score (DNS-level filters can never pass it) and
instead tells browser-level from network-level blocking. The page copy
says outright that blocking everything is not automatically better.

Ships with the x shortcut, locale copy for every full locale and a
changelog entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:53:43 +08:00

370 lines
15 KiB
JavaScript

// use-shortcuts depends on shortcut.js, which calls document.addEventListener at module load time.
// Here we inject minimal document / window stubs first, then dynamically import the tested module.
//
// pulse-beacon.js pulls `@/utils/...` (Vite alias) and the `p` shortcut gates
// on its build-time env flag — both are awkward in the Node runner. A load
// hook stubs that module with a mutable `hasPulseBackend` so we can cover
// both branches (the shortcut's other gate, configs.cloudFlare, is plain
// test input).
import assert from 'node:assert/strict';
import { describe, it, after } from 'node:test';
import { registerHooks } from 'node:module';
import { ref, computed, reactive, nextTick } from 'vue';
const documentHandlers = {};
// Per-test override (see "o" shortcut cases below). Default: no card is
// currently highlighted via J/K, so any lookup returns null.
let querySelectorImpl = () => null;
globalThis.document = {
addEventListener(type, handler) { documentHandlers[type] = handler; },
getElementById() { return null; },
querySelector(sel) { return querySelectorImpl(sel); },
};
globalThis.window = {
scrollTo() {},
scrollY: 0,
pageYOffset: 0,
};
registerHooks({
load(url, context, nextLoad) {
if (url.includes('/utils/features/pulse-beacon.js')) {
return {
format: 'module',
shortCircuit: true,
source: `
export const PULSE_BEACON_URL = 'https://pulse.test';
export const sendVisitBeacon = () => {};
export let hasPulseBackend = false;
globalThis.__setPulseBackendForTest = (v) => { hasPulseBackend = Boolean(v); };
`,
};
}
return nextLoad(url, context);
},
});
const { useShortcuts } = await import('../frontend/composables/use-shortcuts.js');
const { onAppEvent } = await import('../frontend/utils/app-events.js');
const { registerAppCommand } = await import('../frontend/utils/app-commands.js');
const t = (k) => `<${k}>`;
// The test-running shortcuts dispatch on the command bus rather than calling
// through refs. Register the five owner commands once, into a shared record
// that loadAndGetKeyMap resets per test.
const commandCalls = { ipRefresh: [], connectivity: [], webrtc: [], dnsleak: [], speedTest: 0 };
registerAppCommand('ipinfo:refresh', (payload) => { commandCalls.ipRefresh.push(payload.index); });
registerAppCommand('connectivity:run', (payload) => { commandCalls.connectivity.push(payload.trigger); });
registerAppCommand('webrtc:run', (payload) => { commandCalls.webrtc.push(payload.isRefresh); });
registerAppCommand('dnsleak:run', (payload) => { commandCalls.dnsleak.push(payload.isRefresh); });
registerAppCommand('speedtest:toggle', () => { commandCalls.speedTest += 1; });
const resetCommandCalls = () => {
commandCalls.ipRefresh = [];
commandCalls.connectivity = [];
commandCalls.webrtc = [];
commandCalls.dnsleak = [];
commandCalls.speedTest = 0;
};
function makeStoreStub() {
const state = reactive({
refreshRequested: false,
toggledSheets: [],
});
return {
state,
setRefreshEveryThing(v) { state.refreshRequested = v; },
toggleSheet(name) { state.toggledSheets.push(name); },
};
}
function makeRefs() {
// UI-chrome calls live here; the test-section calls land in commandCalls
// (merged into the returned record so assertions read one object).
const calls = {
queryOpen: 0,
helpOpen: 0,
shareOpen: 0,
advancedNavigate: [],
advancedFullScreen: 0,
mask: 0,
get speedTest() { return commandCalls.speedTest; },
get ipRefresh() { return commandCalls.ipRefresh; },
get connectivity() { return commandCalls.connectivity; },
get webrtc() { return commandCalls.webrtc; },
get dnsleak() { return commandCalls.dnsleak; },
};
const advancedToolsRef = ref({
openTool(slug) { calls.advancedNavigate.push(slug); },
fullScreen() { calls.advancedFullScreen += 1; },
});
return {
refs: {
queryIPRef: ref({ openModal: () => { calls.queryOpen += 1; } }),
helpModalRef: ref({ openModal: () => { calls.helpOpen += 1; }, keyMap: null }),
shareReportRef: ref({ openDialog: () => { calls.shareOpen += 1; } }),
advancedToolsRef,
isInfosLoaded: ref(true),
toggleInfoMask: () => { calls.mask += 1; },
},
calls,
};
}
after(() => {
globalThis.__setPulseBackendForTest?.(false);
});
function loadAndGetKeyMap({
originalSite = false,
ipHistoryEnabled = true,
pulseBackend = false,
cloudFlare = false,
} = {}) {
globalThis.__setPulseBackendForTest(pulseBackend);
resetCommandCalls();
const store = makeStoreStub();
const { refs, calls } = makeRefs();
const configs = computed(() => ({ originalSite, map: true, cloudFlare }));
const userPreferences = computed(() => ({ ipCardsToShow: 2, ipHistoryEnabled }));
const { loadShortcuts } = useShortcuts({
refs, store, t, configs, userPreferences,
});
loadShortcuts();
// helpModalRef.keyMap receives the global keyMap after loadShortcuts
const keyMap = refs.helpModalRef.value.keyMap || [];
return { keyMap, store, refs, calls };
}
describe('useShortcuts()', () => {
it('loadShortcuts() registers a keymap of 26+ entries on a non-original site', () => {
const { keyMap } = loadAndGetKeyMap({ originalSite: false, pulseBackend: false, cloudFlare: false });
// 26 base entries (no invisibility / enhanced-DNS / pulse).
const distinctKeys = new Set(keyMap.map((e) => e.keys));
assert.ok(distinctKeys.size >= 26, `expected ≥26 distinct shortcut keys, got ${distinctKeys.size}`);
assert.ok(distinctKeys.has('R'));
assert.ok(distinctKeys.has('?'));
assert.ok(distinctKeys.has('g'));
assert.ok(distinctKeys.has('o'));
assert.ok(distinctKeys.has('H'));
assert.ok(distinctKeys.has('x'), 'Ad & Tracker Blocking Test is a public tool');
assert.equal(distinctKeys.has('p'), false, 'pulse shortcut stays off without a pulse backend or cloudFlare key');
assert.equal(distinctKeys.has('P'), false, 'persona shortcut stays off a self-hosted instance');
assert.equal(distinctKeys.has('B'), false, 'blocklist shortcut stays off a self-hosted instance');
assert.equal(distinctKeys.has('A'), false, 'ASN Profile shortcut stays off without a cloudFlare key');
// Overlays take no keys at all (utils/shortcut.js), so there is no
// drawer-only entry left to register.
assert.equal(distinctKeys.has('f'), false, 'no shortcut acts on an open overlay');
});
it('originalSite=true adds invisibility ("i"), enhanced DNS-leak ("D"), blocklist ("B") and persona ("P") shortcuts', () => {
const { keyMap, calls } = loadAndGetKeyMap({ originalSite: true });
const hasInvisibility = keyMap.some((e) => e.keys === 'i');
assert.ok(hasInvisibility, 'key "i" should be present on originalSite');
const D = keyMap.findLast((e) => e.keys === 'D');
assert.ok(D, 'key "D" should be present on originalSite');
D.action();
assert.deepEqual(calls.advancedNavigate, ['enhanceddnsleaktest']);
// Uppercase B is its own key — lowercase b is Browser Info's.
const B = keyMap.findLast((e) => e.keys === 'B');
assert.ok(B, 'key "B" should be present on originalSite');
B.action();
// Uppercase P is its own key — lowercase p is Earth Online's.
const P = keyMap.findLast((e) => e.keys === 'P');
assert.ok(P, 'key "P" should be present on originalSite');
P.action();
assert.deepEqual(calls.advancedNavigate, ['enhanceddnsleaktest', 'blocklist', 'personacheck']);
});
it('configs.cloudFlare adds the ASN Profile shortcut ("A")', () => {
const { keyMap, calls } = loadAndGetKeyMap({ cloudFlare: true });
// Uppercase A is its own key — lowercase a is the IP calculator's.
const A = keyMap.findLast((e) => e.keys === 'A');
assert.ok(A, 'key "A" should be present when configs.cloudFlare is set');
A.action();
assert.deepEqual(calls.advancedNavigate, ['asn']);
});
it('"R" action triggers store.setRefreshEveryThing(true)', () => {
const { keyMap, store } = loadAndGetKeyMap();
const entry = keyMap.findLast((e) => e.keys === 'R');
assert.ok(entry, '"R" key should be registered');
entry.action();
assert.equal(store.state.refreshRequested, true);
});
it('advanced-tool shortcuts open via advancedToolsRef.openTool(slug)', () => {
const { keyMap, calls } = loadAndGetKeyMap();
const l = keyMap.findLast((e) => e.keys === 'l'); l.action();
const M = keyMap.findLast((e) => e.keys === 'M'); M.action();
const t_ = keyMap.findLast((e) => e.keys === 't'); t_.action();
assert.deepEqual(calls.advancedNavigate, ['pingtest', 'macchecker', 'mtrtest']);
});
it('numeric regex "[1-6]" passes through number argument to refreshCard', () => {
const { keyMap, calls } = loadAndGetKeyMap();
const entry = keyMap.findLast((e) => e.type === 'regex');
assert.ok(entry);
entry.action(2); // user typed "2"
assert.deepEqual(calls.ipRefresh, [1], 'action receives num; refreshCard(card, num-1)');
});
it('numeric shortcut respects userPreferences.ipCardsToShow upper bound', () => {
const { keyMap, calls } = loadAndGetKeyMap();
const entry = keyMap.findLast((e) => e.type === 'regex');
entry.action(6); // beyond ipCardsToShow (2)
assert.deepEqual(calls.ipRefresh, [], 'num > ipCardsToShow → no-op');
});
it('"c"/"w"/"d"/"s" dispatch the section commands with their payloads', () => {
const { keyMap, calls } = loadAndGetKeyMap();
keyMap.findLast((e) => e.keys === 'c').action();
keyMap.findLast((e) => e.keys === 'w').action();
keyMap.findLast((e) => e.keys === 'd').action();
keyMap.findLast((e) => e.keys === 's').action();
assert.deepEqual(calls.connectivity, ['manual']);
assert.deepEqual(calls.webrtc, [false]);
assert.deepEqual(calls.dnsleak, [true]);
assert.equal(calls.speedTest, 1, 'the s key stays a run/pause toggle');
});
it('"?" opens help modal and emits the shortcut:help-opened app event', () => {
// The achievement itself (CleverTrickery) is the engine's concern —
// use-shortcuts only emits the domain event. See achievement-rules tests.
let emitted = 0;
const off = onAppEvent('shortcut:help-opened', () => { emitted += 1; });
const { keyMap, calls } = loadAndGetKeyMap();
const entry = keyMap.findLast((e) => e.keys === '?');
entry.action();
off();
assert.equal(calls.helpOpen, 1);
assert.equal(emitted, 1);
});
it('"e" opens the share report dialog', () => {
const { keyMap, calls } = loadAndGetKeyMap();
const entry = keyMap.findLast((e) => e.keys === 'e');
assert.ok(entry, '"e" key should be registered');
entry.action();
assert.equal(calls.shareOpen, 1);
});
it('"h" toggles info mask only when infos loaded', () => {
const { keyMap, calls } = loadAndGetKeyMap();
const entry = keyMap.findLast((e) => e.keys === 'h');
entry.action();
assert.equal(calls.mask, 1);
});
it('"H" opens the IP history sheet when ipHistoryEnabled', () => {
const { keyMap, store } = loadAndGetKeyMap({ ipHistoryEnabled: true });
const entry = keyMap.findLast((e) => e.keys === 'H');
assert.ok(entry, '"H" key should be registered');
entry.action();
assert.deepEqual(store.state.toggledSheets, ['ipHistory']);
});
it('"H" is a no-op when ipHistoryEnabled is false', () => {
const { keyMap, store } = loadAndGetKeyMap({ ipHistoryEnabled: false });
const entry = keyMap.findLast((e) => e.keys === 'H');
entry.action();
assert.deepEqual(store.state.toggledSheets, []);
});
it('the pulse backend flag adds the pulse shortcut (key "p")', () => {
const { keyMap, store } = loadAndGetKeyMap({ pulseBackend: true });
const entry = keyMap.findLast((e) => e.keys === 'p');
assert.ok(entry, '"p" key should be present when hasPulseBackend');
entry.action();
assert.deepEqual(store.state.toggledSheets, ['pulse']);
});
it('configs.cloudFlare alone adds the pulse shortcut (outage-only fork)', () => {
const { keyMap, store } = loadAndGetKeyMap({ pulseBackend: false, cloudFlare: true });
const entry = keyMap.findLast((e) => e.keys === 'p');
assert.ok(entry, '"p" key should be present when configs.cloudFlare is set');
entry.action();
assert.deepEqual(store.state.toggledSheets, ['pulse']);
});
it('configs landing after registration re-registers the map (late "p")', async () => {
globalThis.__setPulseBackendForTest(false);
const store = makeStoreStub();
const { refs } = makeRefs();
// Reactive configs source so the watcher inside useShortcuts can fire.
const configsData = ref({ originalSite: false, map: true });
const configs = computed(() => configsData.value);
const userPreferences = computed(() => ({ ipCardsToShow: 2, ipHistoryEnabled: true }));
const { loadShortcuts } = useShortcuts({ refs, store, t, configs, userPreferences });
loadShortcuts();
const keyMap = refs.helpModalRef.value.keyMap;
assert.equal(keyMap.some((e) => e.keys === 'p'), false, 'no "p" before configs resolve');
configsData.value = { originalSite: false, map: true, cloudFlare: true };
await nextTick(); // flush the configs watcher
assert.equal(keyMap.some((e) => e.keys === 'p'), true, '"p" appears once cloudFlare lands');
});
it('"o" opens the highlighted advanced tool by its data-adv-slug', () => {
const { keyMap, calls } = loadAndGetKeyMap();
// Stub the highlighted card to be an advanced tool card pointing at mtrtest.
querySelectorImpl = (sel) => {
if (sel === '.keyboard-shortcut-card[data-keyboard-hover="true"]') {
return {
getAttribute: (name) => name === 'data-adv-slug' ? 'mtrtest' : null,
querySelector: () => null,
};
}
return null;
};
const entry = keyMap.findLast((e) => e.keys === 'o');
entry.action();
querySelectorImpl = () => null;
assert.deepEqual(calls.advancedNavigate, ['mtrtest']);
});
it('"o" clicks the highlighted Connectivity card\'s site link (a[data-card-open])', () => {
const { keyMap, calls } = loadAndGetKeyMap();
let clicked = 0;
querySelectorImpl = (sel) => {
if (sel === '.keyboard-shortcut-card[data-keyboard-hover="true"]') {
return {
getAttribute: () => null,
querySelector: (inner) => (inner === 'a[data-card-open][href]' ? { click: () => { clicked += 1; } } : null),
};
}
return null;
};
const entry = keyMap.findLast((e) => e.keys === 'o');
entry.action();
querySelectorImpl = () => null;
assert.equal(clicked, 1);
assert.deepEqual(calls.advancedNavigate, []);
});
it('"o" is a no-op when the highlighted card has no slug and no site link (e.g. IP card)', () => {
const { keyMap, calls } = loadAndGetKeyMap();
querySelectorImpl = () => ({ getAttribute: () => null, querySelector: () => null });
const entry = keyMap.findLast((e) => e.keys === 'o');
entry.action();
querySelectorImpl = () => null;
assert.deepEqual(calls.advancedNavigate, []);
});
it('"o" is a no-op when nothing is highlighted', () => {
const { keyMap, calls } = loadAndGetKeyMap();
// querySelectorImpl default returns null (nothing highlighted).
const entry = keyMap.findLast((e) => e.keys === 'o');
entry.action();
assert.deepEqual(calls.advancedNavigate, []);
});
});