Amélioration avec des fonctions d'un vrai dashboard automotive et containerisation du projet pour faciliter le déploiement
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
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});
|
||||
try {
|
||||
const context=await browser.newContext({viewport:{width:1280,height:800}});
|
||||
const external=[];
|
||||
await context.route(/https:\/\/(music\.youtube\.com|www\.youtube\.com|open\.spotify\.com|waze\.com|media\.cunatbrule\.fr)\//,r=>{external.push(r.request().url());return r.fulfill({contentType:'text/html',body:'<title>Service de test</title>'});});
|
||||
const page=await context.newPage(),errors=[];page.on('pageerror',e=>errors.push(e.message));
|
||||
const base=process.env.CARIA_URL||'http://127.0.0.1:8080';
|
||||
await page.goto(base+'/#apps');
|
||||
await page.locator('#applications-grid .application-card').first().waitFor();
|
||||
assert.equal(await page.locator('#favorite-apps a').count(),5);
|
||||
assert.equal(await page.locator('#applications-grid .application-card').count(),9);
|
||||
assert.deepEqual(external,[]);
|
||||
await page.locator('#applications-customize').click();
|
||||
await page.locator('[data-favorite-slot="0"]').selectOption('youtube-music');
|
||||
await page.locator('[data-favorite-slot="1"]').selectOption('youtube-music');
|
||||
assert.equal(await page.locator('[data-favorite-slot="0"]').inputValue(),'drive');
|
||||
await page.getByLabel('Afficher Spotify',{exact:true}).uncheck();
|
||||
await page.getByLabel('Ouverture de YouTube',{exact:true}).selectOption('same');
|
||||
await page.locator('#application-settings-close').click();
|
||||
await page.reload();
|
||||
assert.equal(await page.locator('#favorite-apps a').nth(1).getAttribute('data-application'),'youtube-music');
|
||||
assert.equal(await page.locator('#applications-grid [data-application="spotify"]').count(),0);
|
||||
assert.equal(await page.locator('#applications-grid [data-application="youtube"]').getAttribute('target'),'_self');
|
||||
const popupEvent=page.waitForEvent('popup');
|
||||
await page.locator('#applications-grid [data-application="youtube-music"]').click();
|
||||
const popup=await popupEvent;await popup.waitForLoadState();
|
||||
assert.equal(popup.url(),'https://music.youtube.com/');
|
||||
assert.equal(page.url(),base+'/#apps');await popup.close();
|
||||
await page.locator('#applications-customize').click();await page.locator('#applications-reset').click();await page.locator('#application-settings-close').click();
|
||||
await page.locator('#favorite-apps [data-view="music"]').click();
|
||||
const wav=Buffer.alloc(44+16000*60);wav.write('RIFF');wav.writeUInt32LE(wav.length-8,4);wav.write('WAVE',8);wav.write('fmt ',12);wav.writeUInt32LE(16,16);wav.writeUInt16LE(1,20);wav.writeUInt16LE(1,22);wav.writeUInt32LE(8000,24);wav.writeUInt32LE(16000,28);wav.writeUInt16LE(2,32);wav.writeUInt16LE(16,34);wav.write('data',36);wav.writeUInt32LE(wav.length-44,40);
|
||||
await page.locator('#music-files').setInputFiles({name:'Test.wav',mimeType:'audio/wav',buffer:wav});await page.locator('#play').click();
|
||||
await page.waitForFunction(()=>!document.querySelector('#audio').paused);
|
||||
await page.locator('#favorite-apps [data-view="apps"]').click();await page.locator('#applications-customize').click();
|
||||
await page.getByLabel('Afficher Musique locale',{exact:true}).uncheck();await page.locator('#application-settings-close').click();
|
||||
assert.equal(await page.evaluate(()=>document.querySelector('#audio').paused),false);
|
||||
assert.equal(await page.locator('#favorite-apps [data-application="music"]').count(),0);
|
||||
assert.equal(await page.locator('#applications-grid [data-application="music"]').count(),0);
|
||||
await page.locator('#applications-customize').click();
|
||||
const names=['Accueil','Conduite','Navigation','Musique locale','Jellyfin','Spotify','YouTube','YouTube Music','Waze'];
|
||||
for (const name of names) await page.getByLabel(`Afficher ${name}`,{exact:true}).uncheck();
|
||||
await page.locator('#application-settings-close').click();assert.equal(await page.locator('#favorite-apps a').count(),1);await page.locator('#applications-empty').waitFor({state:'visible'});
|
||||
await page.locator('#applications-customize').click();await page.locator('#applications-reset').click();await page.locator('#application-settings-close').click();
|
||||
for (const size of [{width:1280,height:800},{width:1024,height:600},{width:800,height:480},{width:390,height:844}]) {await page.setViewportSize(size);assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),true);}
|
||||
await page.setViewportSize({width:1280,height:800});
|
||||
if(process.env.QA_SCREENSHOT) await page.screenshot({path:process.env.QA_SCREENSHOT,fullPage:true});
|
||||
assert.deepEqual(errors,[]);console.log('PASS catalog; favorites swap; hide/restore; persistence; opening modes; YouTube Music popup; no preloading; audio survives hiding; empty catalog; responsive layouts.');
|
||||
} finally {await browser.close();}
|
||||
@@ -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();
|
||||
@@ -0,0 +1,104 @@
|
||||
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});
|
||||
try {
|
||||
const context=await browser.newContext({viewport:{width:1200,height:1080},hasTouch:true,reducedMotion:'reduce'});
|
||||
const external=[];
|
||||
await context.route(/https:\/\/(open\.spotify\.com|www\.youtube\.com|media\.cunatbrule\.fr)\//,r=>{external.push(r.request().url());return r.fulfill({contentType:'text/html',body:'<html><body>Lecteur de test</body></html>'});});
|
||||
const page=await context.newPage(),errors=[];page.on('pageerror',e=>errors.push(e.message));
|
||||
const base=process.env.CARIA_URL||'http://127.0.0.1:8080';
|
||||
await page.goto(base+'/#drive');await page.locator('#speed-gauge svg').waitFor();
|
||||
assert.equal(await page.locator('main > header').count(),1);
|
||||
assert.equal(await page.locator('.workspace > footer').count(),0);
|
||||
assert.equal(await page.locator('#settings footer #footer-status').count(),1);
|
||||
const pageCount = context.pages().length;
|
||||
assert.equal(await page.locator('#drive-immersive').count(),0);
|
||||
await page.locator('#rail-toggle').click();
|
||||
assert.equal(context.pages().length,pageCount);
|
||||
assert.equal(await page.locator('#side-rail').isVisible(),false);
|
||||
assert.ok((await page.locator('.page-top').boundingBox()).height<=44);
|
||||
await page.locator('#rail-toggle').click();
|
||||
const gauge=await page.locator('#speed-gauge svg').boundingBox();assert.ok(gauge.width>350);
|
||||
await page.locator('#rail-toggle').click();assert.equal(await page.locator('#side-rail').isVisible(),false);
|
||||
await page.reload();assert.equal(await page.locator('#side-rail').isVisible(),false);
|
||||
await page.waitForFunction(()=>document.querySelector('[data-value="fuelPercent"]').textContent==='68');
|
||||
if(process.env.QA_DIRECTORY)await page.screenshot({path:process.env.QA_DIRECTORY+'/caria-touch-drive.png',fullPage:true});
|
||||
await page.getByRole('link',{name:'Détails du véhicule'}).click();await page.locator('#vehicle-details').waitFor({state:'visible'});
|
||||
assert.equal(await page.locator('#vehicle-details .card').count(),8);
|
||||
await page.locator('#rail-toggle').click();await page.locator('#favorite-apps [data-view="music"]').click();
|
||||
await page.locator('#settings-open').click();
|
||||
await page.locator('#dial-style').selectOption('digital');
|
||||
await page.locator('#settings-form button[type="submit"]').click();
|
||||
assert.equal(await page.evaluate(()=>document.body.classList.contains('digital-dials')),true);
|
||||
await page.reload();
|
||||
assert.equal(await page.evaluate(()=>document.body.classList.contains('digital-dials')),true);
|
||||
await page.waitForFunction(()=>document.querySelector('[data-level="fuelPercent"]').getAttribute('aria-valuenow')==='68');
|
||||
assert.equal(await page.locator('[data-level="coolantCelsius"]').getAttribute('aria-valuenow'),'89');
|
||||
await page.evaluate(()=>location.hash='drive');
|
||||
await page.locator('#drive').waitFor({state:'visible'});
|
||||
assert.equal(await page.locator('#speed-gauge .needle').isVisible(),false);
|
||||
assert.equal(await page.locator('#speed-gauge .tick.major').first().isVisible(),true);
|
||||
assert.equal(await page.locator('#speed-gauge .tick.intermediate').first().isVisible(),true);
|
||||
assert.equal(await page.locator('#speed-gauge .tick.quarter').first().isVisible(),false);
|
||||
assert.equal(await page.locator('#rpm-gauge .rpm-red-zone').isVisible(),true);
|
||||
assert.equal(await page.locator('#speed-gauge .digital-progress').isVisible(),true);
|
||||
assert.equal(await page.locator('#drive .quick-sensors').count(),0);
|
||||
if(process.env.QA_DIRECTORY)await page.screenshot({path:process.env.QA_DIRECTORY+'/caria-digital-drive.png'});
|
||||
await page.evaluate(()=>location.hash='music');
|
||||
await page.locator('#settings-open').click();
|
||||
assert.equal(await page.locator('#settings').evaluate(el=>getComputedStyle(el).overflowY),'auto');
|
||||
await page.locator('#settings-close').click();
|
||||
const wav=Buffer.alloc(44+16000*120);wav.write('RIFF');wav.writeUInt32LE(wav.length-8,4);wav.write('WAVE',8);wav.write('fmt ',12);wav.writeUInt32LE(16,16);wav.writeUInt16LE(1,20);wav.writeUInt16LE(1,22);wav.writeUInt32LE(8000,24);wav.writeUInt32LE(16000,28);wav.writeUInt16LE(2,32);wav.writeUInt16LE(16,34);wav.write('data',36);wav.writeUInt32LE(wav.length-44,40);
|
||||
await page.locator('#music-files').setInputFiles({name:'Night Drive.wav',mimeType:'audio/wav',buffer:wav});
|
||||
await page.getByRole('link',{name:'Vue lecture immersive'}).click();await page.locator('#now-playing').waitFor({state:'visible'});
|
||||
await page.locator('#immersive-play').click();await page.waitForFunction(()=>!document.querySelector('#audio').paused);
|
||||
assert.equal(await page.locator('#immersive-seek').count(),0); await page.locator('#audio').evaluate(el=>el.currentTime=60);
|
||||
await page.waitForFunction(()=>document.querySelector('#audio').currentTime>=60);
|
||||
await page.waitForFunction(()=>Number(document.querySelector('#track-arc').style.strokeDashoffset)<=50);
|
||||
await page.locator('#immersive-volume').fill('0.3');await page.locator('#immersive-volume').dispatchEvent('input');assert.equal(await page.locator('#audio').evaluate(el=>el.volume),.3);
|
||||
await page.locator('#immersive-repeat').click();assert.equal(await page.locator('#audio').evaluate(el=>el.loop),true);
|
||||
await page.locator('#immersive-play').click();assert.equal(await page.locator('#audio').evaluate(el=>el.paused),true);
|
||||
if(process.env.QA_DIRECTORY)await page.screenshot({path:process.env.QA_DIRECTORY+'/caria-touch-music.png',fullPage:true});
|
||||
assert.equal(await page.locator('.brand,.wordmark').count(),0);
|
||||
await page.locator('#immersive-mute').click();assert.equal(await page.locator('#audio').evaluate(el=>el.muted),true);
|
||||
await page.locator('#immersive-mute').click();assert.equal(await page.locator('#audio').evaluate(el=>el.muted),false);
|
||||
assert.equal(await page.locator('#audio').evaluate(el=>el.volume),.3);
|
||||
await page.locator('#immersive-repeat').click();
|
||||
await page.locator('#music-files').setInputFiles(['Second','Third'].map(name=>({name:name+'.wav',mimeType:'audio/wav',buffer:wav})));
|
||||
await page.locator('#immersive-shuffle').click();
|
||||
assert.equal(await page.locator('#immersive-shuffle').getAttribute('aria-pressed'),'true');
|
||||
const titles=[await page.locator('#immersive-title').textContent()];
|
||||
for(let i=0;i<2;i++){
|
||||
await page.locator('#immersive-next').click();
|
||||
await page.waitForFunction(previous=>document.querySelector('#immersive-title').textContent!==previous,titles.at(-1));
|
||||
titles.push(await page.locator('#immersive-title').textContent());
|
||||
}
|
||||
assert.equal(new Set(titles).size,3,'Shuffle visits each remaining track once');
|
||||
await page.locator('#audio').evaluate(el=>el.currentTime=30);
|
||||
await page.locator('#immersive-previous').click();
|
||||
assert.ok(await page.locator('#audio').evaluate(el=>el.currentTime<2));
|
||||
assert.equal(await page.locator('#immersive-title').textContent(),titles[2]);
|
||||
await page.locator('#immersive-previous').click();
|
||||
await page.waitForFunction(title=>document.querySelector('#immersive-title').textContent===title,titles[1]);
|
||||
await page.locator('#audio').evaluate(el=>el.dispatchEvent(new Event('ended')));
|
||||
await page.waitForFunction(title=>document.querySelector('#immersive-title').textContent!==title,titles[1]);
|
||||
await page.locator('#audio').evaluate(el=>el.pause());
|
||||
await page.locator('#favorite-apps [data-view="apps"]').click();await page.locator('#applications-customize').click();await page.getByLabel('Ouverture de Spotify',{exact:true}).selectOption('embed');await page.locator('#application-settings-close').click();
|
||||
assert.deepEqual(external,[]);
|
||||
await page.locator('#applications-grid [data-application="spotify"]').click();await page.locator('#embedded').waitFor({state:'visible'});
|
||||
assert.equal(await page.locator('#embed-container iframe').count(),0);
|
||||
await page.locator('#embed-url').fill('https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M');await page.getByRole('button',{name:'Charger le lecteur',exact:true}).click();
|
||||
assert.equal(await page.locator('#embed-container iframe').getAttribute('src'),'https://open.spotify.com/embed/playlist/37i9dQZF1DXcBWIGoYBM5M');
|
||||
await page.locator('#favorite-apps [data-view="apps"]').click();await page.locator('#embed-container iframe').waitFor({state:'detached'});
|
||||
for (const size of [{width:1200,height:1080},{width:1920,height:1080},{width:1024,height:600},{width:390,height:844}]) {
|
||||
await page.setViewportSize(size);
|
||||
for(const view of ['home','drive','vehicle-details','music','now-playing','apps','embedded']){
|
||||
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}`);
|
||||
assert.equal(await page.evaluate(()=>document.documentElement.scrollHeight<=innerHeight),true,`Vertical overflow ${view} ${size.width}`);
|
||||
assert.ok(await page.locator('#'+view).evaluate(el=>el.getBoundingClientRect().bottom<=innerHeight+1),`View fits ${view} ${size.width}`);
|
||||
}
|
||||
}
|
||||
await page.goto(base+'/?display=drive#drive');await page.locator('#speed-gauge svg').waitFor();assert.equal(await page.locator('#side-rail').isVisible(),false);
|
||||
assert.deepEqual(errors,[]);console.log('PASS landscape/touch; sidebar persistence; dedicated drive; large gauges; sensor details; shared arc player/seek/volume/repeat; opt-in embed/unload; 7 views × 4 sizes.');
|
||||
}finally{await browser.close();}
|
||||
Reference in New Issue
Block a user