diff --git a/playwright.config.ts b/playwright.config.ts index 04a4dccda490..4373e7eb06c6 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -10,12 +10,21 @@ export default defineConfig({ expect: { timeout: 10_000 }, use: { baseURL: 'https://meet.internxt.com/', + permissions: ['camera', 'microphone'], + launchOptions: { + args: [ + '--use-fake-ui-for-media-stream', + '--use-fake-device-for-media-stream', + ], + }, ignoreHTTPSErrors: true, trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure', - }, + +}, projects: [ { name: 'chromium', use: { browserName: 'chromium' } }, + ], }); \ No newline at end of file diff --git a/playwright/helpers/timer.ts b/playwright/helpers/timer.ts new file mode 100644 index 000000000000..5b778b148f3c --- /dev/null +++ b/playwright/helpers/timer.ts @@ -0,0 +1,56 @@ +import { expect, Locator } from '@playwright/test'; + +const DEBUG = process.env.DEBUG_TESTS === 'true'; + +function parseTimer(value: string): number { + const [minutes, seconds] = value.split(':').map(Number); + return minutes * 60 + seconds; +} + +export async function expectTimerToIncrease(timer: Locator) { + await expect(timer).toBeVisible(); + + const firstText = (await timer.textContent())?.trim() ?? ''; + expect(firstText).toMatch(/^\d{1,2}:\d{2}$/); + + const firstSeconds = parseTimer(firstText); + + // wait until timer changes + await expect(timer).not.toHaveText(firstText, { timeout: 5000 }); + + const secondText = (await timer.textContent())?.trim() ?? ''; + expect(secondText).toMatch(/^\d{1,2}:\d{2}$/); + + const secondSeconds = parseTimer(secondText); + + expect( + secondSeconds, + `Timer should increase. First: ${firstText}, Second: ${secondText}` + ).toBeGreaterThan(firstSeconds); +} + +export async function expectTimerRunning(timer: Locator, checks = 3) { + await expect(timer).toBeVisible(); + + let previous = 0; + + for (let i = 0; i < checks; i++) { + const value = (await timer.textContent())?.trim() ?? ''; + if (DEBUG) console.log(`Timer check ${i}:`, value); + + expect(value).toMatch(/^\d{1,2}:\d{2}$/); + + const seconds = parseTimer(value); + + if (i > 0) { + expect( + seconds, + `Timer should keep running. Previous: ${previous}, Current: ${seconds}` + ).toBeGreaterThanOrEqual(previous); + } + + previous = seconds; + + await timer.page().waitForTimeout(1000); + } +} \ No newline at end of file diff --git a/playwright/tests/create-meeting-flow.spec.ts b/playwright/tests/create-meeting-flow.spec.ts new file mode 100644 index 000000000000..2573d67c068d --- /dev/null +++ b/playwright/tests/create-meeting-flow.spec.ts @@ -0,0 +1,73 @@ +import { test, expect } from '@playwright/test'; +import { login } from '../helpers/auth'; +import { MeetHomePage } from '../pages/meet-home-page'; +import { expectTimerToIncrease, expectTimerRunning } from '../helpers/timer'; + +const ultimateEmail = process.env.PLAYWRIGHT_ULTIMATE_EMAIL!; +const ultimatePassword = process.env.PLAYWRIGHT_ULTIMATE_PASSWORD!; +const ultimateName = process.env.PLAYWRIGHT_ULTIMATE_NAME!; +expect(ultimateName).toBeTruthy(); + + +test.beforeAll(() => { + expect(ultimateEmail).toBeTruthy(); + expect(ultimatePassword).toBeTruthy(); + expect(ultimateName).toBeTruthy(); +}); + +test('Ultimate user can create a meeting', async ({ page }) => { + const meetHome = new MeetHomePage(page); + + await test.step('GIVEN Ultimate user is authenticated', async () => { + await login(page, ultimateEmail, ultimatePassword); + }); + + await test.step('WHEN user clicks New meeting', async () => { + await meetHome.expectUserLoggedIn(); + await meetHome.expectNewMeetingVisible(); + await meetHome.newMeetingButton.click(); + }); + + await test.step('THEN prejoin modal is displayed ', async () => { +await expect(page.getByText('Your meeting is private')).toBeVisible(); +await expect(page.getByRole('textbox', { name: /enter your name/i })).toHaveValue(ultimateName); +await expect(page.getByText('Up to 5 participants')).toBeVisible(); + }); + +await test.step('WHEN user confirms meeting creation', async () => { + // TODO: temporary workaround nth(1)— UI currently renders two "New Meeting" buttons. Should be one. + const newMeetingButtons = page.getByRole('button', { name: 'New Meeting' }); + await expect(newMeetingButtons).toHaveCount(2); + await newMeetingButtons.nth(1).click(); +}); + +await test.step('THEN moderator message appears briefly', async () => { + const moderatorToast = page.getByText(/you're now a moderator/i); + + await expect(moderatorToast).toBeVisible({ timeout: 5000 }); + await expect(moderatorToast).toBeHidden({ timeout: 5000 }); +}); + +await test.step('AND meeting UI is visible', async () => { + await expect(page).toHaveURL(/meet\.internxt\.com\/[0-9a-fA-F-]{36}/); + await expect(page.getByRole('status')).toBeVisible(); + await expect(page.getByRole('button', { name: 'Gallery' })).toBeVisible(); +}); + +await test.step('AND timer is visible', async () => { + const timer = page.locator('text=/^\\d{1,2}:\\d{2}$/').first(); + await expect(timer).toBeVisible(); + + await expectTimerToIncrease(timer); + await expectTimerRunning(timer); +}); + +await test.step('AND meeting controls are visible', async () => { + const defaultControls = page.locator('[data-circle-button="default"]'); + const leaveControl = page.locator('[data-circle-button="cancel"]'); + + // TODO: temporary workaround until stable data-testid attributes are added + await expect(defaultControls).toHaveCount(5); + await expect(leaveControl).toHaveCount(1); +}); +})