# Instructions - Following Playwright test failed. - Explain why, be concise, respect Playwright best practices. - Provide a snippet of code with the fix, if possible. # Test info - Name: specs/epic-1-visibility.spec.js >> Epic 1: Core Camera Visibility Control >> FR-4: AV Tile Visual State Indicators >> Self-muted state shows camera-off icon - Location: specs/epic-1-visibility.spec.js:132:5 # Error details ``` Test timeout of 120000ms exceeded while running "beforeEach" hook. ``` ``` Error: page.waitForFunction: Test timeout of 120000ms exceeded. ``` # Page snapshot ```yaml - generic [active] [ref=e1]: - list: - listitem [ref=e2]: - text:  - paragraph [ref=e3]: Foundry Virtual Tabletop nécessite une résolution d'écran de 1366px par 768px ou plus. Votre écran a une résolution de 1280px par 720px. Vous devez augmenter votre résolution ou utiliser un autre périphérique d'affichage, sinon certaines fonctionnalités du logiciel ne fonctionneront pas correctement. - text:  - banner [ref=e5]: - heading "Donjon & Cie" [level=1] [ref=e6] - generic [ref=e7]: - generic [ref=e8]: - heading "Rejoindre la partie" [level=2] [ref=e9] - combobox [ref=e14] [cursor=pointer]: - option [selected] - option "Gamemaster" [disabled] - textbox "Mot de passe" [ref=e19] - contentinfo [ref=e20]: - button "Rejoindre la partie" [ref=e21] [cursor=pointer]: - generic: Rejoindre la partie - generic [ref=e22]: - heading "Détails de la session" [level=2] [ref=e23] - generic [ref=e25]: Prochaine partie - generic [ref=e27]: - generic [ref=e28]: Joueurs présents - generic [ref=e30]: - generic [ref=e31]: "1" - generic [ref=e32]: / - generic [ref=e33]: "1" - generic [ref=e34]: - heading "Retour à l’accueil" [level=2] [ref=e35] - textbox [ref=e39] - contentinfo [ref=e40]: - button "Retour à l’accueil" [ref=e41] [cursor=pointer]: - generic: Retour à l’accueil - article [ref=e42]: - heading "Description du monde" [level=2] [ref=e43] - contentinfo [ref=e44]: - paragraph [ref=e45]: Version 14 Build 361 ``` # Test source ```ts 1 | /** 2 | * Helpers pour les tests E2E avec FoundryVTT 3 | * 4 | * Fournit des fonctions utilitaires pour : 5 | * - Attendre que Foundry soit prêt 6 | * - Attendre que le module soit chargé 7 | * - Interagir avec l'UI FoundryVTT 8 | * - Gérer les sélecteurs spécifiques au module 9 | */ 10 | 11 | import { expect } from '@playwright/test'; 12 | 13 | /** 14 | * Attend que FoundryVTT soit complètement chargé 15 | * @param {import('@playwright/test').Page} page - La page Playwright 16 | * @param {number} timeout - Timeout en ms (défaut: 30000) 17 | */ 18 | export async function waitForFoundryReady(page, timeout = 30000) { > 19 | await page.waitForFunction(() => { | ^ Error: page.waitForFunction: Test timeout of 120000ms exceeded. 20 | return typeof game !== 'undefined' && game.ready; 21 | }, { timeout }); 22 | } 23 | 24 | /** 25 | * Attend que le module Video View Manager soit actif 26 | * @param {import('@playwright/test').Page} page - La page Playwright 27 | * @param {number} timeout - Timeout en ms (défaut: 15000) 28 | */ 29 | export async function waitForVVMModule(page, timeout = 15000) { 30 | await page.waitForFunction(() => { 31 | const module = game.modules?.get?.('video-view-manager'); 32 | return module?.active === true; 33 | }, { timeout }); 34 | } 35 | 36 | /** 37 | * Attend qu'un élément du module soit présent 38 | * @param {import('@playwright/test').Page} page - La page Playwright 39 | * @param {string} selector - Sélecteur CSS 40 | * @param {number} timeout - Timeout en ms (défaut: 10000) 41 | */ 42 | export async function waitForVVMElement(page, selector, timeout = 10000) { 43 | await page.waitForSelector(selector, { 44 | state: 'visible', 45 | timeout 46 | }); 47 | } 48 | 49 | /** 50 | * Clique sur un bouton dans l'UI Foundry avec retry 51 | * @param {import('@playwright/test').Page} page - La page Playwright 52 | * @param {string|import('@playwright/test').Locator} button - Sélecteur ou Locator 53 | * @param {number} retries - Nombre de tentatives (défaut: 3) 54 | */ 55 | export async function clickFoundryButton(page, button, retries = 3) { 56 | for (let i = 0; i < retries; i++) { 57 | try { 58 | const locator = typeof button === 'string' ? page.locator(button) : button; 59 | await locator.click({ timeout: 5000 }); 60 | return; 61 | } catch (error) { 62 | if (i === retries - 1) throw error; 63 | await page.waitForTimeout(1000); 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * Ouvre le sidebar de configuration Foundry 70 | * @param {import('@playwright/test').Page} page - La page Playwright 71 | */ 72 | export async function openFoundrySidebar(page) { 73 | await clickFoundryButton(page, 'button[aria-label="Configure Settings"]'); 74 | await page.waitForSelector('.app-v2.settings', { state: 'visible', timeout: 10000 }); 75 | } 76 | 77 | /** 78 | * Ouvre le Director's Board (Epic 2) 79 | * @param {import('@playwright/test').Page} page - La page Playwright 80 | */ 81 | export async function openDirectorsBoard(page) { 82 | // Le Director's Board a un bouton dédié dans la sidebar 83 | await page.waitForSelector('button[aria-label*="Director\'s Board"]', { timeout: 10000 }); 84 | await clickFoundryButton(page, 'button[aria-label*="Director\'s Board"]'); 85 | 86 | // Attendre que le board soit ouvert 87 | await page.waitForSelector('.scrying-pool-directors-board', { 88 | state: 'visible', 89 | timeout: 10000 90 | }); 91 | } 92 | 93 | /** 94 | * Ouvre le Player Privacy Panel pour un utilisateur 95 | * @param {import('@playwright/test').Page} page - La page Playwright 96 | * @param {string} userId - L'ID de l'utilisateur 97 | */ 98 | export async function openPlayerPrivacyPanel(page, userId) { 99 | // Le panel s'ouvre via les paramètres du module 100 | await openFoundrySidebar(page); 101 | 102 | // Naviguer vers les paramètres du module 103 | await clickFoundryButton(page, 'button:has-text("Video View Manager")'); 104 | await page.waitForTimeout(1000); 105 | 106 | // Cliquer sur le bouton Player Privacy 107 | await clickFoundryButton(page, 'button:has-text("Player Privacy")'); 108 | 109 | // Attendre le panel 110 | await page.waitForSelector('.sp-player-privacy-panel', { 111 | state: 'visible', 112 | timeout: 10000 113 | }); 114 | } 115 | 116 | /** 117 | * Sélectionne un utilisateur dans une liste Foundry 118 | * @param {import('@playwright/test').Page} page - La page Playwright 119 | * @param {string} username - Le nom de l'utilisateur ```