import AxeBuilder from '@axe-core/playwright' import { expect, Page, test } from '@playwright/test' const projects=[ {id:'project-tv',name:'TV Hub',path:'/home/tv-hub',owner_id:'owner',owner_username:'alice',kind:'local',archived:0,full_access:0,git_available:true,execution_group:'',groups:[]}, {id:'project-player',name:'Lecteur TV',path:'/home/tv-player',owner_id:'owner',owner_username:'alice',kind:'local',archived:0,full_access:0,git_available:true,execution_group:'',groups:[]}, {id:'project-remote',name:'Télécommande',path:'/home/tv-remote',owner_id:'owner',owner_username:'alice',kind:'local',archived:0,full_access:0,git_available:true,execution_group:'',groups:[]}, ] async function mockApi(page:Page){ let groups:any[]=[] await page.route('**/api/**',async route=>{ const request=route.request(),url=new URL(request.url()),path=url.pathname,method=request.method() let body:unknown={} if(path==='/api/bootstrap')body={setup_required:false,csrf_token:'csrf'} else if(path==='/api/auth/me')body={username:'alice',is_admin:true,csrf_token:'csrf'} else if(path==='/api/projects'&&url.searchParams.get('archived')==='true')body=[] else if(path==='/api/projects')body=projects else if(path==='/api/projects/activity')body={running_project_ids:['project-player']} else if(path==='/api/application/preferences')body={auto_compact_context:false,interface_type:'dashboard',theme:'dark'} else if(path==='/api/duck/preferences')body={enabled:true,reminder_minutes:30} else if(path==='/api/activity')body={cards:[],counts:{running:0,waiting:0,review:0},observed_at:'2026-11-12T20:00:00Z'} else if(path==='/api/dashboard')body={connected:true,hostname:'NasPi',platform:'Linux',cpu_percent:17,cpu_count:4,ram_percent:42,ram_used:4_000_000_000,ram_total:8_000_000_000,temperature_c:48,disk_percent:55,disk_used:55_000_000_000,disk_total:100_000_000_000,uptime_seconds:86400,active_sessions:1,total_sessions:3,docker:{available:true,version:'27'},codex:{available:true,version:'1'},git:{available:true,version:'2.47'},codex_quota:{available:false}} else if(path==='/api/project-groups'&&method==='GET')body=groups else if(path==='/api/project-groups'&&method==='POST'){ const input=request.postDataJSON() groups=[{id:'group-tv',key:'tv',owner_id:'owner',owner_username:'alice',name:input.name,description:input.description,member_count:input.project_ids.length,members:projects.filter(item=>input.project_ids.includes(item.id)),updates:[],created_at:'2026-11-12T20:00:00Z',updated_at:'2026-11-12T20:00:00Z'}] body=groups[0] }else if(path==='/api/search')body=[] return route.fulfill({status:200,contentType:'application/json',body:JSON.stringify(body)}) }) } test.beforeEach(async({page})=>{ await mockApi(page) await page.goto('/?view=company') await expect(page.getByRole('heading',{name:'Qui fait quoi ?'})).toBeVisible() }) test('crée un groupe à partir de plusieurs projets existants',async({page})=>{ await page.getByRole('button',{name:'+ Groupe'}).first().click() await page.getByLabel('Nom du groupe').fill('TV') await page.getByLabel('Objectif commun').fill('Applications qui communiquent avec la télévision.') await page.locator('label').filter({hasText:'TV Hub'}).click() await page.locator('label').filter({hasText:'Lecteur TV'}).click() const requestPromise=page.waitForRequest(request=>request.url().endsWith('/api/project-groups')&&request.method()==='POST') await page.getByRole('button',{name:'Créer le groupe'}).click() const request=await requestPromise expect(request.postDataJSON().project_ids.sort()).toEqual(['project-player','project-tv']) await expect(page.locator('.company-groups article>header b').filter({hasText:/^TV$/})).toBeVisible() }) test('conserve la vue dans l’URL, au retour et au rechargement',async({page})=>{ await page.getByRole('button',{name:'Projets',exact:true}).click() await expect(page).toHaveURL(/view=dashboard/) await expect(page.getByRole('heading',{name:'Projets et chefs d’équipe'})).toBeVisible() await page.goBack() await expect(page).toHaveURL(/view=company/) await page.reload() await expect(page.getByRole('heading',{name:'Qui fait quoi ?'})).toBeVisible() }) test('le lanceur est utilisable au clavier et la vue ne présente pas de violation axe sérieuse',async({page})=>{ await page.keyboard.press('Control+K') const dialog=page.getByRole('dialog',{name:'Rechercher et ouvrir'}) await expect(dialog).toBeVisible() await page.keyboard.press('Escape') await expect(dialog).toBeHidden() const results=await new AxeBuilder({page}).withTags(['wcag2a','wcag2aa','wcag21a','wcag21aa']).analyze() expect(results.violations.filter(item=>item.impact==='critical'||item.impact==='serious')).toEqual([]) }) test('capture la vue Organisation de référence',async({page})=>{ await expect(page).toHaveScreenshot('organisation.png',{fullPage:true}) })