Skip to content

Commit

Permalink
✅ Add test for signing up and setup for dev mailcatcher (#115)
Browse files Browse the repository at this point in the history
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
  • Loading branch information
patrick91 and tiangolo authored Jun 25, 2024
1 parent 9d2665b commit 12fad08
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 2 deletions.
13 changes: 12 additions & 1 deletion docker-compose.override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ services:
networks:
- traefik-public
- default

db:
restart: "no"
ports:
Expand All @@ -62,6 +62,11 @@ services:
INSTALL_DEV: ${INSTALL_DEV-true}
# command: sleep infinity # Infinite loop to keep container alive doing nothing
command: /start-reload.sh
environment:
SMTP_HOST: "mailcatcher"
SMTP_PORT: "1025"
SMTP_TLS: "false"
EMAILS_FROM_EMAIL: "noreply@example.com"

frontend:
restart: "no"
Expand All @@ -71,6 +76,12 @@ services:
- VITE_API_URL=http://${DOMAIN?Variable not set}
- NODE_ENV=development

mailcatcher:
image: schickling/mailcatcher
ports:
- "1080:1080"
- "1025:1025"

networks:
traefik-public:
# For local dev, don't expect an external Traefik network
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Common/EmailConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const EmailConfirmation = () => {
</Box>
)}
{mutation.isSuccess && (
<Box>
<Box data-testid="result">
<Text fontWeight="bolder" fontSize="2xl">
Successful Email Verification
</Text>
Expand Down
38 changes: 38 additions & 0 deletions frontend/tests/signup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { expect, test } from "@playwright/test"
import { findLastEmail } from "./utils/mailcatcher"
import { randomEmail } from "./utils/random"

test.use({ storageState: { cookies: [], origins: [] } })

test("Signup", async ({ page, request }) => {
const email = randomEmail()

await page.goto("/signup")
await page.getByPlaceholder("Full Name").fill("Playwright Test")
await page.getByPlaceholder("Email").fill(email)
await page.getByPlaceholder("Password", { exact: true }).fill("changethis")
await page.getByPlaceholder("Repeat Password").fill("changethis")
await page.getByRole("button", { name: "Sign Up" }).click()

const emailData = await findLastEmail({
request,
filter: (e) => e.recipients.includes(`<${email}>`),
timeout: 5000,
})

await page.goto(`http://localhost:1080/messages/${emailData.id}.html`)

const selector = 'a[href*="/verify-email?token="]'

let url = await page.getAttribute(selector, "href")

// TODO: update var instead of doing a replace
url = url!.replace("http://localhost/", "http://localhost:5173/")

await page.goto(url)

await expect(page.getByTestId("result")).toContainText(
"Successful Email Verification",
{ timeout: 5000 },
)
})
64 changes: 64 additions & 0 deletions frontend/tests/utils/mailcatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { 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("http://localhost:1080/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 findEmailPromise = new Promise<Email>(async (resolve, reject) => {
try {
while (true) {
const emailData = await findEmail({ request, filter })

if (emailData) {
resolve(emailData)
return
}

await new Promise((resolve) => setTimeout(resolve, 100))
}
} catch (e) {
reject(e)
}
})

return Promise.race([timeoutPromise, findEmailPromise])
}
2 changes: 2 additions & 0 deletions frontend/tests/utils/random.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const randomEmail = () =>
`test-${Math.random().toString(36).substring(7)}@example.com`

0 comments on commit 12fad08

Please sign in to comment.