<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{ site_name }}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css"
integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF"
crossorigin="anonymous"></script>
<script>
window.axeptioSettings = {
clientId: "66137db6ae83de5e566cfc53",
cookiesVersion: "first-id sandbox-fr-EU",
};
(function (d, s) {
var t = d.getElementsByTagName(s)[0],
e = d.createElement(s);
e.async = false;
e.src = "//static.axept.io/tcf/sdk.js";
e.type = "module";
t.parentNode.insertBefore(e, t);
})(document, "script");
</script>
<style>
.test-card {
border: 2px solid #dee2e6;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
transition: background-color 0.3s ease;
}
.test-card.success { background-color: #d4edda; border-color: #28a745; }
.test-card.error { background-color: #f8d7da; border-color: #dc3545; }
.test-card.running { background-color: #fff3cd; border-color: #ffc107; }
.test-card.disabled { opacity: 0.5; pointer-events: none; }
.log-output {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
max-height: 300px;
overflow-y: auto;
font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
word-break: break-all;
}
.log-entry { margin-bottom: 5px; }
.log-entry.error { color: #dc3545; font-weight: bold; }
.log-entry.debug { color: #007bff; }
.log-entry.info { color: #28a745; }
iframe { width: 100%; height: 400px; border: 1px solid #dee2e6; border-radius: 4px; }
#globalSummary { display: none; }
#globalSummary.visible { display: block; }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{{ path('app_index') }}">{{ site_name }}</a>
</nav>
<div class="container mt-4">
<div class="row mb-3">
<div class="col-md-12">
<label for="environmentSelect" class="font-weight-bold">Environnement API</label>
<select id="environmentSelect" class="form-control form-control-lg" style="max-width: 400px;">
<option value="preprod" selected>Preprod — api.preprod.first-id.fr</option>
<option value="prod">Prod — api.first-id.fr</option>
</select>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h1>Suite de Tests Complète</h1>
<p class="lead">Validation automatique de toutes les APIs FirstID</p>
<button id="startAllTests" class="btn btn-primary btn-lg mb-4">Démarrer tous les tests</button>
</div>
</div>
<div id="globalSummary" class="alert mb-4">
<h4 id="summaryTitle"></h4>
<p id="summaryDetails"></p>
<p id="summaryDuration"></p>
</div>
<div class="row">
<div class="col-md-12">
<div id="test1" class="test-card">
<h3>Test 1: Premier chargement du SDK</h3>
<p>Suppression des cookies et chargement initial du SDK</p>
<div class="test-status"><span class="badge badge-secondary" id="test1-status">En attente</span></div>
<div class="mt-3"><button class="btn btn-sm btn-primary" onclick="runTest1()">Exécuter Test 1</button></div>
<div class="log-output mt-3" id="test1-log"></div>
</div>
<div id="test2" class="test-card">
<h3>Test 2: Rechargement du SDK</h3>
<p>Rechargement du SDK sans suppression des cookies</p>
<div class="test-status"><span class="badge badge-secondary" id="test2-status">En attente</span></div>
<div class="mt-3">
<button class="btn btn-sm btn-primary" onclick="runTest2()">Exécuter Test 2</button>
<span class="badge badge-warning ml-2">Nécessite un first id (ou Test 1)</span>
</div>
<div class="log-output mt-3" id="test2-log"></div>
</div>
<div id="test3" class="test-card">
<h3>Test 3: Appels APP API</h3>
<p>Tests des appels APP API</p>
<div class="test-status"><span class="badge badge-secondary" id="test3-status">En attente</span></div>
<div class="mt-3"><button class="btn btn-sm btn-primary" onclick="runTest3()">Exécuter Test 3</button></div>
<div class="log-output mt-3" id="test3-log"></div>
</div>
<div id="test4" class="test-card">
<h3>Test 4: Redirection Gate</h3>
<p>Test de la redirection vers la gate dans une iframe (page dédiée)</p>
<div class="test-status"><span class="badge badge-secondary" id="test4-status">En attente</span></div>
<div class="mt-3"><button class="btn btn-sm btn-primary" onclick="runTest4()">Exécuter Test 4</button></div>
<div class="log-output mt-3" id="test4-log"></div>
<div class="mt-3" id="test4-iframe-container"></div>
</div>
<div id="test5" class="test-card">
<h3>Test 5: SDK Premium Lite dans iframe</h3>
<p>Test du SDK Premium Lite chargé dans une iframe dédiée</p>
<div class="test-status"><span class="badge badge-secondary" id="test5-status">En attente</span></div>
<div class="mt-3"><button class="btn btn-sm btn-primary" onclick="runTest5()">Exécuter Test 5</button></div>
<div class="log-output mt-3" id="test5-log"></div>
<div class="mt-3" id="test5-iframe-container"></div>
</div>
<div id="test6" class="test-card">
<h3>Test 6: Email hashé</h3>
<p>Test de l'ajout d'un email hashé via le SDK</p>
<div class="test-status"><span class="badge badge-secondary" id="test6-status">En attente</span></div>
<div class="mt-3">
<button class="btn btn-sm btn-primary" onclick="runTest6()">Exécuter Test 6</button>
<span class="badge badge-warning ml-2">Nécessite un SDK chargé (ou Test 1)</span>
</div>
<div class="log-output mt-3" id="test6-log"></div>
</div>
<div id="test7" class="test-card">
<h3>Test 7: setCustomData avec cxenseid</h3>
<p>Test de l'ajout de customData (cxenseid) et récupération du firstId après suppression des cookies</p>
<div class="test-status"><span class="badge badge-secondary" id="test7-status">En attente</span></div>
<div class="mt-3">
<button class="btn btn-sm btn-primary" onclick="runTest7()">Exécuter Test 7</button>
<span class="badge badge-warning ml-2">Nécessite un SDK chargé (ou Test 1)</span>
</div>
<div class="log-output mt-3" id="test7-log"></div>
</div>
<div id="test8" class="test-card">
<h3>Test 8: getDeviceId et getFirstIdEidsObject</h3>
<p>Test de récupération du deviceId et de l'objet eids pour Prebid.js</p>
<div class="test-status"><span class="badge badge-secondary" id="test8-status">En attente</span></div>
<div class="mt-3">
<button class="btn btn-sm btn-primary" onclick="runTest8()">Exécuter Test 8</button>
<span class="badge badge-warning ml-2">Nécessite un SDK chargé (ou Test 1)</span>
</div>
<div class="log-output mt-3" id="test8-log"></div>
</div>
<div id="test9" class="test-card">
<h3>Test 9: getMasterFirstId</h3>
<p>Test de récupération du master firstId depuis un slave firstId</p>
<div class="test-status"><span class="badge badge-secondary" id="test9-status">En attente</span></div>
<div class="mt-3">
<button class="btn btn-sm btn-primary" onclick="runTest9()">Exécuter Test 9</button>
<span class="badge badge-info ml-2">Prépare automatiquement les données</span>
</div>
<div class="log-output mt-3" id="test9-log"></div>
</div>
</div>
</div>
</div>
<script>
const COOKIES_TO_DELETE = [
'firstid', 'firstid_flex_type', 'fid_email_hashed', 'fid_email_hash',
'fidsdk_consent', 'fid_customdata_hash', 'fidflexlastsync',
'fidflexemaillastsync', 'firstid_refresh', 'fidflexlastcheck'
];
const SDK_LOAD_TIMEOUT_MS = 5000;
const IFRAME_TIMEOUT_MS = 20000;
const FIRSTID_VENDOR_ID = 1178;
const MAID_MATCHING_API_PROXY_URL = '/test-suite/api-proxy';
const TOTAL_TESTS = 9;
const API_PREPROD_BASE = 'https://api.preprod.first-id.fr';
const API_PROD_BASE = 'https://api.first-id.fr';
const CDN_PREPROD_BASE = 'https://cdn.preprod.first-id.fr';
const CDN_PROD_BASE = 'https://cdn.first-id.fr';
const DELETE_FG_PREPROD_V4 = 'https://api-v4.preprod.first-id.fr/fg';
const DELETE_FG_PREPROD_V6 = 'https://api-v6.preprod.first-id.fr/fg';
let testState = {};
let originalConsoleDebug = console.debug;
let originalConsoleError = console.error;
let consoleInterceptActive = false;
let interceptedErrors = [];
function resetTestState() {
testState = {
results: {},
hasConsent: false,
suiteStartTime: null
};
}
resetTestState();
function getSelectedEnvironment() {
const select = document.getElementById('environmentSelect');
return select && select.value === 'prod' ? 'prod' : 'preprod';
}
function getApiBaseUrl() {
return getSelectedEnvironment() === 'prod' ? API_PROD_BASE : API_PREPROD_BASE;
}
function getCdnBaseUrl() {
return getSelectedEnvironment() === 'prod' ? CDN_PROD_BASE : CDN_PREPROD_BASE;
}
function getCdnLoaderFlexUrl() {
return getCdnBaseUrl() + '/sdk/loader/loader-latest-flex.js?id=1234567890';
}
function getCdnLoaderPremiumLiteUrl() {
return getCdnBaseUrl() + '/sdk/loader/loader-latest-premium-lite.js?id=1234567890';
}
function setupConsoleInterceptor(testNumber) {
interceptedErrors = [];
consoleInterceptActive = true;
console.debug = function (...args) {
if (consoleInterceptActive) {
logToTest(testNumber, 'debug', '[DEBUG] ' + args.join(' '));
}
originalConsoleDebug.apply(console, args);
};
console.error = function (...args) {
if (consoleInterceptActive) {
interceptedErrors.push(args.join(' '));
logToTest(testNumber, 'error', '[ERROR] ' + args.join(' '));
}
originalConsoleError.apply(console, args);
};
}
function teardownConsoleInterceptor() {
consoleInterceptActive = false;
console.debug = originalConsoleDebug;
console.error = originalConsoleError;
}
function logToTest(testNumber, level, message) {
const logElement = document.getElementById(`test${testNumber}-log`);
if (!logElement) return;
const entry = document.createElement('div');
entry.className = `log-entry ${level}`;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logElement.appendChild(entry);
logElement.scrollTop = logElement.scrollHeight;
}
function setTestStatus(testNumber, status, message) {
const statusElement = document.getElementById(`test${testNumber}-status`);
const testCard = document.getElementById(`test${testNumber}`);
testCard.classList.remove('running', 'success', 'error');
const statusMapping = {
running: { className: 'badge badge-warning', text: 'En cours...' },
success: { className: 'badge badge-success', text: 'Réussi' },
error: { className: 'badge badge-danger', text: 'Échec' },
waiting: { className: 'badge badge-secondary', text: 'En attente' }
};
const statusConfig = statusMapping[status];
if (statusConfig) {
statusElement.className = statusConfig.className;
statusElement.textContent = statusConfig.text;
if (status !== 'waiting') testCard.classList.add(status);
}
if (message) {
logToTest(testNumber, status === 'error' ? 'error' : 'info', message);
}
}
function resetAllTestStatuses() {
for (let testIndex = 1; testIndex <= TOTAL_TESTS; testIndex++) {
setTestStatus(testIndex, 'waiting');
const logElement = document.getElementById(`test${testIndex}-log`);
if (logElement) logElement.innerHTML = '';
}
const summaryElement = document.getElementById('globalSummary');
summaryElement.classList.remove('visible', 'alert-success', 'alert-danger', 'alert-warning');
}
function deleteCookies() {
const domain = window.location.hostname.split('.').slice(-2).join('.');
COOKIES_TO_DELETE.forEach(cookieName => {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain}`;
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.${domain}`;
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`;
});
}
function getCookieValue(cookieName) {
const cookieArray = document.cookie.split(";");
for (let index = 0; index < cookieArray.length; index++) {
const cookiePair = cookieArray[index].split("=");
if (cookieName === cookiePair[0].trim()) {
return decodeURIComponent(cookiePair[1]);
}
}
return null;
}
async function deleteFingerprints(testNumber) {
if (getSelectedEnvironment() === 'prod') {
logToTest(testNumber, 'info', 'Environnement prod: suppression des fingerprints non disponible, étape ignorée');
return;
}
logToTest(testNumber, 'info', 'Suppression des fingerprints dans la matrice...');
const browserSignature = [
navigator?.userAgent ?? '',
navigator?.language ?? '',
screen?.width ?? 0,
screen?.height ?? 0,
navigator?.deviceMemory ?? 0,
navigator?.hardwareConcurrency ?? 0
].join("::");
const queryParams = new URLSearchParams({ bs: browserSignature });
const deleteUrlV4 = DELETE_FG_PREPROD_V4 + '?' + queryParams.toString();
const deleteUrlV6 = DELETE_FG_PREPROD_V6 + '?' + queryParams.toString();
const [responseV4, responseV6] = await Promise.allSettled([
fetch(deleteUrlV4, { method: 'DELETE', credentials: 'include' }),
fetch(deleteUrlV6, { method: 'DELETE', credentials: 'include' })
]);
const isV4Ok = responseV4.status === 'fulfilled' && responseV4.value.ok;
const isV6Ok = responseV6.status === 'fulfilled' && responseV6.value.ok;
if (isV4Ok || isV6Ok) {
logToTest(testNumber, 'info', 'Fingerprints supprimés avec succès');
} else {
logToTest(testNumber, 'debug', 'Suppression des fingerprints échouée (non bloquant)');
}
}
function cleanSdkGlobals() {
delete window.FIRSTID;
delete window.FIRSTID_BY_TYPE;
delete window.FIRSTID_LOADING;
const existingScripts = document.querySelectorAll('script[src*="first-id.fr"]');
existingScripts.forEach(script => script.remove());
}
function configureSdkWindow(extraConfig = {}) {
window.firstId = {
callbacks: [],
debug: true,
cookieName: 'firstid',
firstIdFlexFeature: true,
cookieSyncEnabled: false,
...extraConfig
};
}
function loadFirstIdScript(testNumber, scriptUrl) {
const loaderUrl = scriptUrl || getCdnLoaderFlexUrl();
return new Promise((resolve, reject) => {
if (document.querySelector('script[src*="first-id.fr"]')) {
logToTest(testNumber, 'info', 'Script FirstID déjà chargé');
resolve();
return;
}
const script = document.createElement('script');
script.src = loaderUrl;
script.defer = true;
script.onload = () => {
logToTest(testNumber, 'info', 'Script FirstID chargé avec succès');
resolve();
};
script.onerror = () => reject(new Error('Échec du chargement du script First-ID'));
document.head.appendChild(script);
});
}
function waitForFirstIdInitialized(testNumber, timeoutMs = SDK_LOAD_TIMEOUT_MS) {
return new Promise((resolve, reject) => {
let settled = false;
const timeout = setTimeout(() => {
if (settled) return;
settled = true;
document.removeEventListener('firstId:initialized', handler);
reject(new Error(`Timeout FirstID après ${timeoutMs}ms`));
}, timeoutMs);
function handler(event) {
if (settled) return;
settled = true;
clearTimeout(timeout);
document.removeEventListener('firstId:initialized', handler);
const firstId = event.detail?.firstId || event.detail;
logToTest(testNumber, 'info', `FirstID initialisé: ${firstId}`);
resolve(firstId);
}
document.addEventListener('firstId:initialized', handler);
});
}
async function ensureSdkLoaded(testNumber, extraConfig = {}) {
if (window.FIRSTID && window.FIRSTID.getId && window.FIRSTID.getId()) {
logToTest(testNumber, 'info', 'SDK déjà chargé, réutilisation');
return window.FIRSTID.getId();
}
cleanSdkGlobals();
configureSdkWindow(extraConfig);
await loadFirstIdScript(testNumber);
logToTest(testNumber, 'info', 'Attente de l\'initialisation du SDK...');
const firstId = await waitForFirstIdInitialized(testNumber);
await new Promise(resolve => setTimeout(resolve, 500));
return firstId;
}
function listenForIframeMessage(expectedType, testNumber, timeoutMs = IFRAME_TIMEOUT_MS) {
return new Promise((resolve, reject) => {
let settled = false;
const timeout = setTimeout(() => {
if (settled) return;
settled = true;
window.removeEventListener('message', handler);
reject(new Error(`Timeout iframe après ${timeoutMs / 1000} secondes`));
}, timeoutMs);
function handler(event) {
if (!event.data || !event.data.type) return;
if (event.data.type === 'iframeLog') {
logToTest(testNumber, 'debug', `[IFRAME] ${event.data.message}`);
return;
}
if (event.data.type === expectedType) {
if (settled) return;
settled = true;
clearTimeout(timeout);
window.removeEventListener('message', handler);
resolve(event.data);
return;
}
if (event.data.type === 'scriptError' || event.data.type === 'gateError') {
if (settled) return;
settled = true;
clearTimeout(timeout);
window.removeEventListener('message', handler);
reject(new Error(event.data.error || 'Erreur dans l\'iframe'));
}
}
window.addEventListener('message', handler);
});
}
function showGlobalSummary() {
const summaryElement = document.getElementById('globalSummary');
const passedTests = Object.values(testState.results).filter(result => result === true).length;
const failedTests = Object.values(testState.results).filter(result => result === false).length;
const totalExecuted = passedTests + failedTests;
const durationMs = Date.now() - testState.suiteStartTime;
const durationSeconds = (durationMs / 1000).toFixed(1);
document.getElementById('summaryTitle').textContent =
passedTests === totalExecuted ? 'Tous les tests sont passés' : `${failedTests} test(s) échoué(s)`;
document.getElementById('summaryDetails').textContent =
`${passedTests}/${totalExecuted} tests réussis`;
document.getElementById('summaryDuration').textContent =
`Durée totale: ${durationSeconds}s`;
summaryElement.classList.remove('alert-success', 'alert-danger', 'alert-warning');
if (failedTests === 0) {
summaryElement.classList.add('alert-success');
} else if (passedTests === 0) {
summaryElement.classList.add('alert-danger');
} else {
summaryElement.classList.add('alert-warning');
}
summaryElement.classList.add('visible');
}
async function runTest1() {
const testNumber = 1;
setTestStatus(testNumber, 'running', 'Démarrage du Test 1...');
setupConsoleInterceptor(testNumber);
try {
logToTest(testNumber, 'info', 'Suppression des cookies...');
deleteCookies();
logToTest(testNumber, 'info', 'Cookies supprimés');
logToTest(testNumber, 'info', 'Réinitialisation de la matrice (fingerprints)...');
await deleteFingerprints(testNumber);
logToTest(testNumber, 'info', 'Chargement du script FirstID...');
cleanSdkGlobals();
configureSdkWindow();
await loadFirstIdScript(testNumber);
logToTest(testNumber, 'info', 'Initialisation du SDK...');
const firstId = await waitForFirstIdInitialized(testNumber);
if (!firstId) throw new Error('FirstID non obtenu après initialisation');
if (interceptedErrors.length > 0) {
throw new Error(`${interceptedErrors.length} erreur(s) console détectée(s)`);
}
logToTest(testNumber, 'info', `✓ FirstID obtenu: ${firstId}`);
logToTest(testNumber, 'info', '✓ Aucune erreur console détectée');
setTestStatus(testNumber, 'success', 'Test 1 réussi');
testState.results[testNumber] = true;
} catch (error) {
setTestStatus(testNumber, 'error', `Test 1 échoué: ${error.message}`);
testState.results[testNumber] = false;
} finally {
teardownConsoleInterceptor();
}
}
async function runTest2() {
const testNumber = 2;
setTestStatus(testNumber, 'running', 'Démarrage du Test 2...');
setupConsoleInterceptor(testNumber);
try {
logToTest(testNumber, 'info', 'Rechargement du SDK sans suppression des cookies...');
cleanSdkGlobals();
configureSdkWindow();
logToTest(testNumber, 'info', 'Variables globales réinitialisées');
await loadFirstIdScript(testNumber);
logToTest(testNumber, 'info', 'Initialisation du SDK...');
const firstId = await waitForFirstIdInitialized(testNumber);
if (!firstId) throw new Error('FirstID non obtenu après réinitialisation');
if (interceptedErrors.length > 0) {
throw new Error(`${interceptedErrors.length} erreur(s) console détectée(s)`);
}
logToTest(testNumber, 'info', `✓ FirstID obtenu: ${firstId}`);
logToTest(testNumber, 'info', '✓ Aucune erreur console détectée');
setTestStatus(testNumber, 'success', 'Test 2 réussi');
testState.results[testNumber] = true;
} catch (error) {
setTestStatus(testNumber, 'error', `Test 2 échoué: ${error.message}`);
testState.results[testNumber] = false;
} finally {
teardownConsoleInterceptor();
}
}
async function callMaidMatchingApi(payload) {
const response = await fetch(MAID_MATCHING_API_PROXY_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API call failed: ${response.status} - ${errorText}`);
}
return await response.json();
}
function generateMobileDeviceData(variant) {
const devices = [
{
deviceType: 'mobile_test_suite', deviceManufacturer: 'Apple',
deviceModel: 'iPhone 14 Pro', osVersion: '17.2', buildId: '21C5050',
deviceRam: 6000, deviceResolution: '1179x2556', appIdentifier: 'com.firstid.testapp'
},
{
deviceType: 'mobile_test_suite', deviceManufacturer: 'Samsung',
deviceModel: 'Galaxy S23', osVersion: '14', buildId: 'TP1A.220624.014',
deviceRam: 8000, deviceResolution: '1080x2340', appIdentifier: 'com.firstid.testapp'
},
{
deviceType: 'mobile_test_suite', deviceManufacturer: 'Google',
deviceModel: 'Pixel 8', osVersion: '14', buildId: 'UP1A.231105.003',
deviceRam: 8000, deviceResolution: '1080x2400', appIdentifier: 'com.firstid.testapp'
}
];
const selectedDevice = devices[variant - 1] || devices[0];
return { ...selectedDevice, dfid: "true" };
}
async function runTest3() {
const testNumber = 3;
setTestStatus(testNumber, 'running', 'Démarrage du Test 3...');
try {
logToTest(testNumber, 'info', '=== Test 3: Appels APP API Mobile ===');
logToTest(testNumber, 'info', '--- Appel 1: Création initiale (sans firstId) ---');
const deviceData1 = generateMobileDeviceData(1);
const payload1 = { ...deviceData1, maid: crypto.randomUUID() };
logToTest(testNumber, 'info', `Device: ${deviceData1.deviceManufacturer} ${deviceData1.deviceModel}`);
const response1 = await callMaidMatchingApi(payload1);
if (!response1.firstId) throw new Error('Premier appel: firstId non reçu');
const obtainedFirstId = response1.firstId;
logToTest(testNumber, 'info', `✓ FirstID obtenu: ${obtainedFirstId}`);
await new Promise(resolve => setTimeout(resolve, 500));
logToTest(testNumber, 'info', '--- Appel 2: Avec firstId + données device différentes ---');
const deviceData2 = generateMobileDeviceData(2);
const payload2 = { ...deviceData2, firstId: obtainedFirstId, maid: crypto.randomUUID() };
const response2 = await callMaidMatchingApi(payload2);
if (!response2.firstId) throw new Error('Deuxième appel: firstId non reçu');
if (response2.firstId !== obtainedFirstId) {
throw new Error('Le firstId retourné est différent de celui envoyé');
}
logToTest(testNumber, 'info', '✓ FirstID identique confirmé');
await new Promise(resolve => setTimeout(resolve, 500));
logToTest(testNumber, 'info', '--- Appel 3: Sans firstId avec mêmes données que appel 2 ---');
const payload3 = { ...deviceData2, maid: payload2.maid };
const response3 = await callMaidMatchingApi(payload3);
if (!response3.firstId) throw new Error('Troisième appel: firstId non reçu');
if (response3.firstId !== obtainedFirstId) {
throw new Error('Le firstId retrouvé ne correspond pas au firstId initial');
}
logToTest(testNumber, 'info', '✓ FirstID retrouvé avec succès via MAID');
logToTest(testNumber, 'info', '=== Résumé: Tous les appels ont retourné le même firstId ===');
setTestStatus(testNumber, 'success', 'Test 3 réussi - API Mobile validée');
testState.results[testNumber] = true;
} catch (error) {
logToTest(testNumber, 'error', `Erreur détaillée: ${error.message}`);
setTestStatus(testNumber, 'error', `Test 3 échoué: ${error.message}`);
testState.results[testNumber] = false;
}
}
async function runTest4() {
const testNumber = 4;
setTestStatus(testNumber, 'running', 'Démarrage du Test 4...');
try {
logToTest(testNumber, 'info', 'Test de la redirection Gate via page dédiée...');
const iframeContainer = document.getElementById('test4-iframe-container');
iframeContainer.innerHTML = '';
const iframe = document.createElement('iframe');
const environment = getSelectedEnvironment();
iframe.src = `/test-suite/iframe/gate?env=${environment}`;
logToTest(testNumber, 'info', `URL iframe Gate: ${iframe.src}`);
iframeContainer.appendChild(iframe);
const messagePromise = listenForIframeMessage('gateFirstIdReceived', testNumber);
const messageData = await messagePromise;
const receivedFirstId = messageData.firstId;
logToTest(testNumber, 'info', `✓ FirstID reçu de la Gate: ${receivedFirstId}`);
await new Promise(resolve => setTimeout(resolve, 1000));
const firstIdCookie = getCookieValue('firstid');
if (firstIdCookie) {
logToTest(testNumber, 'info', `✓ Cookie firstid trouvé: ${firstIdCookie}`);
} else {
logToTest(testNumber, 'info', 'Cookie firstid non trouvé sur le domaine parent (attendu en cross-domain)');
}
setTestStatus(testNumber, 'success', 'Test 4 réussi');
testState.results[testNumber] = true;
} catch (error) {
setTestStatus(testNumber, 'error', `Test 4 échoué: ${error.message}`);
testState.results[testNumber] = false;
}
}
async function runTest5() {
const testNumber = 5;
setTestStatus(testNumber, 'running', 'Démarrage du Test 5...');
try {
logToTest(testNumber, 'info', '=== Test 5: SDK Premium Lite dans iframe dédiée ===');
const iframeContainer = document.getElementById('test5-iframe-container');
iframeContainer.innerHTML = '';
const iframe = document.createElement('iframe');
iframe.id = 'test5-iframe';
const environment = getSelectedEnvironment();
iframe.src = `/test-suite/iframe/premium-lite?env=${environment}`;
logToTest(testNumber, 'info', `URL iframe Premium Lite: ${iframe.src}`);
iframeContainer.appendChild(iframe);
const messageData = await listenForIframeMessage('firstIdInitialized', testNumber);
const iframeFirstId = messageData.firstId;
logToTest(testNumber, 'info', `✓ FirstID obtenu via SDK dans iframe: ${iframeFirstId}`);
await new Promise(resolve => setTimeout(resolve, 2000));
const firstIdCookie = getCookieValue('firstid');
if (firstIdCookie) {
logToTest(testNumber, 'info', `✓ Cookie firstid vérifié: ${firstIdCookie}`);
if (firstIdCookie === iframeFirstId) {
logToTest(testNumber, 'info', '✓ FirstID du SDK correspond au cookie');
} else {
logToTest(testNumber, 'info', `FirstID SDK=${iframeFirstId}, Cookie=${firstIdCookie} (peut différer en cross-domain)`);
}
} else {
logToTest(testNumber, 'info', 'Cookie firstid non trouvé sur le domaine parent (attendu en cross-domain)');
}
setTestStatus(testNumber, 'success', 'Test 5 réussi');
testState.results[testNumber] = true;
} catch (error) {
setTestStatus(testNumber, 'error', `Test 5 échoué: ${error.message}`);
testState.results[testNumber] = false;
}
}
async function runTest6() {
const testNumber = 6;
setTestStatus(testNumber, 'running', 'Démarrage du Test 6...');
setupConsoleInterceptor(testNumber);
const TEST_EMAIL_HASH = 'e473c325a4d7cd827f532e93b1f61932b8acba5595c172f47c3cf498ac45d913';
try {
logToTest(testNumber, 'info', '=== Test 6: Ajout d\'un email hashé ===');
logToTest(testNumber, 'info', `Email hashé à tester: ${TEST_EMAIL_HASH}`);
await ensureSdkLoaded(testNumber);
if (!window.FIRSTID) throw new Error('SDK FirstID non disponible après chargement');
if (!window.FIRSTID.setEmailHashed) {
throw new Error('La méthode setEmailHashed n\'est pas disponible dans ce SDK');
}
const firstIdBefore = window.FIRSTID.getId ? window.FIRSTID.getId() : null;
logToTest(testNumber, 'info', `FirstID avant ajout email: ${firstIdBefore || 'non disponible'}`);
await window.FIRSTID.setEmailHashed(TEST_EMAIL_HASH);
logToTest(testNumber, 'info', '✓ setEmailHashed appelé avec succès');
await new Promise(resolve => setTimeout(resolve, 2000));
const emailHashCookie = getCookieValue('fid_email_hashed');
if (emailHashCookie) {
logToTest(testNumber, 'info', `✓ Cookie fid_email_hashed trouvé: ${emailHashCookie}`);
} else {
logToTest(testNumber, 'info', 'Cookie fid_email_hashed non trouvé (peut être normal selon l\'implémentation)');
}
logToTest(testNumber, 'info', '✓ Email hashé ajouté avec succès');
setTestStatus(testNumber, 'success', 'Test 6 réussi - Email hashé ajouté');
testState.results[testNumber] = true;
} catch (error) {
logToTest(testNumber, 'error', `Erreur détaillée: ${error.message}`);
setTestStatus(testNumber, 'error', `Test 6 échoué: ${error.message}`);
testState.results[testNumber] = false;
} finally {
teardownConsoleInterceptor();
}
}
async function runTest7() {
const testNumber = 7;
setTestStatus(testNumber, 'running', 'Démarrage du Test 7...');
setupConsoleInterceptor(testNumber);
const timestamp = Date.now();
const CXENSE_ID = `TestSuite_${timestamp}`;
try {
logToTest(testNumber, 'info', '=== Test 7: setCustomData avec cxenseid ===');
logToTest(testNumber, 'info', `cxenseid à tester: ${CXENSE_ID}`);
await ensureSdkLoaded(testNumber);
if (!window.FIRSTID) throw new Error('SDK FirstID non disponible après chargement');
if (!window.FIRSTID.setCustomData) {
throw new Error('La méthode setCustomData n\'est pas disponible dans ce SDK');
}
const firstIdInitial = window.FIRSTID.getId ? window.FIRSTID.getId() : null;
if (!firstIdInitial) throw new Error('Aucun firstId disponible avant setCustomData');
logToTest(testNumber, 'info', `FirstID initial: ${firstIdInitial}`);
await window.FIRSTID.setCustomData({ cxenseid: CXENSE_ID });
logToTest(testNumber, 'info', '✓ setCustomData appelé avec succès');
await new Promise(resolve => setTimeout(resolve, 2000));
logToTest(testNumber, 'info', 'Suppression de tous les cookies...');
deleteCookies();
logToTest(testNumber, 'info', 'Réinitialisation de la matrice (fingerprints)...');
await deleteFingerprints(testNumber);
logToTest(testNumber, 'info', 'Réinitialisation et rechargement du SDK...');
cleanSdkGlobals();
configureSdkWindow({ customData: { cxenseid: CXENSE_ID } });
await loadFirstIdScript(testNumber);
logToTest(testNumber, 'info', 'Attente de l\'initialisation du SDK...');
await waitForFirstIdInitialized(testNumber);
if (!window.FIRSTID) throw new Error('SDK FirstID non disponible après rechargement');
const firstIdRecovered = window.FIRSTID.getId ? window.FIRSTID.getId() : null;
if (!firstIdRecovered) throw new Error('Aucun firstId récupéré après rechargement avec customData');
logToTest(testNumber, 'info', `FirstID récupéré: ${firstIdRecovered}`);
if (firstIdRecovered !== firstIdInitial) {
throw new Error('Le firstId récupéré ne correspond pas au firstId initial');
}
logToTest(testNumber, 'info', '✓ FirstID identique confirmé');
logToTest(testNumber, 'info', '✓ Le customData a permis de récupérer le même firstId');
setTestStatus(testNumber, 'success', 'Test 7 réussi - setCustomData validé');
testState.results[testNumber] = true;
} catch (error) {
logToTest(testNumber, 'error', `Erreur détaillée: ${error.message}`);
setTestStatus(testNumber, 'error', `Test 7 échoué: ${error.message}`);
testState.results[testNumber] = false;
} finally {
teardownConsoleInterceptor();
}
}
async function runTest8() {
const testNumber = 8;
setTestStatus(testNumber, 'running', 'Démarrage du Test 8...');
setupConsoleInterceptor(testNumber);
try {
logToTest(testNumber, 'info', '=== Test 8: getDeviceId et getFirstIdEidsObject ===');
await ensureSdkLoaded(testNumber);
if (!window.FIRSTID) throw new Error('SDK FirstID non disponible après chargement');
const firstId = window.FIRSTID.getId ? window.FIRSTID.getId() : null;
if (!firstId) throw new Error('Aucun firstId disponible');
logToTest(testNumber, 'info', `FirstID disponible: ${firstId}`);
logToTest(testNumber, 'info', '--- Test getDeviceId ---');
if (!window.FIRSTID.getDeviceId) {
throw new Error('La méthode getDeviceId n\'est pas disponible dans ce SDK');
}
const deviceId = await window.FIRSTID.getDeviceId();
if (!deviceId) throw new Error('getDeviceId a retourné null ou undefined');
logToTest(testNumber, 'info', `✓ DeviceId obtenu: ${deviceId}`);
const deviceIdSecondCall = await window.FIRSTID.getDeviceId();
if (deviceIdSecondCall !== deviceId) {
throw new Error('Le deviceId n\'est pas stable entre les appels');
}
logToTest(testNumber, 'info', '✓ DeviceId identique confirmé');
logToTest(testNumber, 'info', '--- Test getFirstIdEidsObject ---');
if (!window.FIRSTID.getFirstIdEidsObject) {
throw new Error('La méthode getFirstIdEidsObject n\'est pas disponible dans ce SDK');
}
const eidsObject = window.FIRSTID.getFirstIdEidsObject();
if (!eidsObject || !Array.isArray(eidsObject) || eidsObject.length === 0) {
throw new Error('getFirstIdEidsObject n\'a pas retourné un tableau valide');
}
const eidsEntry = eidsObject[0];
if (!eidsEntry.source || eidsEntry.source !== 'first-id.fr') {
throw new Error(`Source incorrecte: ${eidsEntry.source}, attendu: first-id.fr`);
}
if (!eidsEntry.uids || !Array.isArray(eidsEntry.uids) || eidsEntry.uids.length === 0) {
throw new Error('uids manquant ou invalide');
}
const uidEntry = eidsEntry.uids[0];
if (uidEntry.atype !== 1) throw new Error(`atype incorrect: ${uidEntry.atype}, attendu: 1`);
if (!uidEntry.ext || uidEntry.ext.stype !== 'ppuid') {
throw new Error(`ext.stype incorrect: ${uidEntry.ext?.stype}, attendu: ppuid`);
}
if (uidEntry.id !== firstId) {
throw new Error(`id incorrect: ${uidEntry.id}, attendu: ${firstId}`);
}
logToTest(testNumber, 'info', '✓ Structure eidsObject valide');
logToTest(testNumber, 'info', ` - source: ${eidsEntry.source}, atype: ${uidEntry.atype}, ext.stype: ${uidEntry.ext.stype}`);
setTestStatus(testNumber, 'success', 'Test 8 réussi - getDeviceId et getFirstIdEidsObject validés');
testState.results[testNumber] = true;
} catch (error) {
logToTest(testNumber, 'error', `Erreur détaillée: ${error.message}`);
setTestStatus(testNumber, 'error', `Test 8 échoué: ${error.message}`);
testState.results[testNumber] = false;
} finally {
teardownConsoleInterceptor();
}
}
async function runTest9() {
const testNumber = 9;
setTestStatus(testNumber, 'running', 'Démarrage du Test 9...');
setupConsoleInterceptor(testNumber);
const EXPECTED_MASTER_TYPE = 1;
const SLAVE_FIRST_ID = 'c8f6459136c6469ca43d3838082b94eb';
try {
logToTest(testNumber, 'info', '=== Test 9: getMasterFirstId ===');
deleteCookies();
logToTest(testNumber, 'info', 'Réinitialisation de la matrice (fingerprints)...');
await deleteFingerprints(testNumber);
cleanSdkGlobals();
configureSdkWindow();
const domain = window.location.hostname.split('.').slice(-2).join('.');
document.cookie = `firstid=${SLAVE_FIRST_ID}; path=/; domain=${domain}`;
logToTest(testNumber, 'info', `Cookie firstid défini avec le slave: ${SLAVE_FIRST_ID}`);
await loadFirstIdScript(testNumber);
logToTest(testNumber, 'info', 'Attente de l\'initialisation du SDK...');
await waitForFirstIdInitialized(testNumber);
if (!window.FIRSTID) throw new Error('SDK FirstID non disponible après chargement');
if (!window.FIRSTID.getMasterFirstId) {
throw new Error('La méthode getMasterFirstId n\'est pas disponible dans ce SDK');
}
const masterFirstId = window.FIRSTID.getId ? window.FIRSTID.getId() : null;
if (!masterFirstId) throw new Error('Aucun firstId disponible');
if (masterFirstId !== SLAVE_FIRST_ID) {
logToTest(testNumber, 'info', `FirstID actuel (${masterFirstId}) différent du slave (${SLAVE_FIRST_ID})`);
} else {
logToTest(testNumber, 'info', `Master FirstID confirmé: ${masterFirstId}`);
}
const firstIdTypeAfter = window.FIRSTID.getIdStruct
? (window.FIRSTID.getIdStruct().t || null)
: null;
if (firstIdTypeAfter !== EXPECTED_MASTER_TYPE) {
throw new Error(`Le type du firstId ne correspond pas. Attendu: ${EXPECTED_MASTER_TYPE}, Reçu: ${firstIdTypeAfter}`);
}
logToTest(testNumber, 'info', `✓ Type correct: ${firstIdTypeAfter}`);
if (masterFirstId === SLAVE_FIRST_ID) {
throw new Error('Le master firstId est identique au slave firstId');
}
logToTest(testNumber, 'info', '✓ Master FirstID différent du slave confirmé');
const cookieAfter = getCookieValue('firstid');
if (cookieAfter && cookieAfter === masterFirstId) {
logToTest(testNumber, 'info', `✓ Cookie firstid mis à jour avec le master: ${cookieAfter}`);
}
logToTest(testNumber, 'info', '=== Résumé du Test 9 ===');
logToTest(testNumber, 'info', `✓ Slave: ${SLAVE_FIRST_ID} → Master: ${masterFirstId} (type: ${firstIdTypeAfter})`);
setTestStatus(testNumber, 'success', 'Test 9 réussi - getMasterFirstId validé');
testState.results[testNumber] = true;
} catch (error) {
logToTest(testNumber, 'error', `Erreur détaillée: ${error.message}`);
setTestStatus(testNumber, 'error', `Test 9 échoué: ${error.message}`);
testState.results[testNumber] = false;
} finally {
teardownConsoleInterceptor();
}
}
function waitForConsent() {
return new Promise((resolve) => {
if (!window.__tcfapi) {
logToTest(1, 'info', 'TCF API non disponible, passage en mode sans consentement');
resolve(true);
return;
}
window.__tcfapi('addEventListener', 2, (tcData, success) => {
if (success && tcData.gdprApplies) {
if (tcData.vendor.consents[FIRSTID_VENDOR_ID] &&
(tcData.eventStatus === 'useractioncomplete' || tcData.eventStatus === 'tcloaded')) {
testState.hasConsent = true;
resolve(true);
}
} else {
resolve(true);
}
});
});
}
document.getElementById('startAllTests').addEventListener('click', async function () {
this.disabled = true;
resetTestState();
resetAllTestStatuses();
testState.suiteStartTime = Date.now();
logToTest(1, 'info', 'Attente du consentement TCF...');
await waitForConsent();
await runTest1();
if (testState.results[1]) {
await runTest2();
}
await runTest3();
await runTest4();
await runTest5();
if (testState.results[1] || testState.results[2]) {
await runTest6();
}
if (testState.results[1] || testState.results[2]) {
await runTest7();
}
if (testState.results[1] || testState.results[2]) {
await runTest8();
}
if (testState.results[1] || testState.results[2]) {
await runTest9();
}
showGlobalSummary();
this.disabled = false;
});
</script>
</body>
</html>