Skip to content

Commit

Permalink
e2e tests for /admin
Browse files Browse the repository at this point in the history
  • Loading branch information
goodeats committed Oct 26, 2023
1 parent 1986301 commit 45fd4ea
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 15 deletions.
16 changes: 16 additions & 0 deletions app/routes/admin+/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { type MetaFunction } from '@remix-run/react'
import { PageContentIndex } from '#app/components/templates/index.ts'

export default function AdminIndexRoute() {
return <PageContentIndex message="Hello admin" />
}

export const meta: MetaFunction = () => {
return [
{ title: `Admin | Epic Notes` },
{
name: 'description',
content: `Admin page for Epic Notes`,
},
]
}
54 changes: 54 additions & 0 deletions app/routes/admin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { json, type DataFunctionArgs } from '@remix-run/node'
import { Outlet, useLoaderData } from '@remix-run/react'
import { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'
import {
Main,
MainContainer,
MainContent,
} from '#app/components/layout/index.ts'
import { PageSidebar } from '#app/components/templates/index.ts'
import { requireAdminUserId } from '#app/utils/auth.server.ts'
import { prisma } from '#app/utils/db.server.ts'
import { invariantResponse } from '#app/utils/misc.tsx'

export async function loader({ request }: DataFunctionArgs) {
const userId = await requireAdminUserId(request)
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
name: true,
username: true,
image: { select: { id: true } },
},
})
invariantResponse(user, 'User not found', { status: 404 })
return json({ user })
}

export default function AdminRoute() {
const data = useLoaderData<typeof loader>()
const { user } = data
return (
<Main>
<MainContainer>
<PageSidebar owner={user} title="Admin" list={[]} />
<MainContent>
<Outlet />
</MainContent>
</MainContainer>
</Main>
)
}

export function ErrorBoundary() {
return (
<GeneralErrorBoundary
statusHandlers={{
404: ({ params }) => (
<p>No user with the username "{params.username}" exists</p>
),
}}
/>
)
}
17 changes: 17 additions & 0 deletions app/utils/auth.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { prisma } from './db.server.ts'
import { combineHeaders, downloadFile } from './misc.tsx'
import { type ProviderUser } from './providers/provider.ts'
import { authSessionStorage } from './session.server.ts'
import { redirectWithToast } from './toast.server.ts'

export const SESSION_EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30
export const getSessionExpirationDate = () =>
Expand Down Expand Up @@ -63,6 +64,22 @@ export async function requireUserId(
return userId
}

export async function requireAdminUserId(request: Request) {
const userId = await requireUserId(request)
const user = await prisma.user.findFirst({
select: { id: true },
where: { id: userId, roles: { some: { name: 'admin' } } },
})
if (!user) {
throw await redirectWithToast(request.headers.get('Referer') ?? '/', {
title: 'Unauthorized',
description: `You do not have permission to access this page.`,
type: 'error',
})
}
return userId
}

export async function requireAnonymous(request: Request) {
const userId = await getUserId(request)
if (userId) {
Expand Down
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default defineConfig({
testDir: './tests/e2e',
timeout: 15 * 1000,
expect: {
timeout: 5 * 1000,
timeout: 3 * 1000,
},
fullyParallel: true,
forbidOnly: !!process.env.CI,
Expand Down
11 changes: 11 additions & 0 deletions tests/e2e/admin/admin-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { type Page } from '@playwright/test'
import { goTo } from '#tests/utils/page-utils.ts'
import { expectUrl } from '#tests/utils/url-utils.ts'

export async function goToAdminPage(page: Page) {
await goTo(page, '/admin')
}

export async function expectAdminPage(page: Page) {
await expectUrl({ page, url: '/admin' })
}
24 changes: 24 additions & 0 deletions tests/e2e/admin/admin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { test } from '#tests/playwright-utils.ts'
import { expectLoginUrl, expectUrl } from '#tests/utils/url-utils.ts'
import { expectAdminPage, goToAdminPage } from './admin-utils.ts'

test.describe('User cannot view Admin', () => {
test('when not logged in', async ({ page }) => {
await goToAdminPage(page)
await expectLoginUrl({ page, redirectTo: '/admin' })
})

test('when logged in as user', async ({ page, login }) => {
await login()
await goToAdminPage(page)
await expectUrl({ page, url: '/' })
})
})

test.describe('User can view Admin', () => {
test('when logged in as admin', async ({ page, login }) => {
await login({ roles: ['user', 'admin'] })
await goToAdminPage(page)
await expectAdminPage(page)
})
})
6 changes: 3 additions & 3 deletions tests/e2e/notes/create.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, expect } from '#tests/playwright-utils.ts'
import { clickLink, goTo } from '#tests/utils/page-utils.ts'
import { expectURL } from '#tests/utils/url-utils.ts'
import { expectUrl } from '#tests/utils/url-utils.ts'
import {
createNote,
expectCreatedNotePage,
Expand All @@ -15,15 +15,15 @@ test.describe('Users cannot create notes', () => {
test.describe('when not authorized', () => {
test('when not logged in', async ({ page, login }) => {
await goTo(page, '/users/username/notes/new')
await expectURL({ page, url: /\/login/ })
await expectUrl({ page, url: /\/login/ })
})

// TODO: what to do when this happens?
// test('when logged in as another user', async ({ page, login }) => {
// const user = await login()
// const anotherUser = await login()
// await goTo(page, `/users/${anotherUser.username}/notes/new`)
// await expectURL({ page, url: new RegExp(`/users/${user.username}/notes`) })
// await expectUrl({ page, url: new RegExp(`/users/${user.username}/notes`) })
// })
})

Expand Down
12 changes: 6 additions & 6 deletions tests/e2e/notes/notes-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type Page } from '@playwright/test'
import { prisma } from '#app/utils/db.server.ts'
import { expect } from '#tests/playwright-utils.ts'
import { fillSubmitForm, goTo } from '#tests/utils/page-utils.ts'
import { expectURL } from '#tests/utils/url-utils.ts'
import { expectUrl } from '#tests/utils/url-utils.ts'

export interface TestNote {
title: string
Expand Down Expand Up @@ -53,17 +53,17 @@ export async function fillAndSubmitNoteForm(

export async function expectNotesPage(page: Page, username: string) {
const url = `/users/${username}/notes`
await expectURL({ page, url })
await expectUrl({ page, url })
}

export async function expectNewNotePage(page: Page, username: string) {
const url = `/users/${username}/notes/new`
await expectURL({ page, url })
await expectUrl({ page, url })
}

export async function expectCreatedNotePage(page: Page, username: string) {
const url = new RegExp(`/users/${username}/notes/.*`)
await expectURL({ page, url })
await expectUrl({ page, url })
}

export async function expectNotePage(
Expand All @@ -72,7 +72,7 @@ export async function expectNotePage(
noteId: string,
) {
const url = `/users/${username}/notes/${noteId}`
await expectURL({ page, url })
await expectUrl({ page, url })
}

export async function expectNoteEditPage(
Expand All @@ -81,7 +81,7 @@ export async function expectNoteEditPage(
noteId: string,
) {
const url = `/users/${username}/notes/${noteId}/edit`
await expectURL({ page, url })
await expectUrl({ page, url })
}

export async function expectFieldInvalid(page: Page, fieldName: string) {
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/users/users-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import {
expectNoLink,
goTo,
} from '#tests/utils/page-utils.ts'
import { expectURL } from '#tests/utils/url-utils.ts'
import { expectUrl } from '#tests/utils/url-utils.ts'

export async function goToUserPage(page: Page, username: string) {
await goTo(page, `/users/${username}`)
}

export async function expectUserPage(page: Page, username: string) {
const url = `/users/${username}`
await expectURL({ page, url })
await expectUrl({ page, url })
}

async function getUser(username: string) {
Expand Down
14 changes: 14 additions & 0 deletions tests/playwright-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ async function getOrInsertUser({
username ??= userData.username
password ??= userData.username
email ??= userData.email

// create roles if they don't exist
for (const role of roles) {
const existingRole = await prisma.role.findUnique({
where: { name: role },
})

if (!existingRole) {
await prisma.role.create({
data: { name: role },
})
}
}

return await prisma.user.create({
select,
data: {
Expand Down
5 changes: 2 additions & 3 deletions tests/utils/url-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface ExpectURLProps {
url: string | RegExp
}

export async function expectURL({ page, url }: ExpectURLProps) {
export async function expectUrl({ page, url }: ExpectURLProps) {
await expect(page).toHaveURL(url)
}

Expand All @@ -21,6 +21,5 @@ export async function expectLoginUrl({
const expectedUrl = redirectTo
? `/login?redirectTo=${encodeURIComponent(redirectTo)}`
: '/login'
await expectURL({ page, url: expectedUrl })
// await expectURL({ page, url: '/login' })
await expectUrl({ page, url: expectedUrl })
}

0 comments on commit 45fd4ea

Please sign in to comment.