templates/test_suite/test_suite.html.twig line 1

Open in your IDE?
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>{{ site_name }}</title>
  6.     <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>">
  7.     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css"
  8.           integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
  9.     <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
  10.             integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
  11.             crossorigin="anonymous"></script>
  12.     <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"
  13.             integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF"
  14.             crossorigin="anonymous"></script>
  15.     <script>
  16.         window.axeptioSettings = {
  17.             clientId: "66137db6ae83de5e566cfc53",
  18.             cookiesVersion: "first-id sandbox-fr-EU",
  19.         };
  20.         (function (d, s) {
  21.             var t = d.getElementsByTagName(s)[0],
  22.                 e = d.createElement(s);
  23.             e.async = false;
  24.             e.src = "//static.axept.io/tcf/sdk.js";
  25.             e.type = "module";
  26.             t.parentNode.insertBefore(e, t);
  27.         })(document, "script");
  28.     </script>
  29.     <style>
  30.         .test-card {
  31.             border: 2px solid #dee2e6;
  32.             border-radius: 8px;
  33.             padding: 20px;
  34.             margin-bottom: 20px;
  35.             transition: background-color 0.3s ease;
  36.         }
  37.         .test-card.success {
  38.             background-color: #d4edda;
  39.             border-color: #28a745;
  40.         }
  41.         .test-card.error {
  42.             background-color: #f8d7da;
  43.             border-color: #dc3545;
  44.         }
  45.         .test-card.running {
  46.             background-color: #fff3cd;
  47.             border-color: #ffc107;
  48.         }
  49.         .test-card.disabled {
  50.             opacity: 0.5;
  51.             pointer-events: none;
  52.         }
  53.         .log-output {
  54.             background-color: #f8f9fa;
  55.             border: 1px solid #dee2e6;
  56.             border-radius: 4px;
  57.             padding: 10px;
  58.             max-height: 300px;
  59.             overflow-y: auto;
  60.             font-family: monospace;
  61.             font-size: 12px;
  62.             white-space: pre-wrap;
  63.             word-break: break-all;
  64.         }
  65.         .log-entry {
  66.             margin-bottom: 5px;
  67.         }
  68.         .log-entry.error {
  69.             color: #dc3545;
  70.             font-weight: bold;
  71.         }
  72.         .log-entry.debug {
  73.             color: #007bff;
  74.         }
  75.         .log-entry.info {
  76.             color: #28a745;
  77.         }
  78.         iframe {
  79.             width: 100%;
  80.             height: 400px;
  81.             border: 1px solid #dee2e6;
  82.             border-radius: 4px;
  83.         }
  84.     </style>
  85. </head>
  86. <body>
  87. <nav class="navbar navbar-expand-lg navbar-light bg-light">
  88.     <a class="navbar-brand" href="{{ path('app_index') }}">{{ site_name }}</a>
  89.     <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
  90.             aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
  91.         <span class="navbar-toggler-icon"></span>
  92.     </button>
  93. </nav>
  94. <div class="container mt-4">
  95.     <div class="row">
  96.         <div class="col-md-12">
  97.             <h1>Suite de Tests Complète</h1>
  98.             <p class="lead">Validation automatique de toutes les APIs FirstID</p>
  99.             <button id="startAllTests" class="btn btn-primary btn-lg mb-4">Démarrer tous les tests</button>
  100.         </div>
  101.     </div>
  102.     <div class="row">
  103.         <div class="col-md-12">
  104.             <div id="test1" class="test-card">
  105.                 <h3>Test 1: Premier chargement du SDK</h3>
  106.                 <p>Suppression des cookies et chargement initial du SDK</p>
  107.                 <div class="test-status">
  108.                     <span class="badge badge-secondary" id="test1-status">En attente</span>
  109.                 </div>
  110.                 <div class="mt-3">
  111.                     <button class="btn btn-sm btn-primary" onclick="runTest1()">Exécuter Test 1</button>
  112.                 </div>
  113.                 <div class="log-output mt-3" id="test1-log"></div>
  114.             </div>
  115.             <div id="test2" class="test-card">
  116.                 <h3>Test 2: Rechargement du SDK</h3>
  117.                 <p>Rechargement du SDK sans suppression des cookies</p>
  118.                 <div class="test-status">
  119.                     <span class="badge badge-secondary" id="test2-status">En attente</span>
  120.                 </div>
  121.                 <div class="mt-3">
  122.                     <button class="btn btn-sm btn-primary" onclick="runTest2()">Exécuter Test 2</button>
  123.                     <span class="badge badge-warning ml-2">Nécessite un first id (ou Test 1)</span>
  124.                 </div>
  125.                 <div class="log-output mt-3" id="test2-log"></div>
  126.             </div>
  127.             <div id="test3" class="test-card">
  128.                 <h3>Test 3: Appels APP API</h3>
  129.                 <p>Tests des appels APP API</p>
  130.                 <div class="test-status">
  131.                     <span class="badge badge-secondary" id="test3-status">En attente</span>
  132.                 </div>
  133.                 <div class="mt-3">
  134.                     <button class="btn btn-sm btn-primary" onclick="runTest3()">Exécuter Test 3</button>
  135.                 </div>
  136.                 <div class="log-output mt-3" id="test3-log"></div>
  137.             </div>
  138.             <div id="test4" class="test-card">
  139.                 <h3>Test 4: Redirection Gate</h3>
  140.                 <p>Test de la redirection vers la gate dans une iframe</p>
  141.                 <div class="test-status">
  142.                     <span class="badge badge-secondary" id="test4-status">En attente</span>
  143.                 </div>
  144.                 <div class="mt-3">
  145.                     <button class="btn btn-sm btn-primary" onclick="runTest4()">Exécuter Test 4</button>
  146.                 </div>
  147.                 <div class="log-output mt-3" id="test4-log"></div>
  148.                 <div class="mt-3" id="test4-iframe-container"></div>
  149.             </div>
  150.         </div>
  151.     </div>
  152. </div>
  153. <script>
  154.     const COOKIES_TO_DELETE = ['firstid', 'firstid_flex_type', 'fid_email_hashed', 'fid_email_hash', 'fidsdk_consent', 'fid_customdata_hash', 'fidflexlastsync', 'fidflexemaillastsync', 'firstid_refresh', 'fidflexlastcheck'];
  155.     const SDK_LOAD_TIMEOUT_MS = 5000;
  156.     const FIRSTID_VENDOR_ID = 1178;
  157.     const MAID_MATCHING_API_PROXY_URL = '/test-suite/api-proxy';
  158.     let testState = {
  159.         currentTest: 0,
  160.         test1Passed: false,
  161.         test2Passed: false,
  162.         test3Passed: false,
  163.         test4Passed: false,
  164.         hasConsent: false
  165.     };
  166.     let originalConsoleDebug = console.debug;
  167.     let originalConsoleError = console.error;
  168.     let consoleInterceptActive = false;
  169.     let interceptedErrors = [];
  170.     function setupConsoleInterceptor(testNumber) {
  171.         interceptedErrors = [];
  172.         consoleInterceptActive = true;
  173.         console.debug = function(...args) {
  174.             if (consoleInterceptActive) {
  175.                 logToTest(testNumber, 'debug', '[DEBUG] ' + args.join(' '));
  176.             }
  177.             originalConsoleDebug.apply(console, args);
  178.         };
  179.         console.error = function(...args) {
  180.             if (consoleInterceptActive) {
  181.                 interceptedErrors.push(args.join(' '));
  182.                 logToTest(testNumber, 'error', '[ERROR] ' + args.join(' '));
  183.             }
  184.             originalConsoleError.apply(console, args);
  185.         };
  186.     }
  187.     function teardownConsoleInterceptor() {
  188.         consoleInterceptActive = false;
  189.         console.debug = originalConsoleDebug;
  190.         console.error = originalConsoleError;
  191.     }
  192.     function logToTest(testNumber, level, message) {
  193.         const logElement = document.getElementById(`test${testNumber}-log`);
  194.         const entry = document.createElement('div');
  195.         entry.className = `log-entry ${level}`;
  196.         entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
  197.         logElement.appendChild(entry);
  198.         logElement.scrollTop = logElement.scrollHeight;
  199.     }
  200.     function setTestStatus(testNumber, status, message) {
  201.         const statusElement = document.getElementById(`test${testNumber}-status`);
  202.         const testCard = document.getElementById(`test${testNumber}`);
  203.         
  204.         testCard.classList.remove('running', 'success', 'error');
  205.         
  206.         switch(status) {
  207.             case 'running':
  208.                 statusElement.className = 'badge badge-warning';
  209.                 statusElement.textContent = 'En cours...';
  210.                 testCard.classList.add('running');
  211.                 break;
  212.             case 'success':
  213.                 statusElement.className = 'badge badge-success';
  214.                 statusElement.textContent = 'Réussi';
  215.                 testCard.classList.add('success');
  216.                 break;
  217.             case 'error':
  218.                 statusElement.className = 'badge badge-danger';
  219.                 statusElement.textContent = 'Échec';
  220.                 testCard.classList.add('error');
  221.                 break;
  222.             case 'waiting':
  223.                 statusElement.className = 'badge badge-secondary';
  224.                 statusElement.textContent = 'En attente';
  225.                 break;
  226.         }
  227.         
  228.         if (message) {
  229.             logToTest(testNumber, status === 'error' ? 'error' : 'info', message);
  230.         }
  231.     }
  232.     function deleteCookies() {
  233.         const domain = window.location.hostname.split('.').slice(-2).join('.');
  234.         
  235.         COOKIES_TO_DELETE.forEach(cookieName => {
  236.             document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain}`;
  237.             document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.${domain}`;
  238.             document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`;
  239.         });
  240.         
  241.         return true;
  242.     }
  243.     function getCookieValue(cookieName) {
  244.         let cookieArr = document.cookie.split(";");
  245.         for (let i = 0; i < cookieArr.length; i++) {
  246.             let cookiePair = cookieArr[i].split("=");
  247.             if (cookieName === cookiePair[0].trim()) {
  248.                 return decodeURIComponent(cookiePair[1]);
  249.             }
  250.         }
  251.         return null;
  252.     }
  253.     function loadFirstIdScript(testNumber) {
  254.         return new Promise((resolve, reject) => {
  255.             if (document.querySelector('script[src*="first-id.fr"]')) {
  256.                 logToTest(testNumber, 'info', 'Script FirstID déjà chargé');
  257.                 resolve();
  258.                 return;
  259.             }
  260.             const script = document.createElement('script');
  261.             script.src = 'https://cdn.preprod.first-id.fr/sdk/loader/loader-latest-flex.js?id=1234567890';
  262.             script.defer = true;
  263.             
  264.             script.onload = () => {
  265.                 logToTest(testNumber, 'info', 'Script FirstID chargé avec succès');
  266.                 resolve();
  267.             };
  268.             
  269.             script.onerror = () => {
  270.                 reject(new Error('Échec du chargement du script First-ID'));
  271.             };
  272.             document.head.appendChild(script);
  273.         });
  274.     }
  275.     function initializeFirstId(testNumber) {
  276.         window.firstId = window.firstId || {
  277.             debug: true,
  278.             cookieName: 'firstid',
  279.             firstIdFlexFeature: true,
  280.             callbacks: []
  281.         };
  282.         return new Promise((resolve, reject) => {
  283.             const timeout = setTimeout(() => {
  284.                 reject(new Error(`Timeout FirstID après ${SDK_LOAD_TIMEOUT_MS}ms`));
  285.             }, SDK_LOAD_TIMEOUT_MS);
  286.             document.addEventListener('firstId:initialized', (event) => {
  287.                 clearTimeout(timeout);
  288.                 const firstId = event.detail.firstId;
  289.                 logToTest(testNumber, 'info', `FirstID initialisé: ${firstId}`);
  290.                 resolve(firstId);
  291.             }, {once: true});
  292.         });
  293.     }
  294.     async function runTest1() {
  295.         const testNumber = 1;
  296.         setTestStatus(testNumber, 'running', 'Démarrage du Test 1...');
  297.         setupConsoleInterceptor(testNumber);
  298.         
  299.         try {
  300.             logToTest(testNumber, 'info', 'Suppression des cookies...');
  301.             deleteCookies();
  302.             logToTest(testNumber, 'info', 'Cookies supprimés');
  303.             
  304.             logToTest(testNumber, 'info', 'Chargement du script FirstID...');
  305.             await loadFirstIdScript(testNumber);
  306.             
  307.             logToTest(testNumber, 'info', 'Initialisation du SDK...');
  308.             const firstId = await initializeFirstId(testNumber);
  309.             
  310.             if (!firstId) {
  311.                 throw new Error('FirstID non obtenu après initialisation');
  312.             }
  313.             
  314.             if (interceptedErrors.length > 0) {
  315.                 throw new Error(`${interceptedErrors.length} erreur(s) console détectée(s)`);
  316.             }
  317.             
  318.             logToTest(testNumber, 'info', `✓ FirstID obtenu: ${firstId}`);
  319.             logToTest(testNumber, 'info', '✓ Aucune erreur console détectée');
  320.             
  321.             setTestStatus(testNumber, 'success', 'Test 1 réussi');
  322.             testState.test1Passed = true;
  323.             
  324.         } catch (error) {
  325.             setTestStatus(testNumber, 'error', `Test 1 échoué: ${error.message}`);
  326.             testState.test1Passed = false;
  327.         } finally {
  328.             teardownConsoleInterceptor();
  329.         }
  330.     }
  331.     async function runTest2() {
  332.         if (!testState.test1Passed) {
  333.             logToTest(2, 'error', 'Le Test 1 doit être réussi avant de lancer le Test 2');
  334.             return;
  335.         }
  336.         
  337.         const testNumber = 2;
  338.         setTestStatus(testNumber, 'running', 'Démarrage du Test 2...');
  339.         setupConsoleInterceptor(testNumber);
  340.         
  341.         try {
  342.             logToTest(testNumber, 'info', 'Rechargement du SDK sans suppression des cookies...');
  343.             
  344.             delete window.FIRSTID;
  345.             delete window.FIRSTID_BY_TYPE;
  346.             delete window.FIRSTID_LOADING;
  347.             
  348.             const existingScripts = document.querySelectorAll('script[src*="first-id.fr"]');
  349.             existingScripts.forEach(script => script.remove());
  350.             
  351.             logToTest(testNumber, 'info', 'Variables globales réinitialisées');
  352.             
  353.             await loadFirstIdScript(testNumber);
  354.             
  355.             logToTest(testNumber, 'info', 'Initialisation du SDK...');
  356.             const firstId = await initializeFirstId(testNumber);
  357.             
  358.             if (!firstId) {
  359.                 throw new Error('FirstID non obtenu après réinitialisation');
  360.             }
  361.             
  362.             if (interceptedErrors.length > 0) {
  363.                 throw new Error(`${interceptedErrors.length} erreur(s) console détectée(s)`);
  364.             }
  365.             
  366.             logToTest(testNumber, 'info', `✓ FirstID obtenu: ${firstId}`);
  367.             logToTest(testNumber, 'info', '✓ Aucune erreur console détectée');
  368.             
  369.             setTestStatus(testNumber, 'success', 'Test 2 réussi');
  370.             testState.test2Passed = true;
  371.             
  372.         } catch (error) {
  373.             setTestStatus(testNumber, 'error', `Test 2 échoué: ${error.message}`);
  374.             testState.test2Passed = false;
  375.         } finally {
  376.             teardownConsoleInterceptor();
  377.         }
  378.     }
  379.     async function callMaidMatchingApi(payload) {
  380.         const response = await fetch(MAID_MATCHING_API_PROXY_URL, {
  381.             method: 'POST',
  382.             headers: {
  383.                 'Content-Type': 'application/json',
  384.             },
  385.             body: JSON.stringify(payload)
  386.         });
  387.         
  388.         if (!response.ok) {
  389.             const errorText = await response.text();
  390.             throw new Error(`API call failed: ${response.status} - ${errorText}`);
  391.         }
  392.         
  393.         return await response.json();
  394.     }
  395.     function generateMobileDeviceData(variant = 1) {
  396.         const devices = [
  397.             {
  398.                 deviceType: 'mobile_test_suite',
  399.                 deviceManufacturer: 'Apple',
  400.                 deviceModel: 'iPhone 14 Pro',
  401.                 osVersion: '17.2',
  402.                 buildId: '21C5050',
  403.                 deviceRam: 6000,
  404.                 deviceResolution: '1179x2556',
  405.                 appIdentifier: 'com.firstid.testapp'
  406.             },
  407.             {
  408.                 deviceType: 'mobile_test_suite',
  409.                 deviceManufacturer: 'Samsung',
  410.                 deviceModel: 'Galaxy S23',
  411.                 osVersion: '14',
  412.                 buildId: 'TP1A.220624.014',
  413.                 deviceRam: 8000,
  414.                 deviceResolution: '1080x2340',
  415.                 appIdentifier: 'com.firstid.testapp'
  416.             },
  417.             {
  418.                 deviceType: 'mobile_test_suite',
  419.                 deviceManufacturer: 'Google',
  420.                 deviceModel: 'Pixel 8',
  421.                 osVersion: '14',
  422.                 buildId: 'UP1A.231105.003',
  423.                 deviceRam: 8000,
  424.                 deviceResolution: '1080x2400',
  425.                 appIdentifier: 'com.firstid.testapp'
  426.             }
  427.         ];
  428.         
  429.         const randomDevice = devices[variant - 1] || devices[0];
  430.         return {
  431.             ...randomDevice,
  432.             dfid: "true"
  433.         };
  434.     }
  435.     async function runTest3() {
  436.         const testNumber = 3;
  437.         setTestStatus(testNumber, 'running', 'Démarrage du Test 3...');
  438.         
  439.         try {
  440.             logToTest(testNumber, 'info', '=== Test 3: Appels APP API Mobile ===');
  441.             logToTest(testNumber, 'info', 'Simulation d\'une application mobile avec appels API');
  442.             
  443.             // Test 3.1: Premier appel sans firstId
  444.             logToTest(testNumber, 'info', '');
  445.             logToTest(testNumber, 'info', '--- Appel 1: Création initiale (sans firstId) ---');
  446.             
  447.             const deviceData1 = generateMobileDeviceData(1);
  448.             const payload1 = {
  449.                 ...deviceData1,
  450.                 maid: crypto.randomUUID(),
  451.             };
  452.             
  453.             logToTest(testNumber, 'info', `Device: ${deviceData1.deviceManufacturer} ${deviceData1.deviceModel}`);
  454.             logToTest(testNumber, 'info', `MAID: ${payload1.maid}`);
  455.             logToTest(testNumber, 'info', 'Appel API en cours...');
  456.             
  457.             const response1 = await callMaidMatchingApi(payload1);
  458.             
  459.             if (!response1.firstId) {
  460.                 throw new Error('Premier appel: firstId non reçu');
  461.             }
  462.             
  463.             const obtainedFirstId = response1.firstId;
  464.             logToTest(testNumber, 'info', `✓ FirstID obtenu: ${obtainedFirstId}`);
  465.             if (response1.matchingStatus) {
  466.                 logToTest(testNumber, 'info', `  Status: ${response1.matchingStatus}`);
  467.             }
  468.             
  469.             // Pause entre les appels
  470.             await new Promise(resolve => setTimeout(resolve, 500));
  471.             
  472.             // Test 3.2: Deuxième appel avec le firstId + données différentes
  473.             logToTest(testNumber, 'info', '');
  474.             logToTest(testNumber, 'info', '--- Appel 2: Avec firstId + données device différentes ---');
  475.             
  476.             const deviceData2 = generateMobileDeviceData(2);
  477.             const payload2 = {
  478.                 ...deviceData2,
  479.                 firstId: obtainedFirstId,
  480.                 maid: crypto.randomUUID(),
  481.             };
  482.             
  483.             logToTest(testNumber, 'info', `Device: ${deviceData2.deviceManufacturer} ${deviceData2.deviceModel}`);
  484.             logToTest(testNumber, 'info', `FirstID envoyé: ${obtainedFirstId}`);
  485.             logToTest(testNumber, 'info', `Nouveau MAID: ${payload2.maid}`);
  486.             logToTest(testNumber, 'info', 'Appel API en cours...');
  487.             
  488.             const response2 = await callMaidMatchingApi(payload2);
  489.             
  490.             if (!response2.firstId) {
  491.                 throw new Error('Deuxième appel: firstId non reçu');
  492.             }
  493.             
  494.             logToTest(testNumber, 'info', `✓ FirstID reçu: ${response2.firstId}`);
  495.             if (response2.matchingStatus) {
  496.                 logToTest(testNumber, 'info', `  Status: ${response2.matchingStatus}`);
  497.             }
  498.             
  499.             if (response2.firstId !== obtainedFirstId) {
  500.                 logToTest(testNumber, 'error', `✗ ERREUR: FirstID différent! Attendu: ${obtainedFirstId}, Reçu: ${response2.firstId}`);
  501.                 throw new Error('Le firstId retourné est différent de celui envoyé');
  502.             }
  503.             
  504.             logToTest(testNumber, 'info', '✓ FirstID identique confirmé');
  505.             
  506.             // Pause entre les appels
  507.             await new Promise(resolve => setTimeout(resolve, 500));
  508.             
  509.             // Test 3.3: Troisième appel sans firstId mais avec les mêmes données que l'appel 2
  510.             logToTest(testNumber, 'info', '');
  511.             logToTest(testNumber, 'info', '--- Appel 3: Sans firstId avec mêmes données que appel 2 ---');
  512.             
  513.             const payload3 = {
  514.                 ...deviceData2,
  515.                 maid: payload2.maid,
  516.             };
  517.             
  518.             logToTest(testNumber, 'info', `Device: ${deviceData2.deviceManufacturer} ${deviceData2.deviceModel} (identique appel 2)`);
  519.             logToTest(testNumber, 'info', `MAID: ${payload3.maid} (identique appel 2)`);
  520.             logToTest(testNumber, 'info', 'Appel API en cours...');
  521.             
  522.             const response3 = await callMaidMatchingApi(payload3);
  523.             
  524.             if (!response3.firstId) {
  525.                 throw new Error('Troisième appel: firstId non reçu');
  526.             }
  527.             
  528.             logToTest(testNumber, 'info', `✓ FirstID reçu: ${response3.firstId}`);
  529.             if (response3.matchingStatus) {
  530.                 logToTest(testNumber, 'info', `  Status: ${response3.matchingStatus}`);
  531.             }
  532.             
  533.             if (response3.firstId !== obtainedFirstId) {
  534.                 logToTest(testNumber, 'error', `✗ ERREUR: FirstID différent! Attendu: ${obtainedFirstId}, Reçu: ${response3.firstId}`);
  535.                 throw new Error('Le firstId retrouvé ne correspond pas au firstId initial');
  536.             }
  537.             
  538.             logToTest(testNumber, 'info', '✓ FirstID retrouvé avec succès via MAID');
  539.             
  540.             // Résumé
  541.             logToTest(testNumber, 'info', '');
  542.             logToTest(testNumber, 'info', '=== Résumé du Test 3 ===');
  543.             logToTest(testNumber, 'info', `✓ Appel 1: FirstID créé (${obtainedFirstId})`);
  544.             logToTest(testNumber, 'info', `✓ Appel 2: FirstID maintenu avec device différent`);
  545.             logToTest(testNumber, 'info', `✓ Appel 3: FirstID retrouvé via MAID matching`);
  546.             logToTest(testNumber, 'info', '✓ Tous les appels ont retourné le même firstId');
  547.             
  548.             setTestStatus(testNumber, 'success', 'Test 3 réussi - API Mobile validée');
  549.             testState.test3Passed = true;
  550.             
  551.         } catch (error) {
  552.             logToTest(testNumber, 'error', `Erreur détaillée: ${error.message}`);
  553.             if (error.stack) {
  554.                 logToTest(testNumber, 'debug', error.stack);
  555.             }
  556.             setTestStatus(testNumber, 'error', `Test 3 échoué: ${error.message}`);
  557.             testState.test3Passed = false;
  558.         }
  559.     }
  560.     async function runTest4() {
  561.         const testNumber = 4;
  562.         setTestStatus(testNumber, 'running', 'Démarrage du Test 4...');
  563.         
  564.         try {
  565.             logToTest(testNumber, 'info', 'Test de la redirection Gate...');
  566.             
  567.             const iframeContainer = document.getElementById('test4-iframe-container');
  568.             iframeContainer.innerHTML = '';
  569.             
  570.             const iframe = document.createElement('iframe');
  571.             const redirectHost = encodeURIComponent(window.location.origin + '/gate/handle-first-id-redirect');
  572.             const redirectUri = encodeURIComponent(window.location.origin + '/gate/handle-first-id-redirect');
  573.             iframe.src = `https://gate.preprod.first-id.fr?redirectHost=${redirectUri}&redirectUri=${redirectUri}`;
  574.             
  575.             logToTest(testNumber, 'info', `URL Gate: ${iframe.src}`);
  576.             
  577.             iframeContainer.appendChild(iframe);
  578.             
  579.             iframe.onload = () => {
  580.                 logToTest(testNumber, 'info', 'Iframe Gate chargée');
  581.                 
  582.                 setTimeout(() => {
  583.                     const firstIdCookie = getCookieValue('firstid');
  584.                     if (firstIdCookie) {
  585.                         logToTest(testNumber, 'info', `✓ Cookie firstid trouvé: ${firstIdCookie}`);
  586.                         setTestStatus(testNumber, 'success', 'Test 4 réussi');
  587.                         testState.test4Passed = true;
  588.                     } else {
  589.                         logToTest(testNumber, 'info', 'Cookie firstid non trouvé (vérification manuelle requise)');
  590.                         setTestStatus(testNumber, 'success', 'Test 4 complété (vérification manuelle)');
  591.                     }
  592.                 }, 2000);
  593.             };
  594.             
  595.             iframe.onerror = (error) => {
  596.                 throw new Error('Erreur de chargement de l\'iframe Gate');
  597.             };
  598.             
  599.         } catch (error) {
  600.             setTestStatus(testNumber, 'error', `Test 4 échoué: ${error.message}`);
  601.             testState.test4Passed = false;
  602.         }
  603.     }
  604.     function waitForConsent() {
  605.         return new Promise((resolve) => {
  606.             if (!window.__tcfapi) {
  607.                 logToTest(0, 'info', 'TCF API non disponible, passage en mode sans consentement');
  608.                 resolve(true);
  609.                 return;
  610.             }
  611.             window.__tcfapi('addEventListener', 2, (tcData, success) => {
  612.                 if (success && tcData.gdprApplies) {
  613.                     if (tcData.vendor.consents[FIRSTID_VENDOR_ID] && 
  614.                         (tcData.eventStatus === 'useractioncomplete' || tcData.eventStatus === 'tcloaded')) {
  615.                         console.log('Consentement TCF obtenu');
  616.                         testState.hasConsent = true;
  617.                         resolve(true);
  618.                     }
  619.                 } else {
  620.                     resolve(true);
  621.                 }
  622.             });
  623.         });
  624.     }
  625.     document.getElementById('startAllTests').addEventListener('click', async function() {
  626.         this.disabled = true;
  627.         
  628.         console.log('Attente du consentement TCF...');
  629.         await waitForConsent();
  630.         
  631.         await runTest1();
  632.         
  633.         if (testState.test1Passed) {
  634.             await new Promise(resolve => setTimeout(resolve, 1000));
  635.             await runTest2();
  636.         }
  637.         
  638.         if (testState.test2Passed) {
  639.             await new Promise(resolve => setTimeout(resolve, 1000));
  640.             await runTest3();
  641.         }
  642.         
  643.         if (testState.test3Passed) {
  644.             await new Promise(resolve => setTimeout(resolve, 1000));
  645.             await runTest4();
  646.         }
  647.         
  648.         this.disabled = false;
  649.     });
  650. </script>
  651. </body>
  652. </html>