Skip to content

Commit b17d304

Browse files
JacquelineLehnerxdubxthisisamir98
authored
test: Write regression tests for blocking users in Playwright [WPB-19940] (#19700)
* add tests from testiny * test: add test for blocking a user * test: connect users manually * test: add regression tests for blocking functionality * test: add regression tests for blocking functionality This commit migrates the regression tests for the 'Block' functionality from Testiny to the new Playwright framework, as described in WPB-19940. Tests related to known bugs (WPB-21052, WPB-18226) are marked as `test.fixme` and will be enabled once the underlying issues are resolved. Refs: WPB-19940 * refactor(block): address SonarQube issues for duplication and reliability Extracts duplicated login logic from `beforeEach` and `no-setup` tests into a new `loginAndSetup` helper function. Also extracts the manual user connection flow into a `connectUsersManually` helper function to reduce duplication in @TC-142 and @TC-144. These changes resolve the "Duplication on New Code" error reported by SonarQube. Additionally, the `api` fixture initialization is simplified to resolve the "Reliability Rating" issue. Refs: WPB-19940 * refactor(block): extract blocking logic into shared userActions helpers SonarQube was still reporting significant code duplication in `block.spec.ts`. This commit resolves this by extracting the common user-blocking workflows into three dedicated helper functions in `utils/userActions.ts`: * `blockUserFromConversationList` * `blockUserFromProfileView` * `blockUserFromOpenGroupProfileView` The `block.spec.ts` file is updated to use these new helpers, resolving the duplication issue. Refs: WPB-19940 --------- Co-authored-by: Klaus Boldt <[email protected]> Co-authored-by: Amir Ghezelbash <[email protected]>
1 parent 2870574 commit b17d304

File tree

5 files changed

+452
-1
lines changed

5 files changed

+452
-1
lines changed

test/e2e_tests/pageManager/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {BlockWarningModal} from './webapp/modals/blockWarning.modal';
3737
import {CallNotEstablishedModal} from './webapp/modals/callNotEstablished.modal';
3838
import {CancelRequestModal} from './webapp/modals/cancelRequest.modal';
3939
import {ConfirmLogoutModal} from './webapp/modals/confirmLogout.modal';
40+
import {ConversationNotConnectedModal} from './webapp/modals/conversationNotConnected.modal';
4041
import {CopyPasswordModal} from './webapp/modals/copyPassword.modal';
4142
import {CreatGuestLinkModal} from './webapp/modals/createGuestLink.modal';
4243
import {DataShareConsentModal} from './webapp/modals/dataShareConsent.modal';
@@ -215,6 +216,8 @@ export class PageManager {
215216
this.getOrCreate('webapp.modals.cellsFileDetailView', () => new CellsFileDetailViewModal(this.page)),
216217
cancelRequest: () =>
217218
this.getOrCreate('webapp.modals.cancelRequestModal', () => new CancelRequestModal(this.page)),
219+
conversationNotConnected: () =>
220+
this.getOrCreate('webapp.modals.conversationNotConnected', () => new ConversationNotConnectedModal(this.page)),
218221
},
219222
components: {
220223
contactList: () => this.getOrCreate('webapp.components.ContactList', () => new ContactList(this.page)),
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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} from '@playwright/test';
21+
22+
import {BaseModal} from './base.modal';
23+
24+
export class ConversationNotConnectedModal extends BaseModal {
25+
constructor(page: Page) {
26+
super(page, "[data-uie-name='modal-template-acknowledge']");
27+
}
28+
}

test/e2e_tests/pageManager/webapp/modals/userProfile.modal.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export class UserProfileModal {
2727
readonly modal: Locator;
2828
readonly connectButton: Locator;
2929
readonly startConversationButton: Locator;
30+
readonly unblockButton: Locator;
3031

3132
constructor(page: Page) {
3233
this.page = page;
@@ -37,6 +38,9 @@ export class UserProfileModal {
3738
this.startConversationButton = page.locator(
3839
`${selectByDataAttribute('modal-user-profile')} ${selectByDataAttribute('start-conversation')}`,
3940
);
41+
this.unblockButton = page.locator(
42+
`${selectByDataAttribute('modal-user-profile')} ${selectByDataAttribute('do-unblock')}`,
43+
);
4044
}
4145

4246
async isVisible() {
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
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 {test as base, expect} from '@playwright/test';
21+
22+
import {getUser, User} from 'test/e2e_tests/data/user';
23+
import {PageManager} from 'test/e2e_tests/pageManager';
24+
import {addCreatedUser, tearDownAll} from 'test/e2e_tests/utils/tearDown.util';
25+
import {
26+
loginAndSetup,
27+
connectUsersManually,
28+
blockUserFromConversationList,
29+
blockUserFromProfileView,
30+
blockUserFromOpenGroupProfileView,
31+
} from 'test/e2e_tests/utils/userActions';
32+
33+
import {ApiManagerE2E} from '../../backend/apiManager.e2e';
34+
35+
type testcaseFixtures = {
36+
pageManager: PageManager;
37+
api: ApiManagerE2E;
38+
userBPageManager: PageManager;
39+
};
40+
41+
export const test = base.extend<testcaseFixtures>({
42+
pageManager: async ({page}, use) => {
43+
await use(PageManager.from(page));
44+
},
45+
46+
api: new ApiManagerE2E(),
47+
48+
userBPageManager: async ({browser}, use) => {
49+
const context = await browser.newContext();
50+
const page = await context.newPage();
51+
const manager = PageManager.from(page);
52+
await use(manager);
53+
await context.close();
54+
},
55+
});
56+
57+
export {expect} from '@playwright/test';
58+
59+
// Generating test users
60+
let userA: User;
61+
let userB: User;
62+
63+
test.describe('Block', () => {
64+
test.beforeEach(async ({pageManager: userAPageManager, userBPageManager, api}, testInfo) => {
65+
userA = getUser();
66+
userB = getUser();
67+
68+
// Step 1: Create and log in users
69+
await test.step('Preconditions: Creating test users via API', async () => {
70+
await api.createPersonalUser(userA);
71+
addCreatedUser(userA);
72+
73+
await api.createPersonalUser(userB);
74+
addCreatedUser(userB);
75+
});
76+
77+
await test.step('Preconditions: Signing in User A and User B', async () => {
78+
await Promise.all([loginAndSetup(userA, userAPageManager), loginAndSetup(userB, userBPageManager)]);
79+
});
80+
81+
// Step 2: Connect users (Conditional)
82+
// Skipped if '@no-setup' is present, as these tests require a manual connection
83+
if (!testInfo.tags.includes('@no-setup')) {
84+
await test.step('Preconditions: Connecting users via API', async () => {
85+
await api.connectUsers(userA, userB);
86+
});
87+
}
88+
});
89+
90+
test(
91+
'I want to cancel blocking a 1on1 conversation from conversation list 0',
92+
{tag: ['@TC-137', '@regression']},
93+
async ({pageManager: userAPageManager}) => {
94+
const {pages: userAPages, modals: userAModals} = userAPageManager.webapp;
95+
96+
await test.step('User A wants to cancel to block User B', async () => {
97+
// Step 1: User A opens conversation with User B
98+
await userAPages.conversationList().openConversation(userB.fullName);
99+
// Step 2: User A opens the options menu for user B
100+
await userAPages.conversationList().clickConversationOptions(userB.fullName);
101+
// Step 3: User A opens modal and clicks 'Block' button
102+
await userAPages.conversationList().clickBlockConversation();
103+
// Step 4: User A clicks 'Cancel' button
104+
await userAModals.blockWarning().clickCancel();
105+
// Step 5: Conversation is still present, and User A can open it
106+
await userAPages.conversationList().openConversation(userB.fullName);
107+
});
108+
},
109+
);
110+
111+
test.fixme(
112+
// TODO: blocked in relation to bug report [WPB-21052]
113+
'Verify you can block a person from profile view 0',
114+
{tag: ['@TC-140', '@regression']},
115+
async ({pageManager: userAPageManager}) => {
116+
await test.step('User A wants to block User B from profile view 0', async () => {
117+
await blockUserFromProfileView(userAPageManager, userB);
118+
119+
// TODO: Remaining steps/assertions of the test
120+
// Step 6: User A gets redirected back to conversation list
121+
// Step 7: Conversation with User B disappeared from main contact list
122+
// Step 8: Next contact of contact list from User A is selected
123+
});
124+
},
125+
);
126+
127+
test(
128+
'Verify you still receive messages from blocked person in a group chat 0',
129+
{tag: ['@TC-141', '@regression']},
130+
async ({pageManager: userAPageManager, userBPageManager}) => {
131+
const {pages: userAPages, components: userAComponents} = userAPageManager.webapp;
132+
const {pages: userBPages} = userBPageManager.webapp;
133+
const conversationName = 'Groupchat with User A and User B';
134+
135+
await test.step('Preconditions: Users A and B are in a group', async () => {
136+
await userAComponents.conversationSidebar().isPageLoaded();
137+
await userAPages.conversationList().clickCreateGroup();
138+
await userAPages.groupCreation().setGroupName(conversationName);
139+
await userAPages.startUI().selectUsers([userB.username]);
140+
await userAPages.groupCreation().clickCreateGroupButton();
141+
await userBPages.conversationList().openConversation(conversationName);
142+
});
143+
144+
// Step 1: User B sends message to group chat with User A
145+
await test.step('User B sends messages to group', async () => {
146+
await userBPages.conversation().sendMessage('message before block');
147+
});
148+
149+
// Step 2: User A blocks User B from group conversation
150+
await test.step('User A blocks User B from group conversation', async () => {
151+
// Ensures User A is in the group before blocking
152+
await userAPages.conversationList().openConversation(conversationName);
153+
await blockUserFromOpenGroupProfileView(userAPageManager, userB);
154+
});
155+
156+
// Step 3: User B writes second message to the group chat after being blocked by User A
157+
await test.step('User B sends messages to group', async () => {
158+
await userBPages.conversation().sendMessage('message after block');
159+
});
160+
161+
// Step 4: User A receives message from User B in Group Chat even though User B is blocked
162+
await test.step('User A receives message in group chat', async () => {
163+
await userAPages.conversationList().openConversation(conversationName);
164+
expect(await userAPages.conversation().messageCount()).toBe(2);
165+
});
166+
},
167+
);
168+
169+
test.fixme(
170+
// TODO: Bug [WPB-18226] Message is not visible in the conversation after sending it
171+
'Verify you can block and unblock user in 1on1 0',
172+
{tag: ['@TC-142', '@regression', '@no-setup']}, // @no-setup because otherwise 'New Device Modal' modal will show up which you cannot click away
173+
async ({pageManager: userAPageManager, userBPageManager}) => {
174+
const {pages: userAPages} = userAPageManager.webapp;
175+
const {pages: userBPages} = userBPageManager.webapp;
176+
177+
await test.step('Preconditions: User A connects with User B', async () => {
178+
await connectUsersManually(userA, userB, userAPageManager, userBPageManager);
179+
});
180+
181+
// Step 1: User A sends message to chat with User B
182+
await test.step('User A sends message 1:1 to User B', async () => {
183+
await userAPages.conversationList().openConversation(userB.fullName);
184+
await userAPages.conversation().sendMessage('message before block');
185+
});
186+
187+
// Step 2: User B receives message prior to blocking user A
188+
await test.step('User B receive 1:1 message from A', async () => {
189+
await userBPages.conversationList().openConversation(userA.fullName);
190+
expect(await userBPages.conversation().messageCount()).toBe(1);
191+
});
192+
193+
// Step 3: User B blocks User A 1:1
194+
await test.step('User B blocks User A in 1:1 conversation', async () => {
195+
await blockUserFromConversationList(userBPageManager, userA);
196+
});
197+
198+
// Step 4: User A writes second message 1:1 to User B after being blocked by User B
199+
await test.step('User A sends messages to chat with User B', async () => {
200+
await userAPages.conversationList().openConversation(userB.fullName);
201+
await userAPages.conversation().sendMessage('message after block');
202+
});
203+
204+
// Step 5: User B does not receive the message from User A
205+
await test.step('User B does not receive message from User A in 1:1', async () => {
206+
await userBPages.conversationList().openConversation(userA.fullName);
207+
expect(await userBPages.conversation().messageCount()).toBe(1);
208+
});
209+
210+
// Step 6: User B unblocks User A
211+
await test.step('User B does not receive message from User A in 1:1', async () => {
212+
await userBPages.startUI().selectUser(userA.fullName);
213+
});
214+
215+
// Step 7: User A sends a message to User B
216+
await test.step('User A sends a message to User B after getting unblocked by User B', async () => {
217+
await userAPages.conversationList().openConversation(userB.fullName);
218+
await userAPages.conversation().sendMessage('message after unblock');
219+
});
220+
221+
// Step 8: User B unblocks User A
222+
await test.step('User B receives message from User A in 1:1', async () => {
223+
await userBPages.conversationList().openConversation(userA.fullName);
224+
expect(await userBPages.conversation().messageCount()).toBe(2);
225+
});
226+
},
227+
);
228+
229+
test(
230+
'Verify you cannot add a person who blocked you to a group chat 0',
231+
{tag: ['@TC-143', '@regression']},
232+
async ({pageManager: userAPageManager, userBPageManager}) => {
233+
const {pages: userAPages, modals: userAModals, components: userAComponents} = userAPageManager.webapp;
234+
const conversationName = 'Groupchat with User A and User B';
235+
236+
// Step 1: User B blocks User A
237+
await test.step('User B blocks User A', async () => {
238+
await blockUserFromConversationList(userBPageManager, userA, {handleUnableToOpenModal: true});
239+
});
240+
241+
// Step 2: User A wants to add B to a group chat after being blocked by User B
242+
await test.step('Users A tries to add B to a group', async () => {
243+
await userAComponents.conversationSidebar().isPageLoaded();
244+
await userAPages.conversationList().clickCreateGroup();
245+
await userAPages.groupCreation().setGroupName(conversationName);
246+
await userAPages.startUI().selectUsers([userB.username]);
247+
await userAPages.groupCreation().clickCreateGroupButton();
248+
249+
// Step 3: Modal 'modalConversationNotConnectedMessageOne' is visible
250+
expect(userAModals.conversationNotConnected().isModalPresent()).toBeTruthy();
251+
});
252+
},
253+
);
254+
255+
test(
256+
'Verify you can block a user you sent a connection request from conversation list 0',
257+
{tag: ['@TC-144', '@regression', '@no-setup']}, // @no-setup because connection request must be sent manually to satisfy test specs
258+
async ({pageManager: userAPageManager, userBPageManager}) => {
259+
// Step 1: User A sends connection request to User B
260+
await test.step('Preconditions: User A connects with User B', async () => {
261+
await connectUsersManually(userA, userB, userAPageManager, userBPageManager);
262+
});
263+
264+
// Step 2: User A blocks User B from conversation list
265+
await test.step('User A blocks User B from conversation list', async () => {
266+
await blockUserFromConversationList(userAPageManager, userB);
267+
});
268+
},
269+
);
270+
271+
// TODO: blocked in relation to bug report [WPB-21052]
272+
// TODO: conversation is still present in conversationList after blocking
273+
test.fixme(
274+
'Verify I can block a 1on1 conversation from conversation list 0',
275+
{tag: ['@TC-145', '@regression']},
276+
async ({pageManager: userAPageManager}) => {
277+
const {pages: userAPages, modals: userAModals} = userAPageManager.webapp;
278+
279+
await test.step('User A wants to block User B', async () => {
280+
// Step 1: User A opens conversation with User B
281+
await userAPages.conversationList().openConversation(userB.fullName);
282+
// Step 2: User A opens the options menu for user B
283+
await userAPages.conversationList().clickConversationOptions(userB.fullName);
284+
// Step 3: User A opens modal and clicks 'Block' button
285+
await userAPages.conversationList().clickBlockConversation();
286+
// Step 4: Block Modal is visible
287+
expect(userAModals.blockWarning().isModalPresent()).toBeTruthy();
288+
// Step 5: User A blocks User B
289+
await userAModals.blockWarning().clickBlock();
290+
// [Unwanted/current behavior] Conversation is still present, and User A can open it
291+
await userAPages.conversationList().openConversation(userB.fullName);
292+
// Step 6: User A gets redirected back to conversation list
293+
// Step 7: Conversation with User B disappeared from main contact list of User A
294+
// Step 8: Conversation with User B disappeared from archive list of User A
295+
// Step 8: Next contact of contact list from User A is selected
296+
// Step 9: Conversation with User A is still in Conversation List of User B
297+
// Step 10: No leave message is displayed
298+
});
299+
},
300+
);
301+
302+
test(
303+
'Verify you can unblock someone from search list',
304+
{tag: ['@TC-148', '@regression']},
305+
async ({pageManager: userAPageManager}) => {
306+
test.slow();
307+
const {pages: userAPages, modals: userAModals, components: userAComponents} = userAPageManager.webapp;
308+
309+
await test.step('User A blocks User B', async () => {
310+
// The 'handleUnableToOpenModal' option takes care of optional modal
311+
await blockUserFromConversationList(userAPageManager, userB, {handleUnableToOpenModal: true});
312+
});
313+
314+
await test.step('User A unblocks User B from Search List', async () => {
315+
await userAComponents.conversationSidebar().clickConnectButton();
316+
await userAPages.startUI().searchInput.fill(userB.username);
317+
await userAPages.startUI().selectUser(userB.username);
318+
await userAModals.userProfile().unblockButton.click();
319+
});
320+
},
321+
);
322+
323+
test.afterAll(async ({api}) => {
324+
await tearDownAll(api);
325+
});
326+
});

0 commit comments

Comments
 (0)