Skip to content

Commit fb0e6bc

Browse files
authored
test(e2e): implement accessibility regression tests [WPB-19934] (#19525)
1 parent 27d740a commit fb0e6bc

File tree

7 files changed

+241
-1
lines changed

7 files changed

+241
-1
lines changed

test/e2e_tests/pageManager/webapp/components/conversationSidebar.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class ConversationSidebar {
3030
readonly allConverationsButton: Locator;
3131
readonly connectButton: Locator;
3232
readonly archiveButton: Locator;
33+
readonly sidebar: Locator;
3334

3435
constructor(page: Page) {
3536
this.page = page;
@@ -40,6 +41,7 @@ export class ConversationSidebar {
4041
this.allConverationsButton = page.locator(`${selectByDataAttribute('go-recent-view')}`);
4142
this.connectButton = page.locator(`button${selectByDataAttribute('go-people')}`);
4243
this.archiveButton = page.locator(selectByDataAttribute('go-archive'));
44+
this.sidebar = page.locator(`.conversations-sidebar-items`);
4345
}
4446

4547
async getPersonalStatusName() {

test/e2e_tests/pageManager/webapp/pages/account.page.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export class AccountPage {
3636
readonly emailInput: Locator;
3737
readonly resetPasswordButton: Locator;
3838
readonly receiveNewsletterCheckbox: Locator;
39+
readonly typingIndicator: Locator;
3940

4041
constructor(page: Page) {
4142
this.page = page;
@@ -52,6 +53,7 @@ export class AccountPage {
5253
this.emailDisplay = page.locator(selectByDataAttribute('email-display'));
5354
this.resetPasswordButton = page.locator(selectByDataAttribute('do-reset-password'));
5455
this.receiveNewsletterCheckbox = page.locator("[data-uie-name='status-preference-marketing']+label");
56+
this.typingIndicator = page.locator("[data-uie-name='status-preference-typing-indicator']+label");
5557
}
5658

5759
async clickBackUpButton() {
@@ -74,6 +76,10 @@ export class AccountPage {
7476
await this.receiveNewsletterCheckbox.click();
7577
}
7678

79+
async toggleTypingIndicator() {
80+
await this.typingIndicator.click();
81+
}
82+
7783
async isSendUsageDataEnabled() {
7884
return this.sendUsageDataCheckbox.isChecked();
7985
}

test/e2e_tests/pageManager/webapp/pages/conversation.page.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export class ConversationPage {
4848
readonly conversationInfoButton: Locator;
4949
readonly pingButton: Locator;
5050
readonly messages: Locator;
51+
readonly isTypingIndicator: Locator;
5152

5253
readonly getImageAltText = (user: User) => `Image from ${user.fullName}`;
5354

@@ -80,6 +81,7 @@ export class ConversationPage {
8081
this.messages = page.locator(
8182
`${selectByDataAttribute('item-message')} ${selectByClass('message-body')}:not(:has(p${selectByClass('text-foreground')})):has(${selectByClass('text')})`,
8283
);
84+
this.isTypingIndicator = page.locator(selectByDataAttribute('typing-indicator-title'));
8385
}
8486

8587
private getImageLocator(user: User): Locator {
@@ -117,6 +119,14 @@ export class ConversationPage {
117119
await this.page.waitForTimeout(5000); // Wait for the message to be sent
118120
}
119121

122+
async typeMessage(message: string) {
123+
await this.messageInput.click();
124+
for (let i = 0; i < message.length; i++) {
125+
await this.page.keyboard.press(message[i]);
126+
await this.page.waitForTimeout(300); // sim user input
127+
}
128+
}
129+
120130
async createGroup(groupName: string) {
121131
await this.createGroupButton.click();
122132
await this.createGroupNameInput.fill(groupName);

test/e2e_tests/pageManager/webapp/pages/conversationList.page.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class ConversationListPage {
7373
}
7474

7575
async openConversation(conversationName: string) {
76-
await this.getConversationLocator(conversationName).click();
76+
await this.getConversationLocator(conversationName).first().click();
7777
}
7878

7979
async openPendingConnectionRequest() {
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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 {getUser} from 'test/e2e_tests/data/user';
21+
import {PageManager} from 'test/e2e_tests/pageManager';
22+
import {setupBasicTestScenario} from 'test/e2e_tests/utils/setup.util';
23+
import {tearDownAll} from 'test/e2e_tests/utils/tearDown.util';
24+
import {createGroup, loginUser} from 'test/e2e_tests/utils/userActions';
25+
26+
import {test, expect} from '../../test.fixtures';
27+
28+
test.describe('Accessibility', () => {
29+
test.slow();
30+
31+
let owner = getUser();
32+
const members = Array.from({length: 2}, () => getUser());
33+
const [memberA, memberB] = members;
34+
const teamName = 'Accessibility';
35+
const conversationName = 'AccTest';
36+
const textMessage = 'long text message';
37+
const narrowViewport = {width: 480, height: 800};
38+
const loginTimeOut = 60_000;
39+
40+
test.beforeAll(async ({api}) => {
41+
const user = await setupBasicTestScenario(api, members, owner, teamName);
42+
owner = {...owner, ...user};
43+
});
44+
45+
const navigateToConversation = async (pageManager: PageManager, name: string) => {
46+
await pageManager.webapp.pages.conversationList().openConversation(name);
47+
};
48+
49+
test(
50+
'I want to see typing indicator in group conversation',
51+
{tag: ['@TC-46', '@regression']},
52+
async ({pageManager: memberPageManagerA, browser}) => {
53+
const memberContext = await browser.newContext();
54+
const memberPage = await memberContext.newPage();
55+
const memberPageManagerB = new PageManager(memberPage);
56+
57+
const toggleTypingIndicator = async (pageManager: PageManager) => {
58+
await pageManager.webapp.components.conversationSidebar().clickPreferencesButton();
59+
await pageManager.webapp.pages.account().toggleTypingIndicator();
60+
await pageManager.webapp.components.conversationSidebar().clickAllConversationsButton();
61+
};
62+
63+
const typeAndCheckIndicator = async (
64+
senderPage: PageManager,
65+
receiverPage: PageManager,
66+
message: string,
67+
expectedState: boolean,
68+
) => {
69+
await senderPage.webapp.pages.conversation().typeMessage(message);
70+
// Wait for a short moment for the indicator to appear/disappear
71+
await senderPage.waitForTimeout(800);
72+
const isVisible = await receiverPage.webapp.pages.conversation().isTypingIndicator.isVisible();
73+
expect(isVisible).toBe(expectedState);
74+
};
75+
76+
// Initial setup
77+
await memberPageManagerA.openMainPage();
78+
await memberPageManagerB.openMainPage();
79+
await loginUser(memberA, memberPageManagerA);
80+
await loginUser(memberB, memberPageManagerB);
81+
82+
await memberPageManagerA.webapp.components
83+
.conversationSidebar()
84+
.personalUserName.waitFor({state: 'visible', timeout: loginTimeOut});
85+
86+
await memberPageManagerA.webapp.modals.dataShareConsent().clickDecline();
87+
await memberPageManagerB.webapp.modals.dataShareConsent().clickDecline();
88+
89+
await createGroup(memberPageManagerA, conversationName, [memberB]);
90+
91+
await navigateToConversation(memberPageManagerA, conversationName);
92+
await navigateToConversation(memberPageManagerB, conversationName);
93+
94+
await test.step('User A starts typing in group and B sees typing indicator', async () => {
95+
await typeAndCheckIndicator(memberPageManagerA, memberPageManagerB, textMessage, true);
96+
});
97+
98+
await test.step('User A starts typing in group and B does not see typing indicator', async () => {
99+
// Disable typing indicator for user A
100+
await toggleTypingIndicator(memberPageManagerA);
101+
await navigateToConversation(memberPageManagerA, conversationName);
102+
103+
await typeAndCheckIndicator(memberPageManagerA, memberPageManagerB, textMessage, false);
104+
105+
// Re-enable typing indicator for user A for subsequent tests
106+
await toggleTypingIndicator(memberPageManagerA);
107+
await navigateToConversation(memberPageManagerA, conversationName);
108+
});
109+
110+
await test.step('User B turns indicator off and does not see typing indicator', async () => {
111+
// Disable typing indicator for user B
112+
await toggleTypingIndicator(memberPageManagerB);
113+
await navigateToConversation(memberPageManagerB, conversationName);
114+
115+
await typeAndCheckIndicator(memberPageManagerA, memberPageManagerB, textMessage, false);
116+
});
117+
118+
await test.step('User B turns indicator on again and sees typing indicator', async () => {
119+
// Re-enable typing indicator for user B
120+
await toggleTypingIndicator(memberPageManagerB);
121+
await navigateToConversation(memberPageManagerB, conversationName);
122+
123+
await typeAndCheckIndicator(memberPageManagerA, memberPageManagerB, textMessage, true);
124+
});
125+
126+
await memberContext.close();
127+
},
128+
);
129+
130+
test('I want to see collapsed view when app is narrow', {tag: ['@TC-48', '@regression']}, async ({pageManager}) => {
131+
await (await pageManager.getPage()).setViewportSize(narrowViewport);
132+
133+
await pageManager.openMainPage();
134+
await loginUser(memberA, pageManager);
135+
const {components, pages} = pageManager.webapp;
136+
137+
await pages.historyInfo().clickConfirmButton();
138+
await components.conversationSidebar().sidebar.waitFor({state: 'visible', timeout: loginTimeOut});
139+
140+
await expect(components.conversationSidebar().sidebar).toHaveAttribute('data-is-collapsed', 'true');
141+
});
142+
143+
test(
144+
'I should not lose a drafted message when switching between conversations in collapsed view',
145+
{tag: ['@TC-51', '@regression']},
146+
async ({pageManager}) => {
147+
const message = 'test';
148+
const {components, modals, pages} = pageManager.webapp;
149+
await pageManager.openMainPage();
150+
await loginUser(memberA, pageManager);
151+
152+
await pages.historyInfo().clickConfirmButton();
153+
await components.conversationSidebar().sidebar.waitFor({state: 'visible', timeout: loginTimeOut});
154+
155+
await createGroup(pageManager, conversationName, [memberB]);
156+
157+
await pages.conversation().typeMessage(message);
158+
const page = await pageManager.getPage();
159+
160+
await components.conversationSidebar().clickConnectButton();
161+
162+
await page.locator('[data-uie-name="highlighted"]').nth(0).click();
163+
await modals.userProfile().clickStartConversation();
164+
await expect(page.locator('[data-uie-name="secondary-line"]')).toHaveText(message);
165+
166+
await pages.conversationList().openConversation(memberB.fullName);
167+
168+
await pages.conversationList().openConversation(conversationName);
169+
await expect(await pages.conversation().messageInput).toHaveText(message);
170+
},
171+
);
172+
173+
test.afterAll(async ({api}) => {
174+
await tearDownAll(api);
175+
});
176+
});

test/e2e_tests/utils/setup.util.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 {addCreatedTeam, addCreatedUser} from './tearDown.util';
21+
import {inviteMembers} from './userActions';
22+
23+
import {ApiManagerE2E} from '../backend/apiManager.e2e';
24+
import {User} from '../data/user';
25+
26+
/**
27+
* add an team with one owner and 2 member
28+
*/
29+
export const setupBasicTestScenario = async (api: ApiManagerE2E, member: User[], owner: User, teamName: string) => {
30+
const user = await api.createTeamOwner(owner, teamName);
31+
// register credentials for cleanup later
32+
addCreatedTeam(user, user.teamId);
33+
await inviteMembers(member, user, api);
34+
35+
for (const [, user] of member.entries()) {
36+
addCreatedUser(user);
37+
}
38+
return user;
39+
};

test/e2e_tests/utils/userActions.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,10 @@ export const sendTextMessageToConversation = async (
8484

8585
expect(await pages.conversation().isMessageVisible(message)).toBeTruthy();
8686
};
87+
88+
export const createGroup = async (pageManager: PageManager, conversationName: string, user: User[]) => {
89+
await pageManager.webapp.pages.conversationList().clickCreateGroup();
90+
await pageManager.webapp.pages.groupCreation().setGroupName(conversationName);
91+
await pageManager.webapp.pages.startUI().selectUsers(user.flatMap(user => user.username));
92+
await pageManager.webapp.pages.groupCreation().clickCreateGroupButton();
93+
};

0 commit comments

Comments
 (0)