feat: Enhance FaceAI functionality and improve login process
All checks were successful
Publish FaceAI Container / publish (push) Successful in 4m43s
All checks were successful
Publish FaceAI Container / publish (push) Successful in 4m43s
- Added a retry mechanism for page navigation in `live-site-test-utils.js` to handle transient errors during login. - Introduced a new function `performLiveLoginRequest` to handle login requests via API, improving the login flow. - Updated the login process to utilize the new API request method, ensuring a more robust authentication. - Implemented new utility functions in `rus-ecom-240621.js` for managing FaceAI state and filtering. - Created `faceai_photo_lookup.jsp` to handle photo lookups, returning JSON responses for better integration with the frontend. - Updated `faceai_return.php` to redirect users with appropriate parameters after FaceAI processing. - Modified `fotoCR.jsp` and `fotoCR-en.jsp` to include FaceAI photo IDs in the image elements for better tracking. - Enhanced the UI to display the number of matched photos dynamically based on FaceAI results.
This commit is contained in:
parent
6f191de115
commit
bba8026b7c
17 changed files with 1077 additions and 95 deletions
|
|
@ -2,11 +2,11 @@ const { test } = require('@playwright/test');
|
|||
const {
|
||||
AUTH_FILE,
|
||||
ensureAuthDirectory,
|
||||
performLiveLogin
|
||||
performLiveLoginRequest
|
||||
} = require('./live-site-test-utils');
|
||||
|
||||
test('authenticate against the live site', async ({ page }) => {
|
||||
test('authenticate against the live site', async ({ request }) => {
|
||||
ensureAuthDirectory();
|
||||
await performLiveLogin(page);
|
||||
await page.context().storageState({ path: AUTH_FILE });
|
||||
await performLiveLoginRequest(request);
|
||||
await request.storageState({ path: AUTH_FILE });
|
||||
});
|
||||
|
|
@ -4,7 +4,6 @@ const {
|
|||
LIVE_SITE_BASE_URL,
|
||||
LIVE_SITE_PORTRAIT_PATH,
|
||||
LIVE_SITE_RACE_URL,
|
||||
LIVE_SITE_RESULT_URL_PATTERN,
|
||||
LIVE_SITE_RUN_UPLOAD_FLOW,
|
||||
ensureLiveAuthenticatedRacePage,
|
||||
requirePortraitFixture
|
||||
|
|
@ -80,6 +79,86 @@ async function waitForSearchCompletion(page, searchId) {
|
|||
});
|
||||
}
|
||||
|
||||
async function readVisibleLegacyPhotoIds(page) {
|
||||
return page.locator('#demo [data-faceai-photo-id]').evaluateAll((elements) => {
|
||||
return elements.map((element) => String(element.getAttribute('data-faceai-photo-id') || '').trim()).filter(Boolean);
|
||||
});
|
||||
}
|
||||
|
||||
async function waitForVisibleLegacyPhotoIds(page, expectedCount) {
|
||||
await expect.poll(async () => {
|
||||
const visiblePhotoIds = await readVisibleLegacyPhotoIds(page);
|
||||
return visiblePhotoIds.length;
|
||||
}, {
|
||||
timeout: 30 * 1000,
|
||||
message: 'Expected the legacy FaceAI gallery to finish loading matched thumbnails.'
|
||||
}).toBe(expectedCount);
|
||||
|
||||
return readVisibleLegacyPhotoIds(page);
|
||||
}
|
||||
|
||||
async function waitForVisibleLegacyThumbs(page, expectedCount) {
|
||||
const thumbs = page.locator('#demo [data-faceai-photo-id] img.thumb');
|
||||
|
||||
await expect(thumbs).toHaveCount(expectedCount);
|
||||
await expect.poll(async () => {
|
||||
return thumbs.evaluateAll((elements) => {
|
||||
return elements.every((element) => {
|
||||
const src = String(element.getAttribute('src') || '').trim();
|
||||
return src.length > 0 && src.indexOf('_imgNotFound') === -1;
|
||||
});
|
||||
});
|
||||
}, {
|
||||
timeout: 30 * 1000,
|
||||
message: 'Expected the legacy FaceAI gallery to resolve real thumbnail image URLs.'
|
||||
}).toBe(true);
|
||||
|
||||
return thumbs;
|
||||
}
|
||||
|
||||
async function waitForLegacyFaceAiCount(page, expectedCount) {
|
||||
await expect.poll(async () => {
|
||||
return page.locator('#faceAiPhotoCountValue').textContent();
|
||||
}, {
|
||||
timeout: 30 * 1000,
|
||||
message: 'Expected the legacy FaceAI count to match the resolved gallery size.'
|
||||
}).toBe(String(expectedCount));
|
||||
}
|
||||
|
||||
async function expectLegacyFaceAiGalleryToRemainStable(page, expectedPhotoIds, holdMs = 4000) {
|
||||
await page.waitForTimeout(holdMs);
|
||||
|
||||
const visiblePhotoIds = await readVisibleLegacyPhotoIds(page);
|
||||
expect(visiblePhotoIds.sort()).toEqual(expectedPhotoIds.slice().sort());
|
||||
|
||||
await expect(page.locator('#demo [data-faceai-photo-id]')).toHaveCount(expectedPhotoIds.length);
|
||||
await waitForVisibleLegacyThumbs(page, expectedPhotoIds.length);
|
||||
await waitForLegacyFaceAiCount(page, expectedPhotoIds.length);
|
||||
}
|
||||
|
||||
test('renders the exact live FaceAI filtered sample URL with visible thumbnails', async ({ page }) => {
|
||||
const samplePhotoIds = [
|
||||
'00.PANORAMICA\\GIC_7918.JPG',
|
||||
'02.PARTENZA\\GIC_7918.JPG'
|
||||
];
|
||||
const sampleUrl = `${LIVE_SITE_BASE_URL}/42%20HALF%20MARATHON%20FIRENZE_gara-1018545---48-1.html?faceaiMatchSource=faceai&faceaiMatchCount=2&faceaiPhotoIds=${encodeURIComponent(samplePhotoIds.join(','))}`;
|
||||
|
||||
await ensureLiveAuthenticatedRacePage(page);
|
||||
await page.goto(sampleUrl, {
|
||||
waitUntil: 'domcontentloaded'
|
||||
});
|
||||
|
||||
await expect(page.locator('form[onsubmit="return searching()"]')).toBeVisible();
|
||||
await expect(page.locator('#faceAiFilterBanner')).toContainText(/Face ID filter active|Filtro Face ID attivo/i);
|
||||
|
||||
const visiblePhotoIds = await waitForVisibleLegacyPhotoIds(page, samplePhotoIds.length);
|
||||
expect(visiblePhotoIds.sort()).toEqual(samplePhotoIds.slice().sort());
|
||||
|
||||
await waitForVisibleLegacyThumbs(page, samplePhotoIds.length);
|
||||
await waitForLegacyFaceAiCount(page, samplePhotoIds.length);
|
||||
await expectLegacyFaceAiGalleryToRemainStable(page, samplePhotoIds);
|
||||
});
|
||||
|
||||
test('loads a live race page with an authenticated session', async ({ page }) => {
|
||||
await ensureLiveAuthenticatedRacePage(page);
|
||||
|
||||
|
|
@ -129,7 +208,8 @@ test('returns to the live race page from FaceAI without leaving the legacy spinn
|
|||
await page.waitForURL((url) => {
|
||||
return url.toString().startsWith(LIVE_SITE_BASE_URL) && !url.toString().startsWith(LIVE_FACEAI_BASE_URL);
|
||||
}, {
|
||||
timeout: 60 * 1000
|
||||
timeout: 60 * 1000,
|
||||
waitUntil: 'commit'
|
||||
});
|
||||
|
||||
await expect(page.locator('form[onsubmit="return searching()"]')).toBeVisible();
|
||||
|
|
@ -149,6 +229,8 @@ test('returns to the live race page from FaceAI without leaving the legacy spinn
|
|||
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 }) => {
|
||||
test.slow();
|
||||
|
||||
requirePortraitFixture();
|
||||
|
||||
await openLiveFaceAi(page);
|
||||
|
|
@ -185,18 +267,24 @@ test('accepts the supplied portrait image for the live upload flow', async ({ pa
|
|||
|
||||
await waitForSearchCompletion(page, searchId);
|
||||
|
||||
await page.waitForURL(new RegExp(`^${escapeRegExp(LIVE_SITE_RESULT_URL_PATTERN)}`), {
|
||||
timeout: 3 * 60 * 1000
|
||||
});
|
||||
|
||||
await expect.poll(async () => page.url(), {
|
||||
timeout: 15 * 1000,
|
||||
message: 'Expected the browser to land on the legacy result page after FaceAI completed.'
|
||||
}).toMatch(new RegExp(`^${escapeRegExp(LIVE_SITE_BASE_URL)}/`));
|
||||
timeout: 30 * 1000,
|
||||
message: 'Expected the browser to land on the legacy race page with FaceAI filter parameters after FaceAI completed.'
|
||||
}).toMatch(new RegExp(`^${escapeRegExp(LIVE_SITE_BASE_URL)}/.*faceaiPhotoIds=`));
|
||||
|
||||
await expect(page.locator('body')).toContainText(/Vista filtrata da FaceAI|foto da FaceAI|ID foto:/i);
|
||||
await expect.poll(async () => page.locator('.gallery-card').count(), {
|
||||
timeout: 15 * 1000,
|
||||
message: 'Expected the legacy return page to render at least one FaceAI result card.'
|
||||
}).toBeGreaterThan(0);
|
||||
});
|
||||
await expect(page.locator('form[onsubmit="return searching()"]')).toBeVisible();
|
||||
await expect(page.locator('#faceAiFilterBanner')).toContainText(/Face ID filter active|Filtro Face ID attivo/i);
|
||||
await expect(page.locator('.gallery-card')).toHaveCount(0);
|
||||
|
||||
const finalUrl = new URL(page.url());
|
||||
const expectedPhotoIds = (finalUrl.searchParams.get('faceaiPhotoIds') || '').split(',').map((value) => value.trim()).filter(Boolean);
|
||||
expect(expectedPhotoIds.length, 'Expected the final race page URL to include at least one FaceAI photo identifier.').toBeGreaterThan(0);
|
||||
|
||||
const visiblePhotoIds = await waitForVisibleLegacyPhotoIds(page, expectedPhotoIds.length);
|
||||
expect(visiblePhotoIds.length, 'Expected at least one legacy race thumbnail to remain visible after FaceAI filtering.').toBeGreaterThan(0);
|
||||
expect(visiblePhotoIds.sort()).toEqual(expectedPhotoIds.slice().sort());
|
||||
|
||||
await waitForVisibleLegacyThumbs(page, expectedPhotoIds.length);
|
||||
await waitForLegacyFaceAiCount(page, expectedPhotoIds.length);
|
||||
await expectLegacyFaceAiGalleryToRemainStable(page, expectedPhotoIds);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -41,10 +41,39 @@ function loginSubmitLocator(page) {
|
|||
return page.locator('a.btn').filter({ hasText: /Accedi|Sign in/i }).first();
|
||||
}
|
||||
|
||||
function buildLoginFormData() {
|
||||
requireCredentials();
|
||||
return {
|
||||
login: LIVE_SITE_USERNAME,
|
||||
pwd: LIVE_SITE_PASSWORD,
|
||||
cmdIU: 'check',
|
||||
act: '',
|
||||
thePage: ''
|
||||
};
|
||||
}
|
||||
|
||||
async function gotoWithRetry(page, url, options = {}, maxAttempts = 3) {
|
||||
let lastError = null;
|
||||
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||
try {
|
||||
await page.goto(url, options);
|
||||
return;
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
if (!/ERR_ABORTED/i.test(String(error)) || attempt === maxAttempts) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
async function performLiveLogin(page) {
|
||||
requireCredentials();
|
||||
|
||||
await page.goto(LIVE_SITE_LOGIN_URL, { waitUntil: 'domcontentloaded' });
|
||||
await gotoWithRetry(page, LIVE_SITE_LOGIN_URL, { waitUntil: 'commit' });
|
||||
await dismissCookieBanner(page);
|
||||
await page.locator('#login').fill(LIVE_SITE_USERNAME);
|
||||
await page.locator('#pwd').fill(LIVE_SITE_PASSWORD);
|
||||
|
|
@ -52,6 +81,24 @@ async function performLiveLogin(page) {
|
|||
await waitForLoggedInUi(page);
|
||||
}
|
||||
|
||||
async function performLiveLoginRequest(requestContext) {
|
||||
const response = await requestContext.post(`${LIVE_SITE_BASE_URL}/Logon.abl`, {
|
||||
form: buildLoginFormData(),
|
||||
failOnStatusCode: false
|
||||
});
|
||||
|
||||
const finalUrl = response.url();
|
||||
const bodyText = await response.text();
|
||||
|
||||
if (!response.ok()) {
|
||||
throw new Error(`Live login request failed with HTTP ${response.status()} at ${finalUrl}`);
|
||||
}
|
||||
|
||||
if (/login_clienti|Username \/ Email|Password/iu.test(bodyText) && !/user_logout|dettaglio_clienti|Il mio account/iu.test(bodyText)) {
|
||||
throw new Error(`Live login request appears to have remained on the login page at ${finalUrl}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForLoggedInUi(page) {
|
||||
const accountMenu = page.locator('#navbarDropdownMenuLink');
|
||||
const accountLink = page.locator('a[href*="dettaglio_clienti"]');
|
||||
|
|
@ -81,14 +128,14 @@ async function expectRacePageLoaded(page) {
|
|||
}
|
||||
|
||||
async function ensureLiveAuthenticatedRacePage(page) {
|
||||
await page.goto(LIVE_SITE_RACE_URL, { waitUntil: 'domcontentloaded' });
|
||||
await gotoWithRetry(page, LIVE_SITE_RACE_URL, { waitUntil: 'commit' });
|
||||
await dismissCookieBanner(page);
|
||||
|
||||
try {
|
||||
await waitForLoggedInUi(page);
|
||||
} catch (error) {
|
||||
await performLiveLogin(page);
|
||||
await page.goto(LIVE_SITE_RACE_URL, { waitUntil: 'domcontentloaded' });
|
||||
await performLiveLoginRequest(page.context().request);
|
||||
await gotoWithRetry(page, LIVE_SITE_RACE_URL, { waitUntil: 'commit' });
|
||||
await dismissCookieBanner(page);
|
||||
await waitForLoggedInUi(page);
|
||||
}
|
||||
|
|
@ -119,6 +166,7 @@ module.exports = {
|
|||
expectRacePageLoaded,
|
||||
loginSubmitLocator,
|
||||
performLiveLogin,
|
||||
performLiveLoginRequest,
|
||||
requirePortraitFixture,
|
||||
requireCredentials,
|
||||
waitForLoggedInUi
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue