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

58 lines
8.1 KiB
JavaScript

// Only outstanding category/materiality/condition criteria; prior creation and A/B/C37 proofs are reused.
const {start,assert,sanitized}=require('./portal-functional-qa-lib.cjs');
void(async()=>{
const q=await start('functional-workflow-chromium-20261011'),sessions=new Map();let page,props;
q.proof.cases=q.prior.cases||[];q.proof.AB_C37_not_repeated=true;
const names={request_revision:'Preparar alteração material',submit:'Submeter para validação',start_review:'Iniciar validação',review_gate:'Guardar revisão especializada',approve:'Aprovar versão',publish:'Disponibilizar com acesso restrito'};
async function actor(role){if(!sessions.has(role)){const s=await q.context(role);sessions.set(role,s);await q.login(s.page,role);}page=sessions.get(role).page;}
async function open(id,edit=false){const r=await q.open(page,`/minhas-oportunidades/${id}/${edit?'edit':'view'}`);assert.equal(r.status(),200);const html=await r.text(),attr=html.match(/data-page="([^"]+)"/);
const raw=attr&&attr[1].startsWith('{')?attr[1].replace(/&quot;/g,'"').replace(/&amp;/g,'&').replace(/&#039;/g,"'"):html.match(/<script[^>]*type="application\/json"[^>]*>([\s\S]*?)<\/script>/)?.[1];props=JSON.parse(raw).props;await page.locator('main').first().waitFor();}
async function action(id,name,gate,options={}){await open(id);assert(props.profileActions.includes(name),name+' must be authorised');const box=page.getByRole('region',{name:'Workflow interno',exact:true});
await box.getByLabel('Fundamentação',{exact:true}).fill('Ensaio sintético QA de '+name+': preservar assinaturas, condições explícitas e decisão independente.');
q.phase('Category '+id+' '+name+(gate?' '+gate:''));
if(gate){await box.getByLabel(/^Revisão/).selectOption(gate);await box.getByLabel(/^Decisão/).selectOption('approved');const d=props.canonicalOptions.resources.documents[0];await box.getByLabel(d.payload.title||d.file.file_name,{exact:true}).check();}
if(options.condition){await box.getByLabel('Descrição da condição',{exact:true}).fill(options.condition);await box.getByLabel(/^Responsável pela preparação da evidência/).selectOption('21');await box.getByLabel('Prazo específico da condição, se definido',{exact:true}).fill('2026-10-13');}
if(options.resolve){await box.getByLabel('Verifiquei com os documentos selecionados: '+options.resolve,{exact:true}).check();}
if(name==='publish')await box.getByLabel(/Confirmei a pré-visualização/).check();
const pending=page.waitForResponse(r=>r.request().method()==='POST'&&new URL(r.url()).pathname===`/minhas-oportunidades/${id}/workflow`);
const label=name==='publish'&&props.values.canonical.visibility==='public'?'Publicar no catálogo público':names[name];await box.getByRole('button',{name:label,exact:true}).click();const r=await pending,data=await r.json();assert.equal(r.status(),options.status||200,name+': '+data.message);
if(r.status()===422){assert(await box.getByRole('alert').first().isVisible());await q.shot(page,`case-${id}-${name}-blocked`,false);}
else await open(id);
return data;
}
const scenarios=[
[30,'risks.0.mitigation',['technical_review']],
[31,'real_estate.title_holder_name',['registry_urban_review']],
[32,'land_rights.holder_name',['land_legal_review']],
[33,'business_transaction.entity_registration.registration_number',['corporate_screening']],
[34,'business_transaction.governance_rights.voting',['legal_tax_due_diligence','partnership_governance_review']],
[35,'business_transaction.entity_registration.registration_number',['franchise_legal_review']],
];
async function recordResponse(id,item){await actor('promoter');await open(id);const version=props.canonicalResponse.data.version;assert(Number.isInteger(version));
const response=await page.evaluate(async route=>{const r=await fetch(route,{headers:{Accept:'application/json'}});return {status:r.status,body:await r.json()};},`/minhas-oportunidades/${id}/response`);
assert.equal(response.status,200);assert.equal(response.body.compatibility.normative_response_complete,false);assert.equal(response.body.review_conditions[0].status,'resolved');assert.equal(response.body.data.version,version);await open(id);assert.equal(props.canonicalResponse.data.version,version);
item.response_read_only=true;item.condition_recovered=true;item.final_state=props.workflowState;await q.shot(page,`case-${id}-recovered-result`,false);q.save();}
try {
for(const [id,field,expected] of scenarios){const completed=q.proof.cases.find(c=>c.id===id);if(completed?.response_read_only)continue;if(completed?.publication){await recordResponse(id,completed);continue;}q.phase('Category '+id+' materiality and conditions');await actor('promoter');await open(id);
let item=q.proof.cases.find(c=>c.id===id);const baseline=item?.baseline_reviews||((item?.saved_and_refreshed?props.profileContext.decision_history?.at(-1)?.reviews:null)||props.profileContext.reviews);
if(!item){item={id,category:props.record.opportunity_category,field,initial_state:props.workflowState};q.proof.cases.push(item);}item.baseline_reviews=baseline;q.save();
if(!item.saved_and_refreshed){
if(['published','approved','suspended'].includes(props.workflowState))await action(id,'request_revision');
await open(id,true);const input=page.locator(`[id="canonical.${field}"]`);assert(await input.isVisible());const previous=await input.inputValue();await input.fill(previous+' QA revisão delimitada');
await page.getByLabel('Motivo da alteração',{exact:true}).fill('Alteração sintética QA da condição material específica; preservar os outros pareceres e originais.');
const saved=page.waitForResponse(r=>r.request().method()==='PUT'&&new URL(r.url()).pathname===`/minhas-oportunidades/${id}/draft`);await page.getByRole('button',{name:/Guardar rascunho/}).click();assert.equal((await saved).status(),200);await open(id);await page.reload({waitUntil:'domcontentloaded'});item.saved_and_refreshed=true;q.save();}
if(!item.reopened){await action(id,'submit');}const actual=props.canonicalResponse.materiality.reopened;assert.deepEqual([...actual].sort(),[...expected].sort());
for(const g of props.canonicalResponse.materiality.retained){assert.equal(props.profileContext.reviews[g].hash,baseline[g].hash);assert.equal(props.profileContext.reviews[g].actor_id,baseline[g].actor_id);assert.equal(props.profileContext.reviews[g].at,baseline[g].at);}
item.reopened=actual;item.retained=props.canonicalResponse.materiality.retained;await q.shot(page,`case-${id}-only-affected-reviews`,false);q.save();
await actor('validator');await open(id);if(props.profileActions.includes('start_review'))await action(id,'start_review');const condition='Condição sintética QA: conferir evidência da revisão '+id;
for(let i=0;i<expected.length;i++){const existing=Object.values(props.profileContext.conditions||{}).find(c=>c.description===condition);if(!props.profileContext.reviews[expected[i]]||(!existing&&i===0))await action(id,'review_gate',expected[i],!existing&&i===0?{condition}:{});}
if(!item.condition_blocks_approval_and_focus){await action(id,'approve',null,{status:422});await page.waitForFunction(()=>document.activeElement?.getAttribute('aria-label')==='Condições de revisão');item.condition_blocks_approval_and_focus=true;q.save();}
await open(id);if(Object.values(props.profileContext.conditions||{}).find(c=>c.description===condition)?.status==='open')await action(id,'review_gate',expected[0],{resolve:condition});if(props.workflowState!=='approved')await action(id,'approve');item.independent_approval=true;q.save();
await actor('publisher');await action(id,'publish',null,{status:id===32?422:200});item.publication=id===32?'blocked_by_real_external_dependency':'visible_and_recovered';
q.save();await recordResponse(id,item);
}
q.proof.result='passed';q.save();console.log(JSON.stringify({result:'passed',categories:q.proof.cases.length,active_writes:0}));
}catch(e){q.proof.result='failed';q.proof.failure=sanitized(e);if(page&&!page.isClosed())q.proof.last_route=new URL(page.url()).pathname;q.save();console.log(JSON.stringify({result:'failed',failure:q.proof.failure,route:q.proof.last_route,active_writes:0}));process.exitCode=1;}
finally{for(const s of sessions.values())await s.ctx.close();await q.browser.close();}
})();