Skip to content

chore: Refactor page object implementation [WPB-18373] #19172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 82 additions & 138 deletions test/e2e_tests/criticalFlow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,49 +21,20 @@ import {faker} from '@faker-js/faker';

import {Services} from './data/serviceInfo';
import {getUser, User} from './data/user';
import {AccountPage} from './pages/account.page';
import {AppLockModal} from './pages/appLock.modal';
import {BlockWarningModal} from './pages/blockWarning.modal';
import {ConversationPage} from './pages/conversation.page';
import {ConversationListPage} from './pages/conversationList.page';
import {ConversationSidebar} from './pages/conversationSidebar.page';
import {DataShareConsentModal} from './pages/dataShareConsent.modal';
import {DeleteAccountModal} from './pages/deleteAccount.modal';
import {DeleteAccountPage} from './pages/deleteAccount.page';
import {EmailVerificationPage} from './pages/emailVerification.page';
import {GroupCreationPage} from './pages/groupCreation.page';
import {LoginPage} from './pages/login.page';
import {MarketingConsentModal} from './pages/marketingConsent.modal';
import {OutgoingConnectionPage} from './pages/outgoingConnection.page';
import {RegistrationPage} from './pages/registration.page';
import {SetUsernamePage} from './pages/setUsername.page';
import {SingleSignOnPage} from './pages/singleSignOn.page';
import {StartUIPage} from './pages/startUI.page';
import {UserProfileModal} from './pages/userProfile.modal';
import {WelcomePage} from './pages/welcome.page';
import {test, expect} from './test.fixtures';
import {generateSecurePassword} from './utils/userDataGenerator';

const webAppPath = process.env.WEBAPP_URL ?? '';
const createdUsers: User[] = [];
const createdTeams: Map<User, string> = new Map();

test('Team owner adds whole team to an all team chat', {tag: ['@TC-8631', '@crit-flow']}, async ({page, api}) => {
test('Team owner adds whole team to an all team chat', {tag: ['@TC-8631', '@crit-flow']}, async ({pages, api}) => {
// Generating test data
const owner = getUser();
const member1 = getUser();
const member2 = getUser();
const teamName = 'Critical';
const conversationName = 'Crits';

// Initializing page objects
const singleSignOnPage = new SingleSignOnPage(page);
const loginPage = new LoginPage(page);
const dataShareConsentModal = new DataShareConsentModal(page);
const conversationListPage = new ConversationListPage(page);
const groupCreationPage = new GroupCreationPage(page);
const startUIPage = new StartUIPage(page);

await test.step('Preconditions: Creating preconditions for the test via API', async () => {
await api.createTeamOwner(owner, teamName);
owner.teamId = await api.team.getTeamIdForUser(owner);
Expand All @@ -79,23 +50,23 @@ test('Team owner adds whole team to an all team chat', {tag: ['@TC-8631', '@crit
});

await test.step('Team owner logs in into a client and creates group conversation', async () => {
await page.goto(webAppPath);
await singleSignOnPage.enterEmailOnSSOPage(owner.email);
await loginPage.inputPassword(owner.password);
await loginPage.clickSignInButton();
await dataShareConsentModal.clickDecline();
await pages.openMainPage();
await pages.singleSignOnPage.enterEmailOnSSOPage(owner.email);
await pages.loginPage.inputPassword(owner.password);
await pages.loginPage.clickSignInButton();
await pages.dataShareConsentModal.clickDecline();
});

await test.step('Team owner adds a service to newly created group', async () => {
await api.team.addServiceToTeamWhitelist(owner.teamId!, Services.POLL_SERVICE, owner.token!);
});

await test.step('Team owner adds team members to a group', async () => {
await conversationListPage.clickCreateGroup();
await groupCreationPage.setGroupName(conversationName);
await startUIPage.selectUsers([member1.username, member2.username]);
await groupCreationPage.clickCreateGroupButton();
expect(await conversationListPage.isConversationItemVisible(conversationName)).toBeTruthy();
await pages.conversationListPage.clickCreateGroup();
await pages.groupCreationPage.setGroupName(conversationName);
await pages.startUIPage.selectUsers([member1.username, member2.username]);
await pages.groupCreationPage.clickCreateGroupButton();
expect(await pages.conversationListPage.isConversationItemVisible(conversationName)).toBeTruthy();
});

// Steps below require [WPB-18075] and [WPB-17547]
Expand All @@ -111,7 +82,7 @@ test('Team owner adds whole team to an all team chat', {tag: ['@TC-8631', '@crit
await test.step('Team owner removes a service from a group', async () => {});
});

test('Account Management', {tag: ['@TC-8639', '@crit-flow']}, async ({page, api}) => {
test('Account Management', {tag: ['@TC-8639', '@crit-flow']}, async ({pages, api}) => {
test.slow(); // Increasing test timeout to 90 seconds to accommodate the full flow

// Generating test data
Expand All @@ -121,15 +92,6 @@ test('Account Management', {tag: ['@TC-8639', '@crit-flow']}, async ({page, api}
const conversationName = 'Tracking';
const appLockPassphrase = generateSecurePassword();

// Initializing page objects
const singleSignOnPage = new SingleSignOnPage(page);
const loginPage = new LoginPage(page);
const dataShareConsentModal = new DataShareConsentModal(page);
const conversationSidebar = new ConversationSidebar(page);
const accountPage = new AccountPage(page);
const appLockModal = new AppLockModal(page);
const conversationListPage = new ConversationListPage(page);

// Creating preconditions for the test via API
await test.step('Preconditions: Creating preconditions for the test via API', async () => {
await api.createTeamOwner(owner, teamName);
Expand All @@ -150,37 +112,37 @@ test('Account Management', {tag: ['@TC-8639', '@crit-flow']}, async ({page, api}

// Test steps
await test.step('Members logs in into the application', async () => {
await page.goto(webAppPath);
await singleSignOnPage.enterEmailOnSSOPage(owner.email);
await loginPage.inputPassword(owner.password);
await loginPage.clickSignInButton();
await dataShareConsentModal.clickDecline();
await pages.openMainPage();
await pages.singleSignOnPage.enterEmailOnSSOPage(owner.email);
await pages.loginPage.inputPassword(owner.password);
await pages.loginPage.clickSignInButton();
await pages.dataShareConsentModal.clickDecline();
});

await test.step('Member opens settings', async () => {
await conversationSidebar.clickPreferencesButton();
await pages.conversationSidebar.clickPreferencesButton();
});

await test.step('Member enables logging in settings', async () => {
await accountPage.toggleSendUsageData();
await pages.accountPage.toggleSendUsageData();
});

await test.step('Member enables applock and sets their password', async () => {
await accountPage.toggleAppLock();
await appLockModal.setPasscode(appLockPassphrase);
await conversationSidebar.clickAllConversationsButton();
expect(await conversationListPage.isConversationItemVisible(conversationName));
await pages.accountPage.toggleAppLock();
await pages.appLockModal.setPasscode(appLockPassphrase);
await pages.conversationSidebar.clickAllConversationsButton();
expect(await pages.conversationListPage.isConversationItemVisible(conversationName));
});

await test.step('Member verifies if applock is working', async () => {
await page.reload();
expect(await appLockModal.isVisible());
expect(await appLockModal.getAppLockModalHeader()).toContain('Enter passcode to unlock');
expect(await appLockModal.getAppLockModalText()).toContain('Passcode');

await appLockModal.unlockAppWithPasscode(appLockPassphrase);
expect(await appLockModal.isHidden());
expect(await conversationListPage.isConversationItemVisible(conversationName));
await pages.refreshPage();
expect(await pages.appLockModal.isVisible());
expect(await pages.appLockModal.getAppLockModalHeader()).toContain('Enter passcode to unlock');
expect(await pages.appLockModal.getAppLockModalText()).toContain('Passcode');

await pages.appLockModal.unlockAppWithPasscode(appLockPassphrase);
expect(await pages.appLockModal.isHidden());
expect(await pages.conversationListPage.isConversationItemVisible(conversationName));
});

// TODO: Missing test steps for TC-8639 from testiny:
Expand All @@ -193,32 +155,14 @@ test('Account Management', {tag: ['@TC-8639', '@crit-flow']}, async ({page, api}
await test.step('Member resets their password ', async () => {});
});

test('Personal Account Lifecycle', {tag: ['@TC-8638', '@crit-flow']}, async ({page, api}) => {
test('Personal Account Lifecycle', {tag: ['@TC-8638', '@crit-flow']}, async ({pages, api}) => {
test.setTimeout(120_000); // Increasing test timeout to 120 seconds to accommodate the full flow

// Generating test data
// userB is the contact user, userA is the user who registers
const userB = getUser();
const userA = getUser();

// Initializing page objects
const singleSignOnPage = new SingleSignOnPage(page);
const welcomePage = new WelcomePage(page);
const registrationPage = new RegistrationPage(page);
const verificationPage = new EmailVerificationPage(page);
const marketingConsentModal = new MarketingConsentModal(page);
const setUsernamePage = new SetUsernamePage(page);
const dataShareConsentModal = new DataShareConsentModal(page);
const conversationSidebar = new ConversationSidebar(page);
const conversationPage = new ConversationPage(page);
const startUIPage = new StartUIPage(page);
const userProfileModal = new UserProfileModal(page);
const conversationListPage = new ConversationListPage(page);
const outgoingConnectionPage = new OutgoingConnectionPage(page);
const blockWarningModal = new BlockWarningModal(page);
const deleteAccountModal = new DeleteAccountModal(page);
const accountPage = new AccountPage(page);

await test.step('Preconditions: Creating preconditions for the test via API', async () => {
await api.createPersonalUser(userB);
createdUsers.push(userB);
Expand All @@ -228,66 +172,66 @@ test('Personal Account Lifecycle', {tag: ['@TC-8638', '@crit-flow']}, async ({pa

// Test steps
await test.step('User A opens the application and registers personal account', async () => {
await page.goto(webAppPath);
await singleSignOnPage.enterEmailOnSSOPage(userA.email);
await welcomePage.clickCreateAccountButton();
await welcomePage.clickCreatePersonalAccountButton();
expect(await registrationPage.isPasswordPolicyInfoVisible());
await pages.openMainPage();
await pages.singleSignOnPage.enterEmailOnSSOPage(userA.email);
await pages.welcomePage.clickCreateAccountButton();
await pages.welcomePage.clickCreatePersonalAccountButton();
expect(await pages.registrationPage.isPasswordPolicyInfoVisible());

await registrationPage.fillInUserInfo(userA);
expect(await registrationPage.isSubmitButtonEnabled()).toBeFalsy();
await pages.registrationPage.fillInUserInfo(userA);
expect(await pages.registrationPage.isSubmitButtonEnabled()).toBeFalsy();

await registrationPage.toggleTermsCheckbox();
expect(await registrationPage.isSubmitButtonEnabled()).toBeTruthy();
await pages.registrationPage.toggleTermsCheckbox();
expect(await pages.registrationPage.isSubmitButtonEnabled()).toBeTruthy();

await registrationPage.clickSubmitButton();
await pages.registrationPage.clickSubmitButton();
const verificationCode = await api.inbucket.getVerificationCode(userA.email);
await verificationPage.enterVerificationCode(verificationCode);
await marketingConsentModal.clickConfirmButton();
await pages.verificationPage.enterVerificationCode(verificationCode);
await pages.marketingConsentModal.clickConfirmButton();
});

await test.step('Personal user A sets user name', async () => {
// Expect that automatic username generation is correct
expect(await setUsernamePage.getHandleInputValue()).toBe(userA.username);
expect(await pages.setUsernamePage.getHandleInputValue()).toBe(userA.username);
const newUsername = userA.username.slice(0, 10) + faker.string.alpha(5).toLowerCase();
userA.username = newUsername;
await setUsernamePage.setUsername(newUsername);
await setUsernamePage.clickNextButton();
await pages.setUsernamePage.setUsername(newUsername);
await pages.setUsernamePage.clickNextButton();
});

await test.step('Personal user A declines sending anonymous usage data', async () => {
await dataShareConsentModal.isModalPresent();
await dataShareConsentModal.clickDecline();
await pages.dataShareConsentModal.isModalPresent();
await pages.dataShareConsentModal.clickDecline();
});

await test.step('Personal user A checks that username was set correctly', async () => {
expect(await conversationSidebar.getPersonalStatusName()).toBe(`${userA.firstName} ${userA.lastName}`);
expect(await conversationSidebar.getPersonalUserName()).toContain(userA.username);
expect(await conversationPage.isWatermarkVisible());
expect(await pages.conversationSidebar.getPersonalStatusName()).toBe(`${userA.firstName} ${userA.lastName}`);
expect(await pages.conversationSidebar.getPersonalUserName()).toContain(userA.username);
expect(await pages.conversationPage.isWatermarkVisible());
});

await test.step('Personal user A searches for other personal user B', async () => {
await conversationSidebar.clickConnectButton();
await startUIPage.selectUser(userB.username);
expect(await userProfileModal.isVisible());
await pages.conversationSidebar.clickConnectButton();
await pages.startUIPage.selectUser(userB.username);
expect(await pages.userProfileModal.isVisible());
});

await test.step('Personal user A sends a connection request to personal user B', async () => {
await userProfileModal.clickConnectButton();
await conversationListPage.openConversation(userB.fullName);
expect(await outgoingConnectionPage.getOutgoingConnectionUsername()).toContain(userB.username);
expect(await outgoingConnectionPage.isPendingIconVisible(userB.fullName));
await pages.userProfileModal.clickConnectButton();
await pages.conversationListPage.openConversation(userB.fullName);
expect(await pages.outgoingConnectionPage.getOutgoingConnectionUsername()).toContain(userB.username);
expect(await pages.outgoingConnectionPage.isPendingIconVisible(userB.fullName));
});

await test.step('Personal user B accepts request', async () => {
await api.acceptConnectionRequest(userB);
expect(await outgoingConnectionPage.isPendingIconHidden(userB.fullName));
expect(await pages.outgoingConnectionPage.isPendingIconHidden(userB.fullName));
});

await test.step('Personal user A and personal user B exchange some messages', async () => {
// TODO: Conversation sometimes closes after connection request was approved, so we need to reopen it
await conversationListPage.openConversation(userB.fullName);
expect(await conversationPage.isConversationOpen(userB.fullName));
await pages.conversationListPage.openConversation(userB.fullName);
expect(await pages.conversationPage.isConversationOpen(userB.fullName));

// TODO: Bug [WPB-18226] Message is not visible in the conversation after sending it
// await conversationPage.sendMessage('Hello there');
Expand All @@ -299,45 +243,45 @@ test('Personal Account Lifecycle', {tag: ['@TC-8638', '@crit-flow']}, async ({pa
});

await test.step('Personal user A blocks personal user B', async () => {
await conversationListPage.clickConversationOptions(userB.fullName);
await conversationListPage.clickBlockConversation();
expect(await blockWarningModal.isModalPresent());
expect(await blockWarningModal.getModalTitle()).toContain(`Block ${userB.fullName}`);
expect(await blockWarningModal.getModalText()).toContain(
await pages.conversationListPage.clickConversationOptions(userB.fullName);
await pages.conversationListPage.clickBlockConversation();
expect(await pages.blockWarningModal.isModalPresent());
expect(await pages.blockWarningModal.getModalTitle()).toContain(`Block ${userB.fullName}`);
expect(await pages.blockWarningModal.getModalText()).toContain(
`${userB.fullName} won’t be able to contact you or add you to group conversations.`,
);

await blockWarningModal.clickBlock();
expect(await conversationListPage.isConversationBlocked(userB.fullName));
await pages.blockWarningModal.clickBlock();
expect(await pages.conversationListPage.isConversationBlocked(userB.fullName));

// [WPB-18093] Backend not returning the blocked 1:1 in conversations list
// When User <Contact> sends message "See this?" to personal MLS conversation <Name>
// Then I do not see text message See this?
});

await test.step('Personal user A opens settings', async () => {
await conversationSidebar.clickPreferencesButton();
await pages.conversationSidebar.clickPreferencesButton();
});

await test.step('Personal User A deletes their account', async () => {
await accountPage.clickDeleteAccountButton();
expect(await deleteAccountModal.isModalPresent());
expect(await deleteAccountModal.getModalTitle()).toContain('Delete account');
expect(await deleteAccountModal.getModalText()).toContain(
await pages.accountPage.clickDeleteAccountButton();
expect(await pages.deleteAccountModal.isModalPresent());
expect(await pages.deleteAccountModal.getModalTitle()).toContain('Delete account');
expect(await pages.deleteAccountModal.getModalText()).toContain(
'We will send you an email. Follow the link to delete your account permanently.',
);

await deleteAccountModal.clickDelete();
await pages.deleteAccountModal.clickDelete();
const url = await api.inbucket.getAccountDeletionURL(userA.email);

const newTab = await page.context().newPage();
await newTab.goto(url);
const deleteAccountPage = new DeleteAccountPage(newTab);
await deleteAccountPage.clickDeleteAccountButton();
expect(await deleteAccountPage.isAccountDeletedHeadlineVisible());
await pages.openNewTab(url, async tab => {
await tab.deleteAccountPage.clickDeleteAccountButton();
expect(await tab.deleteAccountPage.isAccountDeletedHeadlineVisible());
});

await newTab.close();
expect(await welcomePage.getLogoutReasonText()).toContain('You were signed out because your account was deleted');
expect(await pages.welcomePage.getLogoutReasonText()).toContain(
'You were signed out because your account was deleted',
);
});
});

Expand Down
Loading
Loading