Compare commits

...
4 changed files with 145 additions and 1133 deletions

No files matched your search

+99 -1133
View File
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,5 @@
import{spawn}from'node:child_process';import{mkdtemp,readFile,writeFile,rm}from'node:fs/promises';import{tmpdir}from'node:os';import{join}from'node:path';import{createServer}from'node:http';import{once}from'node:events';import assert from'node:assert/strict';
const[root,out,chromePath,display='headless']=process.argv.slice(2),home=await mkdtemp(join(tmpdir(),'rea-7167-owned-'));const server=createServer((q,r)=>{r.setHeader('Content-Type','text/html');r.end('<title>REA connect control</title><main id="result">ready</main>')});server.listen(0,'127.0.0.1');await once(server,'listening');const url='http://127.0.0.1:'+server.address().port;const profile=join(home,'profile');const browser=spawn(chromePath,[...(display==='headless'?['--headless']:[]),'--no-sandbox','--no-first-run','--disable-dev-shm-usage','--disable-background-networking','--disable-default-apps','--disable-extensions','--remote-debugging-port=0','--user-data-dir='+profile,url],{env:{HOME:home,PATH:process.env.PATH},stdio:['ignore','ignore','pipe']});let stderr='';browser.stderr.on('data',x=>{if(stderr.length<100000)stderr+=x});const get=async(u)=>{const r=await fetch(u,{signal:AbortSignal.timeout(5000)});assert.ok(r.ok,r.status);return r};
try{let port;for(let n=0;n<100;n++){try{port=(await readFile(join(profile,'DevToolsActivePort'),'utf8')).split('\n')[0];break}catch{await new Promise(r=>setTimeout(r,100))}}assert.ok(port);const endpoint='http://127.0.0.1:'+port;let targets=await(await get(endpoint+'/json/list')).json();const page=targets.find(x=>x.type==='page');assert.ok(page);const results=[];
for(const mode of ['live-page','zero-pages']){if(mode==='zero-pages'){for(const t of targets.filter(x=>x.type==='page'))await get(endpoint+'/json/close/'+t.id);for(let n=0;n<100;n++){targets=await(await get(endpoint+'/json/list')).json();if(!targets.some(x=>x.type==='page'))break;await new Promise(r=>setTimeout(r,50))}assert.equal(targets.filter(x=>x.type==='page').length,0,JSON.stringify(targets))}const path=out+'.'+mode+'.scenario.json';await writeFile(path,JSON.stringify({browser:{mode:'connect',cdp_endpoint:endpoint,target_id:page.id},start_url:{url},actions:[{step_id:'settle',action:'wait_for_timeout',duration_ms:10}],command_timeout_ms:8000}));const started=performance.now();const result=await new Promise((resolve,reject)=>{const child=spawn(process.execPath,[root+'/scripts/rea.mjs','capture-browser-scenario',path,'--json'],{cwd:root,env:{HOME:home,PATH:process.env.PATH,REA_LOG_LEVEL:'silent'},stdio:['ignore','pipe','pipe']});let stdout='',stderr='';child.stdout.on('data',x=>stdout+=x);child.stderr.on('data',x=>stderr+=x);child.once('error',reject);child.once('exit',code=>resolve({code,stdout,stderr}))});await writeFile(out+'.'+mode+'.reply.json',result.stdout);await writeFile(out+'.'+mode+'.stderr',result.stderr);let value;try{value=JSON.parse(result.stdout)}catch{}results.push({mode,exitCode:result.code,elapsedMs:performance.now()-started,errorCode:value?.code??null,errorDetails:value?.code?value.details:null,success:result.code===0,targetCount:(await(await get(endpoint+'/json/list')).json()).filter(x=>x.type==='page').length});}
console.log(JSON.stringify({endpoint,selectedTarget:page.id,results}));}finally{if(browser.exitCode===null){browser.kill('SIGTERM');await Promise.race([once(browser,'exit'),new Promise(r=>setTimeout(r,5000))]);if(browser.exitCode===null){browser.kill('SIGKILL');await once(browser,'exit')}}await new Promise(r=>server.close(r));await writeFile(out+'.chromium.stderr',stderr);await rm(home,{recursive:true,force:true})}
@@ -0,0 +1,9 @@
import {pathToFileURL} from 'node:url';
const [root,executable]=process.argv.slice(2);
const load=p=>import(pathToFileURL(root+'/'+p));
const {startRuntimeFixtureBrowser}=await load('scripts/lib/browser-runtime-fixture-lifecycle.mjs');
const {startBrowserVerifierSite}=await load('scripts/fixtures/browser-verifier-site.mjs');
const {verifyScenarioEnvironment}=await load('scripts/lib/browser-scenario-environment-e2e.mjs');
const {withVerifierCleanup}=await load('scripts/lib/verifier-cleanup.mjs');
let site,fixture;
await withVerifierCleanup(async()=>{site=await startBrowserVerifierSite();fixture=await startRuntimeFixtureBrowser(executable,site.origin+'/app');const endpoint='http://127.0.0.1:'+fixture.port;const targets=await(await fetch(endpoint+'/json/list')).json();const page=targets.find(t=>t.type==='page');console.log(JSON.stringify(await verifyScenarioEnvironment(endpoint,page.id,site.origin)));},[()=>fixture?.close(),()=>site?.close()]);
@@ -0,0 +1,32 @@
import {spawn} from 'node:child_process';
import {mkdtemp,readFile,rm,writeFile} from 'node:fs/promises';
import {tmpdir,platform,arch} from 'node:os';
import {join} from 'node:path';
import {once} from 'node:events';
import {createRequire} from 'node:module';
import assert from 'node:assert/strict';
const [chromePath,oldPackage,newPackage,out,display='headless',productionRoot]=process.argv.slice(2);
const implementations=[['pinned-default',createRequire(oldPackage+'/package.json')('./index.js').chromium,{}],['upstream-default',createRequire(newPackage+'/package.json')('./index.js').chromium,{}],['upstream-noDefaults',createRequire(newPackage+'/package.json')('./index.js').chromium,{noDefaults:true}]];
const home=await mkdtemp(join(tmpdir(),'rea-7167-matrix-'));const profile=join(home,'profile');
const child=spawn(chromePath,[...(display==='headless'?['--headless']:[]),'--no-sandbox','--no-first-run','--disable-dev-shm-usage','--disable-background-networking','--disable-default-apps','--disable-extensions','--remote-debugging-port=0','--user-data-dir='+profile,'about:blank'],{detached:true,env:{HOME:home,PATH:process.env.PATH},stdio:['ignore','ignore','pipe']});
let stderr='';child.stderr.on('data',b=>{if(stderr.length<64000)stderr+=b});const results=[];let endpoint;
const get=async path=>{const r=await fetch(endpoint+path,{signal:AbortSignal.timeout(4000)});assert.ok(r.ok,String(r.status));return r};
try{for(let n=0;n<100;n++){try{endpoint='http://127.0.0.1:'+(await readFile(join(profile,'DevToolsActivePort'),'utf8')).split('\n')[0];break}catch{await new Promise(r=>setTimeout(r,100))}}assert.ok(endpoint);const version=await(await get('/json/version')).json();
for(const state of ['live-page','zero-pages']){if(state==='zero-pages'){for(const t of await(await get('/json/list')).json())if(t.type==='page')await get('/json/close/'+t.id);for(let n=0;n<100;n++){if(!(await(await get('/json/list')).json()).some(t=>t.type==='page'))break;await new Promise(r=>setTimeout(r,50))}}
for(const [name,launcher,options] of implementations){const before=(await(await get('/json/list')).json()).filter(t=>t.type==='page').map(t=>t.id).sort();let browser;const started=performance.now();let error=null;
try{browser=await launcher.connectOverCDP(endpoint,{timeout:8000,...options});assert.equal(browser.contexts().length,1);if(state==='live-page'){assert.equal(browser.contexts()[0].pages().length,1);assert.equal(await browser.contexts()[0].pages()[0].title(),'');}}catch(e){error=String(e)}finally{await browser?.close()}
const after=(await(await get('/json/list')).json()).filter(t=>t.type==='page').map(t=>t.id).sort();assert.deepEqual(after,before);results.push({state,name,elapsedMs:performance.now()-started,error,before,after});}
if(productionRoot&&state==='zero-pages'){
const {pathToFileURL}=await import('node:url');
const {openPlaywrightScenarioBrowser}=await import(pathToFileURL(productionRoot+'/dist/browser/PlaywrightScenarioBrowser.js'));
const {browserScenarioSchema}=await import(pathToFileURL(productionRoot+'/dist/domain/browserScenario.js'));
const scenario=browserScenarioSchema.parse({browser:{mode:'connect',cdp_endpoint:endpoint,target_id:'missing-selected-target'},start_url:{url:'http://127.0.0.1:1'},environment:{service_workers:'allow'},actions:[{step_id:'unused',action:'wait_for_timeout',duration_ms:1}],command_timeout_ms:8000});
const started=performance.now();let reason;const leases=[];
try{const opened=await openPlaywrightScenarioBrowser(scenario,{}, {retainCleanup:lease=>leases.push(lease)});await opened.cleanup.close();assert.fail('Missing target unexpectedly opened')}catch(e){reason=e.reason;assert.equal(reason,'target_not_found',String(e))}
const cleanup=await Promise.allSettled(leases.map(lease=>lease.release()));
const after=(await(await get('/json/list')).json()).filter(t=>t.type==='page').map(t=>t.id);assert.deepEqual(after,[]);
results.push({state,name:'production-owner',reason,elapsedMs:performance.now()-started,after,cleanupFailures:cleanup.filter(r=>r.status==='rejected').length});
}}
await writeFile(out,JSON.stringify({platform:platform(),arch:arch(),display,chrome:version.Browser,results},null,2));console.log(JSON.stringify({platform:platform(),arch:arch(),display,chrome:version.Browser,results}));
}finally{if(child.exitCode===null){child.kill('SIGTERM');await Promise.race([once(child,'exit'),new Promise(r=>setTimeout(r,3000))]);}try{process.kill(-child.pid,'SIGKILL')}catch(e){if(e.code!=='ESRCH')throw e}await rm(home,{recursive:true,force:true});await writeFile(out+'.stderr',stderr)}