Skip to content

Commit b251829

Browse files
authored
chore: Implement TC-8631 critical flow test [WPB-18358] (#19167)
* chore: Implement TC-8631 critical flow test [WPB-18358] * Fixed applock assertions
1 parent ac3a36a commit b251829

File tree

8 files changed

+198
-21
lines changed

8 files changed

+198
-21
lines changed

test/e2e_tests/backend/conversationRepository.e2e.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020
import {BackendClientE2E} from './backendClient.e2e';
2121

2222
export class ConversationRepositoryE2E extends BackendClientE2E {
23-
async inviteToConversation(inviteeId: string, inviterToken: string, teamId: string, conversationName: string) {
23+
async inviteToConversation(
24+
inviteeIds: string | string[],
25+
inviterToken: string,
26+
teamId: string,
27+
conversationName: string,
28+
) {
2429
await this.axiosInstance.post(
2530
'conversations',
2631
{
@@ -33,7 +38,7 @@ export class ConversationRepositoryE2E extends BackendClientE2E {
3338
teamid: teamId,
3439
},
3540
qualified_users: [],
36-
users: [inviteeId],
41+
users: [inviteeIds].flat(),
3742
},
3843
{
3944
headers: {

test/e2e_tests/backend/teamRepository.e2e.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import {BackendClientE2E} from './backendClient.e2e';
2121

22+
import {Service} from '../data/serviceInfo';
2223
import {User} from '../data/user';
2324

2425
export class TeamRepositoryE2E extends BackendClientE2E {
@@ -39,17 +40,17 @@ export class TeamRepositoryE2E extends BackendClientE2E {
3940
throw new Error('No teams found for the user');
4041
}
4142

42-
async inviteUserToTeam(teamId: string, emailOfInvitee: string, inviterName: string, token: string): Promise<string> {
43+
async inviteUserToTeam(emailOfInvitee: string, teamOwner: User): Promise<string> {
4344
const response = this.axiosInstance.post(
44-
`teams/${teamId}/invitations`,
45+
`teams/${teamOwner.teamId}/invitations`,
4546
{
46-
inviterName: inviterName,
47+
inviterName: `${teamOwner.firstName} ${teamOwner.lastName}`,
4748
role: 'member',
4849
email: emailOfInvitee,
4950
},
5051
{
5152
headers: {
52-
Authorization: `Bearer ${token}`,
53+
Authorization: `Bearer ${teamOwner.token}`,
5354
'Content-Type': 'application/json',
5455
},
5556
},
@@ -58,6 +59,23 @@ export class TeamRepositoryE2E extends BackendClientE2E {
5859
return (await response).data.id;
5960
}
6061

62+
async addServiceToTeamWhitelist(teamId: string, service: Service, token: string) {
63+
await this.axiosInstance.post(
64+
`teams/${teamId}/services/whitelist`,
65+
{
66+
provider: service.providerId,
67+
id: service.serviceId,
68+
whitelisted: true,
69+
},
70+
{
71+
headers: {
72+
Authorization: `Bearer ${token}`,
73+
'Content-Type': 'application/json',
74+
},
75+
},
76+
);
77+
}
78+
6179
async deleteTeam(user: User, teamId: string) {
6280
await this.axiosInstance.request({
6381
url: `teams/${teamId}`,

test/e2e_tests/criticalFlow.spec.ts

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import {faker} from '@faker-js/faker';
2121

22+
import {Services} from './data/serviceInfo';
2223
import {getUser, User} from './data/user';
2324
import {AccountPage} from './pages/account.page';
2425
import {AppLockModal} from './pages/appLock.modal';
@@ -30,6 +31,7 @@ import {DataShareConsentModal} from './pages/dataShareConsent.modal';
3031
import {DeleteAccountModal} from './pages/deleteAccount.modal';
3132
import {DeleteAccountPage} from './pages/deleteAccount.page';
3233
import {EmailVerificationPage} from './pages/emailVerification.page';
34+
import {GroupCreationPage} from './pages/groupCreation.page';
3335
import {LoginPage} from './pages/login.page';
3436
import {MarketingConsentModal} from './pages/marketingConsent.modal';
3537
import {OutgoingConnectionPage} from './pages/outgoingConnection.page';
@@ -46,6 +48,69 @@ const webAppPath = process.env.WEBAPP_URL ?? '';
4648
const createdUsers: User[] = [];
4749
const createdTeams: Map<User, string> = new Map();
4850

51+
test('Team owner adds whole team to an all team chat', {tag: ['@TC-8631', '@crit-flow']}, async ({page, api}) => {
52+
// Generating test data
53+
const owner = getUser();
54+
const member1 = getUser();
55+
const member2 = getUser();
56+
const teamName = 'Critical';
57+
const conversationName = 'Crits';
58+
59+
// Initializing page objects
60+
const singleSignOnPage = new SingleSignOnPage(page);
61+
const loginPage = new LoginPage(page);
62+
const dataShareConsentModal = new DataShareConsentModal(page);
63+
const conversationListPage = new ConversationListPage(page);
64+
const groupCreationPage = new GroupCreationPage(page);
65+
const startUIPage = new StartUIPage(page);
66+
67+
await test.step('Preconditions: Creating preconditions for the test via API', async () => {
68+
await api.createTeamOwner(owner, teamName);
69+
owner.teamId = await api.team.getTeamIdForUser(owner);
70+
createdTeams.set(owner, owner.teamId);
71+
const invitationIdForMember1 = await api.team.inviteUserToTeam(member1.email, owner);
72+
const invitationCodeForMember1 = await api.brig.getTeamInvitationCodeForEmail(owner.teamId, invitationIdForMember1);
73+
74+
const invitationIdForMember2 = await api.team.inviteUserToTeam(member2.email, owner);
75+
const invitationCodeForMember2 = await api.brig.getTeamInvitationCodeForEmail(owner.teamId, invitationIdForMember2);
76+
77+
await api.createPersonalUser(member1, invitationCodeForMember1);
78+
await api.createPersonalUser(member2, invitationCodeForMember2);
79+
});
80+
81+
await test.step('Team owner logs in into a client and creates group conversation', async () => {
82+
await page.goto(webAppPath);
83+
await singleSignOnPage.enterEmailOnSSOPage(owner.email);
84+
await loginPage.inputPassword(owner.password);
85+
await loginPage.clickSignInButton();
86+
await dataShareConsentModal.clickDecline();
87+
});
88+
89+
await test.step('Team owner adds a service to newly created group', async () => {
90+
await api.team.addServiceToTeamWhitelist(owner.teamId!, Services.POLL_SERVICE, owner.token!);
91+
});
92+
93+
await test.step('Team owner adds team members to a group', async () => {
94+
await conversationListPage.clickCreateGroup();
95+
await groupCreationPage.setGroupName(conversationName);
96+
await startUIPage.selectUsers([member1.username, member2.username]);
97+
await groupCreationPage.clickCreateGroupButton();
98+
expect(await conversationListPage.isConversationItemVisible(conversationName)).toBeTruthy();
99+
});
100+
101+
// Steps below require [WPB-18075] and [WPB-17547]
102+
103+
await test.step('All group participants send messages in a group', async () => {});
104+
105+
await test.step('Team owner and group members react on received messages with reactions', async () => {});
106+
107+
await test.step('All group participants make sure they see reactions from other group participants', async () => {});
108+
109+
await test.step('Team owner removes one group member from a group', async () => {});
110+
111+
await test.step('Team owner removes a service from a group', async () => {});
112+
});
113+
49114
test('Account Management', {tag: ['@TC-8639', '@crit-flow']}, async ({page, api}) => {
50115
test.slow(); // Increasing test timeout to 90 seconds to accommodate the full flow
51116

@@ -73,12 +138,7 @@ test('Account Management', {tag: ['@TC-8639', '@crit-flow']}, async ({page, api}
73138
}
74139
const teamId = await api.team.getTeamIdForUser(owner);
75140
createdTeams.set(owner, teamId);
76-
const invitationId = await api.team.inviteUserToTeam(
77-
teamId,
78-
member.email,
79-
`${owner.firstName} ${owner.lastName}`,
80-
owner.token,
81-
);
141+
const invitationId = await api.team.inviteUserToTeam(member.email, owner);
82142
const invitationCode = await api.brig.getTeamInvitationCodeForEmail(teamId, invitationId);
83143

84144
await api.createPersonalUser(member, invitationCode);
@@ -115,7 +175,6 @@ test('Account Management', {tag: ['@TC-8639', '@crit-flow']}, async ({page, api}
115175
await test.step('Member verifies if applock is working', async () => {
116176
await page.reload();
117177
expect(await appLockModal.isVisible());
118-
expect(await conversationListPage.isConversationItemVisible(conversationName)).toBeFalsy();
119178
expect(await appLockModal.getAppLockModalHeader()).toContain('Enter passcode to unlock');
120179
expect(await appLockModal.getAppLockModalText()).toContain('Passcode');
121180

@@ -209,8 +268,7 @@ test('Personal Account Lifecycle', {tag: ['@TC-8638', '@crit-flow']}, async ({pa
209268

210269
await test.step('Personal user A searches for other personal user B', async () => {
211270
await conversationSidebar.clickConnectButton();
212-
await startUIPage.searchForUser(userB.username);
213-
await startUIPage.clickUserFromSearchResults(userB.username);
271+
await startUIPage.selectUser(userB.username);
214272
expect(await userProfileModal.isVisible());
215273
});
216274

test/e2e_tests/data/serviceInfo.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2025 Wire Swiss GmbH
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see http://www.gnu.org/licenses/.
17+
*
18+
*/
19+
20+
export class Service {
21+
constructor(
22+
public providerId: string,
23+
public serviceId: string,
24+
) {}
25+
}
26+
27+
export const Services = {
28+
POLL_SERVICE: new Service('d1e52fa0-46bc-46fa-acc1-95bd91735de1', '40085205-4499-4cd7-a093-ca7d3c1d8b21'),
29+
ECHO_SERVICE: new Service('d64af9ae-e0c5-4ce6-b38a-02fd9363b54c', 'd693bd64-79ae-4970-ad12-4df49cfe4038'),
30+
TRACKER_SERVICE: new Service('d64af9ae-e0c5-4ce6-b38a-02fd9363b54c', '7ba4aac9-1bbb-41bd-b782-b57157665157'),
31+
};

test/e2e_tests/pages/appLock.modal.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ export class AppLockModal {
5353
}
5454

5555
async isVisible() {
56+
await this.appLockModal.waitFor({state: 'visible'});
5657
return await this.appLockModal.isVisible();
5758
}
5859

5960
async isHidden() {
61+
await this.appLockModal.waitFor({state: 'hidden'});
6062
return await this.appLockModal.isHidden();
6163
}
6264

test/e2e_tests/pages/conversationList.page.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,23 @@ export class ConversationListPage {
2525
readonly page: Page;
2626

2727
readonly blockConversationMenuButton: Locator;
28+
readonly createGroupButton: Locator;
2829

2930
constructor(page: Page) {
3031
this.page = page;
3132

3233
this.blockConversationMenuButton = page.locator('#btn-block[data-uie-name="conversation-list-options-menu"]');
34+
this.createGroupButton = page.locator(
35+
'[data-uie-name="conversation-list-header"] [data-uie-name="go-create-group"]',
36+
);
3337
}
3438

3539
async isConversationItemVisible(conversationName: string) {
3640
const conversation = this.getConversationLocator(conversationName);
41+
await conversation.waitFor({state: 'visible'});
3742
return await conversation.isVisible();
3843
}
3944

40-
async isConversationVisible(conversationName: string) {
41-
return await this.getConversationLocator(conversationName).isVisible();
42-
}
43-
4445
async isConversationBlocked(conversationName: string) {
4546
return await this.getConversationLocator(conversationName).locator('[data-uie-name="status-blocked"]').isVisible();
4647
}
@@ -58,6 +59,10 @@ export class ConversationListPage {
5859
await this.blockConversationMenuButton.click();
5960
}
6061

62+
async clickCreateGroup() {
63+
await this.createGroupButton.click();
64+
}
65+
6166
private getConversationLocator(conversationName: string) {
6267
return this.page.locator(`[data-uie-name='item-conversation'][data-uie-value='${escapeHtml(conversationName)}']`);
6368
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2025 Wire Swiss GmbH
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see http://www.gnu.org/licenses/.
17+
*
18+
*/
19+
20+
import {Page, Locator} from '@playwright/test';
21+
22+
export class GroupCreationPage {
23+
readonly page: Page;
24+
25+
readonly groupCreationModal: Locator;
26+
readonly groupNameInput: Locator;
27+
readonly nextButton: Locator;
28+
readonly createGroupButton: Locator;
29+
30+
constructor(page: Page) {
31+
this.page = page;
32+
33+
this.groupCreationModal = page.locator('#group-creation-modal');
34+
this.groupNameInput = page.locator('[data-uie-name="enter-group-name"]');
35+
this.nextButton = page.locator('[data-uie-name="go-next"]');
36+
this.createGroupButton = page.locator('[data-uie-name="do-create-group"]');
37+
}
38+
39+
async setGroupName(name: string) {
40+
await this.groupNameInput.fill(name);
41+
await this.nextButton.click();
42+
}
43+
44+
async clickCreateGroupButton() {
45+
await this.createGroupButton.click();
46+
}
47+
}

test/e2e_tests/pages/startUI.page.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,22 @@ export class StartUIPage {
3232
this.searchResults = page.locator('[data-uie-name="item-user"] [data-uie-name="status-username"]');
3333
}
3434

35-
async searchForUser(username: string) {
35+
async selectUsers(usernames: string[]) {
36+
for (const username of usernames) {
37+
await this.selectUser(username);
38+
}
39+
}
40+
41+
async selectUser(username: string) {
42+
await this.searchForUser(username);
43+
await this.clickUserFromSearchResults(username);
44+
}
45+
46+
private async searchForUser(username: string) {
3647
await this.searchInput.fill(username);
3748
}
3849

39-
async clickUserFromSearchResults(username: string) {
50+
private async clickUserFromSearchResults(username: string) {
4051
await this.searchResults.first().waitFor({state: 'visible'});
4152
for (const result of await this.searchResults.all()) {
4253
const text = await result.textContent();

0 commit comments

Comments
 (0)