diff --git a/meet-ce/frontend/webcomponent/tests/e2e/core/webhooks.test.ts b/meet-ce/frontend/webcomponent/tests/e2e/core/webhooks.test.ts index 5f3f9fa3..3fdc3965 100644 --- a/meet-ce/frontend/webcomponent/tests/e2e/core/webhooks.test.ts +++ b/meet-ce/frontend/webcomponent/tests/e2e/core/webhooks.test.ts @@ -1,9 +1,13 @@ +import { MeetRoomStatus } from '@openvidu-meet/typings'; import { expect, test } from '@playwright/test'; import { MEET_TESTAPP_URL } from '../../config.js'; import { createTestRoom, deleteAllRecordings, deleteAllRooms, + getRecordingFromAPI, + getRoomFromAPI, + getRoomFromWebhookStorage, joinRoomAs, prepareForJoiningRoom, startStopRecording @@ -56,11 +60,25 @@ test.describe('Web Component E2E Tests', () => { const meetingStartedElements = await page.locator('.webhook-meetingStarted').all(); expect(meetingStartedElements.length).toBe(1); + // Get the actual room object from localStorage and compare deeply + let [meetingStartedWebhook, actualRoom] = await Promise.all([ + getRoomFromWebhookStorage(page, roomId, 'meetingStarted'), + getRoomFromAPI(roomId) + ]); + + expect(meetingStartedWebhook.data).toMatchObject(actualRoom); + // End the meeting await page.click('#end-meeting-btn'); await page.waitForSelector('.webhook-meetingEnded'); const meetingEndedElements = await page.locator('.webhook-meetingEnded').all(); expect(meetingEndedElements.length).toBe(1); + + // Verify meetingEnded webhook also matches room object + const meetingEndedWebhook = await getRoomFromWebhookStorage(page, roomId, 'meetingEnded'); + // Update actualRoom status to OPEN for comparison + actualRoom.status = MeetRoomStatus.OPEN; + expect(meetingEndedWebhook.data).toMatchObject(actualRoom); }); test('should successfully receive recordingStarted, recordingUpdated and recordingEnded webhooks', async ({ @@ -74,17 +92,53 @@ test.describe('Web Component E2E Tests', () => { const recordingStartedElements = await page.locator('.webhook-recordingStarted').all(); expect(recordingStartedElements.length).toBe(1); + // Verify recordingStarted webhook payload + const recordingStartedWebhook = await getRoomFromWebhookStorage(page, roomId, 'recordingStarted'); + expect(recordingStartedWebhook.event).toBe('recordingStarted'); + expect(recordingStartedWebhook.data).toBeDefined(); + expect(recordingStartedWebhook.data.recordingId).toBeDefined(); + + const recordingId = recordingStartedWebhook.data.recordingId; + + // Get the actual recording object from API and compare + const actualRecording = await getRecordingFromAPI(recordingId); + expect(recordingStartedWebhook.data).toMatchObject({ + ...actualRecording, + startDate: expect.any(Number), + status: expect.stringMatching(/active|starting/) + }); + // Update recording await page.waitForTimeout(2000); // Wait for a bit before updating await page.waitForSelector('.webhook-recordingUpdated'); const recordingUpdatedElements = await page.locator('.webhook-recordingUpdated').all(); expect(recordingUpdatedElements.length).toBe(1); + // Verify recordingUpdated webhook payload + const recordingUpdatedWebhook = await getRoomFromWebhookStorage(page, roomId, 'recordingUpdated'); + expect(recordingUpdatedWebhook.event).toBe('recordingUpdated'); + expect(recordingUpdatedWebhook.data).toBeDefined(); + expect(recordingUpdatedWebhook.data.recordingId).toBe(recordingId); + + // Get updated recording from API and compare + const updatedRecording = await getRecordingFromAPI(recordingId); + expect(recordingUpdatedWebhook.data).toMatchObject(updatedRecording); + // End recording await startStopRecording(page, 'stop'); await page.waitForSelector('.webhook-recordingEnded'); const recordingEndedElements = await page.locator('.webhook-recordingEnded').all(); expect(recordingEndedElements.length).toBe(1); + + // Verify recordingEnded webhook payload + const recordingEndedWebhook = await getRoomFromWebhookStorage(page, roomId, 'recordingEnded'); + expect(recordingEndedWebhook.event).toBe('recordingEnded'); + expect(recordingEndedWebhook.data).toBeDefined(); + expect(recordingEndedWebhook.data.recordingId).toBe(recordingId); + + // Get final recording state from API and compare + const endedRecording = await getRecordingFromAPI(recordingId); + expect(recordingEndedWebhook.data).toMatchObject(endedRecording); }); }); }); diff --git a/meet-ce/frontend/webcomponent/tests/helpers/function-helpers.ts b/meet-ce/frontend/webcomponent/tests/helpers/function-helpers.ts index 82895ed1..ed33764c 100644 --- a/meet-ce/frontend/webcomponent/tests/helpers/function-helpers.ts +++ b/meet-ce/frontend/webcomponent/tests/helpers/function-helpers.ts @@ -746,3 +746,94 @@ export const muteAudio = async (page: Page) => { export const sleep = (ms: number): Promise => { return new Promise((resolve) => setTimeout(resolve, ms)); }; + +/** + * Gets the webhook event object from webhook storage in sessionStorage. + * This allows comparing the actual room state with webhook payload data. + * @param page - Playwright page object + * @param roomId - The room ID to retrieve + * @param eventName - The webhook event name (e.g., 'meetingStarted') + * @returns The complete webhook event object from sessionStorage (with event, data, creationDate) + */ +export async function getRoomFromWebhookStorage(page: Page, roomId: string, eventName: string): Promise { + // Wait a bit to ensure the webhook is saved to localStorage + await page.waitForTimeout(500); + + const webhookEvent = await page.evaluate( + ({ roomId, eventName }) => { + const data = sessionStorage.getItem('webhookEventsByRoom'); + if (!data) { + console.log('No webhookEventsByRoom in sessionStorage'); + return null; + } + + const map = JSON.parse(data); + const events = map[roomId] || []; + + console.log(`Looking for event '${eventName}' in room '${roomId}'`); + console.log('Available events:', events.map((e: any) => e.event)); + + // Find the specific event and return the complete event object + const event = events.find((e: any) => e.event === eventName); + if (!event) { + console.log(`Event '${eventName}' not found`); + return null; + } + + return event; // Return the complete event { event, data, creationDate } + }, + { roomId, eventName } + ); + + if (!webhookEvent) { + throw new Error(`Webhook event '${eventName}' not found in sessionStorage for room '${roomId}'`); + } + + return webhookEvent; +} + +/** + * Gets a recording details from the Meet API + * @param recordingId - The recording ID to retrieve + * @returns The complete recording object from the API + */ +export async function getRecordingFromAPI(recordingId: string): Promise { + const response = await fetch(`${MEET_API_URL}/api/v1/recordings/${recordingId}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': MEET_API_KEY + } + }); + + if (!response.ok) { + const errorResponse = await response.json(); + console.error('Error getting recording:', errorResponse); + throw new Error(`Failed to get recording: ${response.status}`); + } + + return await response.json(); +} + +/** + * Gets the room details from the Meet API + * @param roomId + * @returns + */ +export async function getRoomFromAPI(roomId: string): Promise { + const response = await fetch(`${MEET_API_URL}/api/v1/rooms/${roomId}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': MEET_API_KEY + } + }); + + if (!response.ok) { + const errorResponse = await response.json(); + console.error('Error getting room:', errorResponse); + throw new Error(`Failed to get room: ${response.status}`); + } + + return await response.json(); +} diff --git a/testapp/public/js/webcomponent.js b/testapp/public/js/webcomponent.js index ae059311..97531019 100644 --- a/testapp/public/js/webcomponent.js +++ b/testapp/public/js/webcomponent.js @@ -24,7 +24,7 @@ const escapeHtml = (unsafe) => { .replace(/'/g, '''); }; const getWebhookEventsFromStorage = (roomId) => { - const data = localStorage.getItem('webhookEventsByRoom'); + const data = sessionStorage.getItem('webhookEventsByRoom'); if (!data) { return []; } @@ -32,23 +32,13 @@ const getWebhookEventsFromStorage = (roomId) => { return map[roomId] || []; }; const saveWebhookEventToStorage = (roomId, event) => { - const data = localStorage.getItem('webhookEventsByRoom'); + const data = sessionStorage.getItem('webhookEventsByRoom'); const map = data ? JSON.parse(data) : {}; if (!map[roomId]) { map[roomId] = []; } map[roomId].push(event); - localStorage.setItem('webhookEventsByRoom', JSON.stringify(map)); -}; -const clearWebhookEventsByRoom = (roomId) => { - const data = localStorage.getItem('webhookEventsByRoom'); - if (!data) - return; - const map = JSON.parse(data); - if (map[roomId]) { - map[roomId] = []; - localStorage.setItem('webhookEventsByRoom', JSON.stringify(map)); - } + sessionStorage.setItem('webhookEventsByRoom', JSON.stringify(map)); }; const shouldShowWebhook = (event) => { return (showAllWebhooksCheckbox === null || showAllWebhooksCheckbox === void 0 ? void 0 : showAllWebhooksCheckbox.checked) || event.data.roomId === roomId; @@ -65,10 +55,6 @@ const listenWebhookServerEvents = () => { return; } addWebhookEventElement(event); - // Clean up the previous events - const isMeetingEnded = event.event === 'meetingEnded'; - if (isMeetingEnded) - clearWebhookEventsByRoom(webhookRoomId); }); }; const renderStoredWebhookEvents = (roomId) => { diff --git a/testapp/public/ts/webcomponent.ts b/testapp/public/ts/webcomponent.ts index 58f39643..60ad96e5 100644 --- a/testapp/public/ts/webcomponent.ts +++ b/testapp/public/ts/webcomponent.ts @@ -31,7 +31,7 @@ const escapeHtml = (unsafe: string): string => { }; const getWebhookEventsFromStorage = (roomId: string): any[] => { - const data = localStorage.getItem('webhookEventsByRoom'); + const data = sessionStorage.getItem('webhookEventsByRoom'); if (!data) { return []; } @@ -41,26 +41,16 @@ const getWebhookEventsFromStorage = (roomId: string): any[] => { }; const saveWebhookEventToStorage = (roomId: string, event: any): void => { - const data = localStorage.getItem('webhookEventsByRoom'); + const data = sessionStorage.getItem('webhookEventsByRoom'); const map = data ? JSON.parse(data) : {}; if (!map[roomId]) { map[roomId] = []; } map[roomId].push(event); - localStorage.setItem('webhookEventsByRoom', JSON.stringify(map)); + sessionStorage.setItem('webhookEventsByRoom', JSON.stringify(map)); }; -const clearWebhookEventsByRoom = (roomId: string): void => { - const data = localStorage.getItem('webhookEventsByRoom'); - if (!data) return; - - const map = JSON.parse(data); - if (map[roomId]) { - map[roomId] = []; - localStorage.setItem('webhookEventsByRoom', JSON.stringify(map)); - } -}; const shouldShowWebhook = (event: any): boolean => { return showAllWebhooksCheckbox?.checked || event.data.roomId === roomId; @@ -81,10 +71,6 @@ const listenWebhookServerEvents = () => { } addWebhookEventElement(event); - - // Clean up the previous events - const isMeetingEnded = event.event === 'meetingEnded'; - if (isMeetingEnded) clearWebhookEventsByRoom(webhookRoomId); }); };