import { createHash, randomBytes } from 'node:crypto'; import { execFile } from 'node:child_process'; import { copyFile, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'; import { resolve } from 'node:path'; import { promisify } from 'node:util'; import { expect, type FullConfig, type Page } from '@playwright/test'; import type { PortfolioFaultId } from '@/testing/fault-hooks'; const execute = promisify(execFile); const repositoryRoot = process.cwd(); const temporaryClientRoot = resolve(repositoryRoot, '.portfolio-test-client'); const run = async (command: string, args: readonly string[], environment = process.env): Promise => { await execute(command, [...args], { cwd: repositoryRoot, env: environment, maxBuffer: 16 * 1024 * 1024, }); }; const firstMatch = (source: string, expression: RegExp, label: string): string => { const matches = source.match(expression); if (matches === null || matches.length !== 1) throw new Error(`Test client is missing ${label}.`); return matches[0]; }; const buildAuthenticatedClient = async (tokenDigest: string): Promise => { await rm(temporaryClientRoot, { recursive: true, force: true }); await run('npm', ['run', 'build:preview']); await run( resolve(repositoryRoot, 'node_modules/.bin/vite'), ['build', '--outDir', temporaryClientRoot, '--manifest'], { ...process.env, PORTFOLIO_TEST_BUILD: '1', PORTFOLIO_TEST_TOKEN_DIGEST: tokenDigest, }, ); const productionIndexPath = resolve(repositoryRoot, 'dist/index.html'); const testIndexPath = resolve(temporaryClientRoot, 'index.html'); const [productionIndex, testIndex] = await Promise.all([ readFile(productionIndexPath, 'utf8'), readFile(testIndexPath, 'utf8'), ]); const testModule = firstMatch(testIndex, /