-
Notifications
You must be signed in to change notification settings - Fork 298
test(WPB-19939): add authentication regression tests #19826
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
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
aea45cb
test(TC-34579: verify signin button is disabled inc ase of empty cred…
markbrockhoff 88849b3
test(TC-3468): Verify sign in error appearance in case of suspended t…
markbrockhoff 85ca70e
test(TC-3460): Verify current browser is set as temporary device
markbrockhoff 1a0037a
test(TC-3465): Verify sign in error appearance in case of wrong crede…
markbrockhoff 3707e27
test(TC-3472): Ensure history is kept after refresh on permanent device
markbrockhoff 9c4864a
test(TC-3473): create data driven test to also ensure history is kept…
markbrockhoff 68c6773
test(TC-1311): verify session expires
markbrockhoff b5d540a
test(TC-3480): check tls versions allowed by webapp server
markbrockhoff 66d3a89
test(TC-1311): Ensure history is gone after logging out of public com…
markbrockhoff File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
test/e2e_tests/pageManager/webapp/modals/password.modal.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| /* | ||
| * Wire | ||
| * Copyright (C) 2025 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 {Locator, Page} from '@playwright/test'; | ||
|
|
||
| import {BaseModal} from './base.modal'; | ||
|
|
||
| export class PasswordModal extends BaseModal { | ||
| readonly passwordInput: Locator; | ||
|
|
||
| constructor(page: Page) { | ||
| // For some reason the password modal is rendered using the `modal-template-option` attribute | ||
| super(page, 'modal-template-option'); | ||
|
|
||
| this.passwordInput = this.modal.getByLabel('Password'); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /* | ||
| * Wire | ||
| * Copyright (C) 2025 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 {Locator, Page} from '@playwright/test'; | ||
|
|
||
| export class DevicesPage { | ||
| readonly page: Page; | ||
|
|
||
| readonly proteusId: Locator; | ||
| readonly activeDevices: Locator; | ||
|
|
||
| constructor(page: Page) { | ||
| this.page = page; | ||
|
|
||
| // The id is not using any label or similar but just multiple paragraphs beneath each other | ||
| this.proteusId = page.locator("p:text('Proteus ID') + p"); | ||
| this.activeDevices = page.getByRole('group', {name: 'Active'}).getByRole('button', {name: /device details/}); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,9 +17,34 @@ | |
| * | ||
| */ | ||
|
|
||
| import {test, expect} from 'test/e2e_tests/test.fixtures'; | ||
| import {IncomingMessage} from 'node:http'; | ||
| import https from 'node:https'; | ||
| import {SecureVersion} from 'node:tls'; | ||
|
|
||
| import {PageManager, webAppPath} from 'test/e2e_tests/pageManager'; | ||
| import {test, expect, withLogin} from 'test/e2e_tests/test.fixtures'; | ||
| import {connectWithUser} from 'test/e2e_tests/utils/userActions'; | ||
|
|
||
| test.describe('Authentication', () => { | ||
| test( | ||
| 'Verify sign in button is disabled in case of empty credentials', | ||
| {tag: ['@TC-3457', '@regression']}, | ||
| async ({pageManager}) => { | ||
| const {pages} = pageManager.webapp; | ||
| const {emailInput, passwordInput, signInButton} = pages.login(); | ||
| await pageManager.openLoginPage(); | ||
|
|
||
| await expect(signInButton).toBeDisabled(); | ||
|
|
||
| await emailInput.fill('invalid@email'); | ||
| await passwordInput.fill('invalidPassword'); | ||
| await expect(signInButton).not.toBeDisabled(); | ||
|
|
||
| await passwordInput.clear(); | ||
| await expect(signInButton).toBeDisabled(); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'I want to be asked to share telemetry data when I log in', | ||
| {tag: ['@TC-8780', '@regression']}, | ||
|
|
@@ -33,4 +58,202 @@ test.describe('Authentication', () => { | |
| await expect(modals.dataShareConsent().modalTitle).toBeVisible(); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'Verify sign in error appearance in case of suspended team account', | ||
| {tag: ['@TC-3468', '@regression']}, | ||
| async ({pageManager}) => { | ||
| const {pages} = pageManager.webapp; | ||
| await pageManager.openLoginPage(); | ||
|
|
||
| const userOfSuspendedTeam = {email: '[email protected]', password: '12345678'}; | ||
| await pages.login().login(userOfSuspendedTeam); | ||
|
|
||
| await expect(pages.login().loginErrorText).toHaveText('This account is no longer authorized to log in'); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'Verify current browser is set as temporary device', | ||
| {tag: ['@TC-3460', '@regression']}, | ||
| async ({pageManager, createUser}) => { | ||
| const user = await createUser(); | ||
| const {pages, components} = pageManager.webapp; | ||
|
|
||
| await test.step('Log in with public computer checked', async () => { | ||
| await pageManager.openLoginPage(); | ||
| await pages.login().publicComputerCheckbox.click(); | ||
| await pages.login().login(user); | ||
| await pages.historyInfo().clickConfirmButton(); | ||
| }); | ||
|
|
||
| let proteusId: string; | ||
| await test.step('Open device settings and get current proteus id', async () => { | ||
| await components.conversationSidebar().clickPreferencesButton(); | ||
| await pages.settings().devicesButton.click(); | ||
|
|
||
| proteusId = (await pages.devices().proteusId.textContent()) ?? ''; | ||
| expect(proteusId).toBeTruthy(); | ||
| }); | ||
|
|
||
| await test.step('Log out of public computer', async () => { | ||
| await pages.settings().accountButton.click(); | ||
| await pages.account().clickLogoutButton(); | ||
| }); | ||
|
|
||
| await test.step('Log in again on non public computer', async () => { | ||
| await pageManager.openLoginPage(); | ||
| await pages.login().login(user); | ||
| }); | ||
|
|
||
| await test.step("Open device settings and ensure the public computer isn't active and the ID was re-generated", async () => { | ||
| await components.conversationSidebar().clickPreferencesButton(); | ||
| await pages.settings().devicesButton.click(); | ||
|
|
||
| await expect(pages.devices().activeDevices).toHaveCount(0); | ||
| await expect(pages.devices().proteusId).not.toContainText(proteusId); | ||
| }); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'Verify sign in error appearance in case of wrong credentials', | ||
| {tag: ['@TC-3465', '@regression']}, | ||
| async ({pageManager}) => { | ||
| const {pages} = pageManager.webapp; | ||
| await pageManager.openLoginPage(); | ||
|
|
||
| await pages.login().login({email: '[email protected]', password: 'invalid'}); | ||
|
|
||
| await expect(pages.login().loginErrorText).toHaveText('Please verify your details and try again'); | ||
| }, | ||
| ); | ||
|
|
||
| [ | ||
| {tag: '@TC-3472', deviceType: 'permanent'}, | ||
| {tag: '@TC-3473', deviceType: 'temporary'}, | ||
| ].forEach(({deviceType, tag}) => { | ||
| test( | ||
| `I want to keep my history after refreshing the page on ${deviceType} device`, | ||
| {tag: [tag, '@regression']}, | ||
| async ({pageManager, createTeam}) => { | ||
| const {pages} = pageManager.webapp; | ||
| const team = await createTeam('Test Team', {withMembers: 1}); | ||
| const userA = team.owner; | ||
| const userB = team.members[0]; | ||
|
|
||
| await test.step('Log in and connect with user B', async () => { | ||
| await pageManager.openLoginPage(); | ||
|
|
||
| if (deviceType === 'temporary') { | ||
| await pages.login().publicComputerCheckbox.click(); | ||
| await pages.login().login(userA); | ||
| await pages.historyInfo().clickConfirmButton(); | ||
| } else { | ||
| await pages.login().login(userA); | ||
| } | ||
|
|
||
| await connectWithUser(pageManager, userB); | ||
| }); | ||
|
|
||
| await test.step('Send a message', async () => { | ||
| await pages.conversationList().openConversation(userB.fullName); | ||
| await pages.conversation().sendMessage('Before refresh'); | ||
| }); | ||
|
|
||
| await test.step('Ensure message is still visible after page refresh', async () => { | ||
| const message = pages.conversation().getMessage({content: 'Before refresh'}); | ||
| await expect(message).toBeVisible(); | ||
|
|
||
| await pageManager.refreshPage(); | ||
|
|
||
| await pages.conversationList().openConversation(userB.fullName); | ||
| await expect(message).toBeVisible(); | ||
| }); | ||
| }, | ||
| ); | ||
| }); | ||
|
|
||
| // Bug: Connecting using TLSv1.2 should not be allowed but succeeds | ||
| test.skip( | ||
| 'I want to make sure i connect to webapp only through TLS >= 1.3 connection', | ||
| {tag: ['@TC-3480', '@regression']}, | ||
| async () => { | ||
| const requestWithTlsVersion = (versions: {min?: SecureVersion; max?: SecureVersion}) => | ||
| new Promise<IncomingMessage>((res, rej) => { | ||
| https.get(webAppPath, {minVersion: versions.min, maxVersion: versions.max}, res).on('error', rej); | ||
| }); | ||
|
|
||
| await expect(requestWithTlsVersion({max: 'TLSv1.2'})).rejects.toBeDefined(); | ||
| await expect(requestWithTlsVersion({min: 'TLSv1.3'})).resolves.toBeDefined(); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'Make sure user does not see data of user of previous sessions on same browser', | ||
| {tag: ['@TC-1311', '@regression']}, | ||
| async ({pageManager, createTeam}) => { | ||
| const {pages, components} = pageManager.webapp; | ||
| const team = await createTeam('Test Team', {withMembers: 1}); | ||
| const userA = team.owner; | ||
| const userB = team.members[0]; | ||
|
|
||
| await test.step('Log in with public computer checked', async () => { | ||
| await pageManager.openLoginPage(); | ||
| await pages.login().publicComputerCheckbox.click(); | ||
| await pages.login().login(userA); | ||
| await pages.historyInfo().clickConfirmButton(); | ||
| }); | ||
|
|
||
| await test.step('Connect with and send message to userB', async () => { | ||
| await connectWithUser(pageManager, userB); | ||
| await pages.conversationList().openConversation(userB.fullName); | ||
| await pages.conversation().sendMessage('Test message'); | ||
| }); | ||
|
|
||
| await test.step('Log out of public computer', async () => { | ||
| await components.conversationSidebar().clickPreferencesButton(); | ||
| await pages.settings().accountButton.click(); | ||
| await pages.account().clickLogoutButton(); | ||
| }); | ||
|
|
||
| await test.step('Log in again', async () => { | ||
| await pageManager.openLoginPage(); | ||
| await pages.login().login(userA); | ||
| await pages.historyInfo().clickConfirmButton(); | ||
| }); | ||
|
|
||
| await test.step('Verify previously sent message is gone', async () => { | ||
| await pages.conversationList().openConversation(userB.fullName); | ||
| await expect(pages.conversation().getMessage({content: 'Test message'})).not.toBeAttached(); | ||
| }); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'Verify session expired info is visible on login page', | ||
| {tag: ['@TC-1311', '@regression']}, | ||
| async ({createPage, createUser}) => { | ||
| const user = await createUser(); | ||
| const device1Pages = PageManager.from(await createPage(withLogin(user))).webapp.pages; | ||
|
|
||
| const { | ||
| pages: device2Pages, | ||
| modals: device2Modals, | ||
| components: device2Components, | ||
| } = PageManager.from(await createPage(withLogin(user))).webapp; | ||
| await device2Pages.historyInfo().clickConfirmButton(); | ||
|
|
||
| await device2Components.conversationSidebar().clickPreferencesButton(); | ||
| await device2Pages.settings().devicesButton.click(); | ||
| await device2Pages.devices().activeDevices.getByRole('button', {name: 'Remove Device'}).click(); | ||
|
|
||
| await device2Modals.password().passwordInput.fill(user.password); | ||
| await device2Modals.password().clickAction(); | ||
|
|
||
| await expect( | ||
| device1Pages.singleSignOn().page.getByText('You were signed out because your device was deleted'), | ||
| ).toBeVisible(); | ||
| }, | ||
| ); | ||
| }); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest to use a better locator. Maybe by role or input type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@e-maad I wanted to do so as well, ideally locating the checkbox by label. However turns out the checkboxes are absolutely not accessible ^^'
That's also the reason I have to use
.click()on the locator instead of the native.check()which I'd prefer. Maybe at some point it would be possible to make the underlying component library accessible.