Skip to content

Commit 6829d7b

Browse files
committed
test: ratchet coverage thresholds
1 parent 6c5a699 commit 6829d7b

12 files changed

Lines changed: 159 additions & 23 deletions

‎.github/workflows/ci.yml‎

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
quality:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
14+
15+
- name: Setup Node
16+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
17+
with:
18+
node-version-file: .node-version
19+
cache: npm
20+
21+
- name: Install dependencies
22+
run: npm ci
23+
24+
- name: Resolve package version
25+
id: package
26+
run: |
27+
VERSION=$(node -p "require('./package.json').version")
28+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
29+
30+
- name: npm audit (high+ severity)
31+
run: npm audit --audit-level=high --omit=optional
32+
33+
- name: Toolchain contract
34+
run: npm run toolchain:check
35+
36+
- name: GitHub Actions pins
37+
run: npm run actions:pins:check
38+
39+
- name: Optional dependency reachability
40+
run: npm run optional-deps:check
41+
42+
- name: Settings schema
43+
run: npm run settings:schema:check
44+
45+
- name: Monaco package contract
46+
run: npm run monaco:package:check
47+
48+
- name: Readability
49+
run: npm run readability:check
50+
51+
- name: Dashboard modules
52+
run: npm run dashboard:modules:check
53+
54+
- name: What's new changelog
55+
run: npm run whatsnew:check
56+
57+
- name: Store copy
58+
run: npm run store-copy:check
59+
60+
- name: CWS remote code
61+
run: npm run cws:remote-code:check -- --target ScriptVault-v${{ steps.package.outputs.version }}.zip
62+
63+
- name: CWS publish tooling
64+
run: npm run cws:check
65+
66+
- name: Release artifact check
67+
run: npm run release:check
68+
69+
- name: Reproducible Chrome ZIP
70+
run: npm run release:reproducible-build:check
71+
72+
- name: Rollback drill
73+
run: npm run release:rollback-drill
74+
75+
- name: Release trust
76+
run: npm run release:trust
77+
78+
- name: CRA SBOM
79+
run: npm run release:trust:cra
80+
81+
- name: Store status
82+
run: npm run release:store-status
83+
84+
- name: scriptvault-firefox-package
85+
run: npm run firefox:package
86+
87+
- name: Coverage
88+
run: npm run test:cov

‎tests/gui-ux-audit.test.js‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ describe("cross-surface UX audit", () => {
131131
expect(sidepanelJs).toContain("requestAnimationFrame(() => restoreSidepanelFocus(focusDescriptor));");
132132
expect(sidepanelJs).toContain("requestAnimationFrame(() => restoreSidepanelFallbackFocus('page'));");
133133
expect(sidepanelJs).toContain("requestAnimationFrame(() => restoreSidepanelFallbackFocus('all'));");
134-
expect(sidepanelJs).toContain('No scripts match "${searchQuery}".');
134+
expect(sidepanelJs).toContain("tSidepanel('sideNoMatchingScriptsForQuery', 'No scripts match \"{query}\".', { query: searchQuery })");
135135
expect(sidepanelJs).toContain("Find for ");
136136
expect(sidepanelHtml).toContain(".sp-context-banner");
137137
expect(sidepanelJs).toContain("const banner = document.createElement('button');");
@@ -192,14 +192,14 @@ describe("cross-surface UX audit", () => {
192192
expect(devtoolsJs).toContain("$('btnCloseDetail').focus({ preventScroll: true });");
193193
expect(devtoolsJs).toContain("if (selectedEntry && $('netDetail').classList.contains('open')) {");
194194
expect(devtoolsJs).toContain("renderDetailContent(selectedEntry);");
195-
expect(devtoolsJs).toContain("clearButton.textContent = 'Reset'");
196-
expect(devtoolsJs).toContain("clearButton.setAttribute('aria-label', 'Reset execution filter');");
195+
expect(devtoolsJs).toContain("clearButton.textContent = tDevtools('resetView', 'Reset View');");
196+
expect(devtoolsJs).toContain("clearButton.setAttribute('aria-label', tDevtools('devtoolsResetExecutionFilter', 'Reset execution filter'));");
197197
expect(devtoolsJs).toContain("tr.setAttribute('aria-selected', String(selectedRow === entry.id));");
198198
expect(devtoolsJs).toContain("Console capture isn’t available here yet. Use Network or Execution for current insight.");
199199
expect(devtoolsJs).toContain("No requests match");
200200
expect(devtoolsJs).toContain("No scripts match");
201-
expect(devtoolsJs).toContain("clearButton.textContent = 'Clear'");
202-
expect(devtoolsJs).toContain("clearButton.setAttribute('aria-label', 'Clear recorded network requests');");
201+
expect(devtoolsJs).toContain("clearButton.textContent = tDevtools('clearAction', 'Clear');");
202+
expect(devtoolsJs).toContain("clearButton.setAttribute('aria-label', tDevtools('devtoolsClearRecordedNetworkRequests', 'Clear recorded network requests'));");
203203
expect(devtoolsJs).toContain("No network or execution data to export yet.");
204204
expect(devtoolsJs).toContain("Diagnostics refresh failed. Showing the last available data.");
205205
expect(devtoolsHtml).toContain('.toolbar-btn[aria-busy="true"]');

‎tests/popup-a11y.test.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ describe("popup UX controller", () => {
128128
expect(popupJs).toMatch(/await copyTextToClipboard\(url\);/);
129129
expect(popupJs).toMatch(/updateBtn\.disabled = !hasUpdateUrl;/);
130130
expect(popupJs).toMatch(/copyUrlBtn\.disabled = !installUrl;/);
131-
expect(popupJs).toMatch(/pinBtn\.textContent = script\?\.settings\?\.pinned \? 'Unpin Script' : 'Pin Script';/);
131+
expect(popupJs).toMatch(/pinBtn\.textContent = script\?\.settings\?\.pinned \? tPopup\('popupUnpinScript', 'Unpin Script'\) : tPopup\('popupPinScript', 'Pin Script'\);/);
132132
expect(popupJs).toMatch(/dropdown\.setAttribute\('aria-label', `Actions for \$\{name\}`\);/);
133133
expect(popupJs).toMatch(/queuePopupFocusRestore\(getPopupFocusDescriptor\(getDropdownTriggerButton\(scriptId\)\)\);/);
134134
expect(popupJs).toMatch(/dropdown\.addEventListener\('keydown', \(e\) => \{/);

‎tests/script-config.test.js‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
import { describe, expect, it } from 'vitest';
2+
import { readFileSync } from 'node:fs';
3+
import { resolve } from 'node:path';
4+
import { compileFunction } from 'node:vm';
25

36
import { parseUserscript } from '../src/background/parser.ts';
4-
import { ScriptConfig } from '../src/modules/script-config.ts';
7+
import { ScriptConfig as SourceScriptConfig } from '../src/modules/script-config.ts';
8+
9+
function loadRuntimeScriptConfig() {
10+
const modulePath = resolve(__dirname, '../modules/script-config.js');
11+
const code = readFileSync(modulePath, 'utf8');
12+
return compileFunction(`${code}\nreturn ScriptConfig;`, [], { filename: modulePath })();
13+
}
14+
15+
const implementations = [
16+
{ label: 'source', api: SourceScriptConfig },
17+
{ label: 'runtime', api: loadRuntimeScriptConfig() },
18+
];
519

620
describe('ScriptConfig userscript @var helpers', () => {
721
it('parses userscript @var metadata into config variables', () => {
@@ -25,7 +39,9 @@ describe('ScriptConfig userscript @var helpers', () => {
2539
{ type: 'select', name: 'mode', label: 'Mode', default: 'auto', options: { auto: 'auto', manual: 'manual' } },
2640
]);
2741
});
42+
});
2843

44+
describe.each(implementations)('ScriptConfig userscript @var helpers ($label)', ({ api: ScriptConfig }) => {
2945
it('coerces saved values and fills defaults for missing variables', () => {
3046
const variables = [
3147
ScriptConfig.parseDirective('number retries "Retries" 2'),

‎tests/subscriptions-dashboard.test.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('subscription dashboard surface', () => {
3030

3131
it('renders feed health indicators', () => {
3232
expect(html).toContain('subscription-health');
33-
expect(js).toContain("Health: ${escapeHtml(health.label)}");
33+
expect(js).toContain("tDashboard('subscriptionHealthPrefix', 'Health: {health}', { health: health.label })");
3434
expect(js).toContain("Needs attention");
3535
expect(js).toContain("Not checked");
3636
expect(js).toContain("Healthy");

‎tests/subscriptions.test.js‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
1-
import { ScriptSubscriptions } from '../src/modules/subscriptions.ts';
1+
import { readFileSync } from 'node:fs';
2+
import { resolve } from 'node:path';
3+
import { compileFunction } from 'node:vm';
4+
5+
import { ScriptSubscriptions as SourceScriptSubscriptions } from '../src/modules/subscriptions.ts';
6+
7+
function loadRuntimeSubscriptions() {
8+
const modulePath = resolve(__dirname, '../modules/subscriptions.js');
9+
const code = readFileSync(modulePath, 'utf8');
10+
return compileFunction(`${code}\nreturn ScriptSubscriptions;`, [], { filename: modulePath })();
11+
}
12+
13+
const implementations = [
14+
{ label: 'source', api: SourceScriptSubscriptions },
15+
{ label: 'runtime', api: loadRuntimeSubscriptions() },
16+
];
217

318
beforeEach(() => {
419
globalThis.__resetStorageMock?.();
520
});
621

7-
describe('script subscriptions', () => {
22+
describe.each(implementations)('script subscriptions ($label)', ({ api: ScriptSubscriptions }) => {
823
it('parses array and object feed entries with relative URLs', () => {
924
const feed = ScriptSubscriptions.parseFeed(JSON.stringify({
1025
name: 'Curated Pack',

‎tests/sync-crypto.test.js‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
import { webcrypto } from 'node:crypto';
2+
import { readFileSync } from 'node:fs';
3+
import { resolve } from 'node:path';
4+
import { compileFunction } from 'node:vm';
25
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
36

4-
import { SyncCrypto } from '../src/modules/sync-crypto.ts';
7+
import { SyncCrypto as SourceSyncCrypto } from '../src/modules/sync-crypto.ts';
8+
9+
function loadRuntimeSyncCrypto() {
10+
const modulePath = resolve(__dirname, '../modules/sync-crypto.js');
11+
const code = readFileSync(modulePath, 'utf8');
12+
return compileFunction(`${code}\nreturn SyncCrypto;`, [], { filename: modulePath })();
13+
}
14+
15+
const implementations = [
16+
{ label: 'source', api: SourceSyncCrypto },
17+
{ label: 'runtime', api: loadRuntimeSyncCrypto() },
18+
];
519

620
const fastEncryptedSettings = {
721
syncEncryptionEnabled: true,
@@ -19,7 +33,7 @@ afterAll(() => {
1933
Object.defineProperty(globalThis, 'crypto', { value: originalCrypto, configurable: true });
2034
});
2135

22-
describe('sync crypto helper', () => {
36+
describe.each(implementations)('sync crypto helper ($label)', ({ api: SyncCrypto }) => {
2337
it('round-trips a v1 sync envelope through PBKDF2 and AES-256-GCM', async () => {
2438
const envelope = {
2539
version: 1,

‎tests/trusted-types-docs.test.js‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ describe('Trusted Types author documentation', () => {
2424
const dashboard = read('pages/dashboard.html');
2525

2626
expect(dashboard).toContain('Trusted Types And MAIN-World Scripts');
27-
expect(dashboard).toContain('ScriptVault runs scripts in <code>USER_SCRIPT</code> world');
28-
expect(dashboard).toContain('Scripts that use page context or <code>unsafeWindow</code>');
29-
expect(dashboard).toContain('<code>GM_addElement</code> with attributes');
30-
expect(dashboard).toContain('<strong>TrustedHTML:</strong>');
27+
expect(dashboard).toContain('data-i18n="helpTrustedTypesDefaultDescription">ScriptVault runs scripts in USER_SCRIPT world');
28+
expect(dashboard).toContain('data-i18n="helpTrustedTypesMainDescription">Scripts that use page context or unsafeWindow');
29+
expect(dashboard).toContain('data-i18n="helpTrustedTypesDomDescription">Prefer textContent, append, createElement, and GM_addElement with attributes.');
30+
expect(dashboard).toContain('data-i18n="helpTrustedTypesHtmlLabel">TrustedHTML:</strong>');
3131
});
3232

3333
it('keeps the cycle documentation-only without adding a runtime policy shim', () => {

‎tests/ts-runtime-modules.test.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @vitest-environment node
12
import { describe, expect, it } from 'vitest';
23
import { readFileSync } from 'node:fs';
34
import { resolve } from 'node:path';

‎tests/user-scripts-onboarding.test.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ describe('Chrome userScripts onboarding diagnostics', () => {
112112
expect(dashboardJs).toContain("action: 'runScriptNow'");
113113
expect(dashboardJs).toContain('scriptId,');
114114
expect(dashboardJs).toContain('tabId: targetTab.id');
115-
expect(dashboardJs).toContain('runButtonTask(event.currentTarget, () => runScriptOnceOnTab(), { busyLabel: \'Running...\' })');
115+
expect(dashboardJs).toContain("runButtonTask(event.currentTarget, () => runScriptOnceOnTab(), { busyLabel: tDashboard('runningEllipsis', 'Running...') })");
116116
expect(dashboardJs).toContain('[data-action="runNow"]');
117117
expect(messagesTs).toContain("interface RunScriptNow");
118118
expect(messagesTs).toContain("action: 'runScriptNow';");

0 commit comments

Comments
 (0)