Skip to content
Open
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
27 changes: 27 additions & 0 deletions apps/webapp/test/e2e_tests/backend/brigRepository.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,31 @@ export class BrigRepositoryE2E {
},
);
}

public async claimDomain(domain: string) {
await this.axiosInstance.put(
`i/domain-registration/${domain}`,
{
backend: {
config_url: 'https://nginz-https.anta.wire.link/deeplink.json',
webapp_url: 'https://webapp.anta.wire.link',
Comment on lines +260 to +261
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we maybe pass these config options as parameters? It feels a bit wrong to have them hardcoded and hidden in here. Maybe we could even re-use the env vars which will be added for the federation tests here, since they also point to Anta?

},
domain_redirect: 'backend',
team_invite: 'not-allowed',
},
{
headers: {
Authorization: `Basic ${BASIC_AUTH}`,
},
},
);
}

public async deleteDomain(domain: string) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 Maybe call it deleteDomainClaim() or something in the sense of "unclaim" domain just so it's clear that it's not a domain which is being deleted here 🙈

await this.axiosInstance.delete(`i/domain-registration/${domain}`, {
headers: {
Authorization: `Basic ${BASIC_AUTH}`,
},
});
}
}
2 changes: 2 additions & 0 deletions apps/webapp/test/e2e_tests/pageManager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import {ParticipantDeviceDetailsPage} from './webapp/pages/participantDeviceDeta
import {ParticipantDevicesPage} from './webapp/pages/participantDevices.page';
import {NewDeviceModal} from './webapp/modals/newDevice.modal';
import {ReadReceiptModal} from './webapp/modals/readReceipt.modal';
import {CustomBackendPage} from './webapp/pages/customBackend.page';

export const webAppPath = process.env.WEBAPP_URL ?? '';

Expand Down Expand Up @@ -201,6 +202,7 @@ export class PageManager {
this.getOrCreate('webapp.pages.verification', () => new EmailVerificationPage(this.page)),
setUsername: () => this.getOrCreate('webapp.pages.setUsername', () => new SetUsernamePage(this.page)),
conversationJoin: () => this.getOrCreate('webapp.pages.conversationJoin', () => ConversationJoinPage(this.page)),
customBackend: () => this.getOrCreate('webapp.pages.customBackend', () => new CustomBackendPage(this.page)),
},
modals: {
appLock: () => this.getOrCreate('webapp.modals.appLock', () => new AppLockModal(this.page)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 CustomBackendPage {
readonly title: Locator;
readonly redirectWarningText: Locator;
readonly adminInfoText: Locator;
readonly connectButton: Locator;
readonly cancelButton: Locator;

constructor(page: Page) {
this.title = page.getByText("Connect to your organization's backend?", {exact: true});
this.redirectWarningText = page.getByText('Your email belongs to another backend.');
this.adminInfoText = page.getByText('Your IT team administers this Wire backend.');
this.connectButton = page.getByRole('button', {name: 'Connect'});
this.cancelButton = page.getByRole('button', {name: 'Cancel'});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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 {expect, test} from '../../test.fixtures';
import {PageManager} from '../../pageManager';
import {faker} from '@faker-js/faker';

const ON_PREM_WEBAPP_URL = 'https://webapp.anta.wire.link';

test('On Prem Login Redirect', {tag: ['@TC-8757', '@regression']}, async ({context, createPage, api}) => {
const domain = faker.internet.domainName();
const email = `redirect-user@${domain}`;

try {
await test.step('Claim domain and configure on-prem redirect', async () => {
await api.brig.claimDomain(domain);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The claiming of the domain is specified as precondition for the test, so no need to do it inside it. I'd suggest to put it in a beforeEach hook outside the test, this way you also don't need the manual try-finally block as you can just do the deletion inside a afterEach hook then :)

});

const page = await createPage(context);
const pageManager = PageManager.from(page);
const {pages} = pageManager.webapp;

await test.step('Open login page and enter email with claimed domain', async () => {
await pageManager.openSSOPage();
await pages.singleSignOn().enterEmailOnSSOPage(email);
await pages.singleSignOn().ssoSignInButton.click();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 I don't think you need to click the sign in button yourself. When testing it it automatically shows the dialog after entering it. It might still work if the button is clicked before the dialog opens automatically, but let's try to avoid undefined timing specific behavior :)

});

await test.step('Verify connect-to-organization backend dialog is shown', async () => {
await expect(pages.customBackend().title).toBeVisible();
await expect(pages.customBackend().redirectWarningText).toBeVisible();
await expect(pages.customBackend().adminInfoText).toBeVisible();
Comment on lines +46 to +48
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 Let's try not to be overly specific here. Testing the exact texts shown on the page isn't a good practice as they easily change which would break the test for no reason.
Looking into the test description I don't think it's necessary to assert the redirectWarningText and adminInfoText. I'd say it should be sufficient to just check the page contains the headline and optionally the domain of the backend the user will be redirected to:

Suggested change
await expect(pages.customBackend().title).toBeVisible();
await expect(pages.customBackend().redirectWarningText).toBeVisible();
await expect(pages.customBackend().adminInfoText).toBeVisible();
await expect(page.getByText("Connect to your organization's backend?")).toBeVisible();
await expect(page.getByText(ON_PREM_WEBAPP_URL)).toBeVisible();

This way the test is less likely to break for unrelated reasons which means less maintenance effort ;)

Also notice ho I inlined the locator for the title. This locator will most likely be only used once in here as it is very specific. I think we don't need the dedicated POM for now. Also having the title in the test itself makes it easier to read :)

});

await test.step('Click connect and verify redirect to on-prem webapp', async () => {
const urlPattern = new RegExp(ON_PREM_WEBAPP_URL.replace(/\./g, '\\.'));

Check warning on line 52 in apps/webapp/test/e2e_tests/specs/CriticalFlow/onPremLoginRedirect-TC-8757.spec.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

`String.raw` should be used to avoid escaping `\`.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZ5z-jcVpj_seCmcHsVD&open=AZ5z-jcVpj_seCmcHsVD&pullRequest=21407

Check warning on line 52 in apps/webapp/test/e2e_tests/specs/CriticalFlow/onPremLoginRedirect-TC-8757.spec.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZ5z-jcVpj_seCmcHsVC&open=AZ5z-jcVpj_seCmcHsVC&pullRequest=21407

await Promise.all([
page.waitForURL(urlPattern, {
timeout: 20_000,
}),
pages.customBackend().connectButton.click(),
]);

await expect(page).toHaveURL(urlPattern);
Comment on lines +52 to +61
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so complicated? In the end all you're doing is clicking a button and waiting for the URL to change, there's no need to start listening for the URL change before the button is clicked. Also the Pattern for matching the URL can be simplified by using String.raw to escape it automatically (no need to maintain the escape regex ourself 😉)

Suggested change
const urlPattern = new RegExp(ON_PREM_WEBAPP_URL.replace(/\./g, '\\.'));
await Promise.all([
page.waitForURL(urlPattern, {
timeout: 20_000,
}),
pages.customBackend().connectButton.click(),
]);
await expect(page).toHaveURL(urlPattern);
await page.getByRole('button', {name: 'Connect'}).click();
await expect(page).toHaveURL(new RegExp(String.raw`${ON_PREM_WEBAPP_URL}`), {timeout: 20_000});

});
} finally {
await test.step('Delete claimed domain registration', async () => {
await api.brig.deleteDomain(domain);
});
}
});
Loading