♻️ Replace Mailcatcher with Mailpit for local email testing (#2436)

This commit is contained in:
Alejandra
2026-08-17 10:38:38 +02:00
committed by GitHub
parent 75b4026443
commit 981ae061b4
9 changed files with 81 additions and 88 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
VITE_API_URL=http://localhost:8000
MAILCATCHER_HOST=http://localhost:1080
MAILPIT_HOST=http://localhost:8025
+5 -9
View File
@@ -1,5 +1,5 @@
import { expect, test } from "@playwright/test"
import { findLastEmail } from "./utils/mailcatcher"
import { emailUrl, findLastEmail } from "./utils/mailpit"
import { randomEmail, randomPassword } from "./utils/random"
import { logInUser, signUpNewUser } from "./utils/user"
@@ -46,13 +46,11 @@ test("User can reset password successfully using the link", async ({
const emailData = await findLastEmail({
request,
filter: (e) => e.recipients.includes(`<${email}>`),
query: `to:${email}`,
timeout: 5000,
})
await page.goto(
`${process.env.MAILCATCHER_HOST}/messages/${emailData.id}.html`,
)
await page.goto(emailUrl(emailData))
const selector = 'a[href*="/reset-password?token="]'
@@ -99,13 +97,11 @@ test("Weak new password validation", async ({ page, request }) => {
const emailData = await findLastEmail({
request,
filter: (e) => e.recipients.includes(`<${email}>`),
query: `to:${email}`,
timeout: 5000,
})
await page.goto(
`${process.env.MAILCATCHER_HOST}/messages/${emailData.id}.html`,
)
await page.goto(emailUrl(emailData))
const selector = 'a[href*="/reset-password?token="]'
const url = await page.getAttribute(selector, "href")
-62
View File
@@ -1,62 +0,0 @@
import type { APIRequestContext } from "@playwright/test"
type Email = {
id: number
recipients: string[]
subject: string
}
async function findEmail({
request,
filter,
}: {
request: APIRequestContext
filter?: (email: Email) => boolean
}) {
const response = await request.get(`${process.env.MAILCATCHER_HOST}/messages`)
let emails = await response.json()
if (filter) {
emails = emails.filter(filter)
}
const email = emails[emails.length - 1]
if (email) {
return email as Email
}
return null
}
export function findLastEmail({
request,
filter,
timeout = 5000,
}: {
request: APIRequestContext
filter?: (email: Email) => boolean
timeout?: number
}) {
const timeoutPromise = new Promise<never>((_, reject) =>
setTimeout(
() => reject(new Error("Timeout while trying to get latest email")),
timeout,
),
)
const checkEmails = async () => {
while (true) {
const emailData = await findEmail({ request, filter })
if (emailData) {
return emailData
}
// Wait for 100ms before checking again
await new Promise((resolve) => setTimeout(resolve, 100))
}
}
return Promise.race([timeoutPromise, checkEmails()])
}
+59
View File
@@ -0,0 +1,59 @@
import type { APIRequestContext } from "@playwright/test"
type Address = {
Name: string
Address: string
}
type Email = {
ID: string
To: Address[]
Subject: string
}
async function findEmail({
request,
query,
}: {
request: APIRequestContext
query: string
}) {
const response = await request.get(
`${process.env.MAILPIT_HOST}/api/v1/search`,
{
params: { query, limit: 1 },
},
)
const { messages }: { messages: Email[] } = await response.json()
return messages[0] ?? null
}
export async function findLastEmail({
request,
query,
timeout = 5000,
}: {
request: APIRequestContext
query: string
timeout?: number
}) {
const deadline = Date.now() + timeout
while (Date.now() < deadline) {
const email = await findEmail({ request, query })
if (email) {
return email
}
await new Promise((resolve) => setTimeout(resolve, 100))
}
throw new Error(`Timeout while trying to get the latest email for "${query}"`)
}
export function emailUrl(email: Email) {
return `${process.env.MAILPIT_HOST}/view/${email.ID}.html`
}