test: harden coverage and browser release gates

This commit is contained in:
KoalaDev
2026-08-21 14:10:35 +02:00
parent 368daa6e2e
commit 230e7f5932
29 changed files with 1176 additions and 806 deletions
+1 -2
View File
@@ -67,8 +67,7 @@ Useful focused checks from the repository root:
node -c extension/background.js
node -c extension/content.js
node -c extension/popup.js
node scripts/test-episode-utils.mjs
node scripts/test-title-privacy.mjs
npx vitest run extension/episode-utils.test.mjs extension/title-privacy.test.mjs
node scripts/test-audio-settings.mjs
node scripts/test-locales.cjs
```
+56
View File
@@ -0,0 +1,56 @@
import { describe, expect, it } from 'vitest';
import { extractEpisodeId, sameEpisode } from './episode-utils.js';
describe('episode title matching', () => {
it.each([
['S01E01', 'S01E01'],
['S1E1', 'S01E01'],
['s01e01', 'S01E01'],
['Season 1 Episode 2', 'S01E02'],
['season 01 episode 02', 'S01E02'],
['S01 - E01', 'S01E01'],
['S01.E01', 'S01E01'],
['S01/E01', 'S01E01'],
['S01:E01', 'S01E01'],
['S01,E01', 'S01E01'],
['S01 E01', 'S01E01'],
['Folge 5', 'EP005'],
['Episode 12', 'EP012'],
['Ep. 3', 'EP003'],
['#42', 'EP042'],
['S01E001', 'S01E001']
])('extracts %s as %s', (title, expected) => {
expect(extractEpisodeId(title)).toBe(expected);
});
it.each([null, undefined, '', 123, 'Some Movie Title', 'Breaking Bad'])(
'returns null for non-episode input %j',
input => expect(extractEpisodeId(input)).toBeNull()
);
it.each([
['S01E01', 'S01E01'],
['S01E01 - Pilot', 'S01E01'],
['Folge 5', 'Episode 5'],
['Episode 12', 'Ep. 12'],
['#42', 'Folge 42'],
[null, null],
['', ''],
['Some Movie', 'Some Movie']
])('matches equivalent titles %j and %j', (left, right) => {
expect(sameEpisode(left, right)).toBe(true);
});
it.each([
['S01E01', 'S01E02'],
['S01E01', 'S02E01'],
['Folge 1', 'Folge 2'],
['Some Movie', 'Other Movie'],
['S01E01', null],
[null, 'Episode 5'],
['S01E05', 'Episode 5'],
['S01E01', 'EP001']
])('rejects different titles %j and %j', (left, right) => {
expect(sameEpisode(left, right)).toBe(false);
});
});
+155
View File
@@ -0,0 +1,155 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import {
HOST_ACCESS_REQUIRED_STATUS,
addTabHostAccessRequest,
describeTabUrl,
inspectTabHostAccess,
isHostAccessError,
normalizeTabId,
removeTabHostAccessRequest,
requestOriginPermission
} from './host-access.js';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
describe('host access helpers', () => {
it('normalizes only positive safe tab IDs', () => {
expect(HOST_ACCESS_REQUIRED_STATUS).toBe('host_permission_required');
for (const invalid of [null, undefined, '', 0, true, [42], '42.5', Number.MAX_SAFE_INTEGER + 1]) {
expect(normalizeTabId(invalid)).toBeNull();
}
expect(normalizeTabId('42')).toBe(42);
expect(normalizeTabId(' 42 ')).toBe(42);
});
it('describes supported origins with Firefox-compatible localhost permissions', () => {
expect(describeTabUrl('https://emby.example:8443/web/index.html')).toEqual({
url: 'https://emby.example:8443/web/index.html',
host: 'emby.example:8443',
originPattern: 'https://emby.example:8443/*'
});
expect(describeTabUrl('http://localhost:8096/web/', { includePort: false })).toEqual({
url: 'http://localhost:8096/web/',
host: 'localhost:8096',
originPattern: 'http://localhost/*'
});
expect(describeTabUrl('chrome://extensions/')).toBeNull();
expect(describeTabUrl('not a url')).toBeNull();
});
it('checks the selected tab origin and preserves an unknown callback result', async () => {
let containsRequest;
const deniedChrome = {
tabs: { get: async tabId => ({ id: tabId, url: 'https://video.example/watch' }) },
permissions: {
contains: async request => {
containsRequest = request;
return false;
}
}
};
await expect(inspectTabHostAccess(deniedChrome, 42)).resolves.toMatchObject({
granted: false,
host: 'video.example',
originPattern: 'https://video.example/*'
});
expect(containsRequest).toEqual({ origins: ['https://video.example/*'] });
const unknownChrome = {
runtime: {},
tabs: { get: async tabId => ({ id: tabId, url: 'https://video.example/watch' }) },
permissions: { contains: (_request, callback) => callback(undefined) }
};
await expect(inspectTabHostAccess(unknownChrome, 42)).resolves.toMatchObject({ granted: null });
});
it('uses Firefox host patterns without ports', async () => {
let containsRequest;
const chromeApi = {
runtime: { getBrowserInfo: async () => ({ name: 'Firefox' }) },
tabs: {
get: async tabId => ({
id: tabId,
url: 'http://localhost:8096/web/',
pendingUrl: 'https://different.example/loading'
})
},
permissions: {
contains: async request => {
containsRequest = request;
return false;
}
}
};
await expect(inspectTabHostAccess(chromeApi, 42)).resolves.toMatchObject({
host: 'localhost:8096',
originPattern: 'http://localhost/*'
});
expect(containsRequest).toEqual({ origins: ['http://localhost/*'] });
});
it('adds, removes, and requests permissions through promise and callback APIs', async () => {
let added;
expect(await addTabHostAccessRequest({
permissions: { addHostAccessRequest: async request => { added = request; } }
}, 42, 'https://video.example/*')).toBe(true);
expect(added).toEqual({ tabId: 42, pattern: 'https://video.example/*' });
expect(await addTabHostAccessRequest({ permissions: {} }, 42)).toBe(false);
let removed;
expect(await removeTabHostAccessRequest({
permissions: { removeHostAccessRequest: async request => { removed = request; } }
}, 42, 'https://video.example/*')).toBe(true);
expect(removed).toEqual({ tabId: 42, pattern: 'https://video.example/*' });
expect(await removeTabHostAccessRequest({ permissions: {} }, 42)).toBe(false);
const callbackChrome = {
runtime: {},
permissions: { request: (_request, callback) => callback(true) }
};
await expect(requestOriginPermission(callbackChrome, 'https://video.example/*')).resolves.toBe(true);
await expect(requestOriginPermission({ permissions: {} }, 'https://video.example/*')).resolves.toBeNull();
expect(isHostAccessError(new Error('Missing host permission for the tab'))).toBe(true);
expect(isHostAccessError(new Error('No tab with id: 42'))).toBe(false);
});
});
describe('host access recovery contracts', () => {
it('keeps activation, permission recovery, and target identity guarded', () => {
const background = fs.readFileSync(path.join(repoRoot, 'extension/background.js'), 'utf8');
const popup = fs.readFileSync(path.join(repoRoot, 'extension/popup.js'), 'utf8');
const popupHtml = fs.readFileSync(path.join(repoRoot, 'extension/popup.html'), 'utf8');
const tabManager = fs.readFileSync(path.join(repoRoot, 'extension/modules/tab-manager.js'), 'utf8');
expect(background).toMatch(/await activateTargetTab\((?:message\.tabId|selectedTabId), message\.tabTitle\)/);
expect(background).toMatch(/addTabHostAccessRequest\(chrome, tabId, access\.originPattern\)/);
expect(background).toMatch(/retryPendingTarget\(\)/);
expect(background).toMatch(/activationGeneration !== targetActivationGeneration/);
expect(background).toMatch(/pendingTargetRequestId/);
expect(background).toMatch(/addedOrigins\.includes\(pending\.originPattern\)/);
expect(background).toMatch(/isCurrentTargetIdentity\(tabId, targetGeneration\)/);
expect(background).toMatch(/message\.expectedTabId/);
expect(background).toMatch(/completeForceSyncBeforeTargetChange\(selectedTabId\)/);
expect(background).toMatch(/FORCE_SYNC_ACK'[\s\S]*ignored_unselected_tab/);
expect(background).toMatch(/removeTabHostAccessRequest\([\s\S]*pendingTabId/);
const activationBody = background.slice(
background.indexOf('async function activateTargetTab'),
background.indexOf('async function retryPendingTarget')
);
expect(activationBody.indexOf('await injectContentScript')).toBeLessThan(
activationBody.indexOf('currentTabId = selectedTabId')
);
expect(popup).toMatch(/response\?\.status === 'host_permission_required'/);
expect(popup).toMatch(/requestOriginPermission\(chrome, requestedOriginPattern\)/);
expect(popup).toMatch(/expectedCurrentTabId: tabId/);
expect(popup).toMatch(/expectedTabId: tabId/);
expect(tabManager).not.toMatch(/injectContentScript/);
expect((background.match(/tabs\.onRemoved\.addListener/g) || []).length
+ (tabManager.match(/tabs\.onRemoved\.addListener/g) || []).length).toBe(1);
expect(popupHtml).toMatch(/id="siteAccessNotice"/);
});
});
+58
View File
@@ -0,0 +1,58 @@
import { describe, expect, it } from 'vitest';
import {
TITLE_PRIVACY_MODES,
applyTitlePrivacyToPayload,
normalizeSendTabTitle,
normalizeTabTitle,
normalizeTitlePrivacyMode,
sanitizeSharedTitle,
sanitizeTabTitle
} from './title-privacy.js';
describe('title privacy', () => {
it('normalizes settings and tab notification prefixes', () => {
expect(normalizeTitlePrivacyMode(undefined)).toBe(TITLE_PRIVACY_MODES.FULL);
expect(normalizeTitlePrivacyMode('unknown')).toBe(TITLE_PRIVACY_MODES.FULL);
expect(normalizeTitlePrivacyMode(TITLE_PRIVACY_MODES.HIDDEN)).toBe(TITLE_PRIVACY_MODES.HIDDEN);
expect(normalizeSendTabTitle(undefined, TITLE_PRIVACY_MODES.FULL)).toBe(true);
expect(normalizeSendTabTitle(undefined, TITLE_PRIVACY_MODES.EPISODE)).toBe(false);
expect(normalizeSendTabTitle(true, TITLE_PRIVACY_MODES.HIDDEN)).toBe(true);
expect(normalizeSendTabTitle(false, TITLE_PRIVACY_MODES.FULL)).toBe(false);
expect(normalizeTabTitle('(12) Testvideo - YouTube')).toBe('Testvideo - YouTube');
expect(normalizeTabTitle('[999+] Testvideo - YouTube')).toBe('Testvideo - YouTube');
expect(normalizeTabTitle('(500) Days of Summer')).toBe('Days of Summer');
expect(normalizeTabTitle(' ')).toBeNull();
});
it('keeps tab-title and media-title privacy independent', () => {
expect(sanitizeTabTitle('(12) Private Tab', true)).toBe('Private Tab');
expect(sanitizeTabTitle('Private Tab', false)).toBeNull();
expect(sanitizeSharedTitle('Example Movie', 'full')).toBe('Example Movie');
expect(sanitizeSharedTitle('Show Name - S01/E04 - Title', 'episode')).toBe('S01E04');
expect(sanitizeSharedTitle('Folge 7 - Private Server', 'episode')).toBe('EP007');
expect(sanitizeSharedTitle('Example Movie', 'episode')).toBeNull();
expect(sanitizeSharedTitle('Show Name - S01E04', 'hidden')).toBeNull();
});
it('rewrites only present media keys without mutating the input', () => {
const input = {
tabTitle: 'Private Tab',
mediaTitle: 'Private Media',
expectedTitle: 'S01E04',
title: 'S01E04',
currentTime: 42
};
expect(applyTitlePrivacyToPayload(input, 'hidden')).toEqual({
tabTitle: 'Private Tab',
mediaTitle: null,
expectedTitle: null,
title: null,
currentTime: 42
});
expect(input.mediaTitle).toBe('Private Media');
expect(applyTitlePrivacyToPayload({ tabTitle: 'Private Tab', status: 'heartbeat' }, 'episode')).toEqual({
tabTitle: 'Private Tab',
status: 'heartbeat'
});
});
});