Amélioration avec des fonctions d'un vrai dashboard automotive et containerisation du projet pour faciliter le déploiement

This commit is contained in:
Christian CUNAT-BRULE
2026-09-12 21:13:24 +02:00
parent d170f95c92
commit d8eb7f4375
31 changed files with 2233 additions and 65 deletions
+66
View File
@@ -0,0 +1,66 @@
const { chromium } = await import(process.env.PLAYWRIGHT_MODULE || 'playwright');
import assert from 'node:assert/strict';
const browser = await chromium.launch({...(process.env.CHROME_PATH ? {executablePath:process.env.CHROME_PATH} : {}),headless:true});
const context = await browser.newContext({viewport:{width:1024,height:600},permissions:['geolocation'],geolocation:{latitude:48.85,longitude:2.35,accuracy:10}});
const page = await context.newPage(), errors=[];
page.on('pageerror',e=>errors.push(e.message));
// Never load public OSM tiles from automated QA.
await context.route('https://tile.openstreetmap.org/**',r=>r.fulfill({status:200,contentType:'image/png',body:Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+j1ioAAAAASUVORK5CYII=','base64')}));
await page.route('**/api/weather?*',r=>r.fulfill({json:{source:'Open-Meteo',temperature:18,code:2,wind:8,min:12,max:22,observedAt:'2026-09-12T09:00'}}));
await page.route('**/api/search?*',r=>r.fulfill({json:[{lat:48.86,lon:2.36,label:'Destination de test'}]}));
await page.route('**/api/route?*',r=>r.fulfill({json:{source:'OSRM',distance:1500,duration:300,geometry:{type:'LineString',coordinates:[[2.35,48.85],[2.355,48.855],[2.36,48.86]]},steps:[{distance:700,duration:140,name:'Rue A',maneuver:{type:'depart',location:[2.35,48.85]}},{distance:800,duration:160,name:'Rue B',maneuver:{type:'turn',modifier:'left',location:[2.355,48.855]}},{distance:0,duration:0,name:'',maneuver:{type:'arrive',location:[2.36,48.86]}}]}}));
await page.goto(process.env.CARIA_URL || 'http://127.0.0.1:8080');
await page.getByText('● Démonstration',{exact:true}).waitFor();
await page.locator('#settings-open').click();
await page.locator('#accent').selectOption('blue');
await page.getByRole('button',{name:'Enregistrer',exact:true}).click();
assert.equal(await page.evaluate(()=>document.body.style.getPropertyValue('--accent')),'#8ac7ff');
await page.reload();
assert.equal(await page.evaluate(()=>document.body.style.getPropertyValue('--accent')),'#8ac7ff');
await page.locator('#favorite-apps [data-view="map"]').click();
await page.locator('.leaflet-container').waitFor();
await page.getByText('Coordonnées manuelles',{exact:true}).click();
await page.locator('#latitude').fill('48.85'); await page.locator('#longitude').fill('2.35');
await page.getByRole('button',{name:'Utiliser ce départ',exact:true}).click();
await page.waitForFunction(()=>document.querySelector('#weather-temperature').textContent==='18');
await page.locator('#place-query').fill('Paris'); await page.getByRole('button',{name:'Rechercher un lieu'}).click();
await page.getByRole('button',{name:'Destination de test',exact:true}).click();
await page.getByText('Aperçu du trajet · activez le GPS pour le guidage',{exact:true}).waitFor();
assert.equal(await page.locator('#route-steps li').count(),3);
await page.locator('[data-waze-destination]').waitFor({state:'visible'});
assert.equal(new URL(await page.locator('[data-waze-destination]').getAttribute('href')).searchParams.get('ll'),'48.86,2.36');
await page.getByRole('button',{name:'⌖ Activer le GPS',exact:true}).click();
await page.waitForFunction(()=>document.querySelector('#position-status').textContent.includes('GPS actif'));
await page.waitForFunction(()=>document.querySelector('#next-instruction').textContent.includes('gauche'));
await page.getByText('Alertes de zones',{exact:true}).click();
const zones = {type:'FeatureCollection',features:[{type:'Feature',geometry:{type:'Point',coordinates:[2.35,48.85]},properties:{id:'test',name:'Zone QA',country:'FR',radiusM:500,source:'TEST AUTOMATISÉ',updatedAt:new Date(Date.now()-100000).toISOString(),expiresAt:new Date(Date.now()+86400000).toISOString()}}]};
await page.locator('#zones-file').setInputFiles({name:'zones.geojson',mimeType:'application/json',buffer:Buffer.from(JSON.stringify(zones))});
await page.waitForFunction(()=>document.querySelector('#zones-status').textContent.includes('1 zones'));
await page.locator('#alert-country').selectOption('FR'); await page.locator('#alerts-enabled').check();
await page.locator('#zone-alert').waitFor({state:'visible'});
await page.locator('#zone-alert-close').click();
await page.locator('#gps-stop').click();
await page.waitForFunction(()=>document.querySelector('#next-instruction').textContent.includes('pause'));
for (const size of [{width:1280,height:800},{width:1024,height:600},{width:800,height:480},{width:390,height:844}]) {
await page.setViewportSize(size);
for (const view of ['home','drive','music','stream','map']) {
await page.evaluate(view=>location.hash=view,view); await page.locator(`#${view}`).waitFor({state:'visible'});
assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),true,`Overflow ${view} ${size.width}`);
}
}
await page.setViewportSize({width:1024,height:600});
await page.locator('#favorite-apps [data-view="map"]').click();
await page.locator('#favorite-apps [data-view="home"]').click();
await page.locator('#settings-open').click(); await page.locator('#theme').selectOption('light'); await page.locator('#accent').selectOption('custom'); await page.locator('#custom-color').fill('#ff0000'); await page.getByRole('button',{name:'Enregistrer',exact:true}).click();
await page.locator('#favorite-apps [data-view="map"]').click();
await page.getByRole('button',{name:'Utiliser ce départ',exact:true}).click();
await page.waitForFunction(()=>document.querySelector('#weather-temperature').textContent==='18');
await page.route('**/api/weather?*',r=>r.fulfill({status:502,json:{error:'Météo hors ligne'}}));
await page.locator('#favorite-apps [data-view="home"]').click();
await page.locator('#weather-refresh').click();
await page.waitForFunction(()=>document.querySelector('#weather-temperature').textContent==='—' && document.querySelector('#weather-status').textContent==='Météo hors ligne');
await page.reload();
assert.equal(await page.locator('#alerts-enabled').isChecked(),false);
assert.deepEqual(errors,[]);
console.log('PASS themes + persistence; manual weather; search/route; GPS guidance; zone import/proximity alert; GPS stop; 5 views × 4 resolutions; no JS errors. Providers mocked; tiles replaced locally.');
await browser.close();