Skip to content

Commit a8e7e8e

Browse files
committed
02/04: per-test app instances with app-launcher
1 parent 227ab68 commit a8e7e8e

File tree

11 files changed

+103
-26
lines changed

11 files changed

+103
-26
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DATABASE_URL="file:./test.db"

exercises/02.authentication/04.solution.third-party-providers/.gitignore

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ node_modules
33

44
/build
55
/server-build
6-
6+
77
.cache
88

9-
/prisma/data.db
10-
/prisma/data.db-journal
9+
/prisma/*.db
10+
/prisma/*.db-journal
1111
/tests/prisma
1212

1313
/test-results/

exercises/02.authentication/04.solution.third-party-providers/app/mocks/handlers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import {
55
GitHubEmailSchema,
66
} from '#app/utils/providers/github.server.ts'
77

8-
const githubUsers = new Collection({
8+
const gitHubUsers = new Collection({
99
schema: GitHubUserResponseSchema,
1010
})
1111

1212
const githubEmails = new Collection({
1313
schema: GitHubEmailSchema,
1414
})
1515

16-
export const mockGitHubUser = await githubUsers.create({
16+
export const mockGitHubUser = await gitHubUsers.create({
1717
id: 1,
1818
login: 'kody',
1919
name: 'Kody the Koala',

exercises/02.authentication/04.solution.third-party-providers/eslint.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ export default [
66
// add custom config objects here:
77
{
88
files: ['**/tests/**/*.ts'],
9-
rules: { 'react-hooks/rules-of-hooks': 'off' },
9+
rules: {
10+
'react-hooks/rules-of-hooks': 'off',
11+
'no-unused-vars': ['warning', { varsIgnorePattern: '^app$' }],
12+
},
1013
},
1114
{
1215
ignores: ['.react-router/*'],

exercises/02.authentication/04.solution.third-party-providers/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@
8484
"execa": "^9.5.2",
8585
"express": "^4.21.2",
8686
"express-rate-limit": "^7.5.0",
87-
"get-port": "^7.1.0",
8887
"glob": "^11.0.2",
8988
"input-otp": "^1.4.2",
9089
"intl-parse-accept-language": "^1.0.0",
@@ -113,6 +112,7 @@
113112
"zod": "^3.24.4"
114113
},
115114
"devDependencies": {
115+
"@epic-web/app-launcher": "^0.3.3",
116116
"@epic-web/config": "^1.20.1",
117117
"@faker-js/faker": "^9.7.0",
118118
"@msw/data": "^1.1.2",
@@ -143,6 +143,7 @@
143143
"esbuild": "^0.25.3",
144144
"eslint": "^9.26.0",
145145
"fs-extra": "^11.3.0",
146+
"get-port": "^7.1.0",
146147
"jsdom": "^25.0.1",
147148
"msw": "^2.7.6",
148149
"npm-run-all": "^4.1.5",

exercises/02.authentication/04.solution.third-party-providers/playwright.config.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { defineConfig, devices } from '@playwright/test'
22
import 'dotenv/config'
3+
import { config } from 'dotenv'
4+
5+
config({
6+
path: new URL('.env.test', import.meta.url),
7+
override: true,
8+
})
39

410
const PORT = process.env.PORT || '3000'
511

@@ -31,16 +37,16 @@ export default defineConfig({
3137
},
3238
],
3339

34-
webServer: {
35-
command: process.env.CI ? 'npm run start:mocks' : 'npm run dev',
36-
port: Number(PORT),
37-
reuseExistingServer: true,
38-
stdout: 'pipe',
39-
stderr: 'pipe',
40-
env: {
41-
PORT,
42-
NODE_ENV: 'test',
43-
DATABASE_PATH: 'file:./test.db',
44-
},
45-
},
40+
// webServer: {
41+
// command: process.env.CI ? 'npm run start:mocks' : 'npm run dev',
42+
// port: Number(PORT),
43+
// reuseExistingServer: true,
44+
// stdout: 'pipe',
45+
// stderr: 'pipe',
46+
// env: {
47+
// PORT,
48+
// NODE_ENV: 'test',
49+
// DATABASE_URL: process.env.DATABASE_URL,
50+
// },
51+
// },
4652
})
Binary file not shown.

exercises/02.authentication/04.solution.third-party-providers/tests/db-utils.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { execSync } from 'node:child_process'
12
import { faker } from '@faker-js/faker'
23
import bcrypt from 'bcryptjs'
34
import { UniqueEnforcer } from 'enforce-unique'
@@ -33,8 +34,6 @@ export function generateUserInfo(info?: Partial<TestUserInfo>): TestUserInfo {
3334
.toLowerCase()
3435
.replace(/[^a-z0-9_]/g, '_')
3536

36-
console.log({ username })
37-
3837
return {
3938
username,
4039
name: info?.name || `${firstName} ${lastName}`,
@@ -164,3 +163,18 @@ export async function getUserImages() {
164163

165164
return userImages
166165
}
166+
167+
export function prepareTestDatabase(filePath: string) {
168+
const env = {
169+
...process.env,
170+
DATABASE_URL: `file:${filePath}`,
171+
}
172+
173+
execSync('npx prisma migrate reset --force --skip-seed', {
174+
env,
175+
})
176+
177+
execSync('npx prisma migrate deploy', {
178+
env,
179+
})
180+
}

exercises/02.authentication/04.solution.third-party-providers/tests/e2e/authentication-github.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { mockGitHubUser } from '#app/mocks/handlers.ts'
22
import { createUser } from '#tests/db-utils.ts'
33
import { test, expect } from '#tests/test-extend.ts'
44

5-
test('onboards a new GitHub user', async ({ navigate, page }) => {
5+
test('onboards a new GitHub user', async ({ app, navigate, page }) => {
66
await navigate('/login')
77
await page.getByRole('button', { name: 'Login with GitHub' }).click()
88

@@ -31,6 +31,7 @@ test('onboards a new GitHub user', async ({ navigate, page }) => {
3131
await expect(
3232
page.getByRole('link', { name: mockGitHubUser.name }),
3333
).toBeVisible()
34+
await expect(page.getByText('Thanks for signing up!')).toBeVisible()
3435
})
3536

3637
test('authenticates an existing GitHub user', async ({ navigate, page }) => {

exercises/02.authentication/04.solution.third-party-providers/tests/test-extend.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { type AppProcess, defineLauncher } from '@epic-web/app-launcher'
12
import { test as testBase, expect } from '@playwright/test'
3+
import getPort from 'get-port'
24
import {
35
definePersona,
46
combinePersonas,
@@ -7,9 +9,10 @@ import {
79
import { href, type Register } from 'react-router'
810
import { getPasswordHash } from '#app/utils/auth.server.ts'
911
import { prisma } from '#app/utils/db.server.ts'
10-
import { generateUserInfo } from '#tests/db-utils'
12+
import { generateUserInfo, prepareTestDatabase } from '#tests/db-utils'
1113

1214
interface Fixtures {
15+
app: AppProcess
1316
navigate: <T extends keyof Register['pages']>(
1417
...args: Parameters<typeof href<T>>
1518
) => Promise<void>
@@ -45,10 +48,46 @@ const user = definePersona('user', {
4548
},
4649
})
4750

51+
const launcher = defineLauncher({
52+
async context() {
53+
return { port: await getPort() }
54+
},
55+
env({ context }) {
56+
return {
57+
PORT: context.port.toString(),
58+
}
59+
},
60+
command() {
61+
return 'npm run dev'
62+
},
63+
url({ context }) {
64+
return `http://localhost:${context.port}`
65+
},
66+
})
67+
4868
export const test = testBase.extend<Fixtures>({
49-
async navigate({ page }, use) {
69+
async app({}, use, testInfo) {
70+
const databasePath = `./test-${testInfo.testId}.db`
71+
prepareTestDatabase(databasePath)
72+
73+
/**
74+
* @todo No need to re-run the whole app on test re-runs.
75+
* Would be nice to spawn the app once, then reuse it across re-runs.
76+
* Just clear the DB between re-runs, that's crucial.
77+
*/
78+
const app = await launcher.run({
79+
env: () => ({
80+
DATABASE_URL: `file:${databasePath}`,
81+
}),
82+
})
83+
84+
await use(app)
85+
await app.dispose()
86+
},
87+
async navigate({ page, app }, use) {
5088
await use(async (...args) => {
51-
await page.goto(href(...args))
89+
const url = new URL(href(...args), app.url)
90+
await page.goto(url.href)
5291
})
5392
},
5493
authenticate: combinePersonas(user),

0 commit comments

Comments
 (0)