258 lines
8.5 KiB
JavaScript
258 lines
8.5 KiB
JavaScript
/**
|
|
* Helpers pour les tests E2E avec FoundryVTT
|
|
*
|
|
* Fournit des fonctions utilitaires pour :
|
|
* - Attendre que Foundry soit prêt
|
|
* - Attendre que le module soit chargé
|
|
* - Interagir avec l'UI FoundryVTT
|
|
* - Gérer les sélecteurs spécifiques au module
|
|
*/
|
|
|
|
import { expect } from '@playwright/test';
|
|
|
|
/**
|
|
* Attend que FoundryVTT soit complètement chargé
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {number} timeout - Timeout en ms (défaut: 30000)
|
|
*/
|
|
export async function waitForFoundryReady(page, timeout = 30000) {
|
|
await page.waitForFunction(() => {
|
|
return typeof game !== 'undefined' && game.ready;
|
|
}, { timeout });
|
|
}
|
|
|
|
/**
|
|
* Attend que le module Scrying Pool soit actif
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {number} timeout - Timeout en ms (défaut: 15000)
|
|
*/
|
|
export async function waitForVVMModule(page, timeout = 15000) {
|
|
await page.waitForFunction(() => {
|
|
const module = game.modules?.get?.('scrying-pool');
|
|
return module?.active === true;
|
|
}, { timeout });
|
|
}
|
|
|
|
/**
|
|
* Attend qu'un élément du module soit présent
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} selector - Sélecteur CSS
|
|
* @param {number} timeout - Timeout en ms (défaut: 10000)
|
|
*/
|
|
export async function waitForVVMElement(page, selector, timeout = 10000) {
|
|
await page.waitForSelector(selector, {
|
|
state: 'visible',
|
|
timeout
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Clique sur un bouton dans l'UI Foundry avec retry
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string|import('@playwright/test').Locator} button - Sélecteur ou Locator
|
|
* @param {number} retries - Nombre de tentatives (défaut: 3)
|
|
*/
|
|
export async function clickFoundryButton(page, button, retries = 3) {
|
|
for (let i = 0; i < retries; i++) {
|
|
try {
|
|
const locator = typeof button === 'string' ? page.locator(button) : button;
|
|
await locator.click({ timeout: 5000 });
|
|
return;
|
|
} catch (error) {
|
|
if (i === retries - 1) throw error;
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ouvre le sidebar de configuration Foundry
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
*/
|
|
export async function openFoundrySidebar(page) {
|
|
await clickFoundryButton(page, 'button[aria-label="Configure Settings"]');
|
|
await page.waitForSelector('.app-v2.settings', { state: 'visible', timeout: 10000 });
|
|
}
|
|
|
|
/**
|
|
* Ouvre le Director's Board (Epic 2)
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
*/
|
|
export async function openDirectorsBoard(page) {
|
|
// Le Director's Board a un bouton dédié dans la sidebar
|
|
await page.waitForSelector('button[aria-label*="Director\'s Board"]', { timeout: 10000 });
|
|
await clickFoundryButton(page, 'button[aria-label*="Director\'s Board"]');
|
|
|
|
// Attendre que le board soit ouvert
|
|
await page.waitForSelector('.scrying-pool-directors-board', {
|
|
state: 'visible',
|
|
timeout: 10000
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Ouvre le Player Privacy Panel pour un utilisateur
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} userId - L'ID de l'utilisateur
|
|
*/
|
|
export async function openPlayerPrivacyPanel(page, userId) {
|
|
// Le panel s'ouvre via les paramètres du module
|
|
await openFoundrySidebar(page);
|
|
|
|
// Naviguer vers les paramètres du module
|
|
await clickFoundryButton(page, 'button:has-text("Scrying Pool")');
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Cliquer sur le bouton Player Privacy
|
|
await clickFoundryButton(page, 'button:has-text("Player Privacy")');
|
|
|
|
// Attendre le panel
|
|
await page.waitForSelector('.sp-player-privacy-panel', {
|
|
state: 'visible',
|
|
timeout: 10000
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sélectionne un utilisateur dans une liste Foundry
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} username - Le nom de l'utilisateur
|
|
*/
|
|
export async function selectUserInList(page, username) {
|
|
const userItem = page.locator(`.sp-user-item:has-text("${username}")`);
|
|
await userItem.click({ timeout: 5000 });
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
/**
|
|
* Attend qu'une notification Foundry apparaisse
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} text - Texte de la notification
|
|
* @param {number} timeout - Timeout en ms
|
|
*/
|
|
export async function waitForNotification(page, text, timeout = 10000) {
|
|
await page.waitForSelector(
|
|
`.notification:has-text("${text}")`,
|
|
{ state: 'visible', timeout }
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Vérifie qu'un AV Tile a un badge de visibilité spécifique
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} userId - L'ID de l'utilisateur
|
|
* @param {string} state - L'état attendu (active, hidden, etc.)
|
|
*/
|
|
export async function verifyAVTileState(page, userId, state) {
|
|
const tile = page.locator(`[data-user-id="${userId}"] .sp-visibility-badge`);
|
|
await expect(tile).toBeVisible({ timeout: 5000 });
|
|
|
|
// Vérifier le texte ou la classe CSS
|
|
const expectedText = {
|
|
active: 'Live',
|
|
hidden: 'Hidden from table',
|
|
'self-muted': 'Camera paused',
|
|
offline: 'Not connected',
|
|
'cam-lost': 'Camera unavailable',
|
|
reconnecting: 'Rejoining view',
|
|
'never-connected': 'Not yet connected',
|
|
ghost: 'Leaving',
|
|
}[state];
|
|
|
|
if (expectedText) {
|
|
await expect(tile).toContainText(expectedText);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Charge un preset de scène
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} presetName - Le nom du preset
|
|
*/
|
|
export async function loadScenePreset(page, presetName) {
|
|
await openDirectorsBoard(page);
|
|
|
|
// Cliquer sur le bouton Load Preset
|
|
await clickFoundryButton(page, 'button:has-text("Load Preset")');
|
|
|
|
// Sélectionner le preset dans la liste
|
|
await page.waitForSelector('.sp-preset-list', { state: 'visible', timeout: 5000 });
|
|
await clickFoundryButton(page, `.sp-preset-item:has-text("${presetName}")`);
|
|
|
|
// Attendre la confirmation
|
|
await waitForNotification(page, `Applied preset: ${presetName}`, 5000);
|
|
}
|
|
|
|
/**
|
|
* Sauvegarde le preset courant
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} presetName - Le nom du preset
|
|
*/
|
|
export async function saveScenePreset(page, presetName) {
|
|
await openDirectorsBoard(page);
|
|
|
|
// Cliquer sur Save Preset
|
|
await clickFoundryButton(page, 'button:has-text("Save Preset")');
|
|
|
|
// Remplir le nom du preset
|
|
await page.waitForSelector('.sp-save-preset-dialog', { state: 'visible', timeout: 5000 });
|
|
await page.locator('.sp-save-preset-dialog input[name="presetName"]').fill(presetName);
|
|
|
|
// Confirmer
|
|
await clickFoundryButton(page, '.sp-save-preset-dialog button:has-text("Save")');
|
|
|
|
// Attendre la confirmation
|
|
await waitForNotification(page, `Saved preset: ${presetName}`, 5000);
|
|
}
|
|
|
|
/**
|
|
* Hide un participant via le context menu
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} username - Le nom du participant
|
|
*/
|
|
export async function hideParticipant(page, username) {
|
|
// Trouver la tuile AV du participant
|
|
const tile = page.locator(`.av-tile:has-text("${username}")`);
|
|
|
|
// Clic droit pour ouvrir le context menu
|
|
await tile.click({ button: 'right', timeout: 5000 });
|
|
|
|
// Sélectionner "Hide from table"
|
|
await page.waitForSelector('.context-menu', { state: 'visible', timeout: 5000 });
|
|
await clickFoundryButton(page, '.context-menu li:has-text("Hide from table")');
|
|
|
|
// Attendre que le badge soit mis à jour
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
/**
|
|
* Show un participant via le context menu
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} username - Le nom du participant
|
|
*/
|
|
export async function showParticipant(page, username) {
|
|
const tile = page.locator(`.av-tile:has-text("${username}")`);
|
|
await tile.click({ button: 'right', timeout: 5000 });
|
|
|
|
await page.waitForSelector('.context-menu', { state: 'visible', timeout: 5000 });
|
|
await clickFoundryButton(page, '.context-menu li:has-text("Show to table")');
|
|
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
/**
|
|
* Utilise le Director's Board pour toggler un participant
|
|
* @param {import('@playwright/test').Page} page - La page Playwright
|
|
* @param {string} username - Le nom du participant
|
|
*/
|
|
export async function toggleParticipantInBoard(page, username) {
|
|
await openDirectorsBoard(page);
|
|
|
|
// Trouver la carte du participant
|
|
const card = page.locator(`.sp-participant-card:has-text("${username}")`);
|
|
await card.click({ timeout: 5000 });
|
|
|
|
// Le toggle devrait se faire automatiquement au clic
|
|
await page.waitForTimeout(500);
|
|
}
|