All checks were successful
Publish FaceAI Container / publish (push) Successful in 3m22s
- Added optional live FaceAI checks in README.md - Implemented relative storage segment parsing in race-storage.js - Updated server.js to include relative directory in race storage - Refactored legacyAssets.js to resolve asset base URL dynamically - Expanded live race tests to validate FaceAI app launch and metadata - Introduced portrait image handling for live upload flow - Updated faceai_handoff.php to process race storage relative directory
121 lines
No EOL
4.9 KiB
JavaScript
121 lines
No EOL
4.9 KiB
JavaScript
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);
|
|
}); |