Skip to content

Commit

Permalink
make e2e tests more reliable
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Jun 11, 2024
1 parent 55ead1d commit 6d90892
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 28 deletions.
5 changes: 3 additions & 2 deletions app/utils/user-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const UsernameSchema = z
message: 'Username can only include letters, numbers, and underscores',
})
// users can type the username in any case, but we store it in lowercase
.transform(value => value.toLowerCase())
.transform(value => value.trim().toLowerCase())

export const PasswordSchema = z
.string({ required_error: 'Password is required' })
Expand All @@ -22,7 +22,8 @@ export const NameSchema = z
export const PhoneNumberSchema = z
.string({ required_error: 'Phone number is required' })
.min(3, { message: 'Phone number is too short' })
.max(20, { message: 'Phone number is too long' })
.max(30, { message: 'Phone number is too long' })
.transform(value => value.trim())

export const PasswordAndConfirmPasswordSchema = z
.object({ password: PasswordSchema, confirmPassword: PasswordSchema })
Expand Down
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
"enforce-unique": "^1.3.0",
"esbuild": "^0.21.4",
"eslint": "^9.4.0",
"filenamify": "^6.0.0",
"fs-extra": "^11.2.0",
"jsdom": "^24.1.0",
"msw": "2.3.1",
Expand Down
18 changes: 8 additions & 10 deletions tests/e2e/onboarding.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { invariant } from '@epic-web/invariant'
import { faker } from '@faker-js/faker'
import { prisma } from '#app/utils/db.server.ts'
import { readText } from '#tests/mocks/utils.ts'
import { createUser, expect, test as base } from '#tests/playwright-utils.ts'
import { waitForText } from '#tests/mocks/utils.ts'
import { test as base, createUser, expect } from '#tests/playwright-utils.ts'

const URL_REGEX = /(?<url>https?:\/\/[^\s$.?#].[^\s]*)/
const CODE_REGEX = /code: (?<code>[\d\w]+)/
Expand Down Expand Up @@ -62,8 +62,7 @@ test('onboarding with link', async ({ page, getOnboardingData }) => {
const sourceNumber = await prisma.sourceNumber.findFirstOrThrow({
select: { phoneNumber: true },
})
const textMessage = await readText(onboardingData.phoneNumber)
invariant(textMessage, 'Text message not found')
const textMessage = await waitForText(onboardingData.phoneNumber)
expect(textMessage.To).toBe(onboardingData.phoneNumber.toLowerCase())
expect(textMessage.From).toBe(sourceNumber.phoneNumber)
expect(textMessage.Body).toMatch(/welcome/i)
Expand Down Expand Up @@ -127,8 +126,9 @@ test('onboarding with a short code', async ({ page, getOnboardingData }) => {
const sourceNumber = await prisma.sourceNumber.findFirstOrThrow({
select: { phoneNumber: true },
})
const textMessage = await readText(onboardingData.phoneNumber)
invariant(textMessage, 'Text message not found')
const textMessage = await waitForText(onboardingData.phoneNumber, {
errorMessage: 'Onboarding code not found',
})
expect(textMessage.To).toBe(onboardingData.phoneNumber)
expect(textMessage.From).toBe(sourceNumber.phoneNumber)
expect(textMessage.Body).toMatch(/welcome/i)
Expand Down Expand Up @@ -176,8 +176,7 @@ test('reset password with a link', async ({ page, insertNewUser }) => {
const sourceNumber = await prisma.sourceNumber.findFirstOrThrow({
select: { phoneNumber: true },
})
const textMessage = await readText(user.phoneNumber)
invariant(textMessage, 'Text message not found')
const textMessage = await waitForText(user.phoneNumber)
expect(textMessage.Body).toMatch(/password reset/i)
expect(textMessage.To).toBe(user.phoneNumber)
expect(textMessage.From).toBe(sourceNumber.phoneNumber)
Expand Down Expand Up @@ -237,8 +236,7 @@ test('reset password with a short code', async ({ page, insertNewUser }) => {
const sourceNumber = await prisma.sourceNumber.findFirstOrThrow({
select: { phoneNumber: true },
})
const textMessage = await readText(user.phoneNumber)
invariant(textMessage, 'Text message not found')
const textMessage = await waitForText(user.phoneNumber)
expect(textMessage.Body).toMatch(/password reset/i)
expect(textMessage.To).toBe(user.phoneNumber)
expect(textMessage.From).toBe(sourceNumber.phoneNumber)
Expand Down
13 changes: 5 additions & 8 deletions tests/e2e/send.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { invariant } from '@epic-web/invariant'
import { faker } from '@faker-js/faker'
import { prisma } from '#app/utils/db.server.ts'
import { readText } from '#tests/mocks/utils.ts'
import { waitForText } from '#tests/mocks/utils.ts'
import {
createMessage,
createRecipient,
expect,
test,
createRecipient,
createMessage,
waitFor,
} from '#tests/playwright-utils.ts'

Expand Down Expand Up @@ -48,8 +47,7 @@ test('Users can write and send a message immediately', async ({
const sourceNumber = await prisma.sourceNumber.findFirstOrThrow({
select: { phoneNumber: true },
})
const textMessage = await readText(recipient.phoneNumber)
invariant(textMessage, 'Text message not found')
const textMessage = await waitForText(recipient.phoneNumber)
expect(textMessage.To).toBe(recipient.phoneNumber)
expect(textMessage.From).toBe(sourceNumber.phoneNumber)

Expand Down Expand Up @@ -116,8 +114,7 @@ test('Scheduled messages go out on schedule', async ({ page, login }) => {
const sourceNumber = await prisma.sourceNumber.findFirstOrThrow({
select: { phoneNumber: true },
})
const textMessage = await readText(recipient.phoneNumber)
invariant(textMessage, 'Text message not found')
const textMessage = await waitForText(recipient.phoneNumber)
expect(textMessage.To).toBe(recipient.phoneNumber)
expect(textMessage.From).toBe(sourceNumber.phoneNumber)

Expand Down
8 changes: 4 additions & 4 deletions tests/e2e/settings-profile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { invariant } from '@epic-web/invariant'
import { faker } from '@faker-js/faker'
import { verifyUserPassword } from '#app/utils/auth.server.ts'
import { prisma } from '#app/utils/db.server.ts'
import { readText } from '#tests/mocks/utils.ts'
import { expect, test, createUser, waitFor } from '#tests/playwright-utils.ts'
import { waitForText } from '#tests/mocks/utils.ts'
import { createUser, expect, test } from '#tests/playwright-utils.ts'

const CODE_REGEX = /Here's your verification code: (?<code>[\d\w]+)/

Expand Down Expand Up @@ -63,7 +63,7 @@ test('Users can change their phone number', async ({ page, login }) => {
.fill(newPhoneNumber)
await page.getByRole('button', { name: /send confirmation/i }).click()
await expect(page.getByText(/check your texts/i)).toBeVisible()
const text = await waitFor(() => readText(newPhoneNumber), {
const text = await waitForText(newPhoneNumber, {
errorMessage: 'Confirmation text message was not sent',
})
invariant(text, 'Text was not sent')
Expand All @@ -80,7 +80,7 @@ test('Users can change their phone number', async ({ page, login }) => {
})
invariant(updatedUser, 'Updated user not found')
expect(updatedUser.phoneNumber).toBe(newPhoneNumber)
const noticeText = await waitFor(() => readText(preUpdateUser.phoneNumber), {
const noticeText = await waitForText(preUpdateUser.phoneNumber, {
errorMessage: 'Notice text was not sent',
})
expect(noticeText.Body).toContain('changed')
Expand Down
16 changes: 12 additions & 4 deletions tests/mocks/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import filenamify from 'filenamify'
import fsExtra from 'fs-extra'
import { z } from 'zod'
import { waitFor } from '#tests/playwright-utils.js'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const fixturesDirPath = path.join(__dirname, '..', 'fixtures')
Expand All @@ -28,7 +30,7 @@ export const TextMessageSchema = z.object({

export async function writeText(rawText: unknown) {
const textMessage = TextMessageSchema.parse(rawText)
await createFixture('texts', textMessage.To, textMessage)
await createFixture('texts', filenamify(textMessage.To), textMessage)
return textMessage
}

Expand All @@ -40,14 +42,20 @@ export async function requireText(recipient: string) {

export async function readText(recipient: string) {
try {
const textMessage = await readFixture('texts', recipient)
const textMessage = await readFixture('texts', filenamify(recipient))
return TextMessageSchema.parse(textMessage)
} catch (error) {
console.error(`Error reading text message to ${recipient}`, error)
} catch {
return null
}
}

export async function waitForText(
recipient: string,
options: Parameters<typeof waitFor>[1] = {},
) {
return waitFor(() => requireText(recipient), options)
}

export function requireHeader(headers: Headers, header: string) {
if (!headers.has(header)) {
const headersString = JSON.stringify(
Expand Down

0 comments on commit 6d90892

Please sign in to comment.