const { start, BASE, assert, sanitized, } = require('./portal-visual-qa-lib.cjs'); void (async () => { const q = await start('promoter-dashboard-chromium-20261010'); q.proof.opportunity = q.prior.opportunity || { title: 'QA dashboard Promotor — ' + q.stamp, }; let page, ctx; try { q.phase('Dashboard QA21 → Minhas oportunidades → Nova oportunidade'); ({ page, ctx } = await q.context('promoter')); await q.login(page, 'promoter'); await q.open(page, '/dashboard'); const root = page.locator('main').last(); await root .getByRole('link', { name: 'Minhas oportunidades', exact: true }) .first() .waitFor(); await root .getByRole('link', { name: 'Nova oportunidade', exact: true }) .first() .waitFor(); await q.shot(page, 'dashboard-promoter'); await root .getByRole('link', { name: 'Minhas oportunidades', exact: true }) .first() .click(); await page.waitForURL(BASE + '/minhas-oportunidades'); await page .getByRole('link', { name: 'Nova oportunidade', exact: true }) .first() .click(); await page.waitForURL(BASE + '/minhas-oportunidades/create'); const e = q.proof.opportunity; if (!e.id) { await page .getByLabel('Título oficial', { exact: true }) .fill(e.title); await page .getByLabel('Motivo da alteração', { exact: true }) .fill('Criação sintética QA pelo dashboard, sem divulgação.'); await page .getByRole('button', { name: 'Guardar rascunho', exact: true }) .click(); await page.waitForURL(/\/minhas-oportunidades\/\d+\/edit$/); e.id = Number(new URL(page.url()).pathname.split('/')[2]); e.created_via_dashboard = true; q.save(); } q.proof.checks.dashboard_links_create_save = true; await q.logout(page); await ctx.close(); q.phase('Fresh MFA session, recovery and editing'); ({ page, ctx } = await q.context('promoter-new-session')); await q.login(page, 'promoter'); await q.open(page, '/dashboard'); await page .getByRole('link', { name: 'Minhas oportunidades', exact: true }) .first() .click(); await page.waitForURL(BASE + '/minhas-oportunidades'); const card = page.locator('article').filter({ has: page.getByRole('heading', { name: e.title, exact: true }), }); await card .getByRole('link', { name: 'Continuar preenchimento', exact: true }) .click(); await page.waitForURL((u) => u.pathname.includes('/minhas-oportunidades/' + e.id), ); if (!new URL(page.url()).pathname.endsWith('/edit')) { await page .getByRole('link', { name: 'Editar proposta privada', exact: true, }) .click(); await page.waitForURL( BASE + '/minhas-oportunidades/' + e.id + '/edit', ); } assert.equal( await page .getByLabel('Título oficial', { exact: true }) .inputValue(), e.title, ); e.recovered_new_session = true; await page .getByLabel('Resumo executivo', { exact: true }) .fill( 'Resumo sintético recuperável; dossier privado separado da oportunidade.', ); await page .getByLabel('Motivo da alteração', { exact: true }) .fill('Edição sintética QA pelo Promotor autorizado.'); await page .getByRole('button', { name: 'Guardar rascunho', exact: true }) .click(); await page .getByText( 'Rascunho guardado no servidor. Não foi submetido, aprovado ou publicado.', { exact: true }, ) .waitFor(); await page.reload({ waitUntil: 'domcontentloaded' }); assert.equal( await page .getByLabel('Resumo executivo', { exact: true }) .inputValue(), 'Resumo sintético recuperável; dossier privado separado da oportunidade.', ); e.edited_recovered = true; assert.equal( await page .getByRole('button', { name: 'Arquivar', exact: true }) .count(), 0, ); assert.equal( await page .getByRole('button', { name: 'Restaurar', exact: true }) .count(), 0, ); await page .getByLabel('Fundamentação', { exact: true }) .fill('Pedido de arquivo sintético QA após teste do dashboard.'); await page .getByRole('button', { name: 'Solicitar arquivo', exact: true }) .click(); await page .getByText( 'Pedido de arquivo registado; aguarda decisão independente.', { exact: false }, ) .waitFor(); await page.reload({ waitUntil: 'domcontentloaded' }); await page .getByText( 'Pedido de arquivo registado; aguarda decisão independente.', { exact: false }, ) .waitFor(); e.archive_request_persisted = true; await q.shot(page, 'promoter-recovered-archive-request'); q.proof.checks.recover_edit_request_archive_no_self_archive = true; await q.logout(page); await ctx.close(); q.phase('Independent authorized publisher archive'); ({ page, ctx } = await q.context('publisher')); await q.login(page, 'publisher'); await q.open(page, '/minhas-oportunidades/' + e.id + '/view'); await page .getByLabel('Fundamentação', { exact: true }) .fill('Decisão independente de arquivo sintético QA para ensaio.'); await page .getByRole('button', { name: 'Arquivar', exact: true }) .click(); await page.reload({ waitUntil: 'domcontentloaded' }); await page.getByText(/Estado da preparação: Arquivado/).waitFor(); e.archived_by_authorized_publisher = true; await q.shot(page, 'publisher-archived'); q.proof.checks.independent_archive_persisted = true; q.proof.restore = { state: 'not_available_for_profile_owned_opportunities', reason: 'The reconciled v1 disallows promoter restore and legacy lifecycle denies profile-owned records. No new restore policy is invented.', }; q.proof.result = 'passed_scoped_restore_pending'; await q.logout(page); await ctx.close(); } catch (e) { q.proof.result = 'failed'; q.proof.error = sanitized(e); if ( page && !['/login', '/two-factor-challenge'].includes( new URL(page.url()).pathname, ) ) { q.proof.failure_state = { path: new URL(page.url()).pathname, headings: await page.locator('h1').allTextContents(), labels: await page.locator('label').allTextContents(), alerts: await page.getByRole('alert').allTextContents(), }; await q.shot(page, 'failure').catch(() => {}); } } finally { q.proof.finished_at = new Date().toISOString(); q.save(); await q.browser.close(); console.log( JSON.stringify({ result: q.proof.result, phase: q.proof.phase, checks: q.proof.checks, receipt: q.receipt, }), ); if (q.proof.result === 'failed') process.exitCode = 1; } })();