Files
Investidor-V2/tools/portal-stability-inspect-ui.cjs
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

37 lines
6.8 KiB
JavaScript

// Changed UUID consumers only. Existing A/B and C37 acceptance is preserved.
const fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict'),crypto=require('node:crypto');
const {chromium}=require('C:/Users/macbook/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright');
const ROOT=path.resolve(__dirname,'..'),DIR=path.join(ROOT,'storage/app/private/investmoz-v2/portal-audit');
const plan=JSON.parse(fs.readFileSync(path.join(DIR,'portal-stability-latest.json'))),BASE=plan.consumer_environment.base,TARGET=plan.consumer_environment.target.database+':55441';
const actors=JSON.parse(fs.readFileSync(path.join(DIR,'profiles-v1-actors-private.json')));
actors.geo=JSON.parse(fs.readFileSync(path.join(DIR,'i0-qa-actor-private.json')));
const receipt=path.join(DIR,'portal-stability-ui-inspection.json');
const proof={at:new Date().toISOString(),origin:BASE,target:TARGET,checks:[],operations:[],errors:[],active_writes:0,AB_C37_not_repeated:true};
const resume=process.argv.includes('--resume');
if(resume){const prior=JSON.parse(fs.readFileSync(receipt));assert.equal(prior.target,TARGET);assert.equal(prior.errors.length,0);assert.equal(prior.checks.length,7);assert(prior.new_opportunity_id&&prior.new_partnership_path);fs.copyFileSync(receipt,receipt.replace('.json','-before-consent-retry.json'));Object.assign(proof,prior,{at:new Date().toISOString(),previous_harness_missing_consent:prior.failure});delete proof.failure;delete proof.result;}
const remaining=process.argv.includes('--remaining');
if(remaining){const prior=JSON.parse(fs.readFileSync(receipt));assert.equal(prior.target,TARGET);assert.equal(prior.errors.length,0);assert.equal(prior.checks.length,15);fs.copyFileSync(receipt,receipt.replace('.json','-before-geometry-memory-correction.json'));Object.assign(proof,prior,{at:new Date().toISOString(),previous_missing_geometry_exemplar:prior.failure});delete proof.failure;delete proof.result;}
const save=()=>fs.writeFileSync(receipt,JSON.stringify(proof,null,2));
const redact=e=>Object.values(actors).reduce((s,a)=>['password','totp_secret','secret'].reduce((v,k)=>a[k]?v.replaceAll(a[k],'[REDACTED]'):v,s),String(e.message||e)).split('\nCall log:')[0].replace(/\b\d{6}\b/g,'[OTP OMITTED]').slice(0,1200);
function otp(secret){let bits=0,v=0;const b=[],abc='ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';for(const c of secret.toUpperCase().replace(/=+$/,'')){v=(v<<5)|abc.indexOf(c);bits+=5;if(bits>=8){b.push((v>>>(bits-8))&255);bits-=8;}}const n=Buffer.alloc(8);n.writeBigUInt64BE(BigInt(Math.floor(Date.now()/30000)));const d=crypto.createHmac('sha1',Buffer.from(b)).update(n).digest(),o=d[19]&15;return String((d.readUInt32BE(o)&0x7fffffff)%1000000).padStart(6,'0');}
(async()=>{const browser=await chromium.launch({headless:true,executablePath:'C:/Users/macbook/AppData/Local/ms-playwright/chromium-1243/chrome-win64/chrome.exe',args:['--host-resolver-rules=MAP portal-investidor.test 127.0.0.1','--no-proxy-server']});let ctx,page,role;
async function actor(name){if(ctx)await ctx.close();role=name;ctx=await browser.newContext({ignoreHTTPSErrors:false,viewport:{width:1440,height:1000}});
await ctx.addInitScript(()=>Object.defineProperty(Crypto.prototype,'randomUUID',{value:undefined,configurable:true}));
await ctx.route('**/*',r=>new URL(r.request().url()).origin===BASE?r.continue():r.abort());page=await ctx.newPage();page.setDefaultTimeout(35000);
page.on('pageerror',e=>{proof.errors.push({role,path:new URL(page.url()).pathname,message:redact(e)});save();});
page.on('request',r=>{if(!['POST','PUT'].includes(r.method())||['/login','/two-factor-challenge'].includes(new URL(r.url()).pathname))return;
let data;try{data=r.postDataJSON();}catch{return;}for(const key of ['client_key','creation_key','idempotency_key'])if(data?.[key]){
assert.match(data[key],/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
proof.operations.push({role,path:new URL(r.url()).pathname,method:r.method(),key_field:key,key_sha256:crypto.createHash('sha256').update(data[key]).digest('hex')});save();}});
await open('/login');const a=actors[name];await page.locator('#email').fill(a.email);await page.locator('#password').fill(a.password);await page.locator('[data-test="login-button"]').click();
await page.waitForURL(u=>u.pathname!=='/login');if(new URL(page.url()).pathname==='/two-factor-challenge'){await page.waitForTimeout(30500-(Date.now()%30000));await page.locator('input[name="code"]').fill(otp(a.totp_secret??a.secret));await page.getByRole('button',{name:'Continue',exact:true}).click();await page.waitForURL(u=>!['/login','/two-factor-challenge'].includes(u.pathname));}}
async function open(route){const r=await page.goto(BASE+route,{waitUntil:'networkidle'});assert.equal(r.headers()['x-investmoz-qa-target'],TARGET);return r;}
async function observed(component,route,expected=200){const r=await open(route);assert.equal(r.status(),expected);await page.locator('h1').first().waitFor();assert(await page.evaluate(()=>isSecureContext&&typeof crypto.randomUUID==='undefined'&&typeof crypto.getRandomValues==='function'));
const row={component,role,route,status:r.status(),uuid_api_absent:true,visible_heading:(await page.locator('h1').first().innerText()).slice(0,150),errors:proof.errors.length};proof.checks.push(row);save();return row;}
async function recover(route,label){await open(route);await page.reload({waitUntil:'networkidle'});assert(await page.locator('h1').first().isVisible());proof.checks.push({criterion:label,role,route,refresh_recovery:true});save();}
try{
const cases=[['promoter','/minhas-oportunidades/53/resources'],['promoter','/balcao-virtual/painel'],['promoter','/balcao-virtual/dossiers/d8ac9133-65b6-4ed4-964e-e230bc656a02'],['public','/perfil/promotor']];
for(const [name,url] of cases){await actor(name);await open(url);proof.checks.push({role:name,route:url,labels:await page.locator('label').allTextContents(),buttons:await page.getByRole('button').allTextContents(),details:await page.locator('summary').allTextContents(),selects:await page.locator('select').evaluateAll(els=>els.map(e=>({id:e.id,options:[...e.options].map(o=>({label:o.label,value:o.value}))})))});save();}
if(ctx)await ctx.close();ctx=await browser.newContext({ignoreHTTPSErrors:false});await ctx.addInitScript(()=>Object.defineProperty(Crypto.prototype,'randomUUID',{value:undefined,configurable:true}));page=await ctx.newPage();await open('/simulador-fiscal');proof.checks.push({role:'anonymous',route:'/simulador-fiscal',labels:await page.locator('label').allTextContents(),buttons:await page.getByRole('button').allTextContents()});proof.result='inspected';save();console.log(JSON.stringify({result:proof.result,checks:proof.checks.length}));
}catch(e){proof.result='failed';proof.failure=redact(e);save();console.log(JSON.stringify({result:'failed',failure:proof.failure}));process.exitCode=1;}finally{if(ctx)await ctx.close();await browser.close();}})();