# 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 >> Hidden state shows grey overlay + lock icon + tooltip
- Location: specs/epic-1-visibility.spec.js:115: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
```