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:'Lecteur de test'});}); 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();}