Skip to content

Commit

Permalink
Merge branch 'main' into feat/playground-workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
jacekradko committed Nov 21, 2024
2 parents 0dda1e4 + 7b80f0c commit 50d15c5
Show file tree
Hide file tree
Showing 95 changed files with 1,485 additions and 847 deletions.
3 changes: 3 additions & 0 deletions .changeset/few-files-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
---

6 changes: 6 additions & 0 deletions .changeset/fifty-cameras-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/expo-passkeys': patch
'@clerk/clerk-expo': patch
---
- Replaced import { Buffer } from 'node:buffer' with import { Buffer } from 'buffer'.
- Moved @clerk/expo-passkeys to a devDependency in @clerk/clerk-expo.
2 changes: 2 additions & 0 deletions .changeset/friendly-mice-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/large-bulldogs-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/orange-radios-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
6 changes: 6 additions & 0 deletions .changeset/red-chicken-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@clerk/clerk-js": patch
"@clerk/types": patch
---

Inject captcha token into every X heartbeats
2 changes: 2 additions & 0 deletions .changeset/rude-lions-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/shaggy-donuts-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/dev-cli': patch
---

Fix framework detection for Next.js. `clerk-dev` will now check for `next` as a dependency.
2 changes: 2 additions & 0 deletions .changeset/shy-months-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/silent-bears-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Change `useReverification` to handle error in a callback, but still allow an error to be thrown via options.
2 changes: 2 additions & 0 deletions .changeset/ten-hornets-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/thin-turkeys-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/unlucky-teachers-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/wet-dryers-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ jobs:
strategy:
fail-fast: false
matrix:
test-name: [ 'generic', 'express', 'quickstart', 'ap-flows', 'elements', 'sessions', 'astro', 'expo-web', 'tanstack-start', 'tanstack-router', 'vue']
test-name: [ 'generic', 'express', 'quickstart', 'ap-flows', 'elements', 'sessions', 'astro', 'expo-web', 'tanstack-start', 'tanstack-router', 'vue', 'nuxt']
test-project: ['chrome']
include:
- test-name: 'nextjs'
Expand Down
3 changes: 1 addition & 2 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
engine-strict=false
legacy-peer-deps=false
link-workspace-packages=true
legacy-peer-deps=false
1 change: 0 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
Expand Down
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"**/.yalc": true,
"**/node_modules": true,
".temp_integration": true,
"packages/*/dist": true
"packages/*/dist": true,
"pnpm-lock.yaml": true,
},
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.tsdk": "node_modules/typescript/lib",
Expand All @@ -23,4 +24,5 @@
"url": "https://json.schemastore.org/chrome-manifest.json"
}
],
"npm.packageManager": "pnpm",
}
2 changes: 2 additions & 0 deletions integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { envs } from './envs';
import { expo } from './expo';
import { express } from './express';
import { next } from './next';
import { nuxt } from './nuxt';
import { react } from './react';
import { remix } from './remix';
import { tanstack } from './tanstack';
Expand Down Expand Up @@ -39,6 +40,7 @@ export const createLongRunningApps = () => {
{ id: 'tanstack.start', config: tanstack.start, env: envs.withEmailCodes },
{ id: 'tanstack.router', config: tanstack.router, env: envs.withEmailCodes },
{ id: 'vue.vite', config: vue.vite, env: envs.withCustomRoles },
{ id: 'nuxt.node', config: nuxt.node, env: envs.withCustomRoles },
] as const;

const apps = configs.map(longRunningApplication);
Expand Down
17 changes: 17 additions & 0 deletions integration/presets/nuxt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { applicationConfig } from '../models/applicationConfig';
import { templates } from '../templates';

const nuxtNode = applicationConfig()
.setName('nuxt-node')
.useTemplate(templates['nuxt-node'])
.setEnvFormatter('public', key => `NUXT_PUBLIC_${key}`)
.setEnvFormatter('private', key => `NUXT_${key}`)
.addScript('setup', 'pnpm install')
.addScript('dev', 'pnpm dev')
.addScript('build', 'pnpm build')
.addScript('serve', 'pnpm preview')
.addDependency('@clerk/nuxt', '*');

export const nuxt = {
node: nuxtNode,
} as const;
1 change: 1 addition & 0 deletions integration/templates/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const templates = {
'tanstack-start': resolve(__dirname, './tanstack-start'),
'tanstack-router': resolve(__dirname, './tanstack-router'),
'vue-vite': resolve(__dirname, './vue-vite'),
'nuxt-node': resolve(__dirname, './nuxt-node'),
} as const;

if (new Set([...Object.values(templates)]).size !== Object.values(templates).length) {
Expand Down
3 changes: 3 additions & 0 deletions integration/templates/nuxt-node/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<NuxtPage />
</template>
16 changes: 16 additions & 0 deletions integration/templates/nuxt-node/middleware/auth.global.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default defineNuxtRouteMiddleware((to, from) => {
const { userId } = useAuth();

const isPublicPage = ['/sign-in'].includes(to.path);
const isProtectedPage = ['/user'].includes(to.path);

// Is authenticated and trying to access a public page
if (userId.value && isPublicPage) {
return navigateTo('/user');
}

// Is not authenticated and trying to access a protected page
if (!userId.value && isProtectedPage) {
return navigateTo('/sign-in');
}
});
3 changes: 3 additions & 0 deletions integration/templates/nuxt-node/nuxt.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default defineNuxtConfig({
modules: ['@clerk/nuxt'],
});
17 changes: 17 additions & 0 deletions integration/templates/nuxt-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "nuxt-clerk-integration-template",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev --port $PORT",
"generate": "nuxt generate",
"postinstall": "nuxt prepare",
"preview": "nuxt preview --port $PORT"
},
"dependencies": {
"nuxt": "^3.14.159",
"vue": "^3.5.12",
"vue-router": "^4.4.5"
}
}
21 changes: 21 additions & 0 deletions integration/templates/nuxt-node/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div>
<h1>Index Route</h1>
<SignedIn>
<p>You are signed in!</p>
<div>
<p>View your profile here</p>
<UserButton />
<OrganizationSwitcher />
</div>
<div>
<SignOutButton />
</div>
</SignedIn>
<SignedOut>
<p>You are signed out</p>

<SignIn />
</SignedOut>
</div>
</template>
8 changes: 8 additions & 0 deletions integration/templates/nuxt-node/pages/only-admin.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<Protect>
<p>I am an admin</p>
<template #fallback>
<p>Not an admin</p>
</template>
</Protect>
</template>
3 changes: 3 additions & 0 deletions integration/templates/nuxt-node/pages/sign-in.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<SignIn />
</template>
11 changes: 11 additions & 0 deletions integration/templates/nuxt-node/pages/user.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup>
const { data: user } = await useFetch('/api/me');
</script>

<template>
<UserProfile />
<ul>
<li>First name: {{ user.firstName }}</li>
<li>Email: {{ user.emailAddresses[0].emailAddress }}</li>
</ul>
</template>
16 changes: 16 additions & 0 deletions integration/templates/nuxt-node/server/api/me.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { clerkClient } from '@clerk/nuxt/server';

export default eventHandler(async event => {
const { userId } = event.context.auth;

if (!userId) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized',
});
}

const user = await clerkClient(event).users.getUser(userId);

return user;
});
101 changes: 101 additions & 0 deletions integration/tests/nuxt/basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
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] })('basic tests for @nuxt', ({ app }) => {
test.describe.configure({ mode: 'parallel' });

let fakeUser: FakeUser;
let fakeOrganization: FakeOrganization;

test.beforeAll(async () => {
const u = createTestUtils({ app });
fakeUser = u.services.users.createFakeUser();
const user = await u.services.users.createBapiUser(fakeUser);
fakeOrganization = await u.services.users.createFakeOrganization(user.id);
});

test.afterAll(async () => {
await fakeOrganization.delete();
await fakeUser.deleteIfExists();

await app.teardown();
});

test.afterEach(async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.signOut();
await u.page.context().clearCookies();
});

test('sign in with hash routing', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();

await u.po.signIn.setIdentifier(fakeUser.email);
await u.po.signIn.continue();
await u.page.waitForURL(`${app.serverUrl}/sign-in#/factor-one`);

await u.po.signIn.setPassword(fakeUser.password);
await u.po.signIn.continue();
await u.po.expect.toBeSignedIn();
});

test('render user profile with SSR data', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();

await u.po.userButton.waitForMounted();
await u.page.goToRelative('/user');
await u.po.userProfile.waitForMounted();

// Fetched from an API endpoint (/api/me), which is server-rendered.
// This also verifies that the server middleware is working.
await expect(u.page.getByText(`First name: ${fakeUser.firstName}`)).toBeVisible();
await expect(u.page.getByText(`Email: ${fakeUser.email}`)).toBeVisible();
});

test('redirects to sign-in when unauthenticated', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

await u.page.goToRelative('/user');
await u.page.waitForURL(`${app.serverUrl}/sign-in`);
await u.po.signIn.waitForMounted();
});

test('renders control components contents in SSR', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

await u.page.goToAppHome();
await expect(u.page.getByText('You are signed out')).toBeVisible();

await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();
await expect(u.page.getByText('You are signed in!')).toBeVisible();
});

test('renders <Protect /> component contents to admin', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();

await u.page.waitForAppUrl('/');
await u.po.organizationSwitcher.waitForMounted();
await u.po.organizationSwitcher.waitForAnOrganizationToSelected();
await u.page.goToRelative('/only-admin');
await expect(u.page.getByText('I am an admin')).toBeVisible();
});
});
6 changes: 3 additions & 3 deletions integration/tests/reverification.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withReverification] })(
});

utils.forEach(type => {
test.skip(`reverification error from ${capitalize(type)}`, async ({ page, context }) => {
test(`reverification error from ${capitalize(type)}`, async ({ page, context }) => {
test.setTimeout(270_000);
const u = createTestUtils({ app, page, context });

Expand All @@ -67,12 +67,12 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withReverification] })(
await u.page.getByRole('button', { name: /LogUserId/i }).click();
await expect(
u.page.getByText(
/\{\s*"clerk_error"\s*:\s*\{\s*"type"\s*:\s*"forbidden"\s*,\s*"reason"\s*:\s*"reverification-mismatch"\s*,\s*"metadata"\s*:\s*\{\s*"reverification"\s*:\s*\{\s*"level"\s*:\s*"secondFactor"\s*,\s*"afterMinutes"\s*:\s*1\s*\}\s*\}\s*\}\s*\}/i,
/\{\s*"clerk_error"\s*:\s*\{\s*"type"\s*:\s*"forbidden"\s*,\s*"reason"\s*:\s*"reverification-error"\s*,\s*"metadata"\s*:\s*\{\s*"reverification"\s*:\s*\{\s*"level"\s*:\s*"second_factor"\s*,\s*"afterMinutes"\s*:\s*1\s*\}\s*\}\s*\}\s*\}/i,
),
).toBeVisible();
});

test.skip(`reverification recovery from ${capitalize(type)}`, async ({ page, context }) => {
test(`reverification recovery from ${capitalize(type)}`, async ({ page, context }) => {
test.setTimeout(270_000);
const u = createTestUtils({ app, page, context });

Expand Down
Loading

0 comments on commit 50d15c5

Please sign in to comment.