Skip to content

Commit e5379aa

Browse files
committed
02/04: bind prisma to test cases
1 parent a8e7e8e commit e5379aa

File tree

4 files changed

+67
-33
lines changed

4 files changed

+67
-33
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ export default [
88
files: ['**/tests/**/*.ts'],
99
rules: {
1010
'react-hooks/rules-of-hooks': 'off',
11-
'no-unused-vars': ['warning', { varsIgnorePattern: '^app$' }],
11+
'no-unused-vars': ['warn', { argsIgnorePattern: '^app$' }],
12+
'@typescript-eslint/no-unused-vars': [
13+
'warn',
14+
{ argsIgnorePattern: '^app$' },
15+
],
1216
},
1317
},
1418
{

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

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
import * as fs from 'node:fs'
12
import { execSync } from 'node:child_process'
23
import { faker } from '@faker-js/faker'
34
import bcrypt from 'bcryptjs'
45
import { UniqueEnforcer } from 'enforce-unique'
5-
import { getPasswordHash } from '#app/utils/auth.server.ts'
66
import { prisma } from '#app/utils/db.server.ts'
77

88
const uniqueUsernameEnforcer = new UniqueEnforcer()
99

10-
interface TestUserInfo {
10+
export interface TestUserInfo {
1111
username: string
1212
name: string
1313
email: string
@@ -41,27 +41,6 @@ export function generateUserInfo(info?: Partial<TestUserInfo>): TestUserInfo {
4141
}
4242
}
4343

44-
export async function createUser(info?: Partial<TestUserInfo>) {
45-
const userInfo = generateUserInfo(info)
46-
const password = 'supersecret'
47-
const user = await prisma.user.create({
48-
data: {
49-
...userInfo,
50-
password: { create: { hash: await getPasswordHash(password) } },
51-
},
52-
})
53-
54-
return {
55-
async [Symbol.asyncDispose]() {
56-
await prisma.user.deleteMany({
57-
where: { id: user.id },
58-
})
59-
},
60-
...user,
61-
password,
62-
}
63-
}
64-
6544
export async function createPasskey(input: {
6645
id: string
6746
userId: string
@@ -170,9 +149,9 @@ export function prepareTestDatabase(filePath: string) {
170149
DATABASE_URL: `file:${filePath}`,
171150
}
172151

173-
execSync('npx prisma migrate reset --force --skip-seed', {
174-
env,
175-
})
152+
if (fs.existsSync(filePath)) {
153+
fs.unlinkSync(filePath)
154+
}
176155

177156
execSync('npx prisma migrate deploy', {
178157
env,

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { mockGitHubUser } from '#app/mocks/handlers.ts'
2-
import { createUser } from '#tests/db-utils.ts'
32
import { test, expect } from '#tests/test-extend.ts'
43

54
test('onboards a new GitHub user', async ({ app, navigate, page }) => {
@@ -34,7 +33,12 @@ test('onboards a new GitHub user', async ({ app, navigate, page }) => {
3433
await expect(page.getByText('Thanks for signing up!')).toBeVisible()
3534
})
3635

37-
test('authenticates an existing GitHub user', async ({ navigate, page }) => {
36+
test('authenticates the user with a connected GitHub account', async ({
37+
app,
38+
createUser,
39+
navigate,
40+
page,
41+
}) => {
3842
await using user = await createUser({
3943
name: mockGitHubUser.name,
4044
username: mockGitHubUser.login,
@@ -45,4 +49,5 @@ test('authenticates an existing GitHub user', async ({ navigate, page }) => {
4549
await page.getByRole('button', { name: 'Login with GitHub' }).click()
4650

4751
await expect(page.getByRole('link', { name: user.name! })).toBeVisible()
52+
await expect(page.getByText('Your "kody" GitHub account')).toBeVisible()
4853
})

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

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type AppProcess, defineLauncher } from '@epic-web/app-launcher'
22
import { test as testBase, expect } from '@playwright/test'
3+
import { PrismaClient, type User } from '@prisma/client'
34
import getPort from 'get-port'
45
import {
56
definePersona,
@@ -9,14 +10,23 @@ import {
910
import { href, type Register } from 'react-router'
1011
import { getPasswordHash } from '#app/utils/auth.server.ts'
1112
import { prisma } from '#app/utils/db.server.ts'
12-
import { generateUserInfo, prepareTestDatabase } from '#tests/db-utils'
13+
import {
14+
generateUserInfo,
15+
prepareTestDatabase,
16+
type TestUserInfo,
17+
} from '#tests/db-utils'
1318

1419
interface Fixtures {
1520
app: AppProcess
21+
databasePath: string
22+
prisma: PrismaClient
1623
navigate: <T extends keyof Register['pages']>(
1724
...args: Parameters<typeof href<T>>
1825
) => Promise<void>
1926
authenticate: AuthenticateFunction<[typeof user]>
27+
createUser: (
28+
info?: Partial<TestUserInfo>,
29+
) => Promise<AsyncDisposable & User & { password: string }>
2030
}
2131

2232
const user = definePersona('user', {
@@ -54,6 +64,7 @@ const launcher = defineLauncher({
5464
},
5565
env({ context }) {
5666
return {
67+
NODE_ENV: 'test',
5768
PORT: context.port.toString(),
5869
}
5970
},
@@ -66,31 +77,66 @@ const launcher = defineLauncher({
6677
})
6778

6879
export const test = testBase.extend<Fixtures>({
69-
async app({}, use, testInfo) {
80+
async databasePath({}, use, testInfo) {
7081
const databasePath = `./test-${testInfo.testId}.db`
82+
await use(databasePath)
83+
},
84+
async prisma({ databasePath }, use) {
85+
const prisma = new PrismaClient({
86+
datasourceUrl: `file:${databasePath}`,
87+
})
88+
await use(prisma)
89+
await prisma.$disconnect()
90+
},
91+
async app({ databasePath }, use) {
7192
prepareTestDatabase(databasePath)
7293

94+
let a = performance.now()
7395
/**
7496
* @todo No need to re-run the whole app on test re-runs.
7597
* Would be nice to spawn the app once, then reuse it across re-runs.
7698
* Just clear the DB between re-runs, that's crucial.
7799
*/
78100
const app = await launcher.run({
79101
env: () => ({
102+
// Configure the app's Prisma client to use the scoped database.
80103
DATABASE_URL: `file:${databasePath}`,
81104
}),
82105
})
106+
console.log('APP RUNNING IN:', performance.now() - a)
83107

84108
await use(app)
85109
await app.dispose()
86110
},
87-
async navigate({ page, app }, use) {
111+
async navigate({ page, app, contextOptions }, use) {
88112
await use(async (...args) => {
89-
const url = new URL(href(...args), app.url)
113+
const url = new URL(href(...args), app.url || contextOptions.baseURL)
90114
await page.goto(url.href)
91115
})
92116
},
93117
authenticate: combinePersonas(user),
118+
async createUser({ prisma }, use) {
119+
await use(async (info) => {
120+
const userInfo = generateUserInfo(info)
121+
const password = 'supersecret'
122+
const user = await prisma.user.create({
123+
data: {
124+
...userInfo,
125+
password: { create: { hash: await getPasswordHash(password) } },
126+
},
127+
})
128+
129+
return {
130+
async [Symbol.asyncDispose]() {
131+
await prisma.user.deleteMany({
132+
where: { id: user.id },
133+
})
134+
},
135+
...user,
136+
password,
137+
}
138+
})
139+
},
94140
})
95141

96142
export { expect }

0 commit comments

Comments
 (0)