-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auth smoke tests #4673
Auth smoke tests #4673
Changes from 15 commits
fc59cb7
fd0acee
9ed479d
71c8b98
ee16ee4
4779f1d
efd4d35
b37dd81
428e567
91d29e7
dcc4c6b
883180d
90f35c3
ace4b34
3ef5127
c517852
1c1d0fe
7f0c0e2
1d30f45
338e95f
c22755c
9a03437
2fe5e6d
05b0533
2594bd4
dbf7273
085ebfc
f04ed82
5e1a12a
8f66711
46c3049
92395db
c7e155c
90b637c
fd819cb
23ecbe3
4dd0eb0
8bc2a61
b69909b
07bb80e
e525796
22392c6
59ceda7
123c6b2
f561e06
e2dff3e
e4dde2d
5c077f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ describe('requireAuth directive', () => { | |
// If you want to set values in context, pass it through e.g. | ||
// mockRedwoodDirective(requireAuth, { context: { currentUser: { id: 1, name: 'Lebron McGretzky' } }}) | ||
const mockExecution = mockRedwoodDirective(requireAuth, { | ||
context: { currentUser: { id: 1, roles: 'ADMIN' } }, | ||
context: { currentUser: { id: 1, roles: 'ADMIN', email: '[email protected]' } }, | ||
}) | ||
|
||
expect(mockExecution).not.toThrowError() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import ProfilePage from './ProfilePage' | ||
|
||
export const generated = () => { | ||
return <ProfilePage /> | ||
} | ||
|
||
export default { title: 'Pages/ProfilePage' } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { render, waitFor, screen } from '@redwoodjs/testing/web' | ||
|
||
import ProfilePage from './ProfilePage' | ||
|
||
describe('ProfilePage', () => { | ||
it('renders successfully', async () => { | ||
mockCurrentUser({ | ||
email: '[email protected]', | ||
id: 84849020, | ||
roles: 'BAZINGA', | ||
}) | ||
|
||
await waitFor(async () => { | ||
expect(() => { | ||
render(<ProfilePage />) | ||
}).not.toThrow() | ||
}) | ||
|
||
expect(await screen.findByText('[email protected]')).toBeInTheDocument() | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { useAuth } from '@redwoodjs/auth' | ||
import { Link, routes } from '@redwoodjs/router' | ||
import { MetaTags } from '@redwoodjs/web' | ||
|
||
const ProfilePage = () => { | ||
const { currentUser, isAuthenticated, hasRole, loading } = useAuth() | ||
|
||
if (loading) { | ||
return <p>Loading...</p> | ||
} | ||
|
||
return ( | ||
<> | ||
<MetaTags title="Profile" description="Profile page" /> | ||
|
||
<h1 className="text-2xl">Profile</h1> | ||
|
||
<table className="rw-table"> | ||
<thead> | ||
<tr> | ||
<th>Key</th> | ||
<th>Value</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{Object.keys(currentUser).map((key) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just prints the keys from currentUser in useAuth. |
||
return ( | ||
<tr key={key}> | ||
<td>{key.toUpperCase()}</td> | ||
<td>{currentUser[key]}</td> | ||
</tr> | ||
) | ||
})} | ||
|
||
<tr key="isAuthenticated"> | ||
<td>isAuthenticated</td> | ||
<td>{JSON.stringify(isAuthenticated)}</td> | ||
</tr> | ||
|
||
<tr key="hasRole"> | ||
<td>Is Admin</td> | ||
<td>{JSON.stringify(hasRole('ADMIN'))}</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</> | ||
) | ||
} | ||
|
||
export default ProfilePage |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,43 @@ | ||
/* eslint-disable no-empty-pattern */ | ||
import { test as base } from '@playwright/test' | ||
import execa from 'execa' | ||
import isPortReachable from 'is-port-reachable' | ||
|
||
import { waitForServer } from '../util' | ||
|
||
// Declare worker fixtures. | ||
type DevServerFixtures = { | ||
export type DevServerFixtures = { | ||
webServerPort: number | ||
apiServerPort: number | ||
server: any | ||
webUrl: string | ||
} | ||
|
||
// Note that we did not provide an test-scoped fixtures, so we pass {}. | ||
const test = base.extend<any, DevServerFixtures>({ | ||
webServerPort: [ | ||
async ({}, use, workerInfo) => { | ||
async ({}, use) => { | ||
// "port" fixture uses a unique value of the worker process index. | ||
await use(9000 + workerInfo.workerIndex) | ||
await use(9000) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changes here are because we actually don't want to spin up multiple dev servers (because our dev process regenerates some files) This does not affect running tests in parallel though, because browser contexts are isolated |
||
}, | ||
{ scope: 'worker' }, | ||
], | ||
apiServerPort: [ | ||
async ({}, use, workerInfo) => { | ||
// "port" fixture uses a unique value of the worker process index. | ||
await use(9001 + workerInfo.workerIndex) | ||
async ({}, use) => { | ||
await use(9001) | ||
}, | ||
{ scope: 'worker' }, | ||
], | ||
webUrl: [ | ||
async ({ webServerPort }, use) => { | ||
await use(`localhost:${webServerPort}`) | ||
}, | ||
{ scope: 'worker' }, | ||
], | ||
|
||
// "server" fixture starts automatically for every worker - we pass "auto" for that. | ||
server: [ | ||
async ({ webServerPort, apiServerPort }, use) => { | ||
console.log('Starting dev server.....') | ||
|
||
const projectPath = process.env.PROJECT_PATH | ||
|
||
if (!projectPath) { | ||
|
@@ -41,28 +46,40 @@ const test = base.extend<any, DevServerFixtures>({ | |
) | ||
} | ||
|
||
console.log(`Launching dev server at ${projectPath}`) | ||
const isServerAlreadyUp = await isPortReachable(webServerPort, { | ||
host: 'localhost', | ||
}) | ||
|
||
// Don't wait for this to finish, because it doens't | ||
const devServerHandler = execa.command( | ||
`yarn rw dev --fwd="--no-open" --no-generate`, | ||
{ | ||
cwd: projectPath, | ||
shell: true, | ||
env: { | ||
WEB_DEV_PORT: webServerPort, | ||
API_DEV_PORT: apiServerPort, | ||
}, | ||
} | ||
) | ||
if (isServerAlreadyUp) { | ||
console.log('Reusing server....') | ||
console.log({ | ||
webServerPort, | ||
apiServerPort, | ||
}) | ||
} else { | ||
console.log(`Launching dev server at ${projectPath}`) | ||
|
||
// Pipe out logs so we can debug, when required | ||
devServerHandler.stdout.on('data', (data) => { | ||
console.log( | ||
'[devServer-fixture] ', | ||
Buffer.from(data, 'utf-8').toString() | ||
// Don't wait for this to finish, because it doens't | ||
const devServerHandler = execa.command( | ||
`yarn rw dev --fwd="--no-open" --no-generate`, | ||
{ | ||
cwd: projectPath, | ||
shell: true, | ||
env: { | ||
WEB_DEV_PORT: webServerPort, | ||
API_DEV_PORT: apiServerPort, | ||
}, | ||
} | ||
) | ||
}) | ||
|
||
// Pipe out logs so we can debug, when required | ||
devServerHandler.stdout.on('data', (data) => { | ||
console.log( | ||
'[devServer-fixture] ', | ||
Buffer.from(data, 'utf-8').toString() | ||
) | ||
}) | ||
} | ||
|
||
console.log('Waiting for dev servers.....') | ||
await waitForServer(webServerPort, 1000) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { | ||
PlaywrightTestArgs, | ||
expect, | ||
PlaywrightWorkerArgs, | ||
} from '@playwright/test' | ||
import execa from 'execa' | ||
import fs from 'node:fs' | ||
import path from 'node:path' | ||
|
||
import devServerTest, { | ||
DevServerFixtures, | ||
} from '../playwright-fixtures/devServer.fixture' | ||
|
||
import { loginAsTestUser, signUpTestUser } from './common' | ||
|
||
// Signs up a user before these tests | ||
|
||
devServerTest.beforeAll(async ({ browser }: PlaywrightWorkerArgs) => { | ||
const page = await browser.newPage() | ||
|
||
await signUpTestUser({ | ||
// @NOTE we can't access webUrl in beforeAll, so hardcoded | ||
// But we can switch to beforeEach if required | ||
webUrl: 'http://localhost:9000', | ||
page, | ||
}) | ||
|
||
await page.close() | ||
}) | ||
|
||
devServerTest( | ||
'useAuth hook, auth redirects checks', | ||
async ({ page, webUrl }: PlaywrightTestArgs & DevServerFixtures) => { | ||
await page.goto(`${webUrl}/profile`) | ||
|
||
// To check redirects to the login page | ||
await expect(page).toHaveURL(`http://${webUrl}/login?redirectTo=/profile`) | ||
|
||
await loginAsTestUser({ page, webUrl }) | ||
|
||
await page.goto(`${webUrl}/profile`) | ||
|
||
const usernameRow = await page.waitForSelector('*css=tr >> text=EMAIL') | ||
await expect(await usernameRow.innerHTML()).toBe( | ||
'<td>EMAIL</td><td>[email protected]</td>' | ||
) | ||
|
||
const isAuthenticatedRow = await page.waitForSelector( | ||
'*css=tr >> text=isAuthenticated' | ||
) | ||
await expect(await isAuthenticatedRow.innerHTML()).toBe( | ||
'<td>isAuthenticated</td><td>true</td>' | ||
) | ||
|
||
const isAdminRow = await page.waitForSelector('*css=tr >> text=Is Admin') | ||
await expect(await isAdminRow.innerHTML()).toBe( | ||
'<td>Is Admin</td><td>false</td>' | ||
) | ||
} | ||
) | ||
|
||
devServerTest('requireAuth graphql checks', async ({ page, webUrl }) => { | ||
// Auth | ||
await page.goto(`${webUrl}/posts/1/edit`) | ||
|
||
await page.locator('input[name="title"]').fill('This is an edited title!') | ||
|
||
// unAuthenticated | ||
await page.click('text=SAVE') | ||
await expect( | ||
page.locator("text=You don't have permission to do that") | ||
).toBeTruthy() | ||
|
||
// Authenticated | ||
await loginAsTestUser({ webUrl, page }) | ||
await page.goto(`${webUrl}/posts/1/edit`) | ||
await page.locator('input[name="title"]').fill('This is an edited title!') | ||
|
||
await Promise.all([ | ||
page.waitForNavigation({ url: '**/' }), | ||
page.click('text=SAVE'), | ||
]) | ||
|
||
// Log Out | ||
await page.goto(`${webUrl}/`) | ||
await page.click('text=Log Out') | ||
await expect(page.locator('text=Login')).toBeTruthy() | ||
|
||
await expect(page.locator('text=This is an edited title!')).toBeTruthy() | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This lets us verify that mockCurrentUser is working