/** Real Chromium render/click acceptance on the existing, isolated QA server. */ const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const 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, '..'); const PRIVATE = path.join( ROOT, 'storage/app/private/investmoz-v2/portal-audit', ); const BASE = 'http://127.0.0.1:18097'; const TARGET = 'desk_restore_20261009_174726_test:55441'; const receiptPath = path.join(PRIVATE, 'legacy-content-browser-qa.json'); const actors = JSON.parse( fs.readFileSync( path.join(PRIVATE, 'profiles-v1-actors-private.json'), 'utf8', ), ); assert.equal( actors.promoter.id, 21, 'Use the designated synthetic Promotor QA21', ); const previous = fs.existsSync(receiptPath) ? JSON.parse(fs.readFileSync(receiptPath, 'utf8')) : {}; if (previous.started_at) { const archive = path.join( PRIVATE, 'legacy-browser-attempt-' + previous.started_at.replace(/[^0-9TZ-]/g, '-') + '.json', ); if (!fs.existsSync(archive)) fs.copyFileSync(receiptPath, archive); } const stamp = new Date().toISOString().replace(/[:.]/g, '-'); const screenshotDir = path.join(PRIVATE, 'legacy-browser-' + stamp); fs.mkdirSync(screenshotDir, { recursive: true }); const proof = { started_at: new Date().toISOString(), base: BASE, target: TARGET, browser: 'installed Chromium 1243, isolated headless contexts', native_control: 'pipe unavailable; two probes', interface_observed: true, active_writes: 0, checks: {}, events: [], screenshots: [], dossier_id: previous.dossier_id || null, continuation_id: previous.continuation_id || null, title: previous.title || 'QA visual — ideia de agronegócio ' + stamp, last_step: 'initialization', }; let browser; function save() { fs.writeFileSync(receiptPath, JSON.stringify(proof, null, 2)); } function step(name) { proof.last_step = name; save(); console.log(JSON.stringify({ step: name })); } function totp(secret) { const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; let bits = 0, value = 0; const bytes = []; for (const char of secret.toUpperCase().replace(/=+$/, '')) { const digit = alphabet.indexOf(char); assert(digit >= 0); value = (value << 5) | digit; bits += 5; if (bits >= 8) { bytes.push((value >>> (bits - 8)) & 255); bits -= 8; } } const counter = Buffer.alloc(8); counter.writeBigUInt64BE(BigInt(Math.floor(Date.now() / 30000))); const digest = crypto .createHmac('sha1', Buffer.from(bytes)) .update(counter) .digest(); const offset = digest[19] & 15; return String( (digest.readUInt32BE(offset) & 0x7fffffff) % 1000000, ).padStart(6, '0'); } async function context(viewport = { width: 1440, height: 1000 }) { const ctx = await browser.newContext({ viewport, locale: 'pt-MZ', reducedMotion: 'reduce', }); ctx.qaConfirmed = false; await ctx.route('**/*', async (route) => { const request = route.request(), url = new URL(request.url()); if (url.origin !== BASE) { proof.events.push({ external_request_blocked: url.origin }); return route.abort(); } if (!['GET', 'HEAD'].includes(request.method())) assert(ctx.qaConfirmed, 'Confirm actual QA before any write'); await route.continue(); }); ctx.on('response', (response) => { const request = response.request(), url = new URL(response.url()); if (url.origin === BASE && !url.pathname.startsWith('/build-qa-')) { const headers = response.headers(); proof.events.push({ method: request.method(), path: url.pathname, status: response.status(), target: headers['x-investmoz-qa-target'] || null, redirect_path: headers.location ? new URL(headers.location, BASE).pathname : null, }); } }); return ctx; } async function open(page, route) { const response = await page.goto(BASE + route, { waitUntil: 'networkidle', }); assert.equal(response.headers()['x-investmoz-qa-target'], TARGET); page.context().qaConfirmed = true; return response; } async function screenshot(page, name, fullPage = true) { const file = path.join(screenshotDir, name + '.png'); await page.screenshot({ path: file, fullPage }); proof.screenshots.push({ name, path: path.relative(ROOT, file).replaceAll('\\', '/'), }); } async function login(page, role) { const account = actors[role]; if (!page.url().startsWith(BASE + '/login')) await open(page, '/login'); await page.locator('#email').fill(account.email); await page.locator('#password').fill(account.password); await page.locator('[data-test="login-button"]').click(); await page.waitForURL((url) => url.pathname === '/two-factor-challenge', { timeout: 15000, }); assert(account.totp_secret, 'Normal MFA is required; never bypass it'); await page.locator('input[name="code"]').fill(totp(account.totp_secret)); await page.getByRole('button', { name: 'Continue', exact: true }).click(); await page.waitForURL( (url) => !['/login', '/two-factor-challenge'].includes(url.pathname), { timeout: 15000 }, ); await page.waitForLoadState('networkidle'); const cookies = await page.context().cookies(BASE); proof.checks['mfa_' + role] = { actor_id: account.id, final_path: new URL(page.url()).pathname, cookies: cookies.map( ({ name, domain, path, secure, sameSite, httpOnly }) => ({ name, domain, path, secure, sameSite, httpOnly, }), ), }; } async function logout(page) { await open(page, '/dashboard'); const triggers = page.locator('button[aria-haspopup="menu"]'); const count = await triggers.count(); for (let i = count - 1; i >= 0; i--) { await triggers.nth(i).click(); const action = page.locator('[data-test="logout-button"]'); if (await action.isVisible()) { await action.click(); await page.waitForURL(BASE + '/'); return; } await page.keyboard.press('Escape'); } throw new Error('Visible logout action missing on dashboard'); } async function assertDossier(page, id) { await page.waitForURL(BASE + '/investimentos/' + id); assert.equal(new URL(page.url()).pathname, '/investimentos/' + id); await page .getByRole('heading', { name: proof.title, exact: true }) .waitFor(); assert.equal( await page.getByRole('heading', { name: /^Fase \d+ ·/ }).count(), 5, ); assert.equal(await page.locator('article[id^="S"]').count(), 19); assert.equal( await page .getByRole('region', { name: 'Fontes da ideia' }) .locator('li') .count(), 1, ); await page .getByRole('heading', { name: 'Próxima acção', exact: true }) .waitFor(); } (async () => { browser = await chromium.launch({ headless: true, executablePath: 'C:/Users/macbook/AppData/Local/ms-playwright/chromium-1243/chrome-win64/chrome.exe', }); step('public desktop rendering'); const anonymous = await context(), page = await anonymous.newPage(); page.setDefaultTimeout(15000); const first = await open(page, '/explorar/agronegocio'); assert.equal(first.status(), 200); assert.equal(await page.title(), 'Explorar agronegócio - InvesteMoz QA'); proof.checks.qa_browser_title = true; await page .getByRole('heading', { name: 'Investir no agronegócio', exact: true }) .waitFor(); await page.locator('main img').waitFor(); await page.evaluate(() => document.fonts.ready); proof.checks.desktop = await page.locator('main').evaluate((main) => ({ background: getComputedStyle(main).backgroundColor, text: getComputedStyle(main).color, font: getComputedStyle(main).fontFamily, headingFont: getComputedStyle(main.querySelector('h1')).fontFamily, notoSansLoaded: document.fonts.check('16px "Noto Sans"'), notoSerifLoaded: document.fonts.check('600 36px "Noto Serif"'), photoLoaded: main.querySelector('img').complete && main.querySelector('img').naturalWidth === 900, horizontalOverflow: document.documentElement.scrollWidth > innerWidth, })); assert(proof.checks.desktop.photoLoaded); assert(!proof.checks.desktop.horizontalOverflow); await screenshot(page, 'public-landing-desktop'); step('visible search hit and miss'); await page.getByRole('searchbox').fill('corredores'); await page.getByRole('button', { name: 'Pesquisar', exact: true }).click(); await page.waitForURL(/q=corredores/); await page .getByRole('link', { name: 'Ler o artigo', exact: true }) .waitFor(); await page.getByRole('searchbox').fill('zzzz-no-content-qa'); await page.getByRole('button', { name: 'Pesquisar', exact: true }).click(); await page .getByText('Nenhum conteúdo corresponde à pesquisa.', { exact: true }) .waitFor(); assert.equal( await page .getByRole('link', { name: 'Ler o artigo', exact: true }) .count(), 0, ); await screenshot(page, 'public-search-no-result'); await page.getByRole('searchbox').fill(''); await page.getByRole('button', { name: 'Pesquisar', exact: true }).click(); await page .getByRole('link', { name: 'Ler o artigo', exact: true }) .waitFor(); proof.checks.search_hit_miss_clear = true; step('keyboard article navigation and focus'); const read = page.getByRole('link', { name: 'Ler o artigo', exact: true }); await read.focus(); await page.keyboard.press('Tab'); await page.keyboard.press('Shift+Tab'); proof.checks.focus = await read.evaluate((el) => ({ focused: el === document.activeElement, outlineStyle: getComputedStyle(el).outlineStyle, outlineWidth: getComputedStyle(el).outlineWidth, })); await page.keyboard.press('Enter'); await page.waitForURL(BASE + '/agronegocio'); const article = page.getByRole('region', { name: 'Artigo educativo' }); await article.waitFor(); assert((await article.innerText()).length > 500); await screenshot(page, 'public-article-desktop'); proof.checks.public_article = { characters_rendered: (await article.innerText()).length, source_visible: true, }; step('consumer links through clicks'); proof.checks.consumer_links = []; for (const name of [ 'Consultar os corredores cadastrados', 'Consultar localizações e mapas disponíveis', 'Exemplar na biblioteca', ]) { const consumer = page.getByRole('link', { name, exact: true }); const destination = new URL(await consumer.getAttribute('href'), BASE); await consumer.click(); await page.waitForURL(destination.href); await page.waitForLoadState('networkidle'); proof.checks.consumer_links.push({ name, path: new URL(page.url()).pathname, rendered_heading: (await page.locator('h1').allTextContents()).filter(Boolean) .length > 0, }); await page.goBack({ waitUntil: 'networkidle' }); await page.waitForURL(BASE + '/agronegocio'); await article.waitFor(); } const opportunity = page .locator('section[aria-labelledby="opportunities-title"] a') .first(); if (await opportunity.count()) { const destination = new URL( await opportunity.getAttribute('href'), BASE, ); await opportunity.click(); await page.waitForURL(destination.href); await page.waitForLoadState('networkidle'); assert(new URL(page.url()).pathname.startsWith('/oportunidades/')); proof.checks.published_opportunity_clicked = new URL( page.url(), ).pathname; await page.goBack({ waitUntil: 'networkidle' }); await article.waitFor(); } step('mobile public rendering'); const mobileCtx = await context({ width: 390, height: 844 }), mobile = await mobileCtx.newPage(); await open(mobile, '/explorar/agronegocio'); await mobile .getByRole('heading', { name: 'Investir no agronegócio', exact: true }) .waitFor(); proof.checks.mobile = await mobile.evaluate(() => ({ width: innerWidth, contentWidth: document.documentElement.scrollWidth, photoLoaded: !!document.querySelector('main img')?.naturalWidth, })); assert(proof.checks.mobile.contentWidth <= proof.checks.mobile.width); await screenshot(mobile, 'public-landing-mobile'); await mobile .getByRole('link', { name: 'Ler o artigo', exact: true }) .click(); await mobile.getByRole('region', { name: 'Artigo educativo' }).waitFor(); assert( await mobile.evaluate( () => document.documentElement.scrollWidth <= innerWidth, ), ); await screenshot(mobile, 'public-article-mobile'); await mobileCtx.close(); step('public CTA, normal Promotor QA21 login and MFA'); await page .getByRole('link', { name: 'Iniciar ou continuar uma ideia', exact: true, }) .click(); await page.waitForURL(BASE + '/login'); await login(page, 'promoter'); assert.equal( new URL(page.url()).pathname, '/investimentos/a-partir-de/agronegocio', ); await page .getByRole('heading', { name: 'Os meus investimentos', exact: true }) .waitFor(); step('create and save dossier using UI'); const createdThisRun = !proof.dossier_id; if (createdThisRun) { await page.getByLabel('Título', { exact: true }).fill(proof.title); await page .getByLabel( 'Autorizo reutilizar o meu perfil neste dossier privado.', ) .check(); await page .getByRole('button', { name: 'Criar e guardar dossier', exact: true, }) .click(); await page.waitForURL(/\/investimentos\/[0-9a-f-]{36}$/); proof.dossier_id = new URL(page.url()).pathname.split('/').pop(); save(); } else await page .getByRole('link', { name: proof.title, exact: true }) .click(); await assertDossier(page, proof.dossier_id); await screenshot(page, 'private-dossier-created', false); proof.checks.created_from_ui = true; proof.checks.created_this_run = createdThisRun; if (!createdThisRun) { // Creation may be an earlier observed action, not another insert on every rerun. const files = fs .readdirSync(PRIVATE) .filter( (name) => name.startsWith('legacy-browser-attempt-') && name.endsWith('.json'), ); const creation = files .map((name) => ({ name, receipt: JSON.parse( fs.readFileSync(path.join(PRIVATE, name), 'utf8'), ), })) .find( ({ receipt }) => receipt.dossier_id === proof.dossier_id && receipt.events?.some( (event) => event.method === 'POST' && event.path === '/investimentos' && event.status === 302, ), ); assert( creation, 'Existing dossier must have retained evidence of UI creation', ); proof.creation_evidence = { receipt: creation.name, started_at: creation.receipt.started_at, action: 'POST /investimentos through UI; subsequent retries recover this same dossier', whole_attempt_result: creation.receipt.result, }; } step('source return and same dossier context'); await page .getByRole('region', { name: 'Fontes da ideia' }) .locator('a') .first() .click(); await article.waitFor(); await page .getByRole('link', { name: 'Continuar: ' + proof.title, exact: true }) .click(); await assertDossier(page, proof.dossier_id); proof.checks.private_return_context = true; step('visible logout and new browser session'); await logout(page); await anonymous.close(); const freshCtx = await context(), fresh = await freshCtx.newPage(); await open(fresh, '/investimentos/' + proof.dossier_id); assert.equal(new URL(fresh.url()).pathname, '/login'); await login(fresh, 'promoter'); await assertDossier(fresh, proof.dossier_id); await fresh.reload({ waitUntil: 'networkidle' }); await assertDossier(fresh, proof.dossier_id); await screenshot(fresh, 'private-dossier-new-session', false); proof.checks.logout_new_session_reload = true; step('continue existing dossier and repeat without duplicate'); await open(fresh, '/investimentos/a-partir-de/agronegocio'); const card = fresh.getByRole('listitem').filter({ has: fresh.getByRole('link', { name: proof.title, exact: true }), }); await card .getByRole('button', { name: 'Continuar neste dossier com o artigo', exact: true, }) .click(); await assertDossier(fresh, proof.dossier_id); proof.checks.existing_dossier_replay_single_visible_reference = true; step('anonymous context does not expose private dossier'); const publicCtx = await context(), publicPage = await publicCtx.newPage(); await open(publicPage, '/agronegocio?context=' + proof.dossier_id); await publicPage .getByRole('region', { name: 'Artigo educativo' }) .waitFor(); assert.equal( await publicPage.getByText(proof.title, { exact: false }).count(), 0, ); await publicPage .getByRole('link', { name: 'Iniciar ou continuar uma ideia', exact: true, }) .waitFor(); proof.checks.anonymous_private_context_hidden = true; await publicCtx.close(); step('other synthetic account denied and public source retained'); const otherCtx = await context(), other = await otherCtx.newPage(); await open(other, '/login'); await login(other, 'outsider'); const denied = await open(other, '/investimentos/' + proof.dossier_id); assert.equal(denied.status(), 403); await open(other, '/agronegocio?context=' + proof.dossier_id); await other.getByRole('region', { name: 'Artigo educativo' }).waitFor(); assert.equal( await other.getByText(proof.title, { exact: false }).count(), 0, ); proof.checks.other_account_denied = { actor_id: actors.outsider.id, dossier_status: 403, public_article_visible: true, }; await otherCtx.close(); await logout(fresh); await freshCtx.close(); proof.result = 'passed'; proof.completed_at = new Date().toISOString(); save(); console.log( JSON.stringify({ result: proof.result, actor_id: 21, dossier_id: proof.dossier_id, interface_observed: true, checks: Object.keys(proof.checks), screenshots: proof.screenshots.length, active_writes: 0, }), ); })() .catch(async (error) => { let message = error.message; for (const account of Object.values(actors)) { for (const key of ['password', 'totp_secret', 'secret']) { if (typeof account[key] === 'string' && account[key]) message = message.replaceAll(account[key], '[REDACTED]'); } } message = message.replace(/\b\d{6}\b/g, '[MFA OMITIDO]'); proof.result = 'failed'; proof.failure = { step: proof.last_step, type: error.name, message: message.slice(0, 2500), pages: [], }; if (browser) for (const ctx of browser.contexts()) for (const page of ctx.pages()) { proof.failure.pages.push({ path: new URL(page.url()).pathname, headings: await page .locator('h1') .allTextContents() .catch(() => []), error_count: await page .getByRole('alert') .count() .catch(() => 0), }); } save(); console.error( JSON.stringify({ step: proof.failure.step, type: proof.failure.type, message: 'Sanitized diagnostic saved privately', }), ); process.exitCode = 1; }) .finally(async () => { if (browser) await browser.close(); });