From 63c45ca5642ea28f2a9fbae8d7617495c14a4741 Mon Sep 17 00:00:00 2001
From: Carlos Santos <4a.santos@gmail.com>
Date: Tue, 13 May 2025 13:12:21 +0200
Subject: [PATCH] webcomponent: update Playwright configuration and enhance E2E
tests with new helper functions
---
frontend/webcomponent/playwright.config.ts | 20 ++-
.../tests/e2e/openvidu-meet.test.ts | 144 +++++++++++++++---
.../tests/helpers/function-helpers.ts | 56 +++++++
3 files changed, 189 insertions(+), 31 deletions(-)
create mode 100644 frontend/webcomponent/tests/helpers/function-helpers.ts
diff --git a/frontend/webcomponent/playwright.config.ts b/frontend/webcomponent/playwright.config.ts
index 7f449f6..e19b186 100644
--- a/frontend/webcomponent/playwright.config.ts
+++ b/frontend/webcomponent/playwright.config.ts
@@ -2,12 +2,16 @@
import { defineConfig } from '@playwright/test';
export default defineConfig({
- testDir: './tests/e2e',
- timeout: 30000,
- retries: 1,
- use: {
- headless: true,
- viewport: { width: 1280, height: 720 },
- ignoreHTTPSErrors: true
- }
+ testDir: './tests/e2e',
+ timeout: 30000,
+ retries: 0,
+ workers: 1,
+ fullyParallel: false,
+ use: {
+ headless: false,
+ viewport: { width: 1280, height: 720 },
+ ignoreHTTPSErrors: true,
+ permissions: ['camera', 'microphone'],
+ video: 'retain-on-failure'
+ }
});
diff --git a/frontend/webcomponent/tests/e2e/openvidu-meet.test.ts b/frontend/webcomponent/tests/e2e/openvidu-meet.test.ts
index d4c7190..f90b762 100644
--- a/frontend/webcomponent/tests/e2e/openvidu-meet.test.ts
+++ b/frontend/webcomponent/tests/e2e/openvidu-meet.test.ts
@@ -1,30 +1,128 @@
-// tests/e2e/openvidu-meet.e2e.ts
-import { test, expect } from '@playwright/test';
+import { test, expect, BrowserContext, Browser, Page, chromium } from '@playwright/test';
+import { waitForElementInIframe } from '../helpers/function-helpers';
+import fs from 'fs';
-test.describe('OpenViduMeet E2E Tests', () => {
- test('should load iframe with correct URL including additional parameters', async ({ page }) => {
- await page.setContent(
- ``
- );
- const iframe = page.locator('iframe');
- await expect(iframe).toHaveAttribute('src', 'https://meet.example.com?room-name=Sala1&pepito-perez=55');
+test.describe('Web Component E2E Tests', () => {
+ const testAppUrl = 'http://localhost:5080';
+ const testRoomPrefix = 'test-room';
+
+ let browser: Browser;
+ let context: BrowserContext;
+ let page: Page;
+
+ test.beforeAll(async () => {
+ // Create a test room before all tests
+ const tempBrowser = await chromium.launch();
+ const tempContext = await tempBrowser.newContext();
+ const tempPage = await tempContext.newPage();
+ await tempPage.goto(testAppUrl);
+ await tempPage.waitForSelector('.create-room');
+ await tempPage.fill('#room-id-prefix', testRoomPrefix);
+ await tempPage.click('.create-room-btn');
+ await tempPage.waitForSelector(`#${testRoomPrefix}`);
+ await tempBrowser.close();
});
- test('should handle postMessage interactions', async ({ page }) => {
- await page.setContent(``);
+ test.beforeEach(async () => {
+ browser = await chromium.launch({ headless: false });
+ const storageState = fs.existsSync('test_localstorage_state.json')
+ ? { storageState: 'test_localstorage_state.json' }
+ : {};
+ context = await browser.newContext(storageState);
+ page = await context.newPage();
+ await page.goto(testAppUrl);
+ await page.waitForSelector('.rooms-container');
+ await page.waitForSelector(`#${testRoomPrefix}`);
+ await page.click('.dropdown-button');
+ await page.waitForSelector('#join-as-moderator');
+ await page.waitForSelector('#join-as-publisher');
+ });
- const [event] = await Promise.all([
- page.evaluate(() => {
- return new Promise((resolve) => {
- const component = document.querySelector('openvidu-meet');
- if (component) {
- component.addEventListener('conference-event', (e) => resolve(e.detail));
- }
- window.postMessage({ event: 'participant-joined', participant: 'María Gómez' }, '*');
- });
- })
- ]);
+ test.afterEach(async () => {
+ await context.storageState({ path: 'test_localstorage_state.json' });
+ await browser.close();
+ });
- expect(event).toEqual({ event: 'participant-joined', participant: 'María Gómez' });
+ test.describe('Component Rendering', () => {
+ test('should load the web component with proper iframe', async () => {
+ await page.click('#join-as-moderator');
+ const component = page.locator('openvidu-meet');
+ await expect(component).toBeVisible();
+
+ const hasIframe = await page.evaluate(() => {
+ const component = document.querySelector('openvidu-meet');
+ return !!component?.shadowRoot?.querySelector('iframe');
+ });
+ expect(hasIframe).toBeTruthy();
+ });
+ });
+
+ test.describe('Event Handling', () => {
+ test('should successfully join as moderator and receive JOIN event', async () => {
+ await page.click('#join-as-moderator');
+ await waitForElementInIframe(page, 'ov-session');
+ await page.waitForSelector('.event-JOIN');
+ const joinElements = await page.locator('.event-JOIN').all();
+ expect(joinElements.length).toBe(1);
+ });
+
+ test('should successfully join as publisher and receive JOIN event', async () => {
+ await page.click('#join-as-publisher');
+ await waitForElementInIframe(page, 'ov-session');
+ await page.waitForSelector('.event-JOIN');
+ const joinElements = await page.locator('.event-JOIN').all();
+ expect(joinElements.length).toBe(1);
+ });
+
+ test('should successfully join to room and receive LEFT event when using leave command', async () => {
+ await page.click('#join-as-moderator');
+ await waitForElementInIframe(page, 'ov-session');
+ await page.click('#leave-room-btn');
+ await page.waitForSelector('.event-LEFT');
+ const leftElements = await page.locator('.event-LEFT').all();
+ expect(leftElements.length).toBe(1);
+ });
+
+ test('should successfully join to room and receive LEFT event when using disconnect button', async () => {
+ await page.click('#join-as-moderator');
+ await waitForElementInIframe(page, 'ov-session');
+ const button = await waitForElementInIframe(page, '#leave-btn');
+ await button.click();
+ await page.waitForSelector('.event-LEFT');
+ const leftElements = await page.locator('.event-LEFT').all();
+ expect(leftElements.length).toBe(1);
+ });
+
+ test('should successfully join to room and receive MEETING_ENDED event when using end meeting command', async () => {
+ await page.click('#join-as-moderator');
+ await waitForElementInIframe(page, 'ov-session');
+ await page.click('#end-meeting-btn');
+ await page.waitForSelector('.event-MEETING_ENDED');
+ const meetingEndedElements = await page.locator('.event-MEETING_ENDED').all();
+ expect(meetingEndedElements.length).toBe(1);
+
+ // Check LEFT event does not exist
+ const leftEventElements = await page.locator('.event-LEFT').all();
+ expect(leftEventElements.length).toBe(0);
+ });
+ });
+
+ test.describe('Webhook Handling', () => {
+ test('should successfully join to room and receive meetingStarted webhook', async () => {
+ await page.click('#join-as-moderator');
+ await waitForElementInIframe(page, 'ov-session');
+ await page.waitForSelector('.webhook-meetingStarted');
+ const meetingStartedElements = await page.locator('.webhook-meetingStarted').all();
+ expect(meetingStartedElements.length).toBe(1);
+ });
+
+ test('should successfully join to room and receive meetingEnded webhook', async () => {
+ await page.click('#join-as-moderator');
+ await waitForElementInIframe(page, 'ov-session');
+ await page.click('#end-meeting-btn');
+ await page.waitForSelector('.webhook-meetingEnded');
+ const meetingEndedElements = await page.locator('.webhook-meetingEnded').all();
+ expect(meetingEndedElements.length).toBe(1);
+ });
});
});
diff --git a/frontend/webcomponent/tests/helpers/function-helpers.ts b/frontend/webcomponent/tests/helpers/function-helpers.ts
new file mode 100644
index 0000000..3dc7bd8
--- /dev/null
+++ b/frontend/webcomponent/tests/helpers/function-helpers.ts
@@ -0,0 +1,56 @@
+import { Page, Locator, FrameLocator } from '@playwright/test';
+
+/**
+ * Gets a FrameLocator for an iframe inside a Shadow DOM
+ * @param page - Playwright page object
+ * @param componentSelector - Selector for the web component with Shadow DOM
+ * @param iframeSelector - Selector for the iframe within the Shadow DOM
+ * @returns FrameLocator that can be used to access iframe contents
+ */
+export async function getIframeInShadowDom(
+ page: Page,
+ componentSelector: string = 'openvidu-meet',
+ iframeSelector: string = 'iframe'
+): Promise {
+ // Verify the component exists
+ await page.waitForSelector(componentSelector);
+
+ // Use frameLocator to access the iframe contents
+ return page.frameLocator(`${componentSelector} >>> ${iframeSelector}`);
+}
+
+/**
+ * Waits for an element inside an iframe within Shadow DOM
+ * @param page - Playwright page object
+ * @param elementSelector - Selector for the element inside the iframe
+ * @param options - Optional configuration
+ * @returns Locator for the found element
+ */
+export async function waitForElementInIframe(
+ page: Page,
+ elementSelector: string,
+ options: {
+ componentSelector?: string;
+ iframeSelector?: string;
+ timeout?: number;
+ state?: 'attached' | 'detached' | 'visible' | 'hidden';
+ } = {}
+): Promise {
+ const {
+ componentSelector = 'openvidu-meet',
+ iframeSelector = 'iframe',
+ timeout = 30000,
+ state = 'visible'
+ } = options;
+
+ // Get the iframe
+ const frameLocator = await getIframeInShadowDom(page, componentSelector, iframeSelector);
+
+ // Get element locator
+ const elementLocator = frameLocator.locator(elementSelector);
+
+ // Wait for the element with the specified state
+ await elementLocator.waitFor({ state, timeout });
+
+ return elementLocator;
+}