From 37dd4f07fdac2beb32e7b3d4b33c054e0d1f9afe Mon Sep 17 00:00:00 2001 From: zskhan Date: Tue, 11 Aug 2026 02:09:38 +0200 Subject: [PATCH 1/2] Add e2e test for meting notification to reduce manual testing effort --- .../meetingNotificationHost.tsx | 1 + .../e2e_tests/backend/brigRepository.e2e.ts | 10 +++ .../e2e_tests/specs/Meetings/meetings.spec.ts | 66 +++++++++++++++++++ apps/webapp/test/e2e_tests/test.fixtures.ts | 7 ++ 4 files changed, 84 insertions(+) create mode 100644 apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts diff --git a/apps/webapp/src/script/components/Meeting/meetingNotificationHost/meetingNotificationHost.tsx b/apps/webapp/src/script/components/Meeting/meetingNotificationHost/meetingNotificationHost.tsx index dd3f77be2cb..915e19daa66 100644 --- a/apps/webapp/src/script/components/Meeting/meetingNotificationHost/meetingNotificationHost.tsx +++ b/apps/webapp/src/script/components/Meeting/meetingNotificationHost/meetingNotificationHost.tsx @@ -90,6 +90,7 @@ export const MeetingNotificationHost = ({isStandalone}: MeetingNotificationHostP css={meetingNotificationHostButtonStyles} aria-expanded={isExpanded} aria-controls="meeting-notification-list" + data-uie-name="meeting-notification-expand" onClick={() => useMeetingNotificationStore.getState().setIsExpanded(!isExpanded)} > diff --git a/apps/webapp/test/e2e_tests/backend/brigRepository.e2e.ts b/apps/webapp/test/e2e_tests/backend/brigRepository.e2e.ts index d36ec986208..6abc357fafa 100644 --- a/apps/webapp/test/e2e_tests/backend/brigRepository.e2e.ts +++ b/apps/webapp/test/e2e_tests/backend/brigRepository.e2e.ts @@ -80,6 +80,16 @@ export class BrigRepositoryE2E { }); } + public async enableMeetingsFeature(teamId: string) { + await this.axiosInstance.patch(`i/teams/${teamId}/features/meetings`, { + status: 'enabled', + }); + } + + public async unlockMeetingsFeature(teamId: string) { + await this.axiosInstance.put(`i/teams/${teamId}/features/meetings/unlocked`, {}); + } + public async configureMLSFeature( teamId: string, config: { diff --git a/apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts b/apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts new file mode 100644 index 00000000000..e4c01262f2b --- /dev/null +++ b/apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts @@ -0,0 +1,66 @@ +/* + * Wire + * Copyright (C) 2026 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +import type {Page} from '@playwright/test'; + +import {PageManager} from 'test/e2e_tests/pageManager'; +import {expect, LOGIN_TIMEOUT, test} from 'test/e2e_tests/test.fixtures'; + +const loginWithMeetingsEnabled = (user: {email: string; password: string}) => async (page: Page) => { + const pageManager = PageManager.from(page); + const {pages, components} = pageManager.webapp; + await pageManager.openLoginPage(); + await pages.login().login(user); + await components.conversationSidebar().sidebar.waitFor({state: 'visible', timeout: LOGIN_TIMEOUT}); + const clientUrl = new URL('/?enabled-features=meetings#/clients', page.url()); + await page.goto(clientUrl.href, {waitUntil: 'domcontentloaded'}); + await expect.poll(() => new URL(page.url()).searchParams.get('enabled-features')).toBe('meetings'); + await components.conversationSidebar().sidebar.waitFor({state: 'visible', timeout: LOGIN_TIMEOUT}); +}; + +test('single participant meeting notification', async ({createUser, createTeam, createPage}) => { + const member = await createUser(); + const team = await createTeam('Meetings', {users: [member], features: {meetings: true}}); + const owner = team.owner; + + const [ownerPage, memberPage] = await Promise.all([ + createPage(loginWithMeetingsEnabled(owner)), + createPage(loginWithMeetingsEnabled(member)), + ]); + + await ownerPage.locator('[data-uie-name="go-meetings"]').click(); + await ownerPage.locator('[data-uie-name="schedule-meeting"]').click(); + + const modal = ownerPage.locator('[data-uie-name="schedule-meeting-modal"]'); + await modal.locator('[data-uie-name="schedule-meeting-title"]').fill('Single participant meeting'); + + const participants = modal.locator('[data-uie-name="schedule-meeting-participants"]'); + await participants.locator('[data-uie-name="schedule-meeting-participants-input"]').fill(member.fullName); + await ownerPage.locator('[data-uie-name="item-user"]').filter({hasText: member.fullName}).click(); + + await modal.locator('[data-uie-name="schedule-meeting-modal-submit"]').click(); + + const host = memberPage.locator('[data-uie-name="meeting-notification-host"]'); + await expect.poll(() => host.count(), {timeout: 30_000}).toBe(1); + await expect(host).toBeVisible(); + await host.getByRole('button', {name: 'Expand'}).click(); + + const cards = host.locator('[data-uie-name^="meeting-notification-card-"]'); + await expect(cards).toHaveCount(1); + await expect(cards).toContainText('Update: Single participant meeting'); +}); diff --git a/apps/webapp/test/e2e_tests/test.fixtures.ts b/apps/webapp/test/e2e_tests/test.fixtures.ts index 004cd4ef3af..ae893d05e0b 100644 --- a/apps/webapp/test/e2e_tests/test.fixtures.ts +++ b/apps/webapp/test/e2e_tests/test.fixtures.ts @@ -203,6 +203,7 @@ export const createTeam = async ( features?: { conferenceCalling?: boolean; channels?: boolean; + meetings?: boolean; mls?: boolean | Parameters[1]; cells?: boolean; }; @@ -261,6 +262,12 @@ export const createTeam = async ( await api.waitForFeatureToBeEnabled(FEATURE_KEY.CHANNELS, teamId, owner.token); } + if (options.features.meetings) { + await api.brig.unlockMeetingsFeature(teamId); + await api.brig.enableMeetingsFeature(teamId); + await api.waitForFeatureToBeEnabled(FEATURE_KEY.MEETINGS, teamId, owner.token); + } + if (options.features.cells) { await api.brig.unlockCellsFeature(teamId); await api.brig.enableCells(teamId); From 594d1e14224eef44c6b4e28ac30bdab05dc97bea Mon Sep 17 00:00:00 2001 From: zskhan Date: Tue, 11 Aug 2026 11:00:09 +0200 Subject: [PATCH 2/2] Scope item-user locator, instead of English translation use testId --- apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts b/apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts index e4c01262f2b..7aa1251aebc 100644 --- a/apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts +++ b/apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts @@ -51,14 +51,15 @@ test('single participant meeting notification', async ({createUser, createTeam, const participants = modal.locator('[data-uie-name="schedule-meeting-participants"]'); await participants.locator('[data-uie-name="schedule-meeting-participants-input"]').fill(member.fullName); - await ownerPage.locator('[data-uie-name="item-user"]').filter({hasText: member.fullName}).click(); + await modal.locator('[data-uie-name="item-user"]').filter({hasText: member.fullName}).click(); await modal.locator('[data-uie-name="schedule-meeting-modal-submit"]').click(); + await expect(modal).toBeHidden(); const host = memberPage.locator('[data-uie-name="meeting-notification-host"]'); await expect.poll(() => host.count(), {timeout: 30_000}).toBe(1); await expect(host).toBeVisible(); - await host.getByRole('button', {name: 'Expand'}).click(); + await host.getByTestId('meeting-notification-expand').click(); const cards = host.locator('[data-uie-name^="meeting-notification-card-"]'); await expect(cards).toHaveCount(1);