chore(01-13): wave-0 — clean broken Approach-A artifacts per 01-11-SUMMARY
Restore a clean baseline before promoting thec647f61prototype to production paths (Wave 1) and building out Approach-B driver scaffolding (Wave 2). All deletions trace back to falsifications documented in 01-11-SUMMARY.md. Deleted — broken Approach-A files: - src/test-hooks/sw-hooks.ts MV3 SW blocks dynamic import (Chromium es_modules.md; w3c/webextensions#212). The gated `await import('../test-hooks/ sw-hooks')` from 01-11 Wave 1 never resolved → SW silently died → production listeners never registered. File was dead-on-arrival; no fix possible while MV3 SWs disallow dynamic import. Approach-B replaces SW-side instrumentation with the extension-internal harness page's chrome.action.* + chrome.notifications.* surface (full privilege; no monkey-patching needed). - tests/uat/lib/{launch,extension,sw,offscreen,assertions}.ts Popup-bridge architecture (01-11dbd977c) — falsification 2 + falsification 3 in 01-11-SUMMARY: `sw.evaluate` exposes only chrome.{loadTimes,csi}, NOT chrome.action.* / chrome.notifications.* / chrome.runtime.sendMessage; setPopup-juggling for extension-id resolution turned out to be unnecessary (browser.extensions() works directly per the prototype). These files will be reborn in Wave 2 around the extension-page architecture. Kept: tests/uat/lib/zip.ts (host-side JSZip work — architecture- agnostic; A12+A13 still use it) and tests/uat/lib/test-hook- contract.d.ts (type mirror — extended in Wave 3 but kept as-is here). - tests/uat/prototype/probe_{offscreen,sw,tabs,tabs2}.mjs Feasibility-research probes (01-11 spike) that empirically falsified the Approach-A hypotheses. The findings are encoded in 01-11- SUMMARY.md; the probes themselves are dead code. - tests/uat/harness.test.ts 01-11 Wave 2 popup-bridge orchestrator (dbd977c). Imports the now-deleted tests/uat/lib/{assertions,extension,sw,offscreen,launch} modules — would not typecheck after this commit. Reborn in Wave 3A as the Approach-B orchestrator (extension-internal page driver + A0 grep gate + 13 assertion drivers). Reverted — SW-side dynamic-import gate comment block: - src/background/index.ts lines 13-29 The existing comment block (post-spike) described the SW-side gated dynamic import that never landed. Rewritten to cite 01-13 Approach-B explicitly, link to 01-11-SUMMARY.md falsification, and clarify that the Tier-1 grep gate's enduring value is catching regressions in the offscreen chunk's __MOKOSH_UAT__ gate (the SW chunk is hook-free by construction). Updated — Tier-1 grep gate FORBIDDEN_HOOK_STRINGS inventory: - tests/background/no-test-hooks-in-prod-bundle.test.ts Removed: `simulateUserStop` (Approach-A naming; replaced by Approach-B `dispatchEndedOnTrack` which matches the W3C dispatchEvent semantics per RESEARCH §7 BLOCKER — track.stop() does NOT fire 'ended' per spec, so the simulation MUST use dispatchEvent). Added: `installFakeDisplayMedia`, `uninstallFakeDisplayMedia`, `dispatchEndedOnTrack`, `__mokoshOffscreenQuery`. Total inventory: 8 surface strings (was 5). Each MUST be absent from every file under dist/ post-build. Verification (all GREEN): - `npm run build` — exit 0; dist/ populated. - `grep -rln <forbidden> dist/` — 0 matches. - `npm run build:test` — exit 0; dist-test/ populated; offscreen-hooks chunk contains `installFakeDisplayMedia` (gate runs correctly against the test build's distinct artifact). - `npx tsc --noEmit` — exit 0 (root + tests/uat/tsconfig.json). - `npx vitest run` — 92/92 tests passing (was 89; the +3 new tests come from the FORBIDDEN_HOOK_STRINGS list expanding 5 → 8 — each forbidden string is one parametric `it(...)` block). Both prior-failing tests now GREEN: - tests/background/sw-bundle-import.test.ts (was missing dist/ → 92/92 requires the test run to have a current dist/; vitest gate test rebuilds via execFile when SKIP_BUILD≠1, otherwise relies on prior `npm run build`). - tests/background/no-test-hooks-in-prod-bundle.test.ts (was failing on stale dist; now GREEN against the freshly-rebuilt clean bundle). Wave 1 (next): promote tests/uat/prototype/{extension-page-harness.html, extension-page-harness.ts,a6.test.ts} to tests/uat/ via `git mv`; update vite.test.config.ts rollup input. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
// Probe — can extension page call chrome.offscreen.createDocument?
|
||||
import puppeteer from 'puppeteer';
|
||||
const browser = await puppeteer.launch({
|
||||
enableExtensions: ['/home/parf/projects/work/repremium/dist-test'],
|
||||
headless: true,
|
||||
pipe: true,
|
||||
protocolTimeout: 90_000,
|
||||
args: ['--no-sandbox'],
|
||||
});
|
||||
try {
|
||||
let exts = await browser.extensions();
|
||||
while (exts.size === 0) { await new Promise(r=>setTimeout(r,100)); exts = await browser.extensions(); }
|
||||
const [extId] = [...exts][0];
|
||||
console.log('extId:', extId);
|
||||
|
||||
const page = await browser.newPage();
|
||||
page.on('console', msg => console.log('[PAGE]', msg.text()));
|
||||
await page.goto(`chrome-extension://${extId}/tests/uat/prototype/extension-page-harness.html`, { waitUntil: 'domcontentloaded' });
|
||||
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
|
||||
const r = await page.evaluate(async () => {
|
||||
try {
|
||||
const offscreenAvailable = typeof chrome.offscreen?.createDocument;
|
||||
const url = chrome.runtime.getURL('src/offscreen/index.html');
|
||||
await chrome.offscreen.createDocument({
|
||||
url,
|
||||
reasons: [chrome.offscreen.Reason.DISPLAY_MEDIA],
|
||||
justification: 'page-test',
|
||||
});
|
||||
return { ok: true, available: offscreenAvailable, url };
|
||||
} catch (e) {
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
console.log('result:', JSON.stringify(r));
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// Probe v11 — attach to SW after waiting, check for sentinel logs
|
||||
import puppeteer from 'puppeteer';
|
||||
process.on('unhandledRejection', (r) => { console.error('UNHANDLED REJECTION:', r); process.exit(2); });
|
||||
process.on('uncaughtException', (e) => { console.error('UNCAUGHT EXCEPTION:', e); process.exit(2); });
|
||||
|
||||
const distPath = process.argv[2] === 'prod' ? '/home/parf/projects/work/repremium/dist' : '/home/parf/projects/work/repremium/dist-test';
|
||||
console.log('Using bundle:', distPath);
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
enableExtensions: [distPath],
|
||||
headless: true,
|
||||
pipe: true,
|
||||
protocolTimeout: 90_000,
|
||||
args: ['--no-sandbox'],
|
||||
});
|
||||
console.log('Launched');
|
||||
try {
|
||||
let exts = await browser.extensions();
|
||||
let start = Date.now();
|
||||
while (exts.size === 0 && Date.now() - start < 5_000) {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
exts = await browser.extensions();
|
||||
}
|
||||
const [extId] = [...exts][0] ?? [];
|
||||
console.log('extension id:', extId);
|
||||
|
||||
// Subscribe to SW console BEFORE the SW chunk runs
|
||||
browser.on('targetcreated', async (t) => {
|
||||
if (t.type() === 'service_worker' && t.url().includes(extId)) {
|
||||
console.log('TARGETCREATED service_worker — attaching console');
|
||||
try {
|
||||
const w = await t.worker();
|
||||
if (w) {
|
||||
w.on('console', msg => process.stdout.write(`[SW ${msg.type()}] ${msg.text()}\n`));
|
||||
w.on('error', err => process.stdout.write(`[SW ERROR] ${err}\n`));
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('worker() failed:', e.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for the SW target via waitForTarget (which also calls worker())
|
||||
try {
|
||||
const swTarget = await browser.waitForTarget(
|
||||
(t) => t.type() === 'service_worker' && t.url().includes(extId),
|
||||
{ timeout: 15_000 },
|
||||
);
|
||||
console.log('SW found:', swTarget.url());
|
||||
} catch (e) {
|
||||
console.log('SW wait timed out:', e.message);
|
||||
}
|
||||
|
||||
// Wait a bit
|
||||
await new Promise(r => setTimeout(r, 3_000));
|
||||
|
||||
// Open popup to trigger sendMessage
|
||||
const page = await browser.newPage();
|
||||
page.on('console', msg => process.stdout.write(`[PAGE ${msg.type()}] ${msg.text()}\n`));
|
||||
await page.goto(`chrome-extension://${extId}/src/popup/index.html`, { waitUntil: 'domcontentloaded' });
|
||||
console.log('popup opened');
|
||||
|
||||
await new Promise(r => setTimeout(r, 2_000));
|
||||
|
||||
// sendMessage
|
||||
const r = await page.evaluate(() => new Promise((resolve) => {
|
||||
const t = setTimeout(() => resolve({ error: 'timeout' }), 5_000);
|
||||
chrome.runtime.sendMessage({ type: 'GET_VIDEO_BUFFER' }, (resp) => {
|
||||
clearTimeout(t);
|
||||
resolve({ resp, lastError: chrome.runtime.lastError?.message });
|
||||
});
|
||||
}));
|
||||
console.log('sendMessage result:', JSON.stringify(r));
|
||||
|
||||
await new Promise(r => setTimeout(r, 1_000));
|
||||
} finally {
|
||||
console.log('closing...');
|
||||
await browser.close();
|
||||
console.log('done.');
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import puppeteer from 'puppeteer';
|
||||
const b = await puppeteer.launch({
|
||||
enableExtensions: ['/home/parf/projects/work/repremium/dist-test'],
|
||||
headless: true, pipe: true, protocolTimeout: 90_000,
|
||||
args: ['--no-sandbox'],
|
||||
});
|
||||
try {
|
||||
let exts = await b.extensions();
|
||||
while (exts.size === 0) { await new Promise(r=>setTimeout(r,100)); exts = await b.extensions(); }
|
||||
const [extId] = [...exts][0];
|
||||
const page = await b.newPage();
|
||||
await page.goto(`chrome-extension://${extId}/tests/uat/prototype/extension-page-harness.html`, {waitUntil:'domcontentloaded'});
|
||||
console.log('opened harness page');
|
||||
|
||||
// Query tabs from the harness page
|
||||
const r = await page.evaluate(async () => {
|
||||
const tabs = await chrome.tabs.query({});
|
||||
const active = await chrome.tabs.query({active: true, currentWindow: true});
|
||||
return {
|
||||
allTabs: tabs.map(t => ({ id: t.id, url: t.url, active: t.active })),
|
||||
activeCurrent: active.map(t => ({ id: t.id, url: t.url, active: t.active })),
|
||||
};
|
||||
});
|
||||
console.log('tabs:', JSON.stringify(r, null, 2));
|
||||
} finally { await b.close(); }
|
||||
@@ -1,33 +0,0 @@
|
||||
import puppeteer from 'puppeteer';
|
||||
const b = await puppeteer.launch({
|
||||
enableExtensions: ['/home/parf/projects/work/repremium/dist-test'],
|
||||
headless: true, pipe: true, protocolTimeout: 90_000,
|
||||
args: ['--no-sandbox'],
|
||||
});
|
||||
try {
|
||||
let exts = await b.extensions();
|
||||
while (exts.size === 0) { await new Promise(r=>setTimeout(r,100)); exts = await b.extensions(); }
|
||||
const [extId] = [...exts][0];
|
||||
|
||||
// Open multiple pages: data URL, harness page, then test query
|
||||
const dataPage = await b.newPage();
|
||||
await dataPage.goto('data:text/html,<html><body>victim</body></html>', {waitUntil:'domcontentloaded'});
|
||||
console.log('opened data: page');
|
||||
|
||||
const harness = await b.newPage();
|
||||
await harness.goto(`chrome-extension://${extId}/tests/uat/prototype/extension-page-harness.html`, {waitUntil:'domcontentloaded'});
|
||||
console.log('opened harness');
|
||||
|
||||
await dataPage.bringToFront();
|
||||
console.log('victim brought to front');
|
||||
|
||||
const r = await harness.evaluate(async () => {
|
||||
const all = await chrome.tabs.query({});
|
||||
const active = await chrome.tabs.query({active: true, currentWindow: true});
|
||||
return {
|
||||
all: all.map(t => ({ id: t.id, url: t.url, active: t.active })),
|
||||
active: active.map(t => ({ id: t.id, url: t.url, active: t.active })),
|
||||
};
|
||||
});
|
||||
console.log('after bringToFront:', JSON.stringify(r, null, 2));
|
||||
} finally { await b.close(); }
|
||||
Reference in New Issue
Block a user