Skip to content

Commit

Permalink
Adds auth hooks headless tests. Refactors headless tests a bit.
Browse files Browse the repository at this point in the history
  • Loading branch information
infomiho committed May 22, 2024
1 parent 6ada19e commit 0580121
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 44 deletions.
7 changes: 0 additions & 7 deletions waspc/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
# Changelog

<<<<<<< HEAD
=======

>>>>>>> f5d285d61 (Updates changelog)
## 0.14.0 (TBD)

### 🎉 New Features

- Simplified Auth User API: Introduced a simpler API for accessing user auth fields (for example `username`, `email`, `isEmailVerified`) directly on the `user` object, eliminating the need for helper functions.
<<<<<<< HEAD
- Improved API for calling Operations (Queries and Actions) directly.
=======
>>>>>>> f5d285d61 (Updates changelog)
- Auth Hooks: you can now hook into the auth process with `onBeforeSignup`, `onAfterSignup` hooks. You can also modify the OAuth redirect URL with `onBeforeOAuthRedirect` hook and get the provider token with `onAfterOAuthTokenReceived` hook.

```wasp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "isOnAfterSignupHookCalled" BOOLEAN NOT NULL DEFAULT false;
20 changes: 20 additions & 0 deletions waspc/headless-test/examples/todoApp/src/auth/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { HttpError } from 'wasp/server'
import type {
OnAfterSignupHookFn,
OnBeforeSignupHookFn,
} from 'wasp/server/auth'

export const onBeforeSignup: OnBeforeSignupHookFn = async (args) => {
if (args.providerId.providerUserId === 'notallowed@email.com') {
throw new HttpError(403, 'On Before Signup Hook disallows this email.')
}
}

export const onAfterSignup: OnAfterSignupHookFn = async (args) => {
await args.prisma.user.update({
where: { id: args.user.id },
data: {
isOnAfterSignupHookCalled: true,
},
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ export const ProfilePage = ({ user }: { user: User }) => {
<div>
Hello <strong>{user.getFirstProviderUserId()}</strong>! Your status is{' '}
<strong>
{user.identities.email && user.identities.email.isEmailVerified
? 'verfied'
: 'unverified'}
{user.identities.email?.isEmailVerified ? 'verfied' : 'unverified'}
</strong>
.
</div>
<div>
Value of <code>user.isOnAfterSignupHookCalled</code> is{' '}
<strong>{user.isOnAfterSignupHookCalled ? 'true' : 'false'}</strong>.
</div>
<br />
<Link to="/">Go to dashboard</Link>
</>
Expand Down
5 changes: 4 additions & 1 deletion waspc/headless-test/examples/todoApp/todoApp.wasp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ app todoApp {
google: {}
},
onAuthFailedRedirectTo: "/login",
onAuthSucceededRedirectTo: "/profile"
onAuthSucceededRedirectTo: "/profile",
onBeforeSignup: import { onBeforeSignup } from "@src/auth/hooks.js",
onAfterSignup: import { onAfterSignup } from "@src/auth/hooks.js",
},
server: {
setupFn: import setup from "@src/server/serverSetup.js",
Expand All @@ -50,6 +52,7 @@ app todoApp {

entity User {=psl
id Int @id @default(autoincrement())
isOnAfterSignupHookCalled Boolean @default(false)
// Business logic
tasks Task[]
psl=}
Expand Down
50 changes: 50 additions & 0 deletions waspc/headless-test/tests/auth-hooks.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { test, expect } from '@playwright/test'
import {
generateRandomCredentials,
performLogin,
performSignup,
} from './helpers'

test.describe('auth hooks', () => {
test.describe.configure({ mode: 'serial' })

/*
We set up the "before signup hook" to throw an error for a specific email address.
*/
test('before signup hook works', async ({ page }) => {
const emailThatThrowsError = 'notallowed@email.com'
const password = '12345678'

await performSignup(page, {
email: emailThatThrowsError,
password,
})

await expect(page.locator('body')).toContainText(
'On Before Signup Hook disallows this email.',
)
})

/*
We set up the "after signup hook" to set a value in the user object.
*/
test('after signup hook works', async ({ page }) => {
const { email, password } = generateRandomCredentials()

await performSignup(page, {
email,
password,
})

await performLogin(page, {
email,
password,
})

await expect(page).toHaveURL('/profile')

await expect(page.locator('body')).toContainText(
'Value of user.isOnAfterSignupHookCalled is true.',
)
})
})
43 changes: 43 additions & 0 deletions waspc/headless-test/tests/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Page } from '@playwright/test'

export async function performSignup(
page: Page,
{ email, password }: { email: string; password: string },
) {
await page.goto('/signup')

await page.waitForSelector('text=Create a new account')

await page.locator("input[type='email']").fill(email)
await page.locator("input[type='password']").fill(password)
await page.locator('button').click()
}

export async function performLogin(
page: Page,
{
email,
password,
}: {
email: string
password: string
},
) {
await page.goto('/login')

await page.waitForSelector('text=Log in to your account')

await page.locator("input[type='email']").fill(email)
await page.locator("input[type='password']").fill(password)
await page.getByRole('button', { name: 'Log in' }).click()
}

export function generateRandomCredentials(): {
email: string
password: string
} {
return {
email: `test${Math.random().toString(36).substring(7)}@test.com`,
password: '12345678',
}
}
40 changes: 20 additions & 20 deletions waspc/headless-test/tests/simple.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { test, expect } from '@playwright/test'
import {
generateRandomCredentials,
performLogin,
performSignup,
} from './helpers'

test('has title', async ({ page }) => {
await page.goto('/')

await expect(page).toHaveTitle(/ToDo App/)
})
test.describe('signup and login', () => {
const randomEmail = `test${Math.random().toString(36).substring(7)}@test.com`
const password = '12345678'
const { email, password } = generateRandomCredentials()

test.describe.configure({ mode: 'serial' })

Expand All @@ -22,38 +26,34 @@ test.describe('signup and login', () => {
})

test('can sign up', async ({ page }) => {
await page.goto('/signup')

await page.waitForSelector('text=Create a new account')

await page.locator("input[type='email']").fill(randomEmail)
await page.locator("input[type='password']").fill(password)
await page.locator('button').click()
await performSignup(page, {
email,
password,
})

await expect(page.locator('body')).toContainText(
`You've signed up successfully! Check your email for the confirmation link.`,
)
})

test('can log in and create a task', async ({ page }) => {
await page.goto('/login')

await page.waitForSelector('text=Log in to your account')

await page.locator("input[type='email']").fill(randomEmail)
await page.locator("input[type='password']").fill('12345678xxx')
await page.getByRole('button', { name: 'Log in' }).click()
await performLogin(page, {
email,
password: '12345678xxx',
})

await expect(page.locator('body')).toContainText(`Invalid credentials`)
await expect(page.locator('body')).toContainText('Invalid credentials')

await page.locator("input[type='password']").fill(password)
await page.getByRole('button', { name: 'Log in' }).click()
await performLogin(page, {
email,
password,
})

await expect(page).toHaveURL('/profile')

await page.goto('/')

const randomTask = 'New Task ' + Math.random().toString(36).substring(7)
const randomTask = `New Task ${Math.random().toString(36).substring(7)}`
await page.locator("input[type='text']").fill(randomTask)
await page.getByText('Create new task').click()

Expand Down
22 changes: 9 additions & 13 deletions waspc/headless-test/tests/user-api.spec.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
import { test, expect } from '@playwright/test'
import { generateRandomCredentials, performSignup } from './helpers'

test.describe('user API', () => {
const randomEmail = `test${Math.random().toString(36).substring(7)}@test.com`
const password = '12345678'
const { email, password } = generateRandomCredentials()

test.describe.configure({ mode: 'serial' })

test.beforeAll(async ({ browser }) => {
const page = await browser.newPage()

// Sign up
await page.goto('/signup')

await page.waitForSelector('text=Create a new account')

await page.locator("input[type='email']").fill(randomEmail)
await page.locator("input[type='password']").fill(password)
await page.locator('button').click()
await performSignup(page, {
email,
password,
})
})

test('user API works on the client', async ({ page }) => {
await page.goto('/login')

await page.waitForSelector('text=Log in to your account')

await page.locator("input[type='email']").fill(randomEmail)
await page.locator("input[type='email']").fill(email)
await page.locator("input[type='password']").fill(password)
await page.getByRole('button', { name: 'Log in' }).click()

await page.waitForSelector('text=Profile page')

await expect(page.locator('body')).toContainText(
`Hello ${randomEmail}! Your status is verfied`,
`Hello ${email}! Your status is verfied`,
)

await expect(page.locator('a[href="/profile"]')).toContainText(randomEmail)
await expect(page.locator('a[href="/profile"]')).toContainText(email)
})
})

0 comments on commit 0580121

Please sign in to comment.