CI / test (pull_request) Successful in 14s
Adds 4 regression tests: - vite.config.ts entry modules must exist as source files - content.js must NOT use inline script injection (textContent) - interceptors.js must be a standalone IIFE file - manifest.json content_scripts must reference content.js Prevents future build-breaking phantom entry point errors (the probeFn.js/scrapeFn.js issue kfj001 hit).
83 lines
3.0 KiB
JavaScript
83 lines
3.0 KiB
JavaScript
/**
|
|
* config_validation.test.mjs — Validate build configuration integrity.
|
|
*
|
|
* Prevents regressions like the phantom entry module issue that kfj001
|
|
* hit: vite.config.ts referenced probeFn.js and scrapeFn.js but those
|
|
* files were never created.
|
|
*/
|
|
import { describe, it } from './runner.mjs';
|
|
import { readFileSync, existsSync } from 'fs';
|
|
import { resolve, dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const root = resolve(__dirname, '..');
|
|
|
|
describe('Config: build integrity', () => {
|
|
it('vite.config.ts entry modules must exist as source files', () => {
|
|
const viteConfig = readFileSync(resolve(root, 'vite.config.ts'), 'utf8');
|
|
|
|
const matches = viteConfig.matchAll(/resolve\(__dirname,\s*'([^']+)'\)/g);
|
|
const inputEntries = [...matches]
|
|
.map(m => m[1])
|
|
.filter(p => p.startsWith('src/'));
|
|
|
|
const missing = inputEntries.filter(p => !existsSync(resolve(root, p)));
|
|
|
|
if (missing.length > 0) {
|
|
throw new Error(
|
|
`vite.config.ts references ${missing.length} missing file(s): ${missing.join(', ')}`
|
|
);
|
|
}
|
|
if (inputEntries.length === 0) {
|
|
throw new Error('No entry points found in vite.config.ts');
|
|
}
|
|
});
|
|
|
|
it('content.js must NOT use inline script injection (textContent)', () => {
|
|
const contentJs = readFileSync(resolve(root, 'src/content/content.js'), 'utf8');
|
|
|
|
// Strip comments before checking for textContent usage
|
|
const codeOnly = contentJs
|
|
.replace(/\/\*[\s\S]*?\*\//g, '') // block comments
|
|
.replace(/\/\/.*$/gm, ''); // line comments
|
|
|
|
if (codeOnly.includes('textContent')) {
|
|
throw new Error(
|
|
'content.js uses inline script injection (textContent). ' +
|
|
'Sites like Pornhub block inline scripts via CSP. Use script.src instead.'
|
|
);
|
|
}
|
|
if (!codeOnly.includes('script.src') || !codeOnly.includes('chrome.runtime.getURL')) {
|
|
throw new Error(
|
|
'content.js does not use CSP-safe script.src = chrome.runtime.getURL(...)'
|
|
);
|
|
}
|
|
});
|
|
|
|
it('interceptors.js must be a standalone file', () => {
|
|
const path = resolve(root, 'src/inject/interceptors.js');
|
|
if (!existsSync(path)) {
|
|
throw new Error('src/inject/interceptors.js does not exist');
|
|
}
|
|
const content = readFileSync(path, 'utf8');
|
|
if (!content.includes('(function ()') || !content.includes('})();')) {
|
|
throw new Error('interceptors.js should be a self-contained IIFE');
|
|
}
|
|
});
|
|
|
|
it('manifest.json content_scripts must reference content.js', () => {
|
|
const manifest = JSON.parse(
|
|
readFileSync(resolve(root, 'manifest.json'), 'utf8')
|
|
);
|
|
const cs = manifest.content_scripts;
|
|
if (!cs || !Array.isArray(cs)) {
|
|
throw new Error('manifest.json has no content_scripts');
|
|
}
|
|
const hasContentScript = cs.some(s => s.js && s.js.includes('content.js'));
|
|
if (!hasContentScript) {
|
|
throw new Error('No content_scripts entry references content.js');
|
|
}
|
|
});
|
|
});
|