From 3c22e5dc68e9095678f37e9c0638544ee26325b8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 27 Jan 2025 15:35:06 +0000 Subject: [PATCH] Switch to mailpit for Playwright tests (#29108) * Switch to mailpit for Playwright tests as mailhog is unsupported and lacks arm64 support Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix yarn.lock Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 2 +- playwright/e2e/crypto/backups-mas.spec.ts | 12 ++++----- playwright/e2e/oidc/index.ts | 14 +++++------ playwright/e2e/oidc/oidc-native.spec.ts | 4 +-- playwright/e2e/register/email.spec.ts | 11 ++++---- .../homeserver/synapse/consentHomeserver.ts | 4 +-- .../homeserver/synapse/emailHomeserver.ts | 4 +-- .../homeserver/synapse/masHomeserver.ts | 2 +- playwright/services.ts | 18 ++++++------- .../testcontainers/{mailhog.ts => mailpit.ts} | 13 ++++++---- playwright/testcontainers/mas.ts | 2 +- yarn.lock | 25 +++++++++++++------ 12 files changed, 62 insertions(+), 49 deletions(-) rename playwright/testcontainers/{mailhog.ts => mailpit.ts} (66%) diff --git a/package.json b/package.json index df4e0801d3..ee388e78c1 100644 --- a/package.json +++ b/package.json @@ -256,7 +256,7 @@ "jsqr": "^1.4.0", "knip": "^5.36.2", "lint-staged": "^15.0.2", - "mailhog": "^4.16.0", + "mailpit-api": "^1.0.5", "matrix-web-i18n": "^3.2.1", "mini-css-extract-plugin": "2.9.2", "minimist": "^1.2.6", diff --git a/playwright/e2e/crypto/backups-mas.spec.ts b/playwright/e2e/crypto/backups-mas.spec.ts index a6f4fb9390..84707eb49d 100644 --- a/playwright/e2e/crypto/backups-mas.spec.ts +++ b/playwright/e2e/crypto/backups-mas.spec.ts @@ -19,19 +19,19 @@ test.use(masHomeserver); test.describe("Encryption state after registration", () => { test.skip(isDendrite, "does not yet support MAS"); - test("Key backup is enabled by default", async ({ page, mailhogClient, app }, testInfo) => { + test("Key backup is enabled by default", async ({ page, mailpitClient, app }, testInfo) => { await page.goto("/#/login"); await page.getByRole("button", { name: "Continue" }).click(); - await registerAccountMas(page, mailhogClient, `alice_${testInfo.testId}`, "alice@email.com", "Pa$sW0rD!"); + await registerAccountMas(page, mailpitClient, `alice_${testInfo.testId}`, "alice@email.com", "Pa$sW0rD!"); await app.settings.openUserSettings("Security & Privacy"); await expect(page.getByText("This session is backing up your keys.")).toBeVisible(); }); - test("user is prompted to set up recovery", async ({ page, mailhogClient, app }, testInfo) => { + test("user is prompted to set up recovery", async ({ page, mailpitClient, app }, testInfo) => { await page.goto("/#/login"); await page.getByRole("button", { name: "Continue" }).click(); - await registerAccountMas(page, mailhogClient, `alice_${testInfo.testId}`, "alice@email.com", "Pa$sW0rD!"); + await registerAccountMas(page, mailpitClient, `alice_${testInfo.testId}`, "alice@email.com", "Pa$sW0rD!"); await page.getByRole("button", { name: "Add room" }).click(); await page.getByRole("menuitem", { name: "New room" }).click(); @@ -47,7 +47,7 @@ test.describe("Key backup reset from elsewhere", () => { test("Key backup is disabled when reset from elsewhere", async ({ page, - mailhogClient, + mailpitClient, request, homeserver, }, testInfo) => { @@ -60,7 +60,7 @@ test.describe("Key backup reset from elsewhere", () => { await page.goto("/#/login"); await page.getByRole("button", { name: "Continue" }).click(); - await registerAccountMas(page, mailhogClient, testUsername, "alice@email.com", testPassword); + await registerAccountMas(page, mailpitClient, testUsername, "alice@email.com", testPassword); await page.getByRole("button", { name: "Add room" }).click(); await page.getByRole("menuitem", { name: "New room" }).click(); diff --git a/playwright/e2e/oidc/index.ts b/playwright/e2e/oidc/index.ts index bfd49b496a..1dcae1c21d 100644 --- a/playwright/e2e/oidc/index.ts +++ b/playwright/e2e/oidc/index.ts @@ -6,14 +6,14 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import { API, Messages } from "mailhog"; +import { MailpitClient } from "mailpit-api"; import { Page } from "@playwright/test"; import { expect } from "../../element-web-test"; export async function registerAccountMas( page: Page, - mailhog: API, + mailpit: MailpitClient, username: string, email: string, password: string, @@ -27,13 +27,13 @@ export async function registerAccountMas( await page.getByRole("textbox", { name: "Confirm Password" }).fill(password); await page.getByRole("button", { name: "Continue" }).click(); - let messages: Messages; + let code: string; await expect(async () => { - messages = await mailhog.messages(); - expect(messages.items).toHaveLength(1); + const messages = await mailpit.listMessages(); + expect(messages.messages[0].To[0].Address).toEqual(email); + const text = await mailpit.renderMessageText(messages.messages[0].ID); + [, code] = text.match(/Your verification code to confirm this email address is: (\d{6})/); }).toPass(); - expect(messages.items[0].to).toEqual(`${username} <${email}>`); - const [, code] = messages.items[0].text.match(/Your verification code to confirm this email address is: (\d{6})/); await page.getByRole("textbox", { name: "6-digit code" }).fill(code); await page.getByRole("button", { name: "Continue" }).click(); diff --git a/playwright/e2e/oidc/oidc-native.spec.ts b/playwright/e2e/oidc/oidc-native.spec.ts index 4d7fc7538d..8c9128c39b 100644 --- a/playwright/e2e/oidc/oidc-native.spec.ts +++ b/playwright/e2e/oidc/oidc-native.spec.ts @@ -19,7 +19,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { context, page, homeserver, - mailhogClient, + mailpitClient, mas, }, testInfo) => { await page.clock.install(); @@ -33,7 +33,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { await page.getByRole("button", { name: "Continue" }).click(); const userId = `alice_${testInfo.testId}`; - await registerAccountMas(page, mailhogClient, userId, "alice@email.com", "Pa$sW0rD!"); + await registerAccountMas(page, mailpitClient, userId, "alice@email.com", "Pa$sW0rD!"); // Eventually, we should end up at the home screen. await expect(page).toHaveURL(/\/#\/home$/, { timeout: 10000 }); diff --git a/playwright/e2e/register/email.spec.ts b/playwright/e2e/register/email.spec.ts index cd990f9eaf..d351893f8b 100644 --- a/playwright/e2e/register/email.spec.ts +++ b/playwright/e2e/register/email.spec.ts @@ -34,7 +34,7 @@ test.describe("Email Registration", async () => { test( "registers an account and lands on the home page", { tag: "@screenshot" }, - async ({ page, mailhogClient, request, checkA11y }) => { + async ({ page, mailpitClient, request, checkA11y }) => { await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible(); // Hide the server text as it contains the randomly allocated Homeserver port const screenshotOptions = { mask: [page.locator(".mx_ServerPicker_server")] }; @@ -51,10 +51,11 @@ test.describe("Email Registration", async () => { await expect(page.getByText("An error was encountered when sending the email")).not.toBeVisible(); - const messages = await mailhogClient.messages(); - expect(messages.items).toHaveLength(1); - expect(messages.items[0].to).toEqual("alice@email.com"); - const [emailLink] = messages.items[0].text.match(/http.+/); + const messages = await mailpitClient.listMessages(); + expect(messages.messages).toHaveLength(1); + expect(messages.messages[0].To[0].Address).toEqual("alice@email.com"); + const text = await mailpitClient.renderMessageText(messages.messages[0].ID); + const [emailLink] = text.match(/http.+/); await request.get(emailLink); // "Click" the link in the email await expect(page.getByText("Welcome alice")).toBeVisible(); diff --git a/playwright/plugins/homeserver/synapse/consentHomeserver.ts b/playwright/plugins/homeserver/synapse/consentHomeserver.ts index e714e8a9c1..7c57e16d22 100644 --- a/playwright/plugins/homeserver/synapse/consentHomeserver.ts +++ b/playwright/plugins/homeserver/synapse/consentHomeserver.ts @@ -10,7 +10,7 @@ import { Fixtures } from "../../../element-web-test.ts"; export const consentHomeserver: Fixtures = { _homeserver: [ - async ({ _homeserver: container, mailhog }, use) => { + async ({ _homeserver: container, mailpit }, use) => { container .withCopyDirectoriesToContainer([ { source: "playwright/plugins/homeserver/synapse/res", target: "/data/res" }, @@ -18,7 +18,7 @@ export const consentHomeserver: Fixtures = { .withConfig({ email: { enable_notifs: false, - smtp_host: "mailhog", + smtp_host: "mailpit", smtp_port: 1025, smtp_user: "username", smtp_pass: "password", diff --git a/playwright/plugins/homeserver/synapse/emailHomeserver.ts b/playwright/plugins/homeserver/synapse/emailHomeserver.ts index f7dee7b01a..b6602b977b 100644 --- a/playwright/plugins/homeserver/synapse/emailHomeserver.ts +++ b/playwright/plugins/homeserver/synapse/emailHomeserver.ts @@ -10,13 +10,13 @@ import { Fixtures } from "../../../element-web-test.ts"; export const emailHomeserver: Fixtures = { _homeserver: [ - async ({ _homeserver: container, mailhog }, use) => { + async ({ _homeserver: container, mailpit }, use) => { container.withConfig({ enable_registration_without_verification: undefined, disable_msisdn_registration: undefined, registrations_require_3pid: ["email"], email: { - smtp_host: "mailhog", + smtp_host: "mailpit", smtp_port: 1025, notif_from: "Your Friendly %(app)s homeserver ", app_name: "my_branded_matrix_server", diff --git a/playwright/plugins/homeserver/synapse/masHomeserver.ts b/playwright/plugins/homeserver/synapse/masHomeserver.ts index d52c446e9b..0cb70835c7 100644 --- a/playwright/plugins/homeserver/synapse/masHomeserver.ts +++ b/playwright/plugins/homeserver/synapse/masHomeserver.ts @@ -11,7 +11,7 @@ import { Fixtures } from "../../../element-web-test.ts"; export const masHomeserver: Fixtures = { mas: [ - async ({ _homeserver: homeserver, logger, network, postgres, mailhog }, use) => { + async ({ _homeserver: homeserver, logger, network, postgres, mailpit }, use) => { const config = { clients: [ { diff --git a/playwright/services.ts b/playwright/services.ts index a501bf6138..1b7514662a 100644 --- a/playwright/services.ts +++ b/playwright/services.ts @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import { test as base } from "@playwright/test"; -import mailhog from "mailhog"; +import { MailpitClient } from "mailpit-api"; import { Network, StartedNetwork } from "testcontainers"; import { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql"; @@ -14,13 +14,13 @@ import { SynapseConfig, SynapseContainer } from "./testcontainers/synapse.ts"; import { Logger } from "./logger.ts"; import { StartedMatrixAuthenticationServiceContainer } from "./testcontainers/mas.ts"; import { HomeserverContainer, StartedHomeserverContainer } from "./testcontainers/HomeserverContainer.ts"; -import { MailhogContainer, StartedMailhogContainer } from "./testcontainers/mailhog.ts"; +import { MailhogContainer, StartedMailhogContainer } from "./testcontainers/mailpit.ts"; import { OAuthServer } from "./plugins/oauth_server"; import { DendriteContainer, PineconeContainer } from "./testcontainers/dendrite.ts"; import { HomeserverType } from "./plugins/homeserver"; export interface TestFixtures { - mailhogClient: mailhog.API; + mailpitClient: MailpitClient; } export interface Services { @@ -28,7 +28,7 @@ export interface Services { network: StartedNetwork; postgres: StartedPostgreSqlContainer; - mailhog: StartedMailhogContainer; + mailpit: StartedMailhogContainer; synapseConfig: SynapseConfig; _homeserver: HomeserverContainer; @@ -90,20 +90,20 @@ export const test = base.extend({ { scope: "worker" }, ], - mailhog: [ + mailpit: [ async ({ logger, network }, use) => { const container = await new MailhogContainer() .withNetwork(network) - .withNetworkAliases("mailhog") - .withLogConsumer(logger.getConsumer("mailhog")) + .withNetworkAliases("mailpit") + .withLogConsumer(logger.getConsumer("mailpit")) .start(); await use(container); await container.stop(); }, { scope: "worker" }, ], - mailhogClient: async ({ mailhog: container }, use) => { - await container.client.deleteAll(); + mailpitClient: async ({ mailpit: container }, use) => { + await container.client.deleteMessages(); await use(container.client); }, diff --git a/playwright/testcontainers/mailhog.ts b/playwright/testcontainers/mailpit.ts similarity index 66% rename from playwright/testcontainers/mailhog.ts rename to playwright/testcontainers/mailpit.ts index c3305607d8..c4c025c05c 100644 --- a/playwright/testcontainers/mailhog.ts +++ b/playwright/testcontainers/mailpit.ts @@ -6,13 +6,16 @@ Please see LICENSE files in the repository root for full details. */ import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait } from "testcontainers"; -import mailhog from "mailhog"; +import { MailpitClient } from "mailpit-api"; export class MailhogContainer extends GenericContainer { constructor() { - super("mailhog/mailhog:latest"); + super("axllent/mailpit:latest"); - this.withExposedPorts(8025).withWaitStrategy(Wait.forListeningPorts()); + this.withExposedPorts(8025).withWaitStrategy(Wait.forListeningPorts()).withEnvironment({ + MP_SMTP_AUTH_ALLOW_INSECURE: "true", + MP_SMTP_AUTH_ACCEPT_ANY: "true", + }); } public override async start(): Promise { @@ -21,10 +24,10 @@ export class MailhogContainer extends GenericContainer { } export class StartedMailhogContainer extends AbstractStartedContainer { - public readonly client: mailhog.API; + public readonly client: MailpitClient; constructor(container: StartedTestContainer) { super(container); - this.client = mailhog({ host: container.getHost(), port: container.getMappedPort(8025) }); + this.client = new MailpitClient(`http://${container.getHost()}:${container.getMappedPort(8025)}`); } } diff --git a/playwright/testcontainers/mas.ts b/playwright/testcontainers/mas.ts index 9b05b521ba..9162bc96e3 100644 --- a/playwright/testcontainers/mas.ts +++ b/playwright/testcontainers/mas.ts @@ -92,7 +92,7 @@ const DEFAULT_CONFIG = { reply_to: '"Authentication Service" ', transport: "smtp", mode: "plain", - hostname: "mailhog", + hostname: "mailpit", port: 1025, username: "username", password: "password", diff --git a/yarn.lock b/yarn.lock index 6e44e5c0b6..89ba84b70e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4076,6 +4076,15 @@ axe-core@^4.10.0, axe-core@~4.10.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df" integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w== +axios@^1.7.8: + version "1.7.9" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a" + integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" @@ -6514,7 +6523,7 @@ focus-lock@^1.3.5: dependencies: tslib "^2.0.3" -follow-redirects@^1.0.0: +follow-redirects@^1.0.0, follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== @@ -7127,7 +7136,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6, iconv-lite@^0.6.3: +iconv-lite@0.6.3, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -8566,12 +8575,12 @@ magic-string@0.30.8: dependencies: "@jridgewell/sourcemap-codec" "^1.4.15" -mailhog@^4.16.0: - version "4.16.0" - resolved "https://registry.yarnpkg.com/mailhog/-/mailhog-4.16.0.tgz#1ad4dda104505399f3f17824737a962696e7d240" - integrity sha512-wXrGik+0MaAy4dbYTImxa8niX9a4aRpZTzC/b1GzCvQs09khhs0aKZgHjgScakI4Y18WInDvvF48hhEz9ifN4g== - optionalDependencies: - iconv-lite "^0.6" +mailpit-api@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/mailpit-api/-/mailpit-api-1.0.5.tgz#3383593707a7bc502af0ae6bf1296160daf8c730" + integrity sha512-55OjUjNv4hwrQKIzN8DqWywuW7UIyzN1FrEd3A87sJ9Ni07LZC/f7hgeW7dp36YYxrmV8voGzUmCY3dWJ3D6Og== + dependencies: + axios "^1.7.8" make-dir@^4.0.0: version "4.0.0"