mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-09-04 15:05:20 +00:00
test: harden release and browser gates
This commit is contained in:
@@ -7,30 +7,83 @@ import { test as base, chromium } from '@playwright/test';
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
|
||||
export const extensionPath = path.join(repoRoot, 'dist/chrome');
|
||||
|
||||
function isLocalTestUrl(rawUrl) {
|
||||
try {
|
||||
const url = new URL(rawUrl);
|
||||
if (!['http:', 'https:', 'ws:', 'wss:'].includes(url.protocol)) return true;
|
||||
return url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1';
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function launchExtensionContext() {
|
||||
if (!fs.existsSync(path.join(extensionPath, 'manifest.json'))) {
|
||||
throw new Error('dist/chrome is missing. Run: npm run build:extension');
|
||||
}
|
||||
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'koalasync-e2e-'));
|
||||
let context;
|
||||
try {
|
||||
context = await chromium.launchPersistentContext(userDataDir, {
|
||||
channel: 'chromium',
|
||||
headless: true,
|
||||
args: [
|
||||
`--disable-extensions-except=${extensionPath}`,
|
||||
`--load-extension=${extensionPath}`,
|
||||
'--autoplay-policy=no-user-gesture-required',
|
||||
'--host-resolver-rules=MAP * 0.0.0.0, EXCLUDE localhost, EXCLUDE 127.0.0.1'
|
||||
]
|
||||
});
|
||||
await context.route('**/*', route => {
|
||||
if (isLocalTestUrl(route.request().url())) return route.continue();
|
||||
return route.abort('blockedbyclient');
|
||||
});
|
||||
if (typeof context.routeWebSocket === 'function') {
|
||||
await context.routeWebSocket(/.*/u, webSocketRoute => {
|
||||
if (isLocalTestUrl(webSocketRoute.url())) {
|
||||
webSocketRoute.connectToServer();
|
||||
} else {
|
||||
webSocketRoute.close({ code: 1008, reason: 'External network blocked by E2E harness' });
|
||||
}
|
||||
});
|
||||
}
|
||||
let [worker] = context.serviceWorkers();
|
||||
if (!worker) worker = await context.waitForEvent('serviceworker');
|
||||
const extensionId = worker.url().split('/')[2];
|
||||
return {
|
||||
context,
|
||||
extensionId,
|
||||
async close() {
|
||||
try {
|
||||
await context.close();
|
||||
} finally {
|
||||
fs.rmSync(userDataDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
if (context) await context.close().catch(() => {});
|
||||
if (fs.existsSync(userDataDir)) {
|
||||
fs.rmSync(userDataDir, { recursive: true, force: true });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A browser with the packed extension loaded, plus its extension id. Each test
|
||||
* gets a throwaway profile so storage from one test cannot leak into the next.
|
||||
*/
|
||||
export const test = base.extend({
|
||||
context: async ({}, use) => {
|
||||
if (!fs.existsSync(path.join(extensionPath, 'manifest.json'))) {
|
||||
throw new Error('dist/chrome is missing. Run: npm run build:extension');
|
||||
// The headless shell does not run MV3 service workers; the full
|
||||
// Chromium build in new headless mode does.
|
||||
const launched = await launchExtensionContext();
|
||||
try {
|
||||
await use(launched.context);
|
||||
} finally {
|
||||
await launched.close();
|
||||
}
|
||||
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'koalasync-e2e-'));
|
||||
const context = await chromium.launchPersistentContext(userDataDir, {
|
||||
// The headless shell does not run MV3 service workers; the full
|
||||
// Chromium build in new headless mode does.
|
||||
channel: 'chromium',
|
||||
headless: true,
|
||||
args: [
|
||||
`--disable-extensions-except=${extensionPath}`,
|
||||
`--load-extension=${extensionPath}`,
|
||||
'--autoplay-policy=no-user-gesture-required'
|
||||
]
|
||||
});
|
||||
await use(context);
|
||||
await context.close();
|
||||
fs.rmSync(userDataDir, { recursive: true, force: true });
|
||||
},
|
||||
extensionId: async ({ context }, use) => {
|
||||
let [worker] = context.serviceWorkers();
|
||||
@@ -85,3 +138,20 @@ export async function readStorage(page, keys) {
|
||||
export async function writeStorage(page, values) {
|
||||
return page.evaluate(v => chrome.storage.local.set(v), values);
|
||||
}
|
||||
|
||||
export async function terminateServiceWorker(context, extensionId) {
|
||||
const page = await context.newPage();
|
||||
let session;
|
||||
try {
|
||||
await page.goto(`chrome-extension://${extensionId}/audio-options.html`);
|
||||
session = await context.newCDPSession(page);
|
||||
const { targetInfos } = await session.send('Target.getTargets');
|
||||
const worker = targetInfos.find(target => target.type === 'service_worker'
|
||||
&& target.url.startsWith(`chrome-extension://${extensionId}/`));
|
||||
if (!worker) throw new Error(`service worker target missing for ${extensionId}`);
|
||||
await session.send('Target.closeTarget', { targetId: worker.targetId });
|
||||
} finally {
|
||||
if (session) await session.detach().catch(() => {});
|
||||
await page.close().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user