-
Notifications
You must be signed in to change notification settings - Fork 280
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests(repo): Integration tests for Protect (#2319)
* tests(repo): Integration tests for `Protect` in components * tests(repo): Integration tests for `Protect` in components * tests(repo): Integration tests for `Protect` with 2 roles * tests(repo): Integration test utils for organization switcher * chore(nextjs): Add sample json key * chore(integration): Introduce E2E_APP_STAGING_CLERK_API_URL * chore(integration): Introduce next.appRouter.withCustomRoles long running app * chore(integration): Replace E2E_APP_STAGING_CLERK_API_URL with hardcoded value As it's going to be removed soon(ish) * chore(integration): Use next.appRouter.withCustomRoles long running app --------- Co-authored-by: Nikos Douvlis <[email protected]>
- Loading branch information
1 parent
6728b24
commit 71fb9f6
Showing
19 changed files
with
257 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,5 +6,9 @@ | |
"with-email-links": { | ||
"pk": "", | ||
"sk": "" | ||
}, | ||
"with-custom-roles": { | ||
"pk": "", | ||
"sk": "" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
integration/templates/next-app-router/src/app/api/settings/route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { auth } from '@clerk/nextjs/server'; | ||
|
||
export function GET() { | ||
const { userId } = auth().protect(has => has({ role: 'admin' }) || has({ role: 'org:editor' })); | ||
return new Response(JSON.stringify({ userId })); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
integration/templates/next-app-router/src/app/settings/auth-has/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { auth } from '@clerk/nextjs/server'; | ||
|
||
export default function Page() { | ||
const { userId, has } = auth(); | ||
if (!userId || !has({ permission: 'org:posts:manage' })) { | ||
return <p>User is missing permissions</p>; | ||
} | ||
return <p>User has access</p>; | ||
} |
6 changes: 6 additions & 0 deletions
6
integration/templates/next-app-router/src/app/settings/auth-protect/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { auth } from '@clerk/nextjs/server'; | ||
|
||
export default function Page() { | ||
auth().protect({ role: 'admin' }); | ||
return <p>User has access</p>; | ||
} |
12 changes: 12 additions & 0 deletions
12
integration/templates/next-app-router/src/app/settings/rcc-protect/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
'use client'; | ||
import { Protect } from '@clerk/nextjs'; | ||
export default function Page() { | ||
return ( | ||
<Protect | ||
permission='org:posts:manage' | ||
fallback={<p>User is missing permissions</p>} | ||
> | ||
<p>User has access</p> | ||
</Protect> | ||
); | ||
} |
12 changes: 12 additions & 0 deletions
12
integration/templates/next-app-router/src/app/settings/rsc-protect/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Protect } from '@clerk/nextjs'; | ||
|
||
export default function Page() { | ||
return ( | ||
<Protect | ||
role='admin' | ||
fallback={<p>User is not admin</p>} | ||
> | ||
<p>User has access</p> | ||
</Protect> | ||
); | ||
} |
13 changes: 13 additions & 0 deletions
13
integration/templates/next-app-router/src/app/settings/useAuth-has/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
'use client'; | ||
import { useAuth } from '@clerk/nextjs'; | ||
|
||
export default function Page() { | ||
const { has, isLoaded } = useAuth(); | ||
if (!isLoaded) { | ||
return <p>Loading</p>; | ||
} | ||
if (!has({ role: 'admin' })) { | ||
return <p>User is not admin</p>; | ||
} | ||
return <p>User has access</p>; | ||
} |
5 changes: 5 additions & 0 deletions
5
integration/templates/next-app-router/src/app/switcher/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { OrganizationSwitcher } from '@clerk/nextjs'; | ||
|
||
export default function Page() { | ||
return <OrganizationSwitcher hidePersonal={true} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { expect } from '@playwright/test'; | ||
|
||
import { common } from './commonPageObject'; | ||
import type { TestArgs } from './signInPageObject'; | ||
|
||
export const createOrganizationSwitcherComponentPageObject = (testArgs: TestArgs) => { | ||
const { page } = testArgs; | ||
|
||
const self = { | ||
...common(testArgs), | ||
goTo: async (relativePath = '/switcher') => { | ||
await page.goToRelative(relativePath); | ||
return self.waitForMounted(); | ||
}, | ||
waitForMounted: () => { | ||
return page.waitForSelector('.cl-organizationSwitcher-root', { state: 'attached' }); | ||
}, | ||
expectNoOrganizationSelected: () => { | ||
return expect(page.getByText(/No organization selected/i)).toBeVisible(); | ||
}, | ||
toggleTrigger: () => { | ||
return page.locator('.cl-organizationSwitcherTrigger').click(); | ||
}, | ||
waitForAnOrganizationToSelected: () => { | ||
return page.waitForSelector('.cl-userPreviewMainIdentifier__personalWorkspace', { state: 'detached' }); | ||
}, | ||
}; | ||
|
||
return self; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import type { OrganizationMembershipRole } from '@clerk/backend'; | ||
import { expect, test } from '@playwright/test'; | ||
|
||
import { appConfigs } from '../presets'; | ||
import type { FakeOrganization, FakeUser } from '../testUtils'; | ||
import { createTestUtils, testAgainstRunningApps } from '../testUtils'; | ||
|
||
testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('authorization @nextjs', ({ app }) => { | ||
test.describe.configure({ mode: 'serial' }); | ||
|
||
let fakeAdmin: FakeUser; | ||
let fakeViewer: FakeUser; | ||
let fakeOrganization: FakeOrganization; | ||
|
||
test.beforeAll(async () => { | ||
const m = createTestUtils({ app }); | ||
fakeAdmin = m.services.users.createFakeUser(); | ||
const { data: admin } = await m.services.users.createBapiUser(fakeAdmin); | ||
fakeOrganization = await m.services.users.createFakeOrganization(admin.id); | ||
fakeViewer = m.services.users.createFakeUser(); | ||
const { data: viewer } = await m.services.users.createBapiUser(fakeViewer); | ||
|
||
await m.services.clerk.organizations.createOrganizationMembership({ | ||
organizationId: fakeOrganization.organization.id, | ||
role: 'org:viewer' as OrganizationMembershipRole, | ||
userId: viewer.id, | ||
}); | ||
}); | ||
|
||
test.afterAll(async () => { | ||
await fakeOrganization.delete(); | ||
await fakeViewer.deleteIfExists(); | ||
await fakeAdmin.deleteIfExists(); | ||
await app.teardown(); | ||
}); | ||
|
||
test('Protect in RSCs and RCCs as `admin`', async ({ page, context }) => { | ||
const u = createTestUtils({ app, page, context }); | ||
|
||
await u.po.signIn.goTo(); | ||
await u.po.signIn.waitForMounted(); | ||
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeAdmin.email, password: fakeAdmin.password }); | ||
await u.po.expect.toBeSignedIn(); | ||
|
||
await u.po.organizationSwitcher.goTo(); | ||
await u.po.organizationSwitcher.waitForMounted(); | ||
await u.po.organizationSwitcher.expectNoOrganizationSelected(); | ||
await u.po.organizationSwitcher.toggleTrigger(); | ||
await u.page.locator('.cl-organizationSwitcherPreviewButton').click(); | ||
await u.po.organizationSwitcher.waitForAnOrganizationToSelected(); | ||
|
||
await u.page.goToRelative('/settings/rsc-protect'); | ||
await expect(u.page.getByText(/User has access/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/rcc-protect'); | ||
await expect(u.page.getByText(/User has access/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/useAuth-has'); | ||
await expect(u.page.getByText(/User has access/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/auth-has'); | ||
await expect(u.page.getByText(/User has access/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/auth-protect'); | ||
await expect(u.page.getByText(/User has access/i)).toBeVisible(); | ||
|
||
// route handler | ||
await u.page.goToRelative('/api/settings/'); | ||
await expect(u.page.getByText(/userId/i)).toBeVisible(); | ||
}); | ||
|
||
test('Protect in RSCs and RCCs as `signed-out user`', async ({ page, context }) => { | ||
const u = createTestUtils({ app, page, context }); | ||
|
||
await u.page.goToRelative('/settings/rsc-protect'); | ||
await expect(u.page.getByText(/User is not admin/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/rcc-protect'); | ||
await expect(u.page.getByText(/User is missing permissions/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/useAuth-has'); | ||
await expect(u.page.getByText(/User is not admin/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/auth-has'); | ||
await expect(u.page.getByText(/User is missing permissions/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/auth-protect'); | ||
await expect(u.page.getByText(/this page could not be found/i)).toBeVisible(); | ||
}); | ||
|
||
test('Protect in RSCs and RCCs as `viewer`', async ({ page, context }) => { | ||
const u = createTestUtils({ app, page, context }); | ||
|
||
await u.po.signIn.goTo(); | ||
await u.po.signIn.waitForMounted(); | ||
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeViewer.email, password: fakeViewer.password }); | ||
await u.po.expect.toBeSignedIn(); | ||
|
||
await u.po.organizationSwitcher.goTo(); | ||
await u.po.organizationSwitcher.waitForMounted(); | ||
await u.po.organizationSwitcher.expectNoOrganizationSelected(); | ||
await u.po.organizationSwitcher.toggleTrigger(); | ||
await u.page.locator('.cl-organizationSwitcherPreviewButton').click(); | ||
await u.po.organizationSwitcher.waitForAnOrganizationToSelected(); | ||
|
||
await u.page.goToRelative('/settings/rsc-protect'); | ||
await expect(u.page.getByText(/User is not admin/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/rcc-protect'); | ||
await expect(u.page.getByText(/User is missing permissions/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/useAuth-has'); | ||
await expect(u.page.getByText(/User is not admin/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/auth-has'); | ||
await expect(u.page.getByText(/User is missing permissions/i)).toBeVisible(); | ||
await u.page.goToRelative('/settings/auth-protect'); | ||
await expect(u.page.getByText(/this page could not be found/i)).toBeVisible(); | ||
|
||
// route handler | ||
await u.page.goToRelative('/api/settings/'); | ||
|
||
// Result of 404 response with empty body | ||
expect(await u.page.content()).toEqual('<html><head></head><body></body></html>'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters