End to end tests

This commit is contained in:
MaddoScientisto 2026-04-12 19:31:12 +02:00
commit fed82d1ae8
26 changed files with 1016 additions and 37 deletions

View file

@ -33,6 +33,7 @@ const copy = {
redirectLoading: 'Reindirizzamento alla pagina legacy filtrata in corso…',
processingLoading: 'Ricerca biometrica in corso su tutte le foto della gara…',
unavailableDefault: 'FaceAI non è disponibile per questa gara.',
noFacesFoundMessage: 'Nessun volto rilevato nella foto caricata. Puoi tornare alla gara oppure provare con un altro selfie.',
readyMessage: 'Seleziona un selfie per avviare una ricerca limitata alla gara corrente.',
completedMessage: 'Ricerca completata. Trovate {count} foto corrispondenti.',
failedMessage: 'La ricerca non è stata completata. Verifica il messaggio di errore e riprova.',
@ -45,6 +46,7 @@ const copy = {
redirectError: 'Impossibile generare il link di ritorno.',
chooseSelfie: 'Seleziona un selfie prima di avviare la ricerca.',
raceDataUnavailable: 'I dati FaceAI non sono disponibili per questa gara.',
invalidRaceData: 'I dati della gara ricevuti non sono validi. Torna alla pagina gara e riapri Face ID dalla gara corretta.',
searchCreateError: 'Impossibile avviare la ricerca.',
faceAiAlt: 'FaceAI',
dropzoneDisabled: 'Il caricamento non è disponibile per questa gara.'
@ -81,6 +83,7 @@ const copy = {
redirectLoading: 'Redirecting to the filtered legacy page…',
processingLoading: 'Biometric search in progress across all race photos…',
unavailableDefault: 'FaceAI is not available for this race.',
noFacesFoundMessage: 'No faces were detected in the uploaded image. You can return to the race page or try another selfie.',
readyMessage: 'Select a selfie to start a search limited to the current race.',
completedMessage: 'Search completed. Found {count} matching photos.',
failedMessage: 'The search did not complete. Check the message and try again.',
@ -93,6 +96,7 @@ const copy = {
redirectError: 'Unable to build the return link.',
chooseSelfie: 'Choose a selfie before starting the search.',
raceDataUnavailable: 'FaceAI data is not available for this race.',
invalidRaceData: 'The race data received for this session is invalid. Go back to the race page and reopen Face ID from the correct race.',
searchCreateError: 'Unable to start the search.',
faceAiAlt: 'FaceAI',
dropzoneDisabled: 'Upload is not available for this race.'
@ -111,6 +115,11 @@ const knownServerMessages = {
};
const simulatorUrl = 'http://localhost:8080/faceai_simulator.php?raceId=101&lang=it';
const legacyHomeUrl = 'http://localhost:8080/index.jsp';
function isInvalidRaceAvailability(availability) {
return availability?.reasonCode === 'RACE_DIRECTORY_NOT_FOUND' || availability?.reasonCode === 'MISSING_RACE_STORAGE';
}
export function useFaceAiHome() {
const session = ref(null);
@ -153,6 +162,14 @@ export function useFaceAiHome() {
return t(fallbackKey);
}
function getAvailabilityUserMessage(availability, fallbackKey = 'unavailableDefault') {
if (isInvalidRaceAvailability(availability)) {
return t('invalidRaceData');
}
return localizeServerMessage(availability?.message, fallbackKey);
}
function shouldLogFaceAiDebug() {
return import.meta.env.DEV || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
}
@ -176,6 +193,24 @@ export function useFaceAiHome() {
console.groupEnd();
}
function reportInvalidRaceAvailability(availability) {
if (!isInvalidRaceAvailability(availability)) {
return;
}
const details = {
raceId: session.value?.race?.id || null,
raceName: session.value?.race?.name || null,
lang: session.value?.lang || currentLocale.value,
reasonCode: availability.reasonCode,
message: availability.message,
storage: availability.storage || null,
raceDir: availability.raceDir || null
};
console.error(`[FaceAI] Invalid race data: ${JSON.stringify(details)}`);
}
const isWorking = computed(() => loading.value || isSubmitting.value || isRedirecting.value || activeSearch.value?.status === 'processing');
const isProcessingSearch = computed(() => isSubmitting.value || activeSearch.value?.status === 'processing');
const raceAvailability = computed(() => session.value?.availability || null);
@ -245,13 +280,17 @@ export function useFaceAiHome() {
const statusLabel = computed(() => {
if (!activeSearch.value) {
if (session.value && raceAvailability.value && !raceAvailability.value.available) {
return localizeServerMessage(raceAvailability.value.message, 'unavailableDefault');
return getAvailabilityUserMessage(raceAvailability.value, 'unavailableDefault');
}
return t('readyMessage');
}
if (activeSearch.value.status === 'completed') {
if (activeSearch.value.completionCode === 'NO_FACES_FOUND') {
return t('noFacesFoundMessage');
}
return t('completedMessage', { count: activeSearch.value.matchCount ?? 0 });
}
@ -347,13 +386,21 @@ export function useFaceAiHome() {
const response = await fetch('/api/session', { credentials: 'include' });
if (!response.ok) {
const payload = await response.json().catch(() => ({}));
loading.value = false;
logFaceAiDebug('Session load failed', { status: response.status });
logFaceAiDebug('Session load failed', { status: response.status, payload });
if (response.status === 401 || response.status === 403) {
window.location.replace(payload.redirectUrl || legacyHomeUrl);
}
return;
}
session.value = await response.json();
loading.value = false;
if (session.value?.availability && !session.value.availability.available && isInvalidRaceAvailability(session.value.availability)) {
errorMessage.value = getAvailabilityUserMessage(session.value.availability, 'invalidRaceData');
reportInvalidRaceAvailability(session.value.availability);
}
logFaceAiDebug('Session loaded');
}
@ -376,6 +423,14 @@ export function useFaceAiHome() {
if (activeSearch.value.status === 'completed') {
isSubmitting.value = false;
if (activeSearch.value.completionCode === 'NO_FACES_FOUND') {
isRedirecting.value = false;
redirectUrl.value = '';
clearSelectedFile();
logFaceAiDebug('Search completed without detectable faces', { searchId });
return;
}
const redirectResponse = await fetch(`/api/searches/${searchId}/redirect`, { credentials: 'include' });
const payload = await redirectResponse.json();
if (!redirectResponse.ok) {
@ -408,7 +463,7 @@ export function useFaceAiHome() {
}
if (!session.value?.access?.faceAiAllowed || !raceAvailability.value?.available) {
errorMessage.value = localizeServerMessage(raceAvailability.value?.message, 'raceDataUnavailable');
errorMessage.value = getAvailabilityUserMessage(raceAvailability.value, 'raceDataUnavailable');
return;
}