Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
dd307ae
fix(e2e): update package links in astro preset and metro config
nikosdouvlis Dec 4, 2025
d9ab27a
refactor(ui,clerk-js): remove DevTools resource and integrate enableE…
nikosdouvlis Dec 7, 2025
bab5973
fix(shared): reorder package exports to fix wildcard resolution in es…
nikosdouvlis Dec 7, 2025
8d46e77
feat(shared): backport intelligent retries to loadClerkJsScript
nikosdouvlis Dec 7, 2025
0e8fd11
fix(nuxt): support NUXT_PUBLIC_CLERK_JS_URL and NUXT_PUBLIC_CLERK_UI_…
nikosdouvlis Dec 7, 2025
41f8ada
feat(ci): use snapshot versions in verdaccio for integration tests
nikosdouvlis Dec 7, 2025
03053cb
refactor(turbo): remove redundant build dependencies from integration…
nikosdouvlis Dec 7, 2025
1e231d8
feat(e2e): automatically setup clerk testing tokens in app.dev()
nikosdouvlis Dec 7, 2025
3e3511a
chore(e2e): drop mailsac and improve error logging in test utilities
nikosdouvlis Dec 7, 2025
0155ad6
fix(e2e): simplify billing tests and fix environment configuration
nikosdouvlis Dec 7, 2025
e3cdf23
chore(e2e): update integration presets and simplify test configuration
nikosdouvlis Dec 7, 2025
ccbc43c
chore(ui,astro): improve error handling, build config, and code organ…
nikosdouvlis Dec 7, 2025
0688ab4
chore(repo): always run e2e tests with debug logging
nikosdouvlis Dec 7, 2025
882ec2a
fix(e2e): add missing clerkJSUrl and clerkUiUrl props to templates
nikosdouvlis Dec 7, 2025
f3c5005
ci(e2e): add vercel protection bypass for integration tests
nikosdouvlis Dec 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/actions/verdaccio/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ runs:
- name: Print published Clerk package versions
shell: bash
run: |
echo "Published @clerk packages under 'integration' tag:"
echo "Published @clerk packages (snapshot versions):"
echo "=================================================="
packages=(
"@clerk/agent-toolkit"
Expand All @@ -102,6 +102,6 @@ runs:
"@clerk/vue"
)
for pkg in "${packages[@]}"; do
version=$(pnpm view "$pkg@integration" version 2>/dev/null || echo "not found")
printf "%-35s %s\n" "$pkg@integration:" "$version"
version=$(pnpm view "$pkg" version 2>/dev/null || echo "not found")
printf "%-35s %s\n" "$pkg:" "$version"
done
64 changes: 36 additions & 28 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ jobs:
- name: Setup
id: config
uses: ./.github/actions/init-blacksmith
with:
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-team: ${{ vars.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
# with:
# turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# turbo-team: ${{ vars.TURBO_TEAM }}
# turbo-token: ${{ secrets.TURBO_TOKEN }}

- name: Verify lockfile is deduped
run: pnpm dedupe --check
Expand Down Expand Up @@ -110,11 +110,11 @@ jobs:
- name: Setup
id: config
uses: ./.github/actions/init-blacksmith
with:
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-summarize: ${{ env.TURBO_SUMMARIZE }}
turbo-team: ${{ vars.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
# with:
# turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# turbo-summarize: ${{ env.TURBO_SUMMARIZE }}
# turbo-team: ${{ vars.TURBO_TEAM }}
# turbo-token: ${{ secrets.TURBO_TOKEN }}

- name: Turbo Build
run: pnpm turbo build $TURBO_ARGS --only
Expand Down Expand Up @@ -154,11 +154,11 @@ jobs:
- name: Setup
id: config
uses: ./.github/actions/init-blacksmith
with:
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-summarize: ${{ env.TURBO_SUMMARIZE }}
turbo-team: ${{ vars.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
# with:
# turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# turbo-summarize: ${{ env.TURBO_SUMMARIZE }}
# turbo-team: ${{ vars.TURBO_TEAM }}
# turbo-token: ${{ secrets.TURBO_TOKEN }}

- name: Check size using bundlewatch
continue-on-error: true
Expand Down Expand Up @@ -231,10 +231,10 @@ jobs:
with:
# Ensures that all builds are cached appropriately with a consistent run name `Unit Tests (20)`.
node-version: ${{ matrix.node-version }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-summarize: ${{ env.TURBO_SUMMARIZE }}
turbo-team: ${{ vars.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
# turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# turbo-summarize: ${{ env.TURBO_SUMMARIZE }}
# turbo-team: ${{ vars.TURBO_TEAM }}
# turbo-token: ${{ secrets.TURBO_TOKEN }}

- name: Rebuild @clerk/shared with CLERK_USE_RQ=true
if: ${{ matrix.clerk-use-rq == 'true' }}
Expand Down Expand Up @@ -280,7 +280,8 @@ jobs:
retention-days: 5

integration-tests:
needs: [check-permissions, build-packages]
# needs: [check-permissions, build-packages]
needs: [check-permissions]
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
name: Integration Tests (${{ matrix.test-name }}, ${{ matrix.test-project }}${{ matrix.next-version && format(', {0}', matrix.next-version) || '' }}${{ matrix.clerk-use-rq == 'true' && ', RQ' || '' }})
permissions:
Expand Down Expand Up @@ -355,9 +356,9 @@ jobs:
id: config
uses: ./.github/actions/init-blacksmith
with:
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-team: ${{ vars.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
# turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# turbo-team: ${{ vars.TURBO_TEAM }}
# turbo-token: ${{ secrets.TURBO_TOKEN }}
playwright-enabled: true

- name: Verify jq is installed
Expand Down Expand Up @@ -405,12 +406,16 @@ jobs:
env:
CLERK_USE_RQ: true

- name: Version packages for snapshot
if: ${{ steps.task-status.outputs.affected == '1' }}
run: npm run version-packages:snapshot ci

- name: Verdaccio
if: ${{ steps.task-status.outputs.affected == '1' }}
uses: ./.github/actions/verdaccio
with:
publish-cmd: |
if [ "$(pnpm config get registry)" = "https://registry.npmjs.org/" ]; then echo 'Error: Using default registry' && exit 1; else CLERK_USE_RQ=${{ matrix.clerk-use-rq }} pnpm turbo build $TURBO_ARGS --only && pnpm changeset publish --no-git-tag --tag integration; fi
if [ "$(pnpm config get registry)" = "https://registry.npmjs.org/" ]; then echo 'Error: Using default registry' && exit 1; else ${{ matrix.clerk-use-rq == 'true' && 'CLERK_USE_RQ=true' || '' }} pnpm turbo build $TURBO_ARGS --only && pnpm changeset publish --no-git-tag --tag latest; fi

- name: Edit .npmrc [link-workspace-packages=false]
run: sed -i -E 's/link-workspace-packages=(deep|true)/link-workspace-packages=false/' .npmrc
Expand All @@ -423,6 +428,8 @@ jobs:
pnpm config set minimum-release-age-exclude @clerk/*
pnpm add @clerk/backend

# Install published packages from Verdaccio to test against real npm install scenarios
# rather than local monorepo builds. Validates package structure, dependencies, and entry points.
- name: Install @clerk/clerk-js in os temp
if: ${{ steps.task-status.outputs.affected == '1' }}
working-directory: ${{runner.temp}}
Expand Down Expand Up @@ -474,6 +481,7 @@ jobs:
timeout-minutes: 25
run: pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS
env:
E2E_DEBUG: "1"
E2E_APP_CLERK_JS_DIR: ${{runner.temp}}
E2E_APP_CLERK_UI_DIR: ${{runner.temp}}
E2E_CLERK_JS_VERSION: "latest"
Expand All @@ -483,8 +491,8 @@ jobs:
E2E_CLERK_ENCRYPTION_KEY: ${{ matrix.clerk-encryption-key }}
CLERK_USE_RQ: ${{ matrix.clerk-use-rq }}
INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }}
MAILSAC_API_KEY: ${{ secrets.MAILSAC_API_KEY }}
NODE_EXTRA_CA_CERTS: ${{ github.workspace }}/integration/certs/rootCA.pem
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}

- name: Upload test-results
if: ${{ cancelled() || failure() }}
Expand Down Expand Up @@ -518,10 +526,10 @@ jobs:
with:
turbo-enabled: true
node-version: 22
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-summarize: ${{ env.TURBO_SUMMARIZE }}
turbo-team: ${{ vars.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
# turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# turbo-summarize: ${{ env.TURBO_SUMMARIZE }}
# turbo-team: ${{ vars.TURBO_TEAM }}
# turbo-token: ${{ secrets.TURBO_TOKEN }}

- name: Publish with pkg-pr-new
run: pnpm run build && pnpx pkg-pr-new@${{ vars.PKG_PR_NEW_VERSION || '0.0.49' }} publish --compact --pnpm './packages/*'
5 changes: 2 additions & 3 deletions .github/workflows/nightly-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,9 @@ jobs:
E2E_CLERK_UI_VERSION: "latest"
E2E_NEXTJS_VERSION: "canary"
E2E_NPM_FORCE: "true"
E2E_REACT_DOM_VERSION: "19.1.0"
E2E_REACT_VERSION: "19.1.0"
E2E_REACT_DOM_VERSION: "19.2.1"
E2E_REACT_VERSION: "19.2.1"
INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }}
MAILSAC_API_KEY: ${{ secrets.MAILSAC_API_KEY }}

# Upload test artifacts if tests failed
- name: Upload Test Artifacts
Expand Down
1 change: 0 additions & 1 deletion integration/.env.local.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
MAILSAC_API_KEY=
VERCEL_PROJECT_ID=
VERCEL_ORG_ID=
VERCEL_TOKEN=
5 changes: 2 additions & 3 deletions integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ Below you can find code snippets for running tests in a specific manner, easily

#### Keep temporary site

During E2E runs a temporary site is created in which the template is copied into. If you want to keep the site around, pass the `CLEANUP` environment variable:
During E2E runs a temporary site is created in which the template is copied into. If you want to keep the site around, pass the `E2E_CLEANUP` environment variable:

```shell
CLEANUP=0 pnpm test:integration:base
E2E_CLEANUP=0 pnpm test:integration:base
```

For all available environment variables, check the [`constants.ts`](../integration/constants.ts) file.
Expand Down Expand Up @@ -578,7 +578,6 @@ Before writing tests, it's important to understand how Playwright handles test i
> [!NOTE]
> The test suite also uses these environment variables to run some tests:
>
> - `MAILSAC_API_KEY`: Used for [Mailsac](https://mailsac.com/) to retrieve email codes and magic links from temporary email addresses.
> - `VERCEL_PROJECT_ID`: Only required if you plan on running deployment tests locally. This is the Vercel project ID, and it points to an application created via the Vercel dashboard. The easiest way to get access to it is by linking a local app to the Vercel project using the Vercel CLI, and then copying the values from the `.vercel` directory.
> - `VERCEL_ORG_ID`: The organization that owns the Vercel project. See above for more details.
> - `VERCEL_TOKEN`: A personal access token. This corresponds to a real user running the deployment command. Attention: Be extra careful with this token as it can't be scoped to a single Vercel project, meaning that the token has access to every project in the account it belongs to.
Expand Down
4 changes: 2 additions & 2 deletions integration/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ export const constants = {
*/
E2E_APP_CLERK_UI_DIR: process.env.E2E_APP_CLERK_UI_DIR,
/**
* If CLEANUP=0 is used, the .tmp_integration directory will not be deleted.
* If E2E_CLEANUP=0 is used, the .tmp_integration directory will not be deleted.
* This is useful for debugging locally.
*/
CLEANUP: !(process.env.CLEANUP === '0' || process.env.CLEANUP === 'false'),
E2E_CLEANUP: !(process.env.E2E_CLEANUP === '0' || process.env.E2E_CLEANUP === 'false'),
DEBUG: process.env.DEBUG === 'true' || process.env.DEBUG === '1',
/**
* Used with E2E_APP_URL if the tests need to access BAPI.
Expand Down
37 changes: 37 additions & 0 deletions integration/models/application.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import * as path from 'node:path';

import { parsePublishableKey } from '@clerk/shared/keys';
import { clerkSetup } from '@clerk/testing/playwright';

import { awaitableTreekill, createLogger, fs, getPort, run, waitForIdleProcess, waitForServer } from '../scripts';
import type { ApplicationConfig } from './applicationConfig.js';
import type { EnvironmentConfig } from './environment.js';
Expand Down Expand Up @@ -46,6 +49,10 @@ export const application = (
const log = logger.child({ prefix: 'setup' }).info;
await run(scripts.setup, { cwd: appDirPath, log });
state.completedSetup = true;
// Print all Clerk package versions (direct and transitive)
const clerkPackagesLog = logger.child({ prefix: 'clerk-packages' }).info;
clerkPackagesLog('Installed @clerk/* packages:');
await run('pnpm list @clerk/* --depth 100', { cwd: appDirPath, log: clerkPackagesLog });
}
},
dev: async (opts: { port?: number; manualStart?: boolean; detached?: boolean; serverUrl?: string } = {}) => {
Expand Down Expand Up @@ -82,6 +89,36 @@ export const application = (
log(`Server started at ${runtimeServerUrl}, pid: ${proc.pid}`);
cleanupFns.push(() => awaitableTreekill(proc.pid, 'SIGKILL'));
state.serverUrl = runtimeServerUrl;

// Setup Clerk testing tokens after the server is running
if (state.env) {
try {
const publishableKey = state.env.publicVariables.get('CLERK_PUBLISHABLE_KEY');
const secretKey = state.env.privateVariables.get('CLERK_SECRET_KEY');
const apiUrl = state.env.privateVariables.get('CLERK_API_URL');

if (publishableKey && secretKey) {
const { instanceType, frontendApi: frontendApiUrl } = parsePublishableKey(publishableKey);

if (instanceType !== 'development') {
log('Skipping clerkSetup for non-development instance');
} else {
await clerkSetup({
publishableKey,
frontendApiUrl,
secretKey,
// @ts-expect-error apiUrl is not a typed option for clerkSetup, but it is accepted at runtime.
apiUrl,
dotenv: false,
});
log('Clerk testing tokens setup complete');
}
}
} catch (error) {
logger.warn('Failed to setup Clerk testing tokens:', error);
}
}

return { port, serverUrl: runtimeServerUrl, pid: proc.pid };
},
build: async () => {
Expand Down
6 changes: 3 additions & 3 deletions integration/presets/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const astroNode = applicationConfig()
.addScript('dev', 'pnpm dev')
.addScript('build', 'pnpm build')
.addScript('serve', 'pnpm preview')
.addDependency('@clerk/astro', linkPackage('astro', 'integration'))
.addDependency('@clerk/shared', linkPackage('types', 'integration'))
.addDependency('@clerk/localizations', linkPackage('localizations', 'integration'));
.addDependency('@clerk/astro', linkPackage('astro'))
.addDependency('@clerk/shared', linkPackage('shared'))
.addDependency('@clerk/localizations', linkPackage('localizations'));

const astroStatic = astroNode.clone().setName('astro-hybrid').useTemplate(templates['astro-hybrid']);

Expand Down
6 changes: 3 additions & 3 deletions integration/presets/custom-flows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const reactVite = applicationConfig()
.addScript('dev', 'pnpm dev')
.addScript('build', 'pnpm build')
.addScript('serve', 'pnpm preview')
.addDependency('@clerk/react', linkPackage('react', 'integration'))
.addDependency('@clerk/shared', linkPackage('shared', 'integration'))
.addDependency('@clerk/ui', linkPackage('ui', 'integration'));
.addDependency('@clerk/react', linkPackage('react'))
.addDependency('@clerk/shared', linkPackage('shared'))
.addDependency('@clerk/ui', linkPackage('ui'));

export const customFlows = {
reactVite,
Expand Down
9 changes: 5 additions & 4 deletions integration/presets/envs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ const withCustomRoles = base
.clone()
.setId('withCustomRoles')
.setEnvVariable('private', 'CLERK_SECRET_KEY', instanceKeys.get('with-custom-roles').sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-custom-roles').pk);
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-custom-roles').pk)
.setEnvVariable('public', 'CLERK_JS_URL', constants.E2E_APP_CLERK_JS || 'http://localhost:18211/clerk.browser.js')
.setEnvVariable('public', 'CLERK_UI_URL', constants.E2E_APP_CLERK_UI || 'http://localhost:18212/ui.browser.js');

const withReverification = base
.clone()
Expand Down Expand Up @@ -165,9 +167,8 @@ const withSessionTasksResetPassword = base
const withBillingJwtV2 = base
.clone()
.setId('withBillingJwtV2')
.setEnvVariable('private', 'CLERK_API_URL', 'https://api.clerkstage.dev')
.setEnvVariable('private', 'CLERK_SECRET_KEY', instanceKeys.get('with-billing-staging').sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-billing-staging').pk);
.setEnvVariable('private', 'CLERK_SECRET_KEY', instanceKeys.get('with-billing').sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-billing').pk);

const withBilling = base
.clone()
Expand Down
6 changes: 3 additions & 3 deletions integration/presets/express.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const vite = applicationConfig()
.addScript('dev', 'pnpm dev')
.addScript('build', 'pnpm build')
.addScript('serve', 'pnpm start')
.addDependency('@clerk/express', linkPackage('express', 'integration'))
.addDependency('@clerk/clerk-js', linkPackage('clerk-js', 'integration'))
.addDependency('@clerk/ui', linkPackage('ui', 'integration'));
.addDependency('@clerk/express', linkPackage('express'))
.addDependency('@clerk/clerk-js', linkPackage('clerk-js'))
.addDependency('@clerk/ui', linkPackage('ui'));

export const express = {
vite,
Expand Down
9 changes: 0 additions & 9 deletions integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,7 @@ export const createLongRunningApps = () => {
* Billing apps
*/
{ id: 'withBillingJwtV2.next.appRouter', config: next.appRouter, env: envs.withBillingJwtV2 },
{ id: 'withBilling.next.appRouter', config: next.appRouter, env: envs.withBilling },
{ id: 'withBillingJwtV2.vue.vite', config: vue.vite, env: envs.withBillingJwtV2 },
{ id: 'withBilling.vue.vite', config: vue.vite, env: envs.withBilling },

/**
* Machine auth apps
*/
{ id: 'withMachine.express.vite', config: express.vite, env: envs.withAPIKeys },
{ id: 'withMachine.next.appRouter', config: next.appRouter, env: envs.withAPIKeys },

/**
* Vite apps - basic flows
Expand All @@ -68,7 +60,6 @@ export const createLongRunningApps = () => {
/**
* Various apps - basic flows
*/
{ id: 'withBilling.astro.node', config: astro.node, env: envs.withBilling },
{ id: 'astro.node.withCustomRoles', config: astro.node, env: envs.withCustomRoles },
{ id: 'astro.static.withCustomRoles', config: astro.static, env: envs.withCustomRoles },
{ id: 'expo.expo-web', config: expo.expoWeb, env: envs.withEmailCodes },
Expand Down
6 changes: 3 additions & 3 deletions integration/presets/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const cra = applicationConfig()
.addScript('dev', 'pnpm start')
.addScript('build', 'pnpm build')
.addScript('serve', 'pnpm start')
.addDependency('@clerk/react', linkPackage('react', 'integration'))
.addDependency('@clerk/shared', linkPackage('shared', 'integration'))
.addDependency('@clerk/ui', linkPackage('ui', 'integration'));
.addDependency('@clerk/react', linkPackage('react'))
.addDependency('@clerk/shared', linkPackage('shared'))
.addDependency('@clerk/ui', linkPackage('ui'));

const vite = cra
.clone()
Expand Down
4 changes: 3 additions & 1 deletion integration/presets/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import path from 'node:path';
export function linkPackage(pkg: string, tag?: string) {
// eslint-disable-next-line turbo/no-undeclared-env-vars
if (process.env.CI === 'true') {
return tag || '*';
// In CI, use '*' to get the latest version from Verdaccio
// which will be the snapshot version we just published
return '*';
}

return `link:${path.resolve(process.cwd(), `packages/${pkg}`)}`;
Expand Down
Loading
Loading