const { test, expect } = require('@playwright/test'); const { LIVE_FACEAI_BASE_URL, LIVE_SITE_BASE_URL, LIVE_SITE_PORTRAIT_PATH, LIVE_SITE_RACE_URL, LIVE_SITE_RUN_UPLOAD_FLOW, ensureLiveAuthenticatedRacePage, requirePortraitFixture } = require('./live-site-test-utils'); function escapeRegExp(value) { return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } async function openLiveFaceAi(page) { const consoleErrors = []; page.on('console', (message) => { if (message.type() === 'error') { consoleErrors.push(message.text()); } }); await ensureLiveAuthenticatedRacePage(page); await expect(page.locator('h1')).toContainText(/HALF MARATHON FIRENZE|Competitions|Gare/i); const launchUrl = await page.evaluate(() => { return typeof buildFaceAiLaunchUrl === 'function' ? buildFaceAiLaunchUrl() : ''; }); expect(launchUrl, 'Expected the legacy race page script to expose a FaceAI handoff URL builder.').toBeTruthy(); const parsedLaunchUrl = new URL(launchUrl, LIVE_SITE_BASE_URL); expect(parsedLaunchUrl.searchParams.get('raceYear')).toBeTruthy(); expect(parsedLaunchUrl.searchParams.get('raceMonthFolder')).toBeTruthy(); expect(parsedLaunchUrl.searchParams.get('raceFolder')).toBeTruthy(); expect(parsedLaunchUrl.searchParams.get('raceStorageRelativeDir')).toBeTruthy(); await expect(page.locator('#faceaiLaunchButton')).toBeVisible(); await page.locator('#faceaiLaunchButton').click(); await page.waitForURL(new RegExp(`^${escapeRegExp(LIVE_FACEAI_BASE_URL)}/`), { timeout: 60 * 1000 }); await expect(page.getByRole('heading', { name: /Trova le tue foto con un selfie|Find your photos with a selfie/i })).toBeVisible(); return { consoleErrors, launchUrl: parsedLaunchUrl }; } test('loads a live race page with an authenticated session', async ({ page }) => { await ensureLiveAuthenticatedRacePage(page); const cookies = await page.context().cookies(LIVE_SITE_RACE_URL); const faceAiIdentityCookie = cookies.find((cookie) => cookie.name === 'rus_faceai_identity'); expect(faceAiIdentityCookie, 'Expected the race page to mint the FaceAI identity cookie for the authenticated session.').toBeTruthy(); expect(faceAiIdentityCookie.httpOnly).toBe(true); expect(faceAiIdentityCookie.secure).toBe(true); expect(faceAiIdentityCookie.value).toMatch(/\./); }); test('launches the live FaceAI app with race storage metadata and a styled header', async ({ page }) => { const { consoleErrors, launchUrl } = await openLiveFaceAi(page); expect(launchUrl.searchParams.get('raceStorageRelativeDir')).toContain('/'); await expect(page.locator('nav.navbar')).toBeVisible(); await expect(page.locator('link[data-legacy-href*="bootstrap.min.css"]')).toHaveCount(1); await expect(page.locator('link[data-legacy-href*="custom-style.css"]')).toHaveCount(1); const legacyStylesheetHrefs = await page.locator('link[data-legacy-href]').evaluateAll((elements) => { return elements.map((element) => element.getAttribute('href') || ''); }); expect(legacyStylesheetHrefs.some((href) => href.startsWith(`${LIVE_SITE_BASE_URL}/`))).toBe(true); const navComputedStyles = await page.locator('nav.navbar').evaluate((element) => { const styles = window.getComputedStyle(element); return { position: styles.position, display: styles.display }; }); expect(navComputedStyles.position).toBe('fixed'); expect(navComputedStyles.display).not.toBe('block'); expect(consoleErrors.find((entry) => entry.includes('MISSING_RACE_STORAGE')) || '').toBe(''); expect(consoleErrors.find((entry) => entry.includes('[FaceAI] Invalid race data:')) || '').toBe(''); }); test.skip(!LIVE_SITE_RUN_UPLOAD_FLOW, 'Set LIVE_SITE_RUN_UPLOAD_FLOW=1 to exercise the live upload flow.'); test('accepts the supplied portrait image for the live upload flow', async ({ page }) => { requirePortraitFixture(); await openLiveFaceAi(page); const fileInput = page.locator('input[type="file"]'); await expect(fileInput).toBeEnabled(); await fileInput.setInputFiles(LIVE_SITE_PORTRAIT_PATH); await expect(page.locator('.faceai-file-name')).toContainText('test_portrait_1.png'); const searchResponsePromise = page.waitForResponse((response) => { return response.request().method() === 'POST' && response.url().includes('/api/searches'); }, { timeout: 60 * 1000 }); await page.getByRole('button', { name: /Avvia ricerca Face ID|Start Face ID search/i }).click(); const searchResponse = await searchResponsePromise; expect(searchResponse.ok(), 'Expected the live upload flow to create a FaceAI search successfully.').toBe(true); const searchPayload = await searchResponse.json(); expect(searchPayload.id || searchPayload.searchId, 'Expected the search creation response to include a search identifier.').toBeTruthy(); await expect(page.locator('.faceai-feedback')).not.toContainText(/Impossibile|Unable|Errore|Error/i); });