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..7aa1251aebc
--- /dev/null
+++ b/apps/webapp/test/e2e_tests/specs/Meetings/meetings.spec.ts
@@ -0,0 +1,67 @@
+/*
+ * 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 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.getByTestId('meeting-notification-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);