From 921d00c8805f0ed4e2572c92f7b4001893199a12 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 17:20:47 -0700 Subject: [PATCH 01/17] ci(repo): hash integration suites from the package builds they install Give each integration suite task a dependsOn list of the package builds its e2e apps install, and drop the packages/* input globs. Turbo folds the dependency build hashes into the suite hash, so a change to a package no suite installs no longer invalidates every suite, while a change to a transitive dependency still does. Force full runs in the merge queue and nightly. --- .changeset/scope-integration-test-inputs.md | 2 + .github/workflows/ci.yml | 1 + .github/workflows/nightly-checks.yml | 2 +- turbo.json | 512 ++++---------------- 4 files changed, 97 insertions(+), 420 deletions(-) create mode 100644 .changeset/scope-integration-test-inputs.md diff --git a/.changeset/scope-integration-test-inputs.md b/.changeset/scope-integration-test-inputs.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/scope-integration-test-inputs.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8f50d993e5..8acef77b459 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -706,6 +706,7 @@ jobs: pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS fi env: + TURBO_FORCE: ${{ github.event_name == 'merge_group' }} CLERK_E2E_OAUTH_PROVIDER_CLIENT_SECRET: ${{ secrets.CLERK_E2E_OAUTH_PROVIDER_CLIENT_SECRET }} CLERK_PLATFORM_API_KEY: ${{ secrets.CLERK_PLATFORM_API_KEY }} E2E_DEBUG: '1' diff --git a/.github/workflows/nightly-checks.yml b/.github/workflows/nightly-checks.yml index e2e36b6d20f..0ce88453081 100644 --- a/.github/workflows/nightly-checks.yml +++ b/.github/workflows/nightly-checks.yml @@ -74,7 +74,7 @@ jobs: if [ "${{ matrix.test-name }}" == "nextjs" ]; then E2E_DEBUG=1 E2E_APP_ID=quickstart.next.appRouter pnpm test:integration:base --grep @quickstart 2>&1 | tee "$OUTPUT_FILE" else - E2E_DEBUG=1 pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS --only 2>&1 | tee "$OUTPUT_FILE" + E2E_DEBUG=1 pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS --only --force 2>&1 | tee "$OUTPUT_FILE" fi echo "exit_code=${PIPESTATUS[0]}" >> $GITHUB_OUTPUT env: diff --git a/turbo.json b/turbo.json index 775c85d7f26..1206c7767a5 100644 --- a/turbo.json +++ b/turbo.json @@ -227,174 +227,75 @@ "outputs": [] }, "//#test:integration:ap-flows": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:generic": { - "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" + "dependsOn": [ + "@clerk/clerk-js#build", + "@clerk/nextjs#build", + "@clerk/testing#build", + "@clerk/ui#build", + "@clerk/vue#build" ], + "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:express": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/express#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:fastify": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/fastify#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:hono": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/hono#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:mosaic": { - "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" + "dependsOn": [ + "@clerk/clerk-js#build", + "@clerk/mosaic#build", + "@clerk/nextjs#build", + "@clerk/testing#build", + "@clerk/ui#build" ], + "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:nextjs": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:nextjs:canary": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:quickstart": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": [ "CLEANUP", "DEBUG", @@ -403,47 +304,19 @@ "INTEGRATION_STAGING_INSTANCE_KEYS", "VERCEL_AUTOMATION_BYPASS_SECRET" ], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:astro": { + "dependsOn": ["@clerk/astro#build", "@clerk/clerk-js#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:localhost": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": [ "CLEANUP", "DEBUG", @@ -452,340 +325,141 @@ "INTEGRATION_STAGING_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS" ], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:sessions": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:sessions:staging": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:handshake": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:handshake:staging": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:expo-web": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/expo#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:tanstack-react-start": { - "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" + "dependsOn": [ + "@clerk/clerk-js#build", + "@clerk/tanstack-react-start#build", + "@clerk/testing#build", + "@clerk/ui#build" ], + "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:vue": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/testing#build", "@clerk/ui#build", "@clerk/vue#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:nuxt": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nuxt#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:react-router": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/react-router#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:billing": { - "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" + "dependsOn": [ + "@clerk/astro#build", + "@clerk/clerk-js#build", + "@clerk/nextjs#build", + "@clerk/testing#build", + "@clerk/ui#build", + "@clerk/vue#build" ], + "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:machine": { - "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" + "dependsOn": [ + "@clerk/astro#build", + "@clerk/clerk-js#build", + "@clerk/express#build", + "@clerk/fastify#build", + "@clerk/hono#build", + "@clerk/nextjs#build", + "@clerk/nuxt#build", + "@clerk/react-router#build", + "@clerk/tanstack-react-start#build", + "@clerk/testing#build", + "@clerk/ui#build" ], + "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:custom": { + "dependsOn": ["@clerk/clerk-js#build", "@clerk/react#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:cache-components": { - "dependsOn": ["@clerk/nextjs#build"], + "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:chrome-extension": { - "dependsOn": ["@clerk/nextjs#build"], - "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" + "dependsOn": [ + "@clerk/chrome-extension#build", + "@clerk/clerk-js#build", + "@clerk/testing#build", + "@clerk/ui#build" ], + "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:electron": { - "dependsOn": ["@clerk/electron#build"], + "dependsOn": ["@clerk/clerk-js#build", "@clerk/electron#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": [ - "integration/**", - "packages/*/package.json", - "packages/*/src/**", - "packages/*/tsconfig.json", - "packages/*/tsconfig.build.json", - "packages/*/tsconfig.declarations.json", - "packages/*/tsdown.config.*", - "packages/*/tsup.config.*", - "packages/*/rspack.config.*", - "packages/*/vite.config.*", - "!packages/*/src/**/*.test.*", - "!packages/*/src/**/__tests__/**", - "!packages/*/src/**/__mocks__/**", - "!packages/*/src/**/__snapshots__/**" - ], + "inputs": ["integration/**", "!integration/expo-native/**"], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, From f06ae3a4889456e310b802fa790f20685721167b Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 18:32:58 -0700 Subject: [PATCH 02/17] ci(repo): scope integration inputs and defer setup until cache misses --- .github/actions/init-blacksmith/action.yml | 6 +- .github/workflows/ci.yml | 96 +---- integration/scripts/run-integration.mjs | 56 +++ package.json | 4 +- scripts/run-integration.test.mjs | 97 +++++ turbo.json | 457 +++++++++++++++++++-- 6 files changed, 598 insertions(+), 118 deletions(-) create mode 100644 integration/scripts/run-integration.mjs create mode 100644 scripts/run-integration.test.mjs diff --git a/.github/actions/init-blacksmith/action.yml b/.github/actions/init-blacksmith/action.yml index b3718ff9410..77de44ff5dc 100644 --- a/.github/actions/init-blacksmith/action.yml +++ b/.github/actions/init-blacksmith/action.yml @@ -9,6 +9,10 @@ inputs: description: 'Enable Playwright?' required: false default: 'false' + playwright-install-enabled: + description: 'Install Playwright browsers after restoring the cache?' + required: false + default: 'true' turbo-summarize: description: 'The token to use for Turbo task summaries' required: false @@ -146,7 +150,7 @@ runs: key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-playwright-${{ steps.playwright-version.outputs.VERSION }}-v2 - name: Install Playwright Browsers - if: inputs.playwright-enabled == 'true' && steps.playwright-cache.outputs.cache-hit != 'true' + if: inputs.playwright-enabled == 'true' && inputs.playwright-install-enabled == 'true' && steps.playwright-cache.outputs.cache-hit != 'true' shell: bash run: | pnpm playwright install chromium diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8acef77b459..6ef8bbff6a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -379,9 +379,9 @@ jobs: env: NODE_VERSION: ${{ matrix.node-version }} - - name: Run verify script tests + - name: Run CI script tests if: matrix.node-version == '24.15.0' - run: pnpm vitest run --root scripts verify-attach verify-flaky + run: pnpm vitest run --root scripts verify-attach verify-flaky run-integration - name: Upload Turbo Summary uses: actions/upload-artifact@v4.6.2 @@ -582,82 +582,16 @@ jobs: turbo-team: ${{ vars.TURBO_TEAM }} turbo-token: ${{ secrets.TURBO_TOKEN }} playwright-enabled: true - - - name: Verify jq is installed - shell: bash - run: | - if ! command -v jq &> /dev/null; then - echo "jq not found, installing..." - sudo apt-get update && sudo apt-get install -y jq - fi - jq --version + playwright-install-enabled: false - name: Validate turbo task - env: - E2E_APP_CLERK_JS_DIR: ${{runner.temp}} - E2E_APP_CLERK_UI_DIR: ${{runner.temp}} - E2E_CLERK_JS_VERSION: 'latest' - E2E_CLERK_UI_VERSION: 'latest' - E2E_NEXTJS_VERSION: ${{ matrix.next-version }} - E2E_PROJECT: ${{ matrix.test-project }} - INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }} run: | - # Validate the turbo task exists. Turbo's --affected flag is a no-op for - # root-level tasks (//#) — they are always returned as affected. The previous - # --affected check only served to silently skip tests when the task was missing - # from turbo.json, hiding real configuration errors. TASK_NAME="test:integration:${{ matrix.test-name }}" - TURBO_STDERR=$(mktemp) - if ! TURBO_RAW=$(pnpm turbo run "$TASK_NAME" --dry=json 2>"$TURBO_STDERR"); then - echo "::error::Turbo task '$TASK_NAME' failed validation" - cat "$TURBO_STDERR" - exit 1 - fi - - # pnpm can prepend non-JSON lines (e.g. `WARN Unsupported engine: ...`) - # to stdout, which would break jq. Drop everything before the first `{`. - TURBO_JSON=$(printf '%s\n' "$TURBO_RAW" | awk '/^\{/{found=1} found') - - if ! TASK_COUNT=$(jq -er '.tasks | length' <<< "$TURBO_JSON"); then - echo "::error::Turbo task '$TASK_NAME' returned invalid JSON or missing .tasks" - printf '%s\n' "$TURBO_JSON" - exit 1 - fi - - if [ "$TASK_COUNT" -eq 0 ]; then - echo "::error::Turbo task '$TASK_NAME' returned 0 tasks" - exit 1 - fi - - echo "Task '$TASK_NAME' validated ($TASK_COUNT tasks in graph)" - - - name: Build packages - run: pnpm turbo build $TURBO_ARGS --only - - - name: Publish to local registry - run: pkglab pub --force + node -e 'const pkg = require("./package.json"); const turbo = require("./turbo.json"); const name = process.argv[1]; if (!pkg.scripts[name] || !turbo.tasks[`//#${name}`]) process.exit(1)' "$TASK_NAME" - name: Edit .npmrc [link-workspace-packages=false] run: sed -i -E 's/link-workspace-packages=(deep|true)/link-workspace-packages=false/' .npmrc - - name: Install @clerk/clerk-js in os temp - working-directory: ${{runner.temp}} - run: | - mkdir clerk-js && cd clerk-js - pnpm init - pkglab add @clerk/clerk-js - - - name: Install @clerk/ui in os temp - working-directory: ${{runner.temp}} - run: | - mkdir clerk-ui && cd clerk-ui - pnpm init - pkglab add @clerk/ui - - - name: Copy components @clerk/astro - if: ${{ matrix.test-name == 'astro' }} - run: cd packages/astro && pnpm copy:components - - name: Write all ENV certificates to files in integration/certs uses: actions/github-script@v7.1.0 env: @@ -675,18 +609,6 @@ jobs: fs.writeFileSync(path.join(process.env.GITHUB_WORKSPACE, 'integration/certs', name), cert); } - - name: LS certs - working-directory: ./integration/certs - run: ls -la && pwd - - - name: Ensure Xvfb is installed - if: ${{ matrix.test-name == 'electron' }} - run: | - if ! command -v xvfb-run &> /dev/null; then - sudo apt-get update - sudo apt-get install -y xvfb - fi - - name: Configure test user cleanup run: echo "INTEGRATION_TEST_RUN_KEY=${RUN_ID}-${RUN_ATTEMPT}-${TEST_NAME}-${TEST_PROJECT}-${NEXT_VERSION:-default}" >> "$GITHUB_ENV" env: @@ -699,14 +621,10 @@ jobs: - name: Run Integration Tests id: integration-tests timeout-minutes: 25 - run: | - if [ "${{ matrix.test-name }}" = "electron" ]; then - xvfb-run -a pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS - else - pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS - fi + run: pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS env: TURBO_FORCE: ${{ github.event_name == 'merge_group' }} + E2E_CI_SETUP: ${{ matrix.test-name }} CLERK_E2E_OAUTH_PROVIDER_CLIENT_SECRET: ${{ secrets.CLERK_E2E_OAUTH_PROVIDER_CLIENT_SECRET }} CLERK_PLATFORM_API_KEY: ${{ secrets.CLERK_PLATFORM_API_KEY }} E2E_DEBUG: '1' @@ -738,7 +656,7 @@ jobs: retention-days: 1 - name: Delete integration-test users - if: ${{ always() && steps.integration-tests.outcome != 'skipped' }} + if: ${{ always() && steps.integration-tests.outputs.tests-started == 'true' }} timeout-minutes: 10 run: pnpm test:integration:cleanup env: diff --git a/integration/scripts/run-integration.mjs b/integration/scripts/run-integration.mjs new file mode 100644 index 00000000000..851e177c209 --- /dev/null +++ b/integration/scripts/run-integration.mjs @@ -0,0 +1,56 @@ +import { spawnSync } from 'node:child_process'; +import { appendFileSync, mkdirSync } from 'node:fs'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..'); +const [command, ...args] = process.argv.slice(2); + +function run(command, args, options = {}) { + const result = spawnSync(command, args, { cwd: root, stdio: 'inherit', ...options }); + if (result.error) { + console.error(result.error); + process.exit(1); + } + if (result.status !== 0) { + process.exit(result.status ?? 1); + } +} + +if (process.env.E2E_CI_SETUP) { + run('pnpm', ['turbo', 'build', ...process.env.TURBO_ARGS.split(/\s+/).filter(Boolean), '--only'], { + env: { ...process.env, TURBO_FORCE: 'false' }, + }); + run('pkglab', ['pub', '--force']); + + for (const [name, packageName, directory] of [ + ['clerk-js', '@clerk/clerk-js', process.env.E2E_APP_CLERK_JS_DIR], + ['clerk-ui', '@clerk/ui', process.env.E2E_APP_CLERK_UI_DIR], + ]) { + const packageDir = join(directory, name); + mkdirSync(packageDir, { recursive: true }); + run('pnpm', ['init'], { cwd: packageDir }); + run('pkglab', ['add', packageName], { cwd: packageDir }); + } + + if (process.env.E2E_CI_SETUP === 'astro') { + run('pnpm', ['copy:components'], { cwd: join(root, 'packages/astro') }); + } + + run('pnpm', ['playwright', 'install', 'chromium']); + + if (process.env.E2E_CI_SETUP === 'electron') { + const xvfb = spawnSync('sh', ['-c', 'command -v xvfb-run'], { stdio: 'ignore' }); + if (xvfb.status !== 0) { + run('sudo', ['apt-get', 'update']); + run('sudo', ['apt-get', 'install', '-y', 'xvfb']); + } + } + + appendFileSync(process.env.GITHUB_OUTPUT, 'tests-started=true\n'); +} + +run( + process.env.E2E_CI_SETUP === 'electron' ? 'xvfb-run' : command, + process.env.E2E_CI_SETUP === 'electron' ? ['-a', command, ...args] : args, +); diff --git a/package.json b/package.json index 5d6f89232fc..ce0dce3e0f5 100644 --- a/package.json +++ b/package.json @@ -38,10 +38,10 @@ "test:cache:clear": "FORCE_COLOR=1 turbo test:cache:clear --continue --concurrency=${TURBO_CONCURRENCY:-80%}", "test:integration:ap-flows": "E2E_DEBUG=1 pnpm test:integration:base --grep @ap-flows", "test:integration:astro": "E2E_APP_ID=astro.* pnpm test:integration:base --grep @astro", - "test:integration:base": "pnpm playwright test --config integration/playwright.config.ts", + "test:integration:base": "node integration/scripts/run-integration.mjs pnpm playwright test --config integration/playwright.config.ts", "test:integration:billing": "E2E_APP_ID=withBillingJwtV2.* pnpm test:integration:base --grep @billing", "test:integration:cache-components": "E2E_APP_ID=next.cacheComponents pnpm test:integration:base --grep @cache-components", - "test:integration:chrome-extension": "pnpm playwright test --config integration/playwright.chrome-extension.config.ts", + "test:integration:chrome-extension": "node integration/scripts/run-integration.mjs pnpm playwright test --config integration/playwright.chrome-extension.config.ts", "test:integration:cleanup": "pnpm playwright test --config integration/playwright.cleanup.config.ts", "test:integration:custom": "pnpm test:integration:base --grep @custom", "test:integration:deployment:nextjs": "pnpm playwright test --config integration/playwright.deployments.config.ts", diff --git a/scripts/run-integration.test.mjs b/scripts/run-integration.test.mjs new file mode 100644 index 00000000000..ff6c8682c9a --- /dev/null +++ b/scripts/run-integration.test.mjs @@ -0,0 +1,97 @@ +import { spawnSync } from 'node:child_process'; +import { mkdtempSync, readFileSync, rmSync, writeFileSync, mkdirSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; + +import { describe, expect, test } from 'vitest'; + +const runner = resolve('integration/scripts/run-integration.mjs'); + +function run(setup, fail = '') { + const dir = mkdtempSync(join(tmpdir(), 'run-integration-')); + const bin = join(dir, 'bin'); + mkdirSync(bin); + const command = `#!/usr/bin/env node\nconst fs = require('node:fs');\nconst path = require('node:path');\nconst name = path.basename(process.argv[1]);\nconst args = process.argv.slice(2);\nfs.appendFileSync(process.env.COMMAND_LOG, JSON.stringify({ name, args, cwd: process.cwd(), turboForce: process.env.TURBO_FORCE }) + '\\n');\nprocess.exit(process.env.FAIL_COMMAND === name || process.env.FAIL_COMMAND === name + ' ' + args.join(' ') ? 7 : 0);\n`; + for (const name of ['pnpm', 'pkglab', 'xvfb-run']) { + const file = join(bin, name); + writeFileSync(file, command, { mode: 0o755 }); + } + const output = join(dir, 'output'); + const log = join(dir, 'log'); + const result = spawnSync(process.execPath, [runner, 'pnpm', 'playwright', 'test', '--grep', '@case'], { + cwd: resolve('.'), + env: { + ...process.env, + PATH: `${bin}:${process.env.PATH}`, + COMMAND_LOG: log, + FAIL_COMMAND: fail, + GITHUB_OUTPUT: output, + E2E_CI_SETUP: setup, + E2E_APP_CLERK_JS_DIR: dir, + E2E_APP_CLERK_UI_DIR: dir, + TURBO_ARGS: '--concurrency=2 --output-logs=new-only', + TURBO_FORCE: 'true', + }, + encoding: 'utf8', + }); + const calls = readFileSync(log, 'utf8').trim().split('\n').map(JSON.parse); + const marker = (() => { + try { + return readFileSync(output, 'utf8'); + } catch { + return ''; + } + })(); + rmSync(dir, { recursive: true, force: true }); + return { result, calls, marker }; +} + +describe('CI integration runner', () => { + test('passes local test arguments through unchanged', () => { + const { result, calls, marker } = run(''); + expect(result.status).toBe(0); + expect(calls).toMatchObject([{ name: 'pnpm', args: ['playwright', 'test', '--grep', '@case'] }]); + expect(marker).toBe(''); + }); + + test('runs CI setup before Playwright and records that tests started', () => { + const { result, calls, marker } = run('generic'); + expect(result.status).toBe(0); + expect(calls.map(({ name, args }) => `${name} ${args.join(' ')}`)).toEqual([ + 'pnpm turbo build --concurrency=2 --output-logs=new-only --only', + 'pkglab pub --force', + 'pnpm init', + 'pkglab add @clerk/clerk-js', + 'pnpm init', + 'pkglab add @clerk/ui', + 'pnpm playwright install chromium', + 'pnpm playwright test --grep @case', + ]); + expect(calls[0].turboForce).toBe('false'); + expect(marker).toBe('tests-started=true\n'); + }); + + test('does not run tests or set cleanup marker after setup failure', () => { + const { result, calls, marker } = run('generic', 'pkglab'); + expect(result.status).toBe(7); + expect(calls.map(call => call.name)).toEqual(['pnpm', 'pkglab']); + expect(marker).toBe(''); + }); + + test('records cleanup marker when Playwright fails', () => { + const { result, calls, marker } = run('generic', 'pnpm playwright test --grep @case'); + expect(result.status).toBe(7); + expect(calls.at(-1)).toMatchObject({ name: 'pnpm', args: ['playwright', 'test', '--grep', '@case'] }); + expect(marker).toBe('tests-started=true\n'); + }); + + test('runs Astro copy and Electron tests under Xvfb', () => { + const astro = run('astro'); + expect(astro.calls.some(call => call.args.join(' ') === 'copy:components')).toBe(true); + const electron = run('electron'); + expect(electron.calls.at(-1)).toMatchObject({ + name: 'xvfb-run', + args: ['-a', 'pnpm', 'playwright', 'test', '--grep', '@case'], + }); + }); +}); diff --git a/turbo.json b/turbo.json index 1206c7767a5..9605f38fc29 100644 --- a/turbo.json +++ b/turbo.json @@ -229,7 +229,23 @@ "//#test:integration:ap-flows": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router-quickstart/**", + "integration/templates/next-app-router-quickstart-v6/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, @@ -242,28 +258,90 @@ "@clerk/vue#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/react-vite/**", + "integration/templates/next-app-router/**", + "integration/templates/vue-vite/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:express": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/express#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/express-vite/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:fastify": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/fastify#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/fastify-vite/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:hono": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/hono#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/hono-vite/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, @@ -276,21 +354,66 @@ "@clerk/ui#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router-mosaic/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:nextjs": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nuxt,react-router,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:nextjs:canary": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nuxt,react-router,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, @@ -304,14 +427,45 @@ "INTEGRATION_STAGING_INSTANCE_KEYS", "VERCEL_AUTOMATION_BYPASS_SECRET" ], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router-quickstart/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:astro": { "dependsOn": ["@clerk/astro#build", "@clerk/clerk-js#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/astro-node/**", + "integration/templates/astro-hybrid/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, @@ -325,42 +479,132 @@ "INTEGRATION_STAGING_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS" ], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:sessions": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:sessions:staging": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:handshake": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:handshake:staging": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:expo-web": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/expo#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/expo-web/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, @@ -372,28 +616,89 @@ "@clerk/ui#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,vue}/**/*.test.ts", + "integration/templates/tanstack-react-start/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:vue": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/testing#build", "@clerk/ui#build", "@clerk/vue#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start}/**/*.test.ts", + "integration/templates/vue-vite/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:nuxt": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nuxt#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/nuxt-node/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:react-router": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/react-router#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/react-router-node/**", + "integration/templates/react-router-library/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, @@ -407,7 +712,24 @@ "@clerk/vue#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-app-router/**", + "integration/templates/vue-vite/**", + "integration/templates/astro-node/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, @@ -426,21 +748,73 @@ "@clerk/ui#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{chrome-extension,custom-flows,electron,expo-web,handshake,localhost,mosaic,next-account-portal,nextjs,session-token-cache,sessions,vue}/**/*.test.ts", + "integration/templates/astro-node/**", + "integration/templates/express-vite/**", + "integration/templates/fastify-vite/**", + "integration/templates/hono-vite/**", + "integration/templates/next-app-router/**", + "integration/templates/nuxt-node/**", + "integration/templates/react-router-node/**", + "integration/templates/tanstack-react-start/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:custom": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/react#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/custom-flows-react-vite/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:cache-components": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/next-cache-components/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, @@ -452,14 +826,45 @@ "@clerk/ui#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/chrome-extension-vite/**", + "integration/templates/react-vite/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, "//#test:integration:electron": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/electron#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "inputs": ["integration/**", "!integration/expo-native/**"], + "passThroughEnv": [ + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" + ], + "inputs": [ + "integration/*", + "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/templates/*", + "integration/tests/**", + "!integration/tests/{astro,chrome-extension,custom-flows,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", + "integration/templates/electron-vite/**" + ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, From 2929ab75d5b978e291bd67164fa7755cf840d1e6 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 18:34:54 -0700 Subject: [PATCH 03/17] ci(repo): avoid shell interpolation warning in task validation --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ef8bbff6a7..9017ad1b5ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -587,7 +587,7 @@ jobs: - name: Validate turbo task run: | TASK_NAME="test:integration:${{ matrix.test-name }}" - node -e 'const pkg = require("./package.json"); const turbo = require("./turbo.json"); const name = process.argv[1]; if (!pkg.scripts[name] || !turbo.tasks[`//#${name}`]) process.exit(1)' "$TASK_NAME" + node -e 'const pkg = require("./package.json"); const turbo = require("./turbo.json"); const name = process.argv[1]; if (!pkg.scripts[name] || !turbo.tasks["//#" + name]) process.exit(1)' "$TASK_NAME" - name: Edit .npmrc [link-workspace-packages=false] run: sed -i -E 's/link-workspace-packages=(deep|true)/link-workspace-packages=false/' .npmrc From 373a296ff5e158fd030b8e2cc0d33a53202e8921 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 19:16:51 -0700 Subject: [PATCH 04/17] ci(repo): check integration suite inputs against the apps their tests boot Derive each suite's dependsOn and inputs from the presets and the tests tagged for it, and fail the CI script tests when turbo.json drifts. Each task now lists only the test directories and templates it owns, and the turbo passthrough variables move to globalPassThroughEnv so the 26 task blocks differ only where the suites differ. --- .github/workflows/ci.yml | 2 +- integration/models/applicationConfig.ts | 3 + scripts/integration-deps.mjs | 206 +++++++++++++ scripts/integration-deps.test.mjs | 26 ++ turbo.json | 379 ++++++------------------ 5 files changed, 329 insertions(+), 287 deletions(-) create mode 100644 scripts/integration-deps.mjs create mode 100644 scripts/integration-deps.test.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9017ad1b5ae..b3cf78675c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -381,7 +381,7 @@ jobs: - name: Run CI script tests if: matrix.node-version == '24.15.0' - run: pnpm vitest run --root scripts verify-attach verify-flaky run-integration + run: pnpm vitest run --root scripts verify-attach verify-flaky run-integration integration-deps - name: Upload Turbo Summary uses: actions/upload-artifact@v4.6.2 diff --git a/integration/models/applicationConfig.ts b/integration/models/applicationConfig.ts index 9e9474505d1..28457d03220 100644 --- a/integration/models/applicationConfig.ts +++ b/integration/models/applicationConfig.ts @@ -153,6 +153,9 @@ export const applicationConfig = () => { get scripts() { return scripts; }, + get template() { + return template; + }, get pkglabDependencies() { return [...dependencies.entries()].filter(([, version]) => version === PKGLAB).map(([name]) => name); }, diff --git a/scripts/integration-deps.mjs b/scripts/integration-deps.mjs new file mode 100644 index 00000000000..b6ace959c01 --- /dev/null +++ b/scripts/integration-deps.mjs @@ -0,0 +1,206 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +const TASK_PREFIX = '//#test:integration:'; +const TESTS = path.join(root, 'integration/tests'); +const HARNESS_PACKAGES = ['@clerk/clerk-js', '@clerk/testing', '@clerk/ui']; +const SHARED_INPUTS = [ + 'integration/*', + 'integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**', + 'integration/tests/*', + 'integration/tests/snapshots/**', +]; +const PRESET_GROUPS = { + astro: 'astro', + chromeExtension: 'chrome-extension', + customFlows: 'custom-flows', + electron: 'electron', + expo: 'expo', + express: 'express', + fastify: 'fastify', + hono: 'hono', + next: 'next', + nuxt: 'nuxt', + react: 'react', + reactRouter: 'react-router', + tanstack: 'tanstack', + vue: 'vue', +}; + +const read = file => fs.readFileSync(file, 'utf8'); +const readJson = file => JSON.parse(read(file)); + +export const integrationSuites = () => + Object.keys(readJson(path.join(root, 'turbo.json')).tasks) + .filter(task => task.startsWith(TASK_PREFIX)) + .map(task => task.slice(TASK_PREFIX.length)); + +export const scriptFor = suite => { + const scripts = readJson(path.join(root, 'package.json')).scripts; + const base = suite.replace(/:(canary|staging)$/, ''); + return scripts[`test:integration:${base}`] ?? scripts[`test:integration:${base}:disabled`]; +}; + +function* walk(dir) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + yield* walk(full); + } else { + yield full; + } + } +} + +const workspaceGraph = () => { + const graph = new Map(); + for (const dir of fs.readdirSync(path.join(root, 'packages'))) { + const manifestPath = path.join(root, 'packages', dir, 'package.json'); + if (!fs.existsSync(manifestPath)) { + continue; + } + const manifest = readJson(manifestPath); + const deps = { ...manifest.dependencies, ...manifest.devDependencies, ...manifest.optionalDependencies }; + graph.set( + manifest.name, + Object.keys(deps).filter(name => name.startsWith('@clerk/')), + ); + } + return graph; +}; + +const transitiveDeps = (graph, name) => { + const seen = new Set(); + const queue = [...(graph.get(name) ?? [])]; + while (queue.length) { + const next = queue.pop(); + if (!seen.has(next) && graph.has(next)) { + seen.add(next); + queue.push(...graph.get(next)); + } + } + return seen; +}; + +const withoutImplied = (graph, names) => + names.filter( + name => + HARNESS_PACKAGES.includes(name) || !names.some(other => other !== name && transitiveDeps(graph, other).has(name)), + ); + +const followImports = seeds => { + const seen = new Map(seeds.map(file => [file, read(file)])); + const queue = [...seen.keys()]; + while (queue.length) { + const file = queue.pop(); + for (const match of seen.get(file).matchAll(/from '(\.{1,2}\/[^']+)'/g)) { + const target = path.resolve(path.dirname(file), match[1]); + if (!target.startsWith(TESTS)) { + continue; + } + const resolved = [target, `${target}.ts`, path.join(target, 'index.ts')].find( + candidate => fs.existsSync(candidate) && fs.statSync(candidate).isFile(), + ); + if (resolved && !seen.has(resolved)) { + seen.set(resolved, read(resolved)); + queue.push(resolved); + } + } + } + return seen; +}; + +const globToRegExp = glob => new RegExp(`^${glob.replace(/[.]/g, '\\.').replace(/\*/g, '.*')}$`); + +const braceGlob = (prefix, names) => + names.length === 1 ? `${prefix}/${names[0]}/**` : `${prefix}/{${names.join(',')}}/**`; + +const loadAppConfigs = async () => { + const configs = new Map(); + for (const [group, file] of Object.entries(PRESET_GROUPS)) { + const module = await import(path.join(root, 'integration/presets', `${file}.ts`)); + for (const [name, config] of Object.entries(Object.values(module)[0])) { + configs.set(`${group}.${name}`, { + packages: config.pkglabDependencies, + template: config.template ? path.relative(root, config.template) : undefined, + }); + } + } + return configs; +}; + +const longRunningAppConfigs = () => { + const source = read(path.join(root, 'integration/presets/longRunningApps.ts')); + return [...source.matchAll(/id: '([^']+)',\s*config: ([a-zA-Z]+)\.([a-zA-Z0-9]+)/g)].map(match => ({ + id: match[1], + config: `${match[2]}.${match[3]}`, + })); +}; + +export const deriveIntegrationTasks = async () => { + const appConfigs = await loadAppConfigs(); + const longRunning = longRunningAppConfigs(); + const graph = workspaceGraph(); + const testFiles = [...walk(TESTS)].filter(file => file.endsWith('.test.ts')); + + const tasks = new Map(); + for (const suite of integrationSuites()) { + const script = scriptFor(suite) ?? ''; + const tag = script.match(/--grep @([a-z-]+)/)?.[1]; + const base = suite.replace(/:(canary|staging)$/, ''); + const tagged = tag + ? testFiles.filter(file => new RegExp(`@${tag}(?![a-z-])`).test(read(file))) + : testFiles.filter(file => file.startsWith(path.join(TESTS, base) + path.sep)); + const sources = followImports(tagged); + + const patterns = script.match(/E2E_APP_ID=([^ ]+)/)?.[1].split(',') ?? []; + const configNames = new Set(); + for (const source of sources.values()) { + for (const match of source.matchAll(/appConfigs\.([a-zA-Z]+)\.([a-zA-Z0-9]+)/g)) { + if (PRESET_GROUPS[match[1]]) { + configNames.add(`${match[1]}.${match[2]}`); + } + } + for (const match of source.matchAll(/(?:withPattern:|getByPattern\()\s*\[([^\]]*)\]/g)) { + patterns.push(...[...match[1].matchAll(/'([^']+)'/g)].map(id => id[1])); + } + } + for (const pattern of patterns) { + const matches = globToRegExp(pattern); + longRunning.filter(app => matches.test(app.id)).forEach(app => configNames.add(app.config)); + } + + const packages = new Set(HARNESS_PACKAGES); + const templates = new Set(); + for (const name of configNames) { + const config = appConfigs.get(name); + if (!config) { + throw new Error(`${suite}: tests reference unknown app config ${name}`); + } + config.packages.filter(pkg => graph.has(pkg)).forEach(pkg => packages.add(pkg)); + if (config.template) { + templates.add(path.basename(config.template)); + } + } + const testDirs = new Set( + [...sources.keys()] + .map(file => path.relative(TESTS, file).split(path.sep)[0]) + .filter(segment => !segment.includes('.') && segment !== 'snapshots'), + ); + + tasks.set(suite, { + dependsOn: withoutImplied(graph, [...packages].sort()).map(pkg => `${pkg}#build`), + inputs: [ + ...SHARED_INPUTS, + ...(testDirs.size ? [braceGlob('integration/tests', [...testDirs].sort())] : []), + 'integration/templates/*', + ...(templates.size ? [braceGlob('integration/templates', [...templates].sort())] : []), + ], + configs: [...configNames].sort(), + }); + } + return tasks; +}; diff --git a/scripts/integration-deps.test.mjs b/scripts/integration-deps.test.mjs new file mode 100644 index 00000000000..005446490d7 --- /dev/null +++ b/scripts/integration-deps.test.mjs @@ -0,0 +1,26 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +import { describe, expect, test } from 'vitest'; + +import { deriveIntegrationTasks, integrationSuites, root, scriptFor } from './integration-deps.mjs'; + +const turbo = JSON.parse(readFileSync(join(root, 'turbo.json'), 'utf8')); +const derived = await deriveIntegrationTasks(); + +describe('integration suite tasks in turbo.json', () => { + test.each(integrationSuites())('%s has a package.json script', suite => { + expect(scriptFor(suite), `test:integration:${suite} has no matching script in package.json`).toBeDefined(); + }); + + test.each([...derived.entries()])('%s depends on the packages its apps install', (suite, expected) => { + const task = turbo.tasks[`//#test:integration:${suite}`]; + const actual = { dependsOn: task.dependsOn, inputs: task.inputs }; + const hint = [ + `turbo.json task //#test:integration:${suite} is out of sync with the apps its tests boot (${expected.configs.join(', ')}).`, + 'Replace its dependsOn and inputs with:', + JSON.stringify({ dependsOn: expected.dependsOn, inputs: expected.inputs }, null, 2), + ].join('\n'); + expect(actual, hint).toEqual({ dependsOn: expected.dependsOn, inputs: expected.inputs }); + }); +}); diff --git a/turbo.json b/turbo.json index 9605f38fc29..990a630e98e 100644 --- a/turbo.json +++ b/turbo.json @@ -39,7 +39,13 @@ "ACTIONS_STEP_DEBUG", "VERCEL_AUTOMATION_BYPASS_SECRET", "PORT", - "UI_PORT" + "UI_PORT", + "TURBO_ARGS", + "TURBO_CACHE", + "TURBO_TEAM", + "TURBO_TOKEN", + "TURBO_REMOTE_CACHE_SIGNATURE_KEY", + "GITHUB_OUTPUT" ], "tasks": { "build": { @@ -229,22 +235,14 @@ "//#test:integration:ap-flows": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/next-account-portal/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/next-app-router-quickstart/**", - "integration/templates/next-app-router-quickstart-v6/**" + "integration/templates/{next-app-router-quickstart,next-app-router-quickstart-v6}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -258,23 +256,14 @@ "@clerk/vue#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/session-token-cache/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/react-vite/**", - "integration/templates/next-app-router/**", - "integration/templates/vue-vite/**" + "integration/templates/{custom-flows-react-vite,next-app-router,react-vite,vue-vite}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -282,20 +271,13 @@ "//#test:integration:express": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/express#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/express/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/express-vite/**" ], "outputs": ["integration/playwright-report/**"], @@ -304,20 +286,13 @@ "//#test:integration:fastify": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/fastify#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/fastify/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/fastify-vite/**" ], "outputs": ["integration/playwright-report/**"], @@ -326,20 +301,13 @@ "//#test:integration:hono": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/hono#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/hono/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/hono-vite/**" ], "outputs": ["integration/playwright-report/**"], @@ -354,20 +322,13 @@ "@clerk/ui#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/mosaic/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/next-app-router-mosaic/**" ], "outputs": ["integration/playwright-report/**"], @@ -376,21 +337,14 @@ "//#test:integration:nextjs": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/{nextjs,session-token-cache}/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nuxt,react-router,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/next-app-router/**" + "integration/templates/{next-app-router,react-vite}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -398,21 +352,14 @@ "//#test:integration:nextjs:canary": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/{nextjs,session-token-cache}/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nuxt,react-router,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/next-app-router/**" + "integration/templates/{next-app-router,react-vite}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -427,20 +374,12 @@ "INTEGRATION_STAGING_INSTANCE_KEYS", "VERCEL_AUTOMATION_BYPASS_SECRET" ], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/next-app-router-quickstart/**" ], "outputs": ["integration/playwright-report/**"], @@ -449,22 +388,14 @@ "//#test:integration:astro": { "dependsOn": ["@clerk/astro#build", "@clerk/clerk-js#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/astro/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/astro-node/**", - "integration/templates/astro-hybrid/**" + "integration/templates/{astro-hybrid,astro-node}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -479,20 +410,13 @@ "INTEGRATION_STAGING_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS" ], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/{localhost,sessions}/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/next-app-router/**" ], "outputs": ["integration/playwright-report/**"], @@ -501,20 +425,13 @@ "//#test:integration:sessions": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/sessions/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,tanstack-start,vue}/**/*.test.ts", "integration/templates/next-app-router/**" ], "outputs": ["integration/playwright-report/**"], @@ -523,20 +440,13 @@ "//#test:integration:sessions:staging": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/sessions/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,tanstack-start,vue}/**/*.test.ts", "integration/templates/next-app-router/**" ], "outputs": ["integration/playwright-report/**"], @@ -545,20 +455,13 @@ "//#test:integration:handshake": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/{handshake,sessions}/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/next-app-router/**" ], "outputs": ["integration/playwright-report/**"], @@ -567,20 +470,13 @@ "//#test:integration:handshake:staging": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/{handshake,sessions}/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/next-app-router/**" ], "outputs": ["integration/playwright-report/**"], @@ -589,20 +485,13 @@ "//#test:integration:expo-web": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/expo#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/expo-web/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/expo-web/**" ], "outputs": ["integration/playwright-report/**"], @@ -616,20 +505,13 @@ "@clerk/ui#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/tanstack-start/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,vue}/**/*.test.ts", "integration/templates/tanstack-react-start/**" ], "outputs": ["integration/playwright-report/**"], @@ -638,20 +520,13 @@ "//#test:integration:vue": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/testing#build", "@clerk/ui#build", "@clerk/vue#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/vue/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start}/**/*.test.ts", "integration/templates/vue-vite/**" ], "outputs": ["integration/playwright-report/**"], @@ -660,20 +535,13 @@ "//#test:integration:nuxt": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nuxt#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/nuxt/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/nuxt-node/**" ], "outputs": ["integration/playwright-report/**"], @@ -682,22 +550,14 @@ "//#test:integration:react-router": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/react-router#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/react-router/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/react-router-node/**", - "integration/templates/react-router-library/**" + "integration/templates/{react-router-library,react-router-node}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -712,23 +572,14 @@ "@clerk/vue#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/astro/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/next-app-router/**", - "integration/templates/vue-vite/**", - "integration/templates/astro-node/**" + "integration/templates/{astro-node,next-app-router,vue-vite}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -748,28 +599,14 @@ "@clerk/ui#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/{astro,express,fastify,hono,nuxt,react-router,tanstack-start}/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{chrome-extension,custom-flows,electron,expo-web,handshake,localhost,mosaic,next-account-portal,nextjs,session-token-cache,sessions,vue}/**/*.test.ts", - "integration/templates/astro-node/**", - "integration/templates/express-vite/**", - "integration/templates/fastify-vite/**", - "integration/templates/hono-vite/**", - "integration/templates/next-app-router/**", - "integration/templates/nuxt-node/**", - "integration/templates/react-router-node/**", - "integration/templates/tanstack-react-start/**" + "integration/templates/{astro-node,express-vite,fastify-vite,hono-vite,next-app-router,nuxt-node,react-router-node,tanstack-react-start}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -777,21 +614,14 @@ "//#test:integration:custom": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/react#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/custom-flows/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/custom-flows-react-vite/**" + "integration/templates/{custom-flows-react-vite,react-vite}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -799,20 +629,12 @@ "//#test:integration:cache-components": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/next-cache-components/**" ], "outputs": ["integration/playwright-report/**"], @@ -826,22 +648,14 @@ "@clerk/ui#build" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/chrome-extension/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,custom-flows,electron,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", - "integration/templates/chrome-extension-vite/**", - "integration/templates/react-vite/**" + "integration/templates/{chrome-extension-vite,react-vite}/**" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -849,20 +663,13 @@ "//#test:integration:electron": { "dependsOn": ["@clerk/clerk-js#build", "@clerk/electron#build", "@clerk/testing#build", "@clerk/ui#build"], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], - "passThroughEnv": [ - "TURBO_ARGS", - "TURBO_CACHE", - "TURBO_TEAM", - "TURBO_TOKEN", - "TURBO_REMOTE_CACHE_SIGNATURE_KEY", - "GITHUB_OUTPUT" - ], "inputs": [ "integration/*", "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", + "integration/tests/*", + "integration/tests/snapshots/**", + "integration/tests/electron/**", "integration/templates/*", - "integration/tests/**", - "!integration/tests/{astro,chrome-extension,custom-flows,expo-web,express,fastify,handshake,hono,localhost,mosaic,next-account-portal,nextjs,nuxt,react-router,session-token-cache,sessions,tanstack-start,vue}/**/*.test.ts", "integration/templates/electron-vite/**" ], "outputs": ["integration/playwright-report/**"], From 3ccf3cb1d13522b82438bb2bd29f6372c3a04815 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 19:16:52 -0700 Subject: [PATCH 05/17] ci(repo): fail the integration runner early when CI setup env is missing Read the environment the CI setup needs up front and exit with a named variable instead of crashing mid-run. --- integration/scripts/run-integration.mjs | 83 ++++++++++++++----------- scripts/run-integration.test.mjs | 19 +++++- 2 files changed, 64 insertions(+), 38 deletions(-) diff --git a/integration/scripts/run-integration.mjs b/integration/scripts/run-integration.mjs index 851e177c209..9b35d7f5fb6 100644 --- a/integration/scripts/run-integration.mjs +++ b/integration/scripts/run-integration.mjs @@ -5,52 +5,63 @@ import { fileURLToPath } from 'node:url'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..'); const [command, ...args] = process.argv.slice(2); +const suite = process.env.E2E_CI_SETUP; -function run(command, args, options = {}) { - const result = spawnSync(command, args, { cwd: root, stdio: 'inherit', ...options }); - if (result.error) { - console.error(result.error); - process.exit(1); - } - if (result.status !== 0) { - process.exit(result.status ?? 1); - } +if (!command) { + fail('usage: node integration/scripts/run-integration.mjs [args...]'); } -if (process.env.E2E_CI_SETUP) { - run('pnpm', ['turbo', 'build', ...process.env.TURBO_ARGS.split(/\s+/).filter(Boolean), '--only'], { - env: { ...process.env, TURBO_FORCE: 'false' }, - }); - run('pkglab', ['pub', '--force']); +if (suite) { + prepareCiRunner(suite); +} - for (const [name, packageName, directory] of [ - ['clerk-js', '@clerk/clerk-js', process.env.E2E_APP_CLERK_JS_DIR], - ['clerk-ui', '@clerk/ui', process.env.E2E_APP_CLERK_UI_DIR], - ]) { - const packageDir = join(directory, name); - mkdirSync(packageDir, { recursive: true }); - run('pnpm', ['init'], { cwd: packageDir }); - run('pkglab', ['add', packageName], { cwd: packageDir }); - } +run(suite === 'electron' ? 'xvfb-run' : command, suite === 'electron' ? ['-a', command, ...args] : args); - if (process.env.E2E_CI_SETUP === 'astro') { +function prepareCiRunner(suite) { + const turboArgs = required('TURBO_ARGS').split(/\s+/).filter(Boolean); + const installs = [ + ['@clerk/clerk-js', join(required('E2E_APP_CLERK_JS_DIR'), 'clerk-js')], + ['@clerk/ui', join(required('E2E_APP_CLERK_UI_DIR'), 'clerk-ui')], + ]; + const githubOutput = required('GITHUB_OUTPUT'); + + run('pnpm', ['turbo', 'build', ...turboArgs, '--only'], { env: { ...process.env, TURBO_FORCE: 'false' } }); + run('pkglab', ['pub', '--force']); + for (const [packageName, dir] of installs) { + mkdirSync(dir, { recursive: true }); + run('pnpm', ['init'], { cwd: dir }); + run('pkglab', ['add', packageName], { cwd: dir }); + } + if (suite === 'astro') { run('pnpm', ['copy:components'], { cwd: join(root, 'packages/astro') }); } - run('pnpm', ['playwright', 'install', 'chromium']); + if (suite === 'electron' && spawnSync('sh', ['-c', 'command -v xvfb-run'], { stdio: 'ignore' }).status !== 0) { + run('sudo', ['apt-get', 'update']); + run('sudo', ['apt-get', 'install', '-y', 'xvfb']); + } + appendFileSync(githubOutput, 'tests-started=true\n'); +} - if (process.env.E2E_CI_SETUP === 'electron') { - const xvfb = spawnSync('sh', ['-c', 'command -v xvfb-run'], { stdio: 'ignore' }); - if (xvfb.status !== 0) { - run('sudo', ['apt-get', 'update']); - run('sudo', ['apt-get', 'install', '-y', 'xvfb']); - } +function required(name) { + const value = process.env[name]; + if (!value) { + fail(`${name} must be set when E2E_CI_SETUP is set`); } + return value; +} - appendFileSync(process.env.GITHUB_OUTPUT, 'tests-started=true\n'); +function run(command, args, options = {}) { + const result = spawnSync(command, args, { cwd: root, stdio: 'inherit', ...options }); + if (result.error) { + fail(result.error.message); + } + if (result.status !== 0) { + process.exit(result.status ?? 1); + } } -run( - process.env.E2E_CI_SETUP === 'electron' ? 'xvfb-run' : command, - process.env.E2E_CI_SETUP === 'electron' ? ['-a', command, ...args] : args, -); +function fail(message) { + console.error(`run-integration: ${message}`); + process.exit(1); +} diff --git a/scripts/run-integration.test.mjs b/scripts/run-integration.test.mjs index ff6c8682c9a..cf572cf0795 100644 --- a/scripts/run-integration.test.mjs +++ b/scripts/run-integration.test.mjs @@ -7,7 +7,7 @@ import { describe, expect, test } from 'vitest'; const runner = resolve('integration/scripts/run-integration.mjs'); -function run(setup, fail = '') { +function run(setup, fail = '', env = {}) { const dir = mkdtempSync(join(tmpdir(), 'run-integration-')); const bin = join(dir, 'bin'); mkdirSync(bin); @@ -31,10 +31,17 @@ function run(setup, fail = '') { E2E_APP_CLERK_UI_DIR: dir, TURBO_ARGS: '--concurrency=2 --output-logs=new-only', TURBO_FORCE: 'true', + ...env, }, encoding: 'utf8', }); - const calls = readFileSync(log, 'utf8').trim().split('\n').map(JSON.parse); + const calls = (() => { + try { + return readFileSync(log, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse); + } catch { + return []; + } + })(); const marker = (() => { try { return readFileSync(output, 'utf8'); @@ -71,6 +78,14 @@ describe('CI integration runner', () => { expect(marker).toBe('tests-started=true\n'); }); + test('refuses CI setup without the environment it needs', () => { + const { result, calls, marker } = run('generic', '', { TURBO_ARGS: '' }); + expect(result.status).toBe(1); + expect(result.stderr).toContain('TURBO_ARGS must be set when E2E_CI_SETUP is set'); + expect(calls).toEqual([]); + expect(marker).toBe(''); + }); + test('does not run tests or set cleanup marker after setup failure', () => { const { result, calls, marker } = run('generic', 'pkglab'); expect(result.status).toBe(7); From 8b16b7462ad42705cae64535f2dd9b7ac0fe9514 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 19:16:53 -0700 Subject: [PATCH 06/17] docs(repo): document integration CI caching and add the hash matrix script Explain how a suite's cache key is built, the --only rule, the merge queue safety net, the in-task setup script, and how to add a suite. The matrix script shows which suites rerun for a set of representative edits. --- .claude/skills/clerk-monorepo/SKILL.md | 41 ++++++++++++++ scripts/integration-hash-matrix.mjs | 75 ++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 scripts/integration-hash-matrix.mjs diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index 05b27921ba1..c23cd90d3d7 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -138,6 +138,47 @@ If the editor or a build reports stale types from `@clerk/shared`, rebuild the f Integration-test variants (`pnpm test:integration:*`) and canary/snapshot releases are the long tail: see [`references/setup-and-footguns.md`](references/setup-and-footguns.md) and `docs/CONTRIBUTING.md`. +## Integration CI caching + +Each Playwright suite is a root turbo task, `//#test:integration:`, run by the `integration-tests` +matrix in `.github/workflows/ci.yml`. Turbo caches a suite's result, so a PR that cannot change the +outcome skips the suite entirely: no build, no local registry publish, no browser install. + +How the cache key is built: + +- `dependsOn` lists the package builds the suite's e2e apps install from the local registry: always + `@clerk/clerk-js`, `@clerk/ui` and `@clerk/testing`, plus the framework packages that suite's apps + add. Turbo folds those build hashes into the suite hash, and package builds already depend on + `^build`, so transitive packages such as `@clerk/shared` are covered without being listed. +- `inputs` covers the harness, the top-level test files, the test directories the suite's tagged tests + live in (and import from), and the templates its apps copy. Other suites' directories are not inputs. +- `scripts/integration-deps.test.mjs` derives both lists from `integration/presets` and the tagged + tests and fails when `turbo.json` drifts. Its failure message prints the block to paste. + +Rules that keep this correct: + +- The integration test step must never pass `--only`. It drops dependency hashes from the task hash, + so package changes would stop invalidating suites. +- The merge queue sets `TURBO_FORCE=true` on the test step and nightly passes `--force`, so every suite + runs for real before a commit lands on `main`. A `dependsOn` gap is a wasted-cache bug on PRs, not a + correctness hole on `main`. +- Per-job setup lives in `integration/scripts/run-integration.mjs`, which the `test:integration:base` + script wraps. When `E2E_CI_SETUP` names the suite it builds, publishes to pkglab, installs clerk-js + and ui into temp dirs, installs Chromium and, for electron, Xvfb, then runs Playwright. Locally + `E2E_CI_SETUP` is unset and it only runs Playwright. A cache hit runs none of it, including the + user cleanup step, which is gated on the `tests-started` output the script writes. + +Adding a suite: + +1. Add `test:integration:` to the root `package.json`, tagging tests with `@` and + pointing `E2E_APP_ID` at the long-running apps it needs. +2. Add `//#test:integration:` to `turbo.json`. Run `pnpm vitest run --root scripts +integration-deps` and paste the `dependsOn` and `inputs` the failure prints. +3. Add the suite to the `integration-tests` matrix in `ci.yml`. + +To see what a change invalidates, run `node scripts/integration-hash-matrix.mjs` on a clean tree. It +touches a few representative files, dry-runs turbo, and prints which suites would rerun for each. + ## The hard rules Each rule below restates `AGENTS.md`; the parenthetical is how it is enforced. diff --git a/scripts/integration-hash-matrix.mjs b/scripts/integration-hash-matrix.mjs new file mode 100644 index 00000000000..2997e1bdbdb --- /dev/null +++ b/scripts/integration-hash-matrix.mjs @@ -0,0 +1,75 @@ +import { spawnSync } from 'node:child_process'; +import { appendFileSync, existsSync, readFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); + +const scenarios = [ + ['upgrade', 'packages/upgrade/src/cli.js'], + ['shared', 'packages/shared/src/index.ts'], + ['expo', 'packages/expo/src/index.ts'], + ['mosaic', 'packages/mosaic/src/global.d.ts'], + ['test file', 'integration/tests/sign-in-flow.test.ts'], + ['vue test', 'integration/tests/vue/components.test.ts'], + ['expo-native', 'integration/expo-native/README.md'], +]; + +const turbo = JSON.parse(readFileSync(resolve(root, 'turbo.json'), 'utf8')); +const suites = Object.keys(turbo.tasks) + .filter(task => task.startsWith('//#test:integration:')) + .map(task => task.slice('//#'.length)); + +function hashes() { + const result = spawnSync('pnpm', ['turbo', 'run', ...suites, '--dry=json'], { cwd: root, encoding: 'utf8' }); + if (result.status !== 0) { + throw new Error(result.stderr); + } + const json = JSON.parse(result.stdout.slice(result.stdout.indexOf('{'))); + return new Map( + json.tasks + .filter(task => task.taskId.startsWith('//#test:integration:')) + .map(task => [ + task.taskId.slice('//#test:integration:'.length), + { hash: task.hash, inputs: Object.keys(task.inputs).length }, + ]), + ); +} + +function git(...args) { + const result = spawnSync('git', args, { cwd: root, encoding: 'utf8' }); + if (result.status !== 0) { + throw new Error(result.stderr); + } +} + +for (const [, file] of scenarios) { + if (!existsSync(resolve(root, file))) { + throw new Error(`${file} does not exist; update the scenarios list`); + } + const status = spawnSync('git', ['status', '--porcelain', '--', file], { cwd: root, encoding: 'utf8' }).stdout; + if (status.trim()) { + throw new Error(`${file} has local changes; commit or revert them before running the matrix`); + } +} + +const baseline = hashes(); +const columns = []; +for (const [label, file] of scenarios) { + appendFileSync(resolve(root, file), '\n'); + try { + columns.push([label, hashes()]); + } finally { + git('checkout', '--', file); + } +} + +const pad = (value, width) => String(value).padEnd(width); +console.log(`${pad('suite', 22)} ${pad('inputs', 6)} ${columns.map(([label]) => pad(label, 11)).join('')}`); +for (const suite of [...baseline.keys()].sort()) { + const cells = columns.map(([, result]) => + pad(result.get(suite).hash === baseline.get(suite).hash ? '.' : 'RERUN', 11), + ); + console.log(`${pad(suite, 22)} ${pad(baseline.get(suite).inputs, 6)} ${cells.join('')}`); +} +console.log('\n. = cache hash unchanged by that edit, RERUN = suite would run again'); From 67c92420d4f134f168c2e8940388283c44694468 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 19:24:39 -0700 Subject: [PATCH 07/17] ci(repo): check the integration matrix against turbo.json --- scripts/integration-deps.test.mjs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scripts/integration-deps.test.mjs b/scripts/integration-deps.test.mjs index 005446490d7..f6814f30604 100644 --- a/scripts/integration-deps.test.mjs +++ b/scripts/integration-deps.test.mjs @@ -8,11 +8,29 @@ import { deriveIntegrationTasks, integrationSuites, root, scriptFor } from './in const turbo = JSON.parse(readFileSync(join(root, 'turbo.json'), 'utf8')); const derived = await deriveIntegrationTasks(); +const ciMatrixSuites = () => { + const workflow = readFileSync(join(root, '.github/workflows/ci.yml'), 'utf8'); + const job = workflow.slice(workflow.indexOf('\n integration-tests:\n')); + const matrix = job.slice(job.indexOf('matrix:'), job.indexOf('\n steps:')); + const listed = [...matrix.matchAll(/test-name:\s*\[([^\]]*)\]/gs)].flatMap(m => + [...m[1].matchAll(/'([^']+)'/g)].map(q => q[1]), + ); + const included = [...matrix.matchAll(/- test-name: '([^']+)'/g)].map(m => m[1]); + return [...new Set([...listed, ...included])]; +}; + describe('integration suite tasks in turbo.json', () => { test.each(integrationSuites())('%s has a package.json script', suite => { expect(scriptFor(suite), `test:integration:${suite} has no matching script in package.json`).toBeDefined(); }); + test.each(ciMatrixSuites())('%s in the ci.yml matrix has a turbo task', suite => { + expect( + turbo.tasks[`//#test:integration:${suite}`], + `ci.yml runs ${suite} but turbo.json has no task for it`, + ).toBeDefined(); + }); + test.each([...derived.entries()])('%s depends on the packages its apps install', (suite, expected) => { const task = turbo.tasks[`//#test:integration:${suite}`]; const actual = { dependsOn: task.dependsOn, inputs: task.inputs }; From 625a20db8e2b813fc4fa4503655a2467044815b3 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 19:33:18 -0700 Subject: [PATCH 08/17] test(e2e): move integration tests into suite directories Each suite's tests now live under integration/tests//, so a suite's turbo inputs are its own directory rather than every top-level test file. Tests tagged for both generic and nextjs live in generic/, and the seven tests no suite tag selects live in unscheduled/. Only the import depth changed inside the moved files. --- .claude/skills/clerk-monorepo/SKILL.md | 6 ++++-- .../tests/{ => billing}/billing-hooks.test.ts | 4 ++-- .../tests/{ => billing}/per-seat-pricing.test.ts | 4 ++-- .../tests/{ => billing}/pricing-table.test.ts | 4 ++-- .../cache-components.test.ts | 6 +++--- .../tests/{ => generic}/client-trust.test.ts | 6 +++--- .../tests/{ => generic}/components.test.ts | 6 +++--- .../{ => generic}/composed-components.test.ts | 12 ++++++------ .../tests/{ => generic}/custom-pages.test.ts | 6 +++--- .../dev-browser-partitioned-cookies.test.ts | 6 +++--- .../tests/{ => generic}/email-code.test.ts | 8 ++++---- integration/tests/{ => generic}/handshake.test.ts | 8 ++++---- .../{ => generic}/impersonation-flow.test.ts | 6 +++--- .../last-authentication-strategy.test.ts | 4 ++-- .../tests/{ => generic}/legal-consent.test.ts | 4 ++-- .../tests/{ => generic}/navigation.test.ts | 4 ++-- .../{ => generic}/non-secure-context.test.ts | 6 +++--- .../offline-session-persistence.test.ts | 6 +++--- integration/tests/{ => generic}/passkeys.test.ts | 8 ++++---- .../tests/{ => generic}/protect-check.test.ts | 8 ++++---- .../tests/{ => generic}/protect-service.test.ts | 4 ++-- .../tests/{ => generic}/resiliency.test.ts | 6 +++--- .../tests/{ => generic}/safari-itp.test.ts | 6 +++--- .../tests/{ => generic}/sign-in-flow.test.ts | 6 +++--- .../tests/{ => generic}/sign-out-smoke.test.ts | 6 +++--- .../tests/{ => generic}/sign-up-flow.test.ts | 4 ++-- .../tests/{ => generic}/update-props.test.ts | 4 ++-- .../tests/{ => generic}/user-avatar.test.ts | 6 +++--- .../tests/{ => generic}/user-profile.test.ts | 10 +++++----- .../{ => generic}/whatsapp-phone-code.test.ts | 8 ++++---- .../{ => machine}/api-keys-component.test.ts | 8 ++++---- .../tests/{ => machine}/next-machine.test.ts | 12 ++++++------ .../{ => nextjs}/content-security-policy.test.ts | 2 +- .../tests/{ => nextjs}/dynamic-keys.test.ts | 6 +++--- integration/tests/{ => nextjs}/next-build.test.ts | 6 +++--- .../{ => nextjs}/next-middleware-keyless.test.ts | 4 ++-- .../tests/{ => nextjs}/oauth-flows.test.ts | 14 +++++++------- .../tests/{ => nextjs}/prefetch-ui.test.ts | 4 ++-- .../tests/{ => nextjs}/protect-jwt-v2.test.ts | 6 +++--- integration/tests/{ => nextjs}/protect.test.ts | 6 +++--- integration/tests/{ => nextjs}/redirects.test.ts | 6 +++--- .../tests/{ => nextjs}/reverification.test.ts | 10 +++++----- .../{ => nextjs}/session-tasks-eject-flow.test.ts | 8 ++++---- .../session-tasks-multi-session.test.ts | 6 +++--- .../{ => nextjs}/session-tasks-setup-mfa.test.ts | 8 ++++---- .../session-tasks-sign-in-reset-password.test.ts | 6 +++--- .../{ => nextjs}/session-tasks-sign-in.test.ts | 12 ++++++------ .../{ => nextjs}/session-tasks-sign-up.test.ts | 14 +++++++------- .../tests/{ => nextjs}/shared-ui-variant.test.ts | 4 ++-- .../{ => nextjs}/sign-in-or-up-component.test.ts | 6 +++--- .../tests/{ => nextjs}/sign-in-or-up-flow.test.ts | 4 ++-- .../sign-in-or-up-restricted-mode.test.ts | 8 ++++---- .../tests/{ => nextjs}/testing-token.test.ts | 2 +- .../tests/{ => nextjs}/transitions.test.ts | 6 +++--- .../tests/{ => nextjs}/transitive-state.test.ts | 6 +++--- .../tests/{ => nextjs}/unsafeMetadata.test.ts | 4 ++-- .../{ => quickstart}/middleware-placement.test.ts | 10 +++++----- .../next-quickstart-keyless.test.ts | 10 +++++----- .../{ => quickstart}/next-quickstart.test.ts | 6 +++--- ...op-all-clerk-themes-render-1-chrome-darwin.png | Bin .../tests/{ => unscheduled}/appearance.test.ts | 6 +++--- .../tests/{ => unscheduled}/bundled-ui.test.ts | 6 +++--- .../tests/{ => unscheduled}/db-jwt.test.ts | 8 ++++---- .../tests/{ => unscheduled}/email-link.test.ts | 6 +++--- .../{ => unscheduled}/restricted-mode.test.ts | 8 ++++---- .../sign-in-or-up-email-links-flow.test.ts | 4 ++-- .../tests/{ => unscheduled}/waitlist-mode.test.ts | 8 ++++---- scripts/integration-hash-matrix.mjs | 2 +- turbo.json | 14 ++++++++------ 69 files changed, 226 insertions(+), 222 deletions(-) rename integration/tests/{ => billing}/billing-hooks.test.ts (96%) rename integration/tests/{ => billing}/per-seat-pricing.test.ts (95%) rename integration/tests/{ => billing}/pricing-table.test.ts (99%) rename integration/tests/{ => cache-components}/cache-components.test.ts (98%) rename integration/tests/{ => generic}/client-trust.test.ts (95%) rename integration/tests/{ => generic}/components.test.ts (94%) rename integration/tests/{ => generic}/composed-components.test.ts (98%) rename integration/tests/{ => generic}/custom-pages.test.ts (99%) rename integration/tests/{ => generic}/dev-browser-partitioned-cookies.test.ts (94%) rename integration/tests/{ => generic}/email-code.test.ts (93%) rename integration/tests/{ => generic}/handshake.test.ts (99%) rename integration/tests/{ => generic}/impersonation-flow.test.ts (93%) rename integration/tests/{ => generic}/last-authentication-strategy.test.ts (98%) rename integration/tests/{ => generic}/legal-consent.test.ts (94%) rename integration/tests/{ => generic}/navigation.test.ts (94%) rename integration/tests/{ => generic}/non-secure-context.test.ts (91%) rename integration/tests/{ => generic}/offline-session-persistence.test.ts (96%) rename integration/tests/{ => generic}/passkeys.test.ts (97%) rename integration/tests/{ => generic}/protect-check.test.ts (97%) rename integration/tests/{ => generic}/protect-service.test.ts (95%) rename integration/tests/{ => generic}/resiliency.test.ts (99%) rename integration/tests/{ => generic}/safari-itp.test.ts (97%) rename integration/tests/{ => generic}/sign-in-flow.test.ts (97%) rename integration/tests/{ => generic}/sign-out-smoke.test.ts (96%) rename integration/tests/{ => generic}/sign-up-flow.test.ts (98%) rename integration/tests/{ => generic}/update-props.test.ts (92%) rename integration/tests/{ => generic}/user-avatar.test.ts (88%) rename integration/tests/{ => generic}/user-profile.test.ts (97%) rename integration/tests/{ => generic}/whatsapp-phone-code.test.ts (97%) rename integration/tests/{ => machine}/api-keys-component.test.ts (99%) rename integration/tests/{ => machine}/next-machine.test.ts (96%) rename integration/tests/{ => nextjs}/content-security-policy.test.ts (86%) rename integration/tests/{ => nextjs}/dynamic-keys.test.ts (95%) rename integration/tests/{ => nextjs}/next-build.test.ts (98%) rename integration/tests/{ => nextjs}/next-middleware-keyless.test.ts (91%) rename integration/tests/{ => nextjs}/oauth-flows.test.ts (98%) rename integration/tests/{ => nextjs}/prefetch-ui.test.ts (90%) rename integration/tests/{ => nextjs}/protect-jwt-v2.test.ts (98%) rename integration/tests/{ => nextjs}/protect.test.ts (97%) rename integration/tests/{ => nextjs}/redirects.test.ts (95%) rename integration/tests/{ => nextjs}/reverification.test.ts (97%) rename integration/tests/{ => nextjs}/session-tasks-eject-flow.test.ts (93%) rename integration/tests/{ => nextjs}/session-tasks-multi-session.test.ts (95%) rename integration/tests/{ => nextjs}/session-tasks-setup-mfa.test.ts (97%) rename integration/tests/{ => nextjs}/session-tasks-sign-in-reset-password.test.ts (96%) rename integration/tests/{ => nextjs}/session-tasks-sign-in.test.ts (91%) rename integration/tests/{ => nextjs}/session-tasks-sign-up.test.ts (90%) rename integration/tests/{ => nextjs}/shared-ui-variant.test.ts (92%) rename integration/tests/{ => nextjs}/sign-in-or-up-component.test.ts (83%) rename integration/tests/{ => nextjs}/sign-in-or-up-flow.test.ts (99%) rename integration/tests/{ => nextjs}/sign-in-or-up-restricted-mode.test.ts (85%) rename integration/tests/{ => nextjs}/testing-token.test.ts (99%) rename integration/tests/{ => nextjs}/transitions.test.ts (98%) rename integration/tests/{ => nextjs}/transitive-state.test.ts (98%) rename integration/tests/{ => nextjs}/unsafeMetadata.test.ts (98%) rename integration/tests/{ => quickstart}/middleware-placement.test.ts (96%) rename integration/tests/{ => quickstart}/next-quickstart-keyless.test.ts (89%) rename integration/tests/{ => quickstart}/next-quickstart.test.ts (95%) rename integration/tests/snapshots/{ => unscheduled}/appearance.test.ts-snapshots/appearance-prop-all-clerk-themes-render-1-chrome-darwin.png (100%) rename integration/tests/{ => unscheduled}/appearance.test.ts (91%) rename integration/tests/{ => unscheduled}/bundled-ui.test.ts (96%) rename integration/tests/{ => unscheduled}/db-jwt.test.ts (94%) rename integration/tests/{ => unscheduled}/email-link.test.ts (96%) rename integration/tests/{ => unscheduled}/restricted-mode.test.ts (94%) rename integration/tests/{ => unscheduled}/sign-in-or-up-email-links-flow.test.ts (94%) rename integration/tests/{ => unscheduled}/waitlist-mode.test.ts (96%) diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index c23cd90d3d7..b4aee8b67dd 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -150,8 +150,10 @@ How the cache key is built: `@clerk/clerk-js`, `@clerk/ui` and `@clerk/testing`, plus the framework packages that suite's apps add. Turbo folds those build hashes into the suite hash, and package builds already depend on `^build`, so transitive packages such as `@clerk/shared` are covered without being listed. -- `inputs` covers the harness, the top-level test files, the test directories the suite's tagged tests - live in (and import from), and the templates its apps copy. Other suites' directories are not inputs. +- `inputs` covers the harness, the test directories the suite's tagged tests live in (and import + from), and the templates its apps copy. Other suites' directories are not inputs. Tests live under + `integration/tests//`; a test tagged for several suites lives in `generic/`, and tests no + suite runs live in `unscheduled/`. - `scripts/integration-deps.test.mjs` derives both lists from `integration/presets` and the tagged tests and fails when `turbo.json` drifts. Its failure message prints the block to paste. diff --git a/integration/tests/billing-hooks.test.ts b/integration/tests/billing/billing-hooks.test.ts similarity index 96% rename from integration/tests/billing-hooks.test.ts rename to integration/tests/billing/billing-hooks.test.ts index f99a40a2f48..c0d6935c3cb 100644 --- a/integration/tests/billing-hooks.test.ts +++ b/integration/tests/billing/billing-hooks.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({})('billing hooks @billing', ({ app }) => { test.describe.configure({ mode: 'parallel' }); diff --git a/integration/tests/per-seat-pricing.test.ts b/integration/tests/billing/per-seat-pricing.test.ts similarity index 95% rename from integration/tests/per-seat-pricing.test.ts rename to integration/tests/billing/per-seat-pricing.test.ts index e8177c6b8af..3970e2321f9 100644 --- a/integration/tests/per-seat-pricing.test.ts +++ b/integration/tests/billing/per-seat-pricing.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import type { FakeOrganization, FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; const INVITEE_EMAIL = 'one+clerk_test@clerk.dev'; diff --git a/integration/tests/pricing-table.test.ts b/integration/tests/billing/pricing-table.test.ts similarity index 99% rename from integration/tests/pricing-table.test.ts rename to integration/tests/billing/pricing-table.test.ts index 4983f3d14c8..9a6665669e3 100644 --- a/integration/tests/pricing-table.test.ts +++ b/integration/tests/billing/pricing-table.test.ts @@ -1,8 +1,8 @@ import type { Locator } from '@playwright/test'; import { expect, test } from '@playwright/test'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({})('pricing table @billing', ({ app }) => { test.describe.configure({ mode: 'parallel' }); diff --git a/integration/tests/cache-components.test.ts b/integration/tests/cache-components/cache-components.test.ts similarity index 98% rename from integration/tests/cache-components.test.ts rename to integration/tests/cache-components/cache-components.test.ts index 6915724b22c..275d0c088f8 100644 --- a/integration/tests/cache-components.test.ts +++ b/integration/tests/cache-components/cache-components.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes], withPattern: ['next.cacheComponents'] })( 'Next.js Cache Components @cache-components', diff --git a/integration/tests/client-trust.test.ts b/integration/tests/generic/client-trust.test.ts similarity index 95% rename from integration/tests/client-trust.test.ts rename to integration/tests/generic/client-trust.test.ts index 0cb11bcf1c5..42e0d537afe 100644 --- a/integration/tests/client-trust.test.ts +++ b/integration/tests/generic/client-trust.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withNeedsClientTrust] })( 'client trust flow @generic @nextjs', diff --git a/integration/tests/components.test.ts b/integration/tests/generic/components.test.ts similarity index 94% rename from integration/tests/components.test.ts rename to integration/tests/generic/components.test.ts index c8b0c71bf50..35e1b9bdc3b 100644 --- a/integration/tests/components.test.ts +++ b/integration/tests/generic/components.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeOrganization, FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('component smoke tests @generic', ({ app }) => { let fakeUser: FakeUser; diff --git a/integration/tests/composed-components.test.ts b/integration/tests/generic/composed-components.test.ts similarity index 98% rename from integration/tests/composed-components.test.ts rename to integration/tests/generic/composed-components.test.ts index 43a5139b4b5..dd6a29206d2 100644 --- a/integration/tests/composed-components.test.ts +++ b/integration/tests/generic/composed-components.test.ts @@ -1,11 +1,11 @@ import { type BrowserContext, expect, type Page, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { PKGLAB } from '../presets/utils'; -import type { FakeOrganization, FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; -import { stringPhoneNumber } from '../testUtils/phoneUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { PKGLAB } from '../../presets/utils'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; +import { stringPhoneNumber } from '../../testUtils/phoneUtils'; /** * E2E parity coverage for the experimental composed exports from `@clerk/ui/experimental`. diff --git a/integration/tests/custom-pages.test.ts b/integration/tests/generic/custom-pages.test.ts similarity index 99% rename from integration/tests/custom-pages.test.ts rename to integration/tests/generic/custom-pages.test.ts index 8dc4e84cdbd..e7aad5fdfe8 100644 --- a/integration/tests/custom-pages.test.ts +++ b/integration/tests/generic/custom-pages.test.ts @@ -1,9 +1,9 @@ /* eslint-disable turbo/no-undeclared-env-vars */ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import type { CreateAppPageObjectArgs, FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import type { Application } from '../../models/application'; +import type { CreateAppPageObjectArgs, FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; const CUSTOM_PROFILE_PAGE = '/custom-user-profile'; const CUSTOM_BUTTON_PAGE = '/custom-user-button'; diff --git a/integration/tests/dev-browser-partitioned-cookies.test.ts b/integration/tests/generic/dev-browser-partitioned-cookies.test.ts similarity index 94% rename from integration/tests/dev-browser-partitioned-cookies.test.ts rename to integration/tests/generic/dev-browser-partitioned-cookies.test.ts index 4525b382cda..0f53af8e700 100644 --- a/integration/tests/dev-browser-partitioned-cookies.test.ts +++ b/integration/tests/generic/dev-browser-partitioned-cookies.test.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; import { parsePublishableKey } from '@clerk/shared/keys'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })( 'dev browser partitioned cookies @generic', diff --git a/integration/tests/email-code.test.ts b/integration/tests/generic/email-code.test.ts similarity index 93% rename from integration/tests/email-code.test.ts rename to integration/tests/generic/email-code.test.ts index 980894d6487..9c757df531c 100644 --- a/integration/tests/email-code.test.ts +++ b/integration/tests/generic/email-code.test.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; test.describe('sign up and sign in with email code @generic', () => { const configs = [appConfigs.next.appRouter, appConfigs.react.vite]; diff --git a/integration/tests/handshake.test.ts b/integration/tests/generic/handshake.test.ts similarity index 99% rename from integration/tests/handshake.test.ts rename to integration/tests/generic/handshake.test.ts index 7c29dee8c56..d3af2c82ddc 100644 --- a/integration/tests/handshake.test.ts +++ b/integration/tests/generic/handshake.test.ts @@ -2,10 +2,10 @@ import * as http from 'node:http'; import { expect, test } from '@playwright/test'; -import type { OrganizationSyncOptions } from '../../packages/backend/src/tokens/types'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { generateConfig, getJwksFromSecretKey } from '../testUtils/handshake'; +import type { OrganizationSyncOptions } from '../../../packages/backend/src/tokens/types'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { generateConfig, getJwksFromSecretKey } from '../../testUtils/handshake'; const PORT = 4199; diff --git a/integration/tests/impersonation-flow.test.ts b/integration/tests/generic/impersonation-flow.test.ts similarity index 93% rename from integration/tests/impersonation-flow.test.ts rename to integration/tests/generic/impersonation-flow.test.ts index aed9e581693..59b36977e82 100644 --- a/integration/tests/impersonation-flow.test.ts +++ b/integration/tests/generic/impersonation-flow.test.ts @@ -1,9 +1,9 @@ import type { User } from '@clerk/backend'; import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('Impersonation Flow @generic', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/last-authentication-strategy.test.ts b/integration/tests/generic/last-authentication-strategy.test.ts similarity index 98% rename from integration/tests/last-authentication-strategy.test.ts rename to integration/tests/generic/last-authentication-strategy.test.ts index ed81e9c1e7a..6464d2c0fbd 100644 --- a/integration/tests/last-authentication-strategy.test.ts +++ b/integration/tests/generic/last-authentication-strategy.test.ts @@ -2,8 +2,8 @@ import type { LastAuthenticationStrategy } from '@clerk/shared/types'; import type { Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; const mockLastAuthenticationStrategyResponse = async ( page: Page, diff --git a/integration/tests/legal-consent.test.ts b/integration/tests/generic/legal-consent.test.ts similarity index 94% rename from integration/tests/legal-consent.test.ts rename to integration/tests/generic/legal-consent.test.ts index 6f232376b0f..1eefda6c082 100644 --- a/integration/tests/legal-consent.test.ts +++ b/integration/tests/generic/legal-consent.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withLegalConsent] })( 'sign up flow with legal consent @generic @nextjs', diff --git a/integration/tests/navigation.test.ts b/integration/tests/generic/navigation.test.ts similarity index 94% rename from integration/tests/navigation.test.ts rename to integration/tests/generic/navigation.test.ts index d065af9bc58..0ec0a098784 100644 --- a/integration/tests/navigation.test.ts +++ b/integration/tests/generic/navigation.test.ts @@ -1,7 +1,7 @@ import { test } from '@playwright/test'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withPattern: ['next.appRouter.withEmailCodes'] })('navigation modes @generic', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/non-secure-context.test.ts b/integration/tests/generic/non-secure-context.test.ts similarity index 91% rename from integration/tests/non-secure-context.test.ts rename to integration/tests/generic/non-secure-context.test.ts index 268868ee868..49a9f5d908b 100644 --- a/integration/tests/non-secure-context.test.ts +++ b/integration/tests/generic/non-secure-context.test.ts @@ -16,9 +16,9 @@ import type { Server } from 'node:http'; import { test } from '@playwright/test'; -import { createProxyServer } from '../scripts/proxyServer'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { createProxyServer } from '../../scripts/proxyServer'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; // This NEEDS to be a domain that points to localhost // and is not listed in the HSTS preload list diff --git a/integration/tests/offline-session-persistence.test.ts b/integration/tests/generic/offline-session-persistence.test.ts similarity index 96% rename from integration/tests/offline-session-persistence.test.ts rename to integration/tests/generic/offline-session-persistence.test.ts index 1f49d8e227f..34058fa8080 100644 --- a/integration/tests/offline-session-persistence.test.ts +++ b/integration/tests/generic/offline-session-persistence.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })( 'offline session persistence @generic', diff --git a/integration/tests/passkeys.test.ts b/integration/tests/generic/passkeys.test.ts similarity index 97% rename from integration/tests/passkeys.test.ts rename to integration/tests/generic/passkeys.test.ts index 77968ed22a5..c568b13e6b5 100644 --- a/integration/tests/passkeys.test.ts +++ b/integration/tests/generic/passkeys.test.ts @@ -1,10 +1,10 @@ import type { BrowserContext, Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; type SavedCredential = { id: string; diff --git a/integration/tests/protect-check.test.ts b/integration/tests/generic/protect-check.test.ts similarity index 97% rename from integration/tests/protect-check.test.ts rename to integration/tests/generic/protect-check.test.ts index 75f74cf6828..0a155f8c7ac 100644 --- a/integration/tests/protect-check.test.ts +++ b/integration/tests/generic/protect-check.test.ts @@ -1,10 +1,10 @@ import type { Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; const waitForProtectCheckSubmit = (page: Page) => page.waitForResponse( diff --git a/integration/tests/protect-service.test.ts b/integration/tests/generic/protect-service.test.ts similarity index 95% rename from integration/tests/protect-service.test.ts rename to integration/tests/generic/protect-service.test.ts index dc1f82ef304..b9c58bc9cd6 100644 --- a/integration/tests/protect-service.test.ts +++ b/integration/tests/generic/protect-service.test.ts @@ -2,8 +2,8 @@ import type { ProtectConfigJSON } from '@clerk/shared/types'; import type { Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; const mockProtectSettings = async (page: Page, config?: ProtectConfigJSON) => { await page.route('*/**/v1/environment*', async route => { diff --git a/integration/tests/resiliency.test.ts b/integration/tests/generic/resiliency.test.ts similarity index 99% rename from integration/tests/resiliency.test.ts rename to integration/tests/generic/resiliency.test.ts index 695e61ad53c..eef56098c7f 100644 --- a/integration/tests/resiliency.test.ts +++ b/integration/tests/generic/resiliency.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; const make429ClerkResponse = () => ({ status: 429, diff --git a/integration/tests/safari-itp.test.ts b/integration/tests/generic/safari-itp.test.ts similarity index 97% rename from integration/tests/safari-itp.test.ts rename to integration/tests/generic/safari-itp.test.ts index d764437a4d3..16b3109630f 100644 --- a/integration/tests/safari-itp.test.ts +++ b/integration/tests/generic/safari-itp.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; /** * Tests Safari ITP (Intelligent Tracking Prevention) workaround diff --git a/integration/tests/sign-in-flow.test.ts b/integration/tests/generic/sign-in-flow.test.ts similarity index 97% rename from integration/tests/sign-in-flow.test.ts rename to integration/tests/generic/sign-in-flow.test.ts index 4aa124b9176..bfafb1ad3db 100644 --- a/integration/tests/sign-in-flow.test.ts +++ b/integration/tests/generic/sign-in-flow.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign in flow @generic @nextjs', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/sign-out-smoke.test.ts b/integration/tests/generic/sign-out-smoke.test.ts similarity index 96% rename from integration/tests/sign-out-smoke.test.ts rename to integration/tests/generic/sign-out-smoke.test.ts index 3aa855d394b..b668ee901b9 100644 --- a/integration/tests/sign-out-smoke.test.ts +++ b/integration/tests/generic/sign-out-smoke.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign out smoke test @generic', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/sign-up-flow.test.ts b/integration/tests/generic/sign-up-flow.test.ts similarity index 98% rename from integration/tests/sign-up-flow.test.ts rename to integration/tests/generic/sign-up-flow.test.ts index 58ef0d393e0..fa4740340d6 100644 --- a/integration/tests/sign-up-flow.test.ts +++ b/integration/tests/generic/sign-up-flow.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign up flow @generic @nextjs', ({ app }) => { test.describe.configure({ mode: 'parallel' }); diff --git a/integration/tests/update-props.test.ts b/integration/tests/generic/update-props.test.ts similarity index 92% rename from integration/tests/update-props.test.ts rename to integration/tests/generic/update-props.test.ts index 7084bb7edf3..b7a7965bf1b 100644 --- a/integration/tests/update-props.test.ts +++ b/integration/tests/generic/update-props.test.ts @@ -1,7 +1,7 @@ import { test } from '@playwright/test'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })('sign in flow @generic', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/user-avatar.test.ts b/integration/tests/generic/user-avatar.test.ts similarity index 88% rename from integration/tests/user-avatar.test.ts rename to integration/tests/generic/user-avatar.test.ts index 66e7684fda9..99b048c7291 100644 --- a/integration/tests/user-avatar.test.ts +++ b/integration/tests/generic/user-avatar.test.ts @@ -1,8 +1,8 @@ import { test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes], diff --git a/integration/tests/user-profile.test.ts b/integration/tests/generic/user-profile.test.ts similarity index 97% rename from integration/tests/user-profile.test.ts rename to integration/tests/generic/user-profile.test.ts index 540e6e095c1..b916d1fb762 100644 --- a/integration/tests/user-profile.test.ts +++ b/integration/tests/generic/user-profile.test.ts @@ -1,10 +1,10 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; -import { stringPhoneNumber } from '../testUtils/phoneUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; +import { stringPhoneNumber } from '../../testUtils/phoneUtils'; test.describe('user profile @generic', () => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/whatsapp-phone-code.test.ts b/integration/tests/generic/whatsapp-phone-code.test.ts similarity index 97% rename from integration/tests/whatsapp-phone-code.test.ts rename to integration/tests/generic/whatsapp-phone-code.test.ts index 7716eab4511..5b3c20f4bdd 100644 --- a/integration/tests/whatsapp-phone-code.test.ts +++ b/integration/tests/generic/whatsapp-phone-code.test.ts @@ -2,10 +2,10 @@ import type { Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; const clickSendCodeViaSmsInstead = async (page: Page, verificationHeading: RegExp) => { await page.getByRole('heading', { name: verificationHeading }).waitFor(); diff --git a/integration/tests/api-keys-component.test.ts b/integration/tests/machine/api-keys-component.test.ts similarity index 99% rename from integration/tests/api-keys-component.test.ts rename to integration/tests/machine/api-keys-component.test.ts index 4dda3e8b234..75ac837377f 100644 --- a/integration/tests/api-keys-component.test.ts +++ b/integration/tests/machine/api-keys-component.test.ts @@ -1,10 +1,10 @@ import type { Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeOrganization, FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; const mockAPIKeysEnvironmentSettings = async ( page: Page, diff --git a/integration/tests/next-machine.test.ts b/integration/tests/machine/next-machine.test.ts similarity index 96% rename from integration/tests/next-machine.test.ts rename to integration/tests/machine/next-machine.test.ts index faaffe5944c..472464499ee 100644 --- a/integration/tests/next-machine.test.ts +++ b/integration/tests/machine/next-machine.test.ts @@ -2,10 +2,10 @@ import type { User } from '@clerk/backend'; import { TokenType } from '@clerk/backend/internal'; import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { createTestUtils } from '../testUtils'; -import type { FakeMachineNetwork, MachineAuthTestAdapter } from '../testUtils/machineAuthHelpers'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { createTestUtils } from '../../testUtils'; +import type { FakeMachineNetwork, MachineAuthTestAdapter } from '../../testUtils/machineAuthHelpers'; import { createFakeMachineNetwork, createJwtM2MToken, @@ -13,8 +13,8 @@ import { registerApiKeyAuthTests, registerM2MAuthTests, registerOAuthAuthTests, -} from '../testUtils/machineAuthHelpers'; -import type { FakeAPIKey, FakeUser } from '../testUtils/usersService'; +} from '../../testUtils/machineAuthHelpers'; +import type { FakeAPIKey, FakeUser } from '../../testUtils/usersService'; const adapter: MachineAuthTestAdapter = { baseConfig: appConfigs.next.appRouter, diff --git a/integration/tests/content-security-policy.test.ts b/integration/tests/nextjs/content-security-policy.test.ts similarity index 86% rename from integration/tests/content-security-policy.test.ts rename to integration/tests/nextjs/content-security-policy.test.ts index 638d260ee77..c589eb1caf6 100644 --- a/integration/tests/content-security-policy.test.ts +++ b/integration/tests/nextjs/content-security-policy.test.ts @@ -1,6 +1,6 @@ import { test } from '@playwright/test'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withPattern: ['next.appRouter.withEmailCodes'] })( 'Content Security Policy @nextjs', diff --git a/integration/tests/dynamic-keys.test.ts b/integration/tests/nextjs/dynamic-keys.test.ts similarity index 95% rename from integration/tests/dynamic-keys.test.ts rename to integration/tests/nextjs/dynamic-keys.test.ts index 21b27dcc50c..236a6496a71 100644 --- a/integration/tests/dynamic-keys.test.ts +++ b/integration/tests/nextjs/dynamic-keys.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { createTestUtils } from '../../testUtils'; test.describe('dynamic keys @nextjs', () => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/next-build.test.ts b/integration/tests/nextjs/next-build.test.ts similarity index 98% rename from integration/tests/next-build.test.ts rename to integration/tests/nextjs/next-build.test.ts index 697384b9922..f9dea3c670e 100644 --- a/integration/tests/next-build.test.ts +++ b/integration/tests/nextjs/next-build.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { PKGLAB } from '../presets/utils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { PKGLAB } from '../../presets/utils'; type RenderingModeTestCase = { name: string; diff --git a/integration/tests/next-middleware-keyless.test.ts b/integration/tests/nextjs/next-middleware-keyless.test.ts similarity index 91% rename from integration/tests/next-middleware-keyless.test.ts rename to integration/tests/nextjs/next-middleware-keyless.test.ts index d699b7790f8..36a2111008a 100644 --- a/integration/tests/next-middleware-keyless.test.ts +++ b/integration/tests/nextjs/next-middleware-keyless.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; const commonSetup = appConfigs.next.appRouter.clone(); diff --git a/integration/tests/oauth-flows.test.ts b/integration/tests/nextjs/oauth-flows.test.ts similarity index 98% rename from integration/tests/oauth-flows.test.ts rename to integration/tests/nextjs/oauth-flows.test.ts index 51024988e17..ffafa3d512b 100644 --- a/integration/tests/oauth-flows.test.ts +++ b/integration/tests/nextjs/oauth-flows.test.ts @@ -2,13 +2,13 @@ import { createClerkClient } from '@clerk/backend'; import type { Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; -import { environmentConfig } from '../models/environment'; -import { appConfigs } from '../presets'; -import { resolveInstanceKeys } from '../presets/envs'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; -import { withRetry } from '../testUtils/retryableClerkClient'; -import { createUserService } from '../testUtils/usersService'; +import { environmentConfig } from '../../models/environment'; +import { appConfigs } from '../../presets'; +import { resolveInstanceKeys } from '../../presets/envs'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; +import { withRetry } from '../../testUtils/retryableClerkClient'; +import { createUserService } from '../../testUtils/usersService'; const grantOAuthConsent = async (page: Page) => { const allowAccessButton = page.getByRole('button', { name: 'Allow' }); diff --git a/integration/tests/prefetch-ui.test.ts b/integration/tests/nextjs/prefetch-ui.test.ts similarity index 90% rename from integration/tests/prefetch-ui.test.ts rename to integration/tests/nextjs/prefetch-ui.test.ts index 33b0b6a6991..6973752d1f5 100644 --- a/integration/tests/prefetch-ui.test.ts +++ b/integration/tests/nextjs/prefetch-ui.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; test.describe('prefetchUI disabled @nextjs', () => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/protect-jwt-v2.test.ts b/integration/tests/nextjs/protect-jwt-v2.test.ts similarity index 98% rename from integration/tests/protect-jwt-v2.test.ts rename to integration/tests/nextjs/protect-jwt-v2.test.ts index 3d2f1d36bae..1a9d4034c1a 100644 --- a/integration/tests/protect-jwt-v2.test.ts +++ b/integration/tests/nextjs/protect-jwt-v2.test.ts @@ -1,9 +1,9 @@ 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'; +import { appConfigs } from '../../presets'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withBillingJwtV2], diff --git a/integration/tests/protect.test.ts b/integration/tests/nextjs/protect.test.ts similarity index 97% rename from integration/tests/protect.test.ts rename to integration/tests/nextjs/protect.test.ts index a103546beb7..afa4718fb62 100644 --- a/integration/tests/protect.test.ts +++ b/integration/tests/nextjs/protect.test.ts @@ -1,9 +1,9 @@ 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'; +import { appConfigs } from '../../presets'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles, appConfigs.envs.withBillingJwtV2], diff --git a/integration/tests/redirects.test.ts b/integration/tests/nextjs/redirects.test.ts similarity index 95% rename from integration/tests/redirects.test.ts rename to integration/tests/nextjs/redirects.test.ts index 90cd9ada1e2..8304340eec4 100644 --- a/integration/tests/redirects.test.ts +++ b/integration/tests/nextjs/redirects.test.ts @@ -1,8 +1,8 @@ import { test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('redirect props @nextjs', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/reverification.test.ts b/integration/tests/nextjs/reverification.test.ts similarity index 97% rename from integration/tests/reverification.test.ts rename to integration/tests/nextjs/reverification.test.ts index e84631bcc11..10a642f982e 100644 --- a/integration/tests/reverification.test.ts +++ b/integration/tests/nextjs/reverification.test.ts @@ -1,11 +1,11 @@ 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'; -import { stringPhoneNumber } from '../testUtils/phoneUtils'; -import { fakerPhoneNumber } from '../testUtils/usersService'; +import { appConfigs } from '../../presets'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; +import { stringPhoneNumber } from '../../testUtils/phoneUtils'; +import { fakerPhoneNumber } from '../../testUtils/usersService'; const utils = [ 'action', diff --git a/integration/tests/session-tasks-eject-flow.test.ts b/integration/tests/nextjs/session-tasks-eject-flow.test.ts similarity index 93% rename from integration/tests/session-tasks-eject-flow.test.ts rename to integration/tests/nextjs/session-tasks-eject-flow.test.ts index bc7ef2baa1c..631a1d9677b 100644 --- a/integration/tests/session-tasks-eject-flow.test.ts +++ b/integration/tests/nextjs/session-tasks-eject-flow.test.ts @@ -1,9 +1,9 @@ import { test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; test.describe('session tasks eject flow @nextjs', () => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/session-tasks-multi-session.test.ts b/integration/tests/nextjs/session-tasks-multi-session.test.ts similarity index 95% rename from integration/tests/session-tasks-multi-session.test.ts rename to integration/tests/nextjs/session-tasks-multi-session.test.ts index 6a77bb5d99c..ad419f33d04 100644 --- a/integration/tests/session-tasks-multi-session.test.ts +++ b/integration/tests/nextjs/session-tasks-multi-session.test.ts @@ -1,8 +1,8 @@ import { test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasks] })( 'session tasks multi-session flow @nextjs', diff --git a/integration/tests/session-tasks-setup-mfa.test.ts b/integration/tests/nextjs/session-tasks-setup-mfa.test.ts similarity index 97% rename from integration/tests/session-tasks-setup-mfa.test.ts rename to integration/tests/nextjs/session-tasks-setup-mfa.test.ts index af35b70d6de..96bfabe1bd1 100644 --- a/integration/tests/session-tasks-setup-mfa.test.ts +++ b/integration/tests/nextjs/session-tasks-setup-mfa.test.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; -import { stringPhoneNumber } from '../testUtils/phoneUtils'; -import { fakerPhoneNumber } from '../testUtils/usersService'; +import { appConfigs } from '../../presets'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; +import { stringPhoneNumber } from '../../testUtils/phoneUtils'; +import { fakerPhoneNumber } from '../../testUtils/usersService'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasksSetupMfa] })( 'session tasks setup-mfa flow @nextjs', diff --git a/integration/tests/session-tasks-sign-in-reset-password.test.ts b/integration/tests/nextjs/session-tasks-sign-in-reset-password.test.ts similarity index 96% rename from integration/tests/session-tasks-sign-in-reset-password.test.ts rename to integration/tests/nextjs/session-tasks-sign-in-reset-password.test.ts index 4322773bc43..72de5af5e26 100644 --- a/integration/tests/session-tasks-sign-in-reset-password.test.ts +++ b/integration/tests/nextjs/session-tasks-sign-in-reset-password.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { hash } from '../models/helpers'; -import { appConfigs } from '../presets'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { hash } from '../../models/helpers'; +import { appConfigs } from '../../presets'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasksResetPassword] })( 'session tasks after sign-in reset password flow @nextjs', diff --git a/integration/tests/session-tasks-sign-in.test.ts b/integration/tests/nextjs/session-tasks-sign-in.test.ts similarity index 91% rename from integration/tests/session-tasks-sign-in.test.ts rename to integration/tests/nextjs/session-tasks-sign-in.test.ts index abb7d623b54..80065de42f8 100644 --- a/integration/tests/session-tasks-sign-in.test.ts +++ b/integration/tests/nextjs/session-tasks-sign-in.test.ts @@ -1,12 +1,12 @@ import { createClerkClient } from '@clerk/backend'; import { test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { resolveInstanceKeys } from '../presets/envs'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; -import { withRetry } from '../testUtils/retryableClerkClient'; -import { createUserService } from '../testUtils/usersService'; +import { appConfigs } from '../../presets'; +import { resolveInstanceKeys } from '../../presets/envs'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; +import { withRetry } from '../../testUtils/retryableClerkClient'; +import { createUserService } from '../../testUtils/usersService'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasks] })( 'session tasks after sign-in flow @nextjs', diff --git a/integration/tests/session-tasks-sign-up.test.ts b/integration/tests/nextjs/session-tasks-sign-up.test.ts similarity index 90% rename from integration/tests/session-tasks-sign-up.test.ts rename to integration/tests/nextjs/session-tasks-sign-up.test.ts index 94146b53f5c..726acf88b22 100644 --- a/integration/tests/session-tasks-sign-up.test.ts +++ b/integration/tests/nextjs/session-tasks-sign-up.test.ts @@ -1,13 +1,13 @@ import { createClerkClient } from '@clerk/backend'; import { expect, test } from '@playwright/test'; -import { environmentConfig } from '../models/environment'; -import { appConfigs } from '../presets'; -import { resolveInstanceKeys } from '../presets/envs'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; -import { withRetry } from '../testUtils/retryableClerkClient'; -import { createUserService } from '../testUtils/usersService'; +import { environmentConfig } from '../../models/environment'; +import { appConfigs } from '../../presets'; +import { resolveInstanceKeys } from '../../presets/envs'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; +import { withRetry } from '../../testUtils/retryableClerkClient'; +import { createUserService } from '../../testUtils/usersService'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasks] })( 'session tasks after sign-up flow @nextjs', diff --git a/integration/tests/shared-ui-variant.test.ts b/integration/tests/nextjs/shared-ui-variant.test.ts similarity index 92% rename from integration/tests/shared-ui-variant.test.ts rename to integration/tests/nextjs/shared-ui-variant.test.ts index 3ca6641f855..c1c01a4170a 100644 --- a/integration/tests/shared-ui-variant.test.ts +++ b/integration/tests/nextjs/shared-ui-variant.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withSharedUIVariant] })( 'shared React variant @nextjs', diff --git a/integration/tests/sign-in-or-up-component.test.ts b/integration/tests/nextjs/sign-in-or-up-component.test.ts similarity index 83% rename from integration/tests/sign-in-or-up-component.test.ts rename to integration/tests/nextjs/sign-in-or-up-component.test.ts index aaeb4a7a01d..889c6df81e5 100644 --- a/integration/tests/sign-in-or-up-component.test.ts +++ b/integration/tests/nextjs/sign-in-or-up-component.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { createTestUtils } from '../../testUtils'; test.describe('sign-in-or-up component initialization flow @nextjs', () => { test.describe.configure({ mode: 'parallel' }); diff --git a/integration/tests/sign-in-or-up-flow.test.ts b/integration/tests/nextjs/sign-in-or-up-flow.test.ts similarity index 99% rename from integration/tests/sign-in-or-up-flow.test.ts rename to integration/tests/nextjs/sign-in-or-up-flow.test.ts index 5d1e7dde6b0..31c6f7b1e76 100644 --- a/integration/tests/sign-in-or-up-flow.test.ts +++ b/integration/tests/nextjs/sign-in-or-up-flow.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { createTestUtils, type FakeUser, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import { createTestUtils, type FakeUser, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withSignInOrUpFlow] })('sign-in-or-up flow @nextjs', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/sign-in-or-up-restricted-mode.test.ts b/integration/tests/nextjs/sign-in-or-up-restricted-mode.test.ts similarity index 85% rename from integration/tests/sign-in-or-up-restricted-mode.test.ts rename to integration/tests/nextjs/sign-in-or-up-restricted-mode.test.ts index 42fefc92f31..641d924cf7a 100644 --- a/integration/tests/sign-in-or-up-restricted-mode.test.ts +++ b/integration/tests/nextjs/sign-in-or-up-restricted-mode.test.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; test.describe('sign-in-or-up restricted mode @nextjs', () => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/testing-token.test.ts b/integration/tests/nextjs/testing-token.test.ts similarity index 99% rename from integration/tests/testing-token.test.ts rename to integration/tests/nextjs/testing-token.test.ts index ca18fe85a82..be92ff33446 100644 --- a/integration/tests/testing-token.test.ts +++ b/integration/tests/nextjs/testing-token.test.ts @@ -4,7 +4,7 @@ import type { AddressInfo } from 'node:net'; import { expect, type Page, test } from '@playwright/test'; -import { setupTestingTokens } from '../testUtils/setupTestingTokens'; +import { setupTestingTokens } from '../../testUtils/setupTestingTokens'; test.describe('OAuth testing tokens @nextjs', () => { let provider: Server; diff --git a/integration/tests/transitions.test.ts b/integration/tests/nextjs/transitions.test.ts similarity index 98% rename from integration/tests/transitions.test.ts rename to integration/tests/nextjs/transitions.test.ts index a80b3d8706c..27b0856b2bd 100644 --- a/integration/tests/transitions.test.ts +++ b/integration/tests/nextjs/transitions.test.ts @@ -2,9 +2,9 @@ import { parsePublishableKey } from '@clerk/shared/keys'; import { clerkSetup } from '@clerk/testing/playwright'; import { test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeOrganization, FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; /* These tests try to verify some existing transition behaviors. They are not comprehensive, and do not necessarily diff --git a/integration/tests/transitive-state.test.ts b/integration/tests/nextjs/transitive-state.test.ts similarity index 98% rename from integration/tests/transitive-state.test.ts rename to integration/tests/nextjs/transitive-state.test.ts index 1026d593476..5f4e0d92fb5 100644 --- a/integration/tests/transitive-state.test.ts +++ b/integration/tests/nextjs/transitive-state.test.ts @@ -2,9 +2,9 @@ import { parsePublishableKey } from '@clerk/shared/keys'; import { clerkSetup } from '@clerk/testing/playwright'; import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeOrganization, FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeOrganization, FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; /* These tests verify that useAuth emits the correct transitive state sequence when switching diff --git a/integration/tests/unsafeMetadata.test.ts b/integration/tests/nextjs/unsafeMetadata.test.ts similarity index 98% rename from integration/tests/unsafeMetadata.test.ts rename to integration/tests/nextjs/unsafeMetadata.test.ts index 72e2770b5ab..6c573842f8d 100644 --- a/integration/tests/unsafeMetadata.test.ts +++ b/integration/tests/nextjs/unsafeMetadata.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('unsafeMetadata @nextjs', ({ app }) => { test.describe.configure({ mode: 'parallel' }); diff --git a/integration/tests/middleware-placement.test.ts b/integration/tests/quickstart/middleware-placement.test.ts similarity index 96% rename from integration/tests/middleware-placement.test.ts rename to integration/tests/quickstart/middleware-placement.test.ts index 31db473590b..f4357e54309 100644 --- a/integration/tests/middleware-placement.test.ts +++ b/integration/tests/quickstart/middleware-placement.test.ts @@ -1,11 +1,11 @@ import { expect, test } from '@playwright/test'; import path from 'path'; -import type { Application } from '../models/application'; -import { stateFile } from '../models/stateFile'; -import { appConfigs } from '../presets'; -import { fs } from '../scripts'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { stateFile } from '../../models/stateFile'; +import { appConfigs } from '../../presets'; +import { fs } from '../../scripts'; +import { createTestUtils } from '../../testUtils'; function parseSemverMajor(range?: string): number | undefined { if (!range) { diff --git a/integration/tests/next-quickstart-keyless.test.ts b/integration/tests/quickstart/next-quickstart-keyless.test.ts similarity index 89% rename from integration/tests/next-quickstart-keyless.test.ts rename to integration/tests/quickstart/next-quickstart-keyless.test.ts index ef9df6cf772..7dbd994c73c 100644 --- a/integration/tests/next-quickstart-keyless.test.ts +++ b/integration/tests/quickstart/next-quickstart-keyless.test.ts @@ -2,11 +2,11 @@ import * as path from 'node:path'; import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { fs } from '../scripts'; -import { createTestUtils } from '../testUtils'; -import { mockClaimedInstanceEnvironmentCall } from '../testUtils/keylessHelpers'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { fs } from '../../scripts'; +import { createTestUtils } from '../../testUtils'; +import { mockClaimedInstanceEnvironmentCall } from '../../testUtils/keylessHelpers'; const commonSetup = appConfigs.next.appRouterQuickstart.clone(); diff --git a/integration/tests/next-quickstart.test.ts b/integration/tests/quickstart/next-quickstart.test.ts similarity index 95% rename from integration/tests/next-quickstart.test.ts rename to integration/tests/quickstart/next-quickstart.test.ts index 4101f47cfff..3173343f571 100644 --- a/integration/tests/next-quickstart.test.ts +++ b/integration/tests/quickstart/next-quickstart.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodesQuickstart] })('nextjs @quickstart', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/snapshots/appearance.test.ts-snapshots/appearance-prop-all-clerk-themes-render-1-chrome-darwin.png b/integration/tests/snapshots/unscheduled/appearance.test.ts-snapshots/appearance-prop-all-clerk-themes-render-1-chrome-darwin.png similarity index 100% rename from integration/tests/snapshots/appearance.test.ts-snapshots/appearance-prop-all-clerk-themes-render-1-chrome-darwin.png rename to integration/tests/snapshots/unscheduled/appearance.test.ts-snapshots/appearance-prop-all-clerk-themes-render-1-chrome-darwin.png diff --git a/integration/tests/appearance.test.ts b/integration/tests/unscheduled/appearance.test.ts similarity index 91% rename from integration/tests/appearance.test.ts rename to integration/tests/unscheduled/appearance.test.ts index 1773312833d..047c077ce90 100644 --- a/integration/tests/appearance.test.ts +++ b/integration/tests/unscheduled/appearance.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { createTestUtils } from '../../testUtils'; test.describe('appearance prop', () => { test.describe.configure({ mode: 'parallel' }); diff --git a/integration/tests/bundled-ui.test.ts b/integration/tests/unscheduled/bundled-ui.test.ts similarity index 96% rename from integration/tests/bundled-ui.test.ts rename to integration/tests/unscheduled/bundled-ui.test.ts index 7b11fa6d8f7..3157e10e880 100644 --- a/integration/tests/bundled-ui.test.ts +++ b/integration/tests/unscheduled/bundled-ui.test.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withPattern: ['next.appRouterBundledUI.*'] })( 'bundled UI smoke tests @bundled-ui', diff --git a/integration/tests/db-jwt.test.ts b/integration/tests/unscheduled/db-jwt.test.ts similarity index 94% rename from integration/tests/db-jwt.test.ts rename to integration/tests/unscheduled/db-jwt.test.ts index d6ced6b2e11..ec4bdbcdf46 100644 --- a/integration/tests/db-jwt.test.ts +++ b/integration/tests/unscheduled/db-jwt.test.ts @@ -1,9 +1,9 @@ import { test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; test.describe('Dev browser test', () => { const configs = []; diff --git a/integration/tests/email-link.test.ts b/integration/tests/unscheduled/email-link.test.ts similarity index 96% rename from integration/tests/email-link.test.ts rename to integration/tests/unscheduled/email-link.test.ts index 76001647a6d..c6014f86589 100644 --- a/integration/tests/email-link.test.ts +++ b/integration/tests/unscheduled/email-link.test.ts @@ -1,9 +1,9 @@ import type { Browser, BrowserContext, Page } from '@playwright/test'; import { test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { createTestUtils } from '../../testUtils'; test.describe('sign up and sign in using email link', () => { const configs = []; diff --git a/integration/tests/restricted-mode.test.ts b/integration/tests/unscheduled/restricted-mode.test.ts similarity index 94% rename from integration/tests/restricted-mode.test.ts rename to integration/tests/unscheduled/restricted-mode.test.ts index 92dae0dbbfe..108120ed501 100644 --- a/integration/tests/restricted-mode.test.ts +++ b/integration/tests/unscheduled/restricted-mode.test.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; test.describe('Sign-up restricted mode', () => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/sign-in-or-up-email-links-flow.test.ts b/integration/tests/unscheduled/sign-in-or-up-email-links-flow.test.ts similarity index 94% rename from integration/tests/sign-in-or-up-email-links-flow.test.ts rename to integration/tests/unscheduled/sign-in-or-up-email-links-flow.test.ts index 77a856f20c9..1515f21c848 100644 --- a/integration/tests/sign-in-or-up-email-links-flow.test.ts +++ b/integration/tests/unscheduled/sign-in-or-up-email-links-flow.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../testUtils'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [] })('sign-in-or-up email links flow', ({ app }) => { test.describe.configure({ mode: 'serial' }); diff --git a/integration/tests/waitlist-mode.test.ts b/integration/tests/unscheduled/waitlist-mode.test.ts similarity index 96% rename from integration/tests/waitlist-mode.test.ts rename to integration/tests/unscheduled/waitlist-mode.test.ts index 1961e0ea449..15024960a19 100644 --- a/integration/tests/waitlist-mode.test.ts +++ b/integration/tests/unscheduled/waitlist-mode.test.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; -import type { Application } from '../models/application'; -import { appConfigs } from '../presets'; -import type { FakeUser } from '../testUtils'; -import { createTestUtils } from '../testUtils'; +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; test.describe('Waitlist mode', () => { test.describe.configure({ mode: 'parallel' }); diff --git a/scripts/integration-hash-matrix.mjs b/scripts/integration-hash-matrix.mjs index 2997e1bdbdb..74354e0d184 100644 --- a/scripts/integration-hash-matrix.mjs +++ b/scripts/integration-hash-matrix.mjs @@ -10,7 +10,7 @@ const scenarios = [ ['shared', 'packages/shared/src/index.ts'], ['expo', 'packages/expo/src/index.ts'], ['mosaic', 'packages/mosaic/src/global.d.ts'], - ['test file', 'integration/tests/sign-in-flow.test.ts'], + ['test file', 'integration/tests/generic/sign-in-flow.test.ts'], ['vue test', 'integration/tests/vue/components.test.ts'], ['expo-native', 'integration/expo-native/README.md'], ]; diff --git a/turbo.json b/turbo.json index 990a630e98e..e202c4d52a5 100644 --- a/turbo.json +++ b/turbo.json @@ -261,7 +261,7 @@ "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", "integration/tests/*", "integration/tests/snapshots/**", - "integration/tests/session-token-cache/**", + "integration/tests/{generic,session-token-cache}/**", "integration/templates/*", "integration/templates/{custom-flows-react-vite,next-app-router,react-vite,vue-vite}/**" ], @@ -342,7 +342,7 @@ "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", "integration/tests/*", "integration/tests/snapshots/**", - "integration/tests/{nextjs,session-token-cache}/**", + "integration/tests/{generic,nextjs,session-token-cache}/**", "integration/templates/*", "integration/templates/{next-app-router,react-vite}/**" ], @@ -357,7 +357,7 @@ "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", "integration/tests/*", "integration/tests/snapshots/**", - "integration/tests/{nextjs,session-token-cache}/**", + "integration/tests/{generic,nextjs,session-token-cache}/**", "integration/templates/*", "integration/templates/{next-app-router,react-vite}/**" ], @@ -379,6 +379,7 @@ "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", "integration/tests/*", "integration/tests/snapshots/**", + "integration/tests/quickstart/**", "integration/templates/*", "integration/templates/next-app-router-quickstart/**" ], @@ -577,7 +578,7 @@ "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", "integration/tests/*", "integration/tests/snapshots/**", - "integration/tests/astro/**", + "integration/tests/{astro,billing}/**", "integration/templates/*", "integration/templates/{astro-node,next-app-router,vue-vite}/**" ], @@ -604,7 +605,7 @@ "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", "integration/tests/*", "integration/tests/snapshots/**", - "integration/tests/{astro,express,fastify,hono,nuxt,react-router,tanstack-start}/**", + "integration/tests/{astro,express,fastify,hono,machine,nuxt,react-router,tanstack-start}/**", "integration/templates/*", "integration/templates/{astro-node,express-vite,fastify-vite,hono-vite,next-app-router,nuxt-node,react-router-node,tanstack-react-start}/**" ], @@ -619,7 +620,7 @@ "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", "integration/tests/*", "integration/tests/snapshots/**", - "integration/tests/custom-flows/**", + "integration/tests/{custom-flows,generic}/**", "integration/templates/*", "integration/templates/{custom-flows-react-vite,react-vite}/**" ], @@ -634,6 +635,7 @@ "integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**", "integration/tests/*", "integration/tests/snapshots/**", + "integration/tests/cache-components/**", "integration/templates/*", "integration/templates/next-cache-components/**" ], From 76c07c2ed4fed2dfd7d82f372329fb2ec182c648 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 19:44:13 -0700 Subject: [PATCH 09/17] ci(repo): escape regex metacharacters when matching app id globs --- scripts/integration-deps.mjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/integration-deps.mjs b/scripts/integration-deps.mjs index b6ace959c01..d9e637b744a 100644 --- a/scripts/integration-deps.mjs +++ b/scripts/integration-deps.mjs @@ -113,7 +113,13 @@ const followImports = seeds => { return seen; }; -const globToRegExp = glob => new RegExp(`^${glob.replace(/[.]/g, '\\.').replace(/\*/g, '.*')}$`); +const globToRegExp = glob => + new RegExp( + `^${glob + .split('*') + .map(part => part.replace(/[\\^$.+?()[\]{}|]/g, '\\$&')) + .join('.*')}$`, + ); const braceGlob = (prefix, names) => names.length === 1 ? `${prefix}/${names[0]}/**` : `${prefix}/{${names.join(',')}}/**`; From 0eb01cfc51f237197501c4b675f22935c12ab94a Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 19:55:17 -0700 Subject: [PATCH 10/17] ci(repo): hash package source that integration tests import directly The chrome-extension suite imports a clerk-js test fixture that the clerk-js build excludes, so a fixture change could reuse a cached pass. The dependency checker now records package source files that tests import directly and adds them to the suite's inputs, and a check asks turbo's dry run to confirm it hashes each one. --- .claude/skills/clerk-monorepo/SKILL.md | 3 ++- scripts/integration-deps.mjs | 35 +++++++++++++++++--------- scripts/integration-deps.test.mjs | 22 ++++++++++++++++ scripts/integration-hash-matrix.mjs | 7 +++--- turbo.json | 12 ++++++--- 5 files changed, 59 insertions(+), 20 deletions(-) diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index b4aee8b67dd..23cc4d2682e 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -151,7 +151,8 @@ How the cache key is built: add. Turbo folds those build hashes into the suite hash, and package builds already depend on `^build`, so transitive packages such as `@clerk/shared` are covered without being listed. - `inputs` covers the harness, the test directories the suite's tagged tests live in (and import - from), and the templates its apps copy. Other suites' directories are not inputs. Tests live under + from), the templates its apps copy, and any package source file a test imports directly, such as a + `packages/*/src/test` fixture the package build excludes. Other suites' directories are not inputs. Tests live under `integration/tests//`; a test tagged for several suites lives in `generic/`, and tests no suite runs live in `unscheduled/`. - `scripts/integration-deps.test.mjs` derives both lists from `integration/presets` and the tagged diff --git a/scripts/integration-deps.mjs b/scripts/integration-deps.mjs index d9e637b744a..7211244f13c 100644 --- a/scripts/integration-deps.mjs +++ b/scripts/integration-deps.mjs @@ -91,26 +91,35 @@ const withoutImplied = (graph, names) => HARNESS_PACKAGES.includes(name) || !names.some(other => other !== name && transitiveDeps(graph, other).has(name)), ); +const INTEGRATION = path.join(root, 'integration'); + +const resolveImport = (from, specifier) => { + const target = path.resolve(path.dirname(from), specifier); + return [target, `${target}.ts`, `${target}.tsx`, path.join(target, 'index.ts')].find( + candidate => fs.existsSync(candidate) && fs.statSync(candidate).isFile(), + ); +}; + const followImports = seeds => { - const seen = new Map(seeds.map(file => [file, read(file)])); - const queue = [...seen.keys()]; + const sources = new Map(seeds.map(file => [file, read(file)])); + const external = new Set(); + const queue = [...sources.keys()]; while (queue.length) { const file = queue.pop(); - for (const match of seen.get(file).matchAll(/from '(\.{1,2}\/[^']+)'/g)) { - const target = path.resolve(path.dirname(file), match[1]); - if (!target.startsWith(TESTS)) { + for (const match of sources.get(file).matchAll(/from '(\.{1,2}\/[^']+)'/g)) { + const resolved = resolveImport(file, match[1]); + if (!resolved || sources.has(resolved) || external.has(resolved)) { continue; } - const resolved = [target, `${target}.ts`, path.join(target, 'index.ts')].find( - candidate => fs.existsSync(candidate) && fs.statSync(candidate).isFile(), - ); - if (resolved && !seen.has(resolved)) { - seen.set(resolved, read(resolved)); + if (resolved.startsWith(TESTS + path.sep)) { + sources.set(resolved, read(resolved)); queue.push(resolved); + } else if (!resolved.startsWith(INTEGRATION + path.sep) && resolved.startsWith(root + path.sep)) { + external.add(resolved); } } } - return seen; + return { sources, external: [...external].map(file => path.relative(root, file)).sort() }; }; const globToRegExp = glob => @@ -160,7 +169,7 @@ export const deriveIntegrationTasks = async () => { const tagged = tag ? testFiles.filter(file => new RegExp(`@${tag}(?![a-z-])`).test(read(file))) : testFiles.filter(file => file.startsWith(path.join(TESTS, base) + path.sep)); - const sources = followImports(tagged); + const { sources, external } = followImports(tagged); const patterns = script.match(/E2E_APP_ID=([^ ]+)/)?.[1].split(',') ?? []; const configNames = new Set(); @@ -204,7 +213,9 @@ export const deriveIntegrationTasks = async () => { ...(testDirs.size ? [braceGlob('integration/tests', [...testDirs].sort())] : []), 'integration/templates/*', ...(templates.size ? [braceGlob('integration/templates', [...templates].sort())] : []), + ...external, ], + external, configs: [...configNames].sort(), }); } diff --git a/scripts/integration-deps.test.mjs b/scripts/integration-deps.test.mjs index f6814f30604..0731b3a87f6 100644 --- a/scripts/integration-deps.test.mjs +++ b/scripts/integration-deps.test.mjs @@ -1,3 +1,4 @@ +import { spawnSync } from 'node:child_process'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; @@ -31,6 +32,27 @@ describe('integration suite tasks in turbo.json', () => { ).toBeDefined(); }); + test('turbo hashes the package source each suite imports directly', () => { + const imported = [...derived.entries()].filter(([, task]) => task.external.length); + expect(imported.map(([suite]) => suite)).toContain('chrome-extension'); + const result = spawnSync( + 'pnpm', + ['turbo', 'run', ...imported.map(([suite]) => `test:integration:${suite}`), '--dry=json'], + { + cwd: root, + encoding: 'utf8', + }, + ); + expect(result.status, result.stderr).toBe(0); + const dry = JSON.parse(result.stdout.slice(result.stdout.search(/^\{/m))); + for (const [suite, task] of imported) { + const hashed = Object.keys(dry.tasks.find(t => t.taskId === `//#test:integration:${suite}`).inputs); + for (const file of task.external) { + expect(hashed, `${file} is imported by the ${suite} suite but turbo does not hash it`).toContain(file); + } + } + }, 120_000); + test.each([...derived.entries()])('%s depends on the packages its apps install', (suite, expected) => { const task = turbo.tasks[`//#test:integration:${suite}`]; const actual = { dependsOn: task.dependsOn, inputs: task.inputs }; diff --git a/scripts/integration-hash-matrix.mjs b/scripts/integration-hash-matrix.mjs index 74354e0d184..f252a7a0da2 100644 --- a/scripts/integration-hash-matrix.mjs +++ b/scripts/integration-hash-matrix.mjs @@ -13,6 +13,7 @@ const scenarios = [ ['test file', 'integration/tests/generic/sign-in-flow.test.ts'], ['vue test', 'integration/tests/vue/components.test.ts'], ['expo-native', 'integration/expo-native/README.md'], + ['js fixture', 'packages/clerk-js/src/test/fixtures.ts'], ]; const turbo = JSON.parse(readFileSync(resolve(root, 'turbo.json'), 'utf8')); @@ -25,7 +26,7 @@ function hashes() { if (result.status !== 0) { throw new Error(result.stderr); } - const json = JSON.parse(result.stdout.slice(result.stdout.indexOf('{'))); + const json = JSON.parse(result.stdout.slice(result.stdout.search(/^\{/m))); return new Map( json.tasks .filter(task => task.taskId.startsWith('//#test:integration:')) @@ -65,10 +66,10 @@ for (const [label, file] of scenarios) { } const pad = (value, width) => String(value).padEnd(width); -console.log(`${pad('suite', 22)} ${pad('inputs', 6)} ${columns.map(([label]) => pad(label, 11)).join('')}`); +console.log(`${pad('suite', 22)} ${pad('inputs', 6)} ${columns.map(([label]) => pad(label, 12)).join('')}`); for (const suite of [...baseline.keys()].sort()) { const cells = columns.map(([, result]) => - pad(result.get(suite).hash === baseline.get(suite).hash ? '.' : 'RERUN', 11), + pad(result.get(suite).hash === baseline.get(suite).hash ? '.' : 'RERUN', 12), ); console.log(`${pad(suite, 22)} ${pad(baseline.get(suite).inputs, 6)} ${cells.join('')}`); } diff --git a/turbo.json b/turbo.json index e202c4d52a5..7841ed270ac 100644 --- a/turbo.json +++ b/turbo.json @@ -263,7 +263,8 @@ "integration/tests/snapshots/**", "integration/tests/{generic,session-token-cache}/**", "integration/templates/*", - "integration/templates/{custom-flows-react-vite,next-app-router,react-vite,vue-vite}/**" + "integration/templates/{custom-flows-react-vite,next-app-router,react-vite,vue-vite}/**", + "packages/backend/src/tokens/types.ts" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -344,7 +345,8 @@ "integration/tests/snapshots/**", "integration/tests/{generic,nextjs,session-token-cache}/**", "integration/templates/*", - "integration/templates/{next-app-router,react-vite}/**" + "integration/templates/{next-app-router,react-vite}/**", + "packages/backend/src/tokens/types.ts" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -359,7 +361,8 @@ "integration/tests/snapshots/**", "integration/tests/{generic,nextjs,session-token-cache}/**", "integration/templates/*", - "integration/templates/{next-app-router,react-vite}/**" + "integration/templates/{next-app-router,react-vite}/**", + "packages/backend/src/tokens/types.ts" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" @@ -657,7 +660,8 @@ "integration/tests/snapshots/**", "integration/tests/chrome-extension/**", "integration/templates/*", - "integration/templates/{chrome-extension-vite,react-vite}/**" + "integration/templates/{chrome-extension-vite,react-vite}/**", + "packages/clerk-js/src/test/fixtures.ts" ], "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" From 606f8a2772bd2cd4825dd016aa0953763955a440 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 20:39:29 -0700 Subject: [PATCH 11/17] ci(repo): hash integration suites through transit nodes and move harness deps out of the root Integration suites now depend on #build:inputs, a task no package defines. It hashes the same inputs as build and chains through ^build:inputs, so a suite's hash still covers every package it uses, but a cache hit restores no build outputs and TURBO_FORCE in the merge queue no longer rebuilds dependencies. @clerk/backend, @clerk/shared and @clerk/testing move from the root package.json to a new private @clerk/integration workspace package. Turbo folded every file of a root workspace dependency into the global hash, so editing any file in those packages, tests included, reran every task in the repo. Suites now list the harness packages explicitly, derived from integration/package.json, and a test keeps workspace packages out of the root manifest. --- .claude/skills/clerk-monorepo/SKILL.md | 17 +- integration/package.json | 9 +- package.json | 3 - pnpm-lock.yaml | 21 +- pnpm-workspace.yaml | 1 + scripts/integration-deps.mjs | 9 +- scripts/integration-deps.test.mjs | 25 ++- scripts/integration-hash-matrix.mjs | 1 + scripts/run-integration.test.mjs | 2 +- turbo.json | 290 ++++++++++++++++++++----- 10 files changed, 301 insertions(+), 77 deletions(-) diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index 23cc4d2682e..adf77b4cdb0 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -146,10 +146,12 @@ outcome skips the suite entirely: no build, no local registry publish, no browse How the cache key is built: -- `dependsOn` lists the package builds the suite's e2e apps install from the local registry: always - `@clerk/clerk-js`, `@clerk/ui` and `@clerk/testing`, plus the framework packages that suite's apps - add. Turbo folds those build hashes into the suite hash, and package builds already depend on - `^build`, so transitive packages such as `@clerk/shared` are covered without being listed. +- `dependsOn` lists `#build:inputs` for each package the suite uses: always `@clerk/clerk-js` and + `@clerk/ui`, the packages the harness imports (the `devDependencies` of `integration/package.json`), + plus the framework packages that suite's apps install from the local registry. `build:inputs` is a transit node: no package defines the script, so it never + runs or restores anything, but it hashes the same inputs as `build` and depends on `^build:inputs`, + so transitive packages such as `@clerk/shared` are covered without being listed. A cache hit + therefore downloads no build outputs; on a miss the runner script builds. - `inputs` covers the harness, the test directories the suite's tagged tests live in (and import from), the templates its apps copy, and any package source file a test imports directly, such as a `packages/*/src/test` fixture the package build excludes. Other suites' directories are not inputs. Tests live under @@ -162,8 +164,13 @@ Rules that keep this correct: - The integration test step must never pass `--only`. It drops dependency hashes from the task hash, so package changes would stop invalidating suites. +- Keep `build:inputs` identical to `build`'s inputs; `integration-deps.test.mjs` enforces it. +- The harness's workspace dependencies live in `integration/package.json`, never the root + `package.json`. Turbo folds every file of a root workspace dependency, tests and READMEs included, + into the global hash, so any edit to it would rerun every task in the repo. - The merge queue sets `TURBO_FORCE=true` on the test step and nightly passes `--force`, so every suite - runs for real before a commit lands on `main`. A `dependsOn` gap is a wasted-cache bug on PRs, not a + runs for real before a commit lands on `main`. Forcing reaches only the suite and its transit nodes; + the runner script's own `turbo build` resets `TURBO_FORCE` so package builds still restore from cache. A `dependsOn` gap is a wasted-cache bug on PRs, not a correctness hole on `main`. - Per-job setup lives in `integration/scripts/run-integration.mjs`, which the `test:integration:base` script wraps. When `E2E_CI_SETUP` names the suite it builds, publishes to pkglab, installs clerk-js diff --git a/integration/package.json b/integration/package.json index e986b24bbae..6de7fbe8c68 100644 --- a/integration/package.json +++ b/integration/package.json @@ -1,4 +1,11 @@ { + "name": "@clerk/integration", + "version": "0.0.0", "private": true, - "type": "module" + "type": "module", + "devDependencies": { + "@clerk/backend": "workspace:*", + "@clerk/shared": "workspace:*", + "@clerk/testing": "workspace:*" + } } diff --git a/package.json b/package.json index ce0dce3e0f5..2b421cce5ab 100644 --- a/package.json +++ b/package.json @@ -78,9 +78,6 @@ "@arethetypeswrong/cli": "0.18.3", "@changesets/cli": "^2.31.1", "@changesets/get-github-info": "^0.8.0", - "@clerk/backend": "workspace:*", - "@clerk/shared": "workspace:*", - "@clerk/testing": "workspace:*", "@commitlint/cli": "^20.5.2", "@commitlint/config-conventional": "^20.5.0", "@eslint-community/eslint-plugin-eslint-comments": "4.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5b4c50c29a..18e999141f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -125,15 +125,6 @@ importers: '@changesets/get-github-info': specifier: ^0.8.0 version: 0.8.0 - '@clerk/backend': - specifier: workspace:* - version: link:packages/backend - '@clerk/shared': - specifier: workspace:* - version: link:packages/shared - '@clerk/testing': - specifier: workspace:* - version: link:packages/testing '@commitlint/cli': specifier: ^20.5.2 version: 20.5.3(@types/node@22.20.1)(conventional-commits-parser@6.4.0)(typescript@6.0.3) @@ -363,6 +354,18 @@ importers: specifier: catalog:repo version: 8.8.5 + integration: + devDependencies: + '@clerk/backend': + specifier: workspace:* + version: link:../packages/backend + '@clerk/shared': + specifier: workspace:* + version: link:../packages/shared + '@clerk/testing': + specifier: workspace:* + version: link:../packages/testing + packages/astro: dependencies: '@clerk/backend': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 48837676c4b..fb268bf10d9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ packages: - packages/* - packages/electron-passkeys/npm/* + - integration catalogs: peer-expo: diff --git a/scripts/integration-deps.mjs b/scripts/integration-deps.mjs index 7211244f13c..cc42b9528e6 100644 --- a/scripts/integration-deps.mjs +++ b/scripts/integration-deps.mjs @@ -6,7 +6,6 @@ export const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ' const TASK_PREFIX = '//#test:integration:'; const TESTS = path.join(root, 'integration/tests'); -const HARNESS_PACKAGES = ['@clerk/clerk-js', '@clerk/testing', '@clerk/ui']; const SHARED_INPUTS = [ 'integration/*', 'integration/{certs,cleanup,configs,deployments,models,presets,scripts,testUtils}/**', @@ -33,6 +32,12 @@ const PRESET_GROUPS = { const read = file => fs.readFileSync(file, 'utf8'); const readJson = file => JSON.parse(read(file)); +const HARNESS_PACKAGES = [ + '@clerk/clerk-js', + '@clerk/ui', + ...Object.keys(readJson(path.join(root, 'integration/package.json')).devDependencies ?? {}), +].sort(); + export const integrationSuites = () => Object.keys(readJson(path.join(root, 'turbo.json')).tasks) .filter(task => task.startsWith(TASK_PREFIX)) @@ -207,7 +212,7 @@ export const deriveIntegrationTasks = async () => { ); tasks.set(suite, { - dependsOn: withoutImplied(graph, [...packages].sort()).map(pkg => `${pkg}#build`), + dependsOn: withoutImplied(graph, [...packages].sort()).map(pkg => `${pkg}#build:inputs`), inputs: [ ...SHARED_INPUTS, ...(testDirs.size ? [braceGlob('integration/tests', [...testDirs].sort())] : []), diff --git a/scripts/integration-deps.test.mjs b/scripts/integration-deps.test.mjs index 0731b3a87f6..7e12cc614c0 100644 --- a/scripts/integration-deps.test.mjs +++ b/scripts/integration-deps.test.mjs @@ -1,5 +1,5 @@ import { spawnSync } from 'node:child_process'; -import { readFileSync } from 'node:fs'; +import { existsSync, readdirSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; import { describe, expect, test } from 'vitest'; @@ -32,6 +32,29 @@ describe('integration suite tasks in turbo.json', () => { ).toBeDefined(); }); + test('the root package.json depends on no workspace packages', () => { + const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')); + const deps = { ...manifest.dependencies, ...manifest.devDependencies, ...manifest.optionalDependencies }; + const workspace = Object.entries(deps) + .filter(([, version]) => version.startsWith('workspace:')) + .map(([name]) => name); + expect( + workspace, + 'turbo folds every file of a root workspace dependency into the global hash, so any edit to it reruns every task. Declare it in integration/package.json instead.', + ).toEqual([]); + }); + + test('build:inputs hashes the same files as build without running anything', () => { + expect(turbo.tasks['build:inputs']).toEqual({ dependsOn: ['^build:inputs'], inputs: turbo.tasks.build.inputs }); + const packages = readdirSync(join(root, 'packages')).filter(dir => + existsSync(join(root, 'packages', dir, 'package.json')), + ); + for (const dir of packages) { + const { scripts = {} } = JSON.parse(readFileSync(join(root, 'packages', dir, 'package.json'), 'utf8')); + expect(scripts['build:inputs'], `packages/${dir} defines a build:inputs script`).toBeUndefined(); + } + }); + test('turbo hashes the package source each suite imports directly', () => { const imported = [...derived.entries()].filter(([, task]) => task.external.length); expect(imported.map(([suite]) => suite)).toContain('chrome-extension'); diff --git a/scripts/integration-hash-matrix.mjs b/scripts/integration-hash-matrix.mjs index f252a7a0da2..d95dffc5049 100644 --- a/scripts/integration-hash-matrix.mjs +++ b/scripts/integration-hash-matrix.mjs @@ -8,6 +8,7 @@ const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const scenarios = [ ['upgrade', 'packages/upgrade/src/cli.js'], ['shared', 'packages/shared/src/index.ts'], + ['shared test', 'packages/shared/src/types/signUpTimezone.type.test.ts'], ['expo', 'packages/expo/src/index.ts'], ['mosaic', 'packages/mosaic/src/global.d.ts'], ['test file', 'integration/tests/generic/sign-in-flow.test.ts'], diff --git a/scripts/run-integration.test.mjs b/scripts/run-integration.test.mjs index cf572cf0795..c95dce54603 100644 --- a/scripts/run-integration.test.mjs +++ b/scripts/run-integration.test.mjs @@ -1,5 +1,5 @@ import { spawnSync } from 'node:child_process'; -import { mkdtempSync, readFileSync, rmSync, writeFileSync, mkdirSync } from 'node:fs'; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; diff --git a/turbo.json b/turbo.json index 7841ed270ac..c9218e07808 100644 --- a/turbo.json +++ b/turbo.json @@ -78,6 +78,34 @@ "outputLogs": "new-only", "outputs": ["*/package.json", "dist/**", "scripts/**"] }, + "build:inputs": { + "dependsOn": ["^build:inputs"], + "inputs": [ + "package.json", + "*.d.ts", + "bundlewatch.config.json", + "src/**", + "tsconfig.json", + "tsconfig.build.json", + "tsconfig.declarations.json", + "tsdown.config.mts", + "tsdown.config.ts", + "tsup.config.ts", + "vite.config.*", + "subpaths.mjs", + "!**/*.test.*", + "!**/test/**", + "!**/tests/**", + "!**/__tests__/**", + "!**/__mocks__/**", + "!**/__snapshots__/**", + "!CHANGELOG.md", + "!coverage/**", + "!dist/**", + "!examples/**", + "!node_modules/**" + ] + }, "build:declarations": { "dependsOn": ["build"], "inputs": ["tsconfig.declarations.json"], @@ -233,7 +261,14 @@ "outputs": [] }, "//#test:integration:ap-flows": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -249,11 +284,13 @@ }, "//#test:integration:generic": { "dependsOn": [ - "@clerk/clerk-js#build", - "@clerk/nextjs#build", - "@clerk/testing#build", - "@clerk/ui#build", - "@clerk/vue#build" + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs", + "@clerk/vue#build:inputs" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ @@ -270,7 +307,14 @@ "outputLogs": "new-only" }, "//#test:integration:express": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/express#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/express#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -285,7 +329,14 @@ "outputLogs": "new-only" }, "//#test:integration:fastify": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/fastify#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/fastify#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -300,7 +351,14 @@ "outputLogs": "new-only" }, "//#test:integration:hono": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/hono#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/hono#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -316,11 +374,13 @@ }, "//#test:integration:mosaic": { "dependsOn": [ - "@clerk/clerk-js#build", - "@clerk/mosaic#build", - "@clerk/nextjs#build", - "@clerk/testing#build", - "@clerk/ui#build" + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/mosaic#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ @@ -336,7 +396,14 @@ "outputLogs": "new-only" }, "//#test:integration:nextjs": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -352,7 +419,14 @@ "outputLogs": "new-only" }, "//#test:integration:nextjs:canary": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -368,7 +442,14 @@ "outputLogs": "new-only" }, "//#test:integration:quickstart": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": [ "CLEANUP", "DEBUG", @@ -390,7 +471,14 @@ "outputLogs": "new-only" }, "//#test:integration:astro": { - "dependsOn": ["@clerk/astro#build", "@clerk/clerk-js#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/astro#build:inputs", + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -405,7 +493,14 @@ "outputLogs": "new-only" }, "//#test:integration:localhost": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": [ "CLEANUP", "DEBUG", @@ -427,7 +522,14 @@ "outputLogs": "new-only" }, "//#test:integration:sessions": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], "inputs": [ "integration/*", @@ -442,7 +544,14 @@ "outputLogs": "new-only" }, "//#test:integration:sessions:staging": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], "inputs": [ "integration/*", @@ -457,7 +566,14 @@ "outputLogs": "new-only" }, "//#test:integration:handshake": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], "inputs": [ "integration/*", @@ -472,7 +588,14 @@ "outputLogs": "new-only" }, "//#test:integration:handshake:staging": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], "inputs": [ "integration/*", @@ -487,7 +610,14 @@ "outputLogs": "new-only" }, "//#test:integration:expo-web": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/expo#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/expo#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -503,10 +633,12 @@ }, "//#test:integration:tanstack-react-start": { "dependsOn": [ - "@clerk/clerk-js#build", - "@clerk/tanstack-react-start#build", - "@clerk/testing#build", - "@clerk/ui#build" + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/tanstack-react-start#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ @@ -522,7 +654,14 @@ "outputLogs": "new-only" }, "//#test:integration:vue": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/testing#build", "@clerk/ui#build", "@clerk/vue#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs", + "@clerk/vue#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -537,7 +676,14 @@ "outputLogs": "new-only" }, "//#test:integration:nuxt": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nuxt#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nuxt#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -552,7 +698,14 @@ "outputLogs": "new-only" }, "//#test:integration:react-router": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/react-router#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/react-router#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -568,12 +721,14 @@ }, "//#test:integration:billing": { "dependsOn": [ - "@clerk/astro#build", - "@clerk/clerk-js#build", - "@clerk/nextjs#build", - "@clerk/testing#build", - "@clerk/ui#build", - "@clerk/vue#build" + "@clerk/astro#build:inputs", + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs", + "@clerk/vue#build:inputs" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ @@ -590,17 +745,19 @@ }, "//#test:integration:machine": { "dependsOn": [ - "@clerk/astro#build", - "@clerk/clerk-js#build", - "@clerk/express#build", - "@clerk/fastify#build", - "@clerk/hono#build", - "@clerk/nextjs#build", - "@clerk/nuxt#build", - "@clerk/react-router#build", - "@clerk/tanstack-react-start#build", - "@clerk/testing#build", - "@clerk/ui#build" + "@clerk/astro#build:inputs", + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/express#build:inputs", + "@clerk/fastify#build:inputs", + "@clerk/hono#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/nuxt#build:inputs", + "@clerk/react-router#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/tanstack-react-start#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ @@ -616,7 +773,14 @@ "outputLogs": "new-only" }, "//#test:integration:custom": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/react#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/react#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -631,7 +795,14 @@ "outputLogs": "new-only" }, "//#test:integration:cache-components": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/nextjs#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/nextjs#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", @@ -647,10 +818,12 @@ }, "//#test:integration:chrome-extension": { "dependsOn": [ - "@clerk/chrome-extension#build", - "@clerk/clerk-js#build", - "@clerk/testing#build", - "@clerk/ui#build" + "@clerk/backend#build:inputs", + "@clerk/chrome-extension#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS"], "inputs": [ @@ -667,7 +840,14 @@ "outputLogs": "new-only" }, "//#test:integration:electron": { - "dependsOn": ["@clerk/clerk-js#build", "@clerk/electron#build", "@clerk/testing#build", "@clerk/ui#build"], + "dependsOn": [ + "@clerk/backend#build:inputs", + "@clerk/clerk-js#build:inputs", + "@clerk/electron#build:inputs", + "@clerk/shared#build:inputs", + "@clerk/testing#build:inputs", + "@clerk/ui#build:inputs" + ], "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], "inputs": [ "integration/*", From df15bc7145a5c515015c4766bd0b4979d4d80923 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 21:43:13 -0700 Subject: [PATCH 12/17] ci(repo): keep test secrets out of the integration runner's package build CLERK_* is in globalEnv, and the integration test step sets CLERK_PLATFORM_API_KEY and CLERK_E2E_OAUTH_PROVIDER_CLIENT_SECRET. Once the build moved into that step, every package hash differed from the Build Packages job, so each integration cache miss rebuilt all 26 packages. The runner now drops CLERK_* from the build's environment. Also removes the no-op .npmrc edit from the integration job and corrects the staging workflow's description of the cache key. --- .claude/skills/clerk-monorepo/SKILL.md | 4 +++- .github/workflows/ci.yml | 3 --- .github/workflows/e2e-staging.yml | 2 +- integration/scripts/run-integration.mjs | 3 ++- scripts/run-integration.test.mjs | 8 +++++++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index adf77b4cdb0..75f6e058c83 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -175,7 +175,9 @@ Rules that keep this correct: - Per-job setup lives in `integration/scripts/run-integration.mjs`, which the `test:integration:base` script wraps. When `E2E_CI_SETUP` names the suite it builds, publishes to pkglab, installs clerk-js and ui into temp dirs, installs Chromium and, for electron, Xvfb, then runs Playwright. Locally - `E2E_CI_SETUP` is unset and it only runs Playwright. A cache hit runs none of it, including the + `E2E_CI_SETUP` is unset and it only runs Playwright. It drops `CLERK_*` from the build's + environment: `CLERK_*` is in `globalEnv`, so the test step's secrets would otherwise change every + build hash and miss the Build Packages job's cache. A cache hit runs none of it, including the user cleanup step, which is gated on the `tests-started` output the script writes. Adding a suite: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3cf78675c4..64d26583645 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -589,9 +589,6 @@ jobs: TASK_NAME="test:integration:${{ matrix.test-name }}" node -e 'const pkg = require("./package.json"); const turbo = require("./turbo.json"); const name = process.argv[1]; if (!pkg.scripts[name] || !turbo.tasks["//#" + name]) process.exit(1)' "$TASK_NAME" - - name: Edit .npmrc [link-workspace-packages=false] - run: sed -i -E 's/link-workspace-packages=(deep|true)/link-workspace-packages=false/' .npmrc - - name: Write all ENV certificates to files in integration/certs uses: actions/github-script@v7.1.0 env: diff --git a/.github/workflows/e2e-staging.yml b/.github/workflows/e2e-staging.yml index 3d99a351fab..29d4eef7771 100644 --- a/.github/workflows/e2e-staging.yml +++ b/.github/workflows/e2e-staging.yml @@ -290,7 +290,7 @@ jobs: run: pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS env: # Staging e2e validates the LIVE staging deploy, which is not a turbo input - # (the cache key is integration/** + env). Force re-execution so a cached green + # (the cache key is the suite's inputs, package sources and env). Force re-execution so a cached green # pass can't stand in for an untested deploy, and so the Playwright JSON report # is regenerated every run instead of being skipped on a `>>> FULL TURBO` hit. TURBO_FORCE: 'true' diff --git a/integration/scripts/run-integration.mjs b/integration/scripts/run-integration.mjs index 9b35d7f5fb6..baf3dbe8667 100644 --- a/integration/scripts/run-integration.mjs +++ b/integration/scripts/run-integration.mjs @@ -25,7 +25,8 @@ function prepareCiRunner(suite) { ]; const githubOutput = required('GITHUB_OUTPUT'); - run('pnpm', ['turbo', 'build', ...turboArgs, '--only'], { env: { ...process.env, TURBO_FORCE: 'false' } }); + const buildEnv = Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.startsWith('CLERK_'))); + run('pnpm', ['turbo', 'build', ...turboArgs, '--only'], { env: { ...buildEnv, TURBO_FORCE: 'false' } }); run('pkglab', ['pub', '--force']); for (const [packageName, dir] of installs) { mkdirSync(dir, { recursive: true }); diff --git a/scripts/run-integration.test.mjs b/scripts/run-integration.test.mjs index c95dce54603..c0212233482 100644 --- a/scripts/run-integration.test.mjs +++ b/scripts/run-integration.test.mjs @@ -11,7 +11,7 @@ function run(setup, fail = '', env = {}) { const dir = mkdtempSync(join(tmpdir(), 'run-integration-')); const bin = join(dir, 'bin'); mkdirSync(bin); - const command = `#!/usr/bin/env node\nconst fs = require('node:fs');\nconst path = require('node:path');\nconst name = path.basename(process.argv[1]);\nconst args = process.argv.slice(2);\nfs.appendFileSync(process.env.COMMAND_LOG, JSON.stringify({ name, args, cwd: process.cwd(), turboForce: process.env.TURBO_FORCE }) + '\\n');\nprocess.exit(process.env.FAIL_COMMAND === name || process.env.FAIL_COMMAND === name + ' ' + args.join(' ') ? 7 : 0);\n`; + const command = `#!/usr/bin/env node\nconst fs = require('node:fs');\nconst path = require('node:path');\nconst name = path.basename(process.argv[1]);\nconst args = process.argv.slice(2);\nfs.appendFileSync(process.env.COMMAND_LOG, JSON.stringify({ name, args, cwd: process.cwd(), turboForce: process.env.TURBO_FORCE, clerkSecret: process.env.CLERK_PLATFORM_API_KEY }) + '\\n');\nprocess.exit(process.env.FAIL_COMMAND === name || process.env.FAIL_COMMAND === name + ' ' + args.join(' ') ? 7 : 0);\n`; for (const name of ['pnpm', 'pkglab', 'xvfb-run']) { const file = join(bin, name); writeFileSync(file, command, { mode: 0o755 }); @@ -31,6 +31,7 @@ function run(setup, fail = '', env = {}) { E2E_APP_CLERK_UI_DIR: dir, TURBO_ARGS: '--concurrency=2 --output-logs=new-only', TURBO_FORCE: 'true', + CLERK_PLATFORM_API_KEY: 'secret', ...env, }, encoding: 'utf8', @@ -75,6 +76,11 @@ describe('CI integration runner', () => { 'pnpm playwright test --grep @case', ]); expect(calls[0].turboForce).toBe('false'); + expect( + calls[0].clerkSecret, + 'CLERK_* is in globalEnv, so passing test secrets to the build misses the build job cache', + ).toBeUndefined(); + expect(calls.at(-1).clerkSecret).toBe('secret'); expect(marker).toBe('tests-started=true\n'); }); From 91e4a0b87f6d04728f327d19fbea493244bb722c Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 21:58:31 -0700 Subject: [PATCH 13/17] test(mosaic): temporary edit to check integration cache scoping (will be reverted) --- packages/mosaic/src/mosaic-provider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mosaic/src/mosaic-provider.tsx b/packages/mosaic/src/mosaic-provider.tsx index 7f90a73d2c0..2dadc9288b5 100644 --- a/packages/mosaic/src/mosaic-provider.tsx +++ b/packages/mosaic/src/mosaic-provider.tsx @@ -28,7 +28,7 @@ export interface MosaicProviderProps { experimental?: MosaicExperimental; } -// Exported Mosaic components annotate a React return type on purpose. The package sets +// Exported Mosaic components annotate a React return type deliberately. The package sets // `jsxImportSource: '@emotion/react'`, so an inferred return leaks Emotion's `JSX.Element` into the // published `.d.ts`, which React 19 consumers reject as a JSX element type. export function MosaicProvider({ From d547f4a0c51b484f61d99d1d686df54f90ae94aa Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 22:04:45 -0700 Subject: [PATCH 14/17] Revert "test(mosaic): temporary edit to check integration cache scoping (will be reverted)" This reverts commit 1dea6d47e9b94fc261667e8b637d8a3f68060d73. --- packages/mosaic/src/mosaic-provider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mosaic/src/mosaic-provider.tsx b/packages/mosaic/src/mosaic-provider.tsx index 2dadc9288b5..7f90a73d2c0 100644 --- a/packages/mosaic/src/mosaic-provider.tsx +++ b/packages/mosaic/src/mosaic-provider.tsx @@ -28,7 +28,7 @@ export interface MosaicProviderProps { experimental?: MosaicExperimental; } -// Exported Mosaic components annotate a React return type deliberately. The package sets +// Exported Mosaic components annotate a React return type on purpose. The package sets // `jsxImportSource: '@emotion/react'`, so an inferred return leaks Emotion's `JSX.Element` into the // published `.d.ts`, which React 19 consumers reject as a JSX element type. export function MosaicProvider({ From 5c8b68c6a7884d4c002be4585aa9dbef33cc64dd Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 23:04:20 -0700 Subject: [PATCH 15/17] ci(repo): hash package-level build input overrides for integration suites --- .claude/skills/clerk-monorepo/SKILL.md | 3 ++- packages/clerk-js/turbo.json | 19 +++++++++++++++++++ packages/express/turbo.json | 14 ++++++++++++++ packages/mosaic/turbo.json | 22 ++++++++++++++++++++++ packages/ui/turbo.json | 21 +++++++++++++++++++++ scripts/integration-deps.test.mjs | 9 +++++++++ 6 files changed, 87 insertions(+), 1 deletion(-) diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index 75f6e058c83..9f7f82d1862 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -164,7 +164,8 @@ Rules that keep this correct: - The integration test step must never pass `--only`. It drops dependency hashes from the task hash, so package changes would stop invalidating suites. -- Keep `build:inputs` identical to `build`'s inputs; `integration-deps.test.mjs` enforces it. +- Keep `build:inputs` identical to `build`'s inputs, in the root `turbo.json` and in every package + `turbo.json` that overrides `build.inputs`; `integration-deps.test.mjs` enforces both. - The harness's workspace dependencies live in `integration/package.json`, never the root `package.json`. Turbo folds every file of a root workspace dependency, tests and READMEs included, into the global hash, so any edit to it would rerun every task in the repo. diff --git a/packages/clerk-js/turbo.json b/packages/clerk-js/turbo.json index 5d5f376289a..7f03cd5639a 100644 --- a/packages/clerk-js/turbo.json +++ b/packages/clerk-js/turbo.json @@ -20,6 +20,25 @@ "!node_modules/**" ] }, + "build:inputs": { + "inputs": [ + "*.d.ts", + "src/**", + "tsconfig.json", + "tsconfig.declarations.json", + "rspack.config.mjs", + + "!**/*.test.*", + "!**/test/**", + "!**/tests/**", + "!**/__tests__/**", + "!**/__mocks__/**", + "!**/__snapshots__/**", + "!coverage/**", + "!examples/**", + "!node_modules/**" + ] + }, "build:sandbox": { "dependsOn": ["^build", "@clerk/localizations#build"], "inputs": ["sandbox/**"], diff --git a/packages/express/turbo.json b/packages/express/turbo.json index 4379d6c1c35..8c8f810a745 100644 --- a/packages/express/turbo.json +++ b/packages/express/turbo.json @@ -14,6 +14,20 @@ "!coverage/**", "!node_modules/**" ] + }, + "build:inputs": { + "inputs": [ + "*.d.ts", + "**/package.json", + "src/**", + "tsconfig.json", + "tsup.config.ts", + + "!**/__tests__/**", + "!**/__snapshots__/**", + "!coverage/**", + "!node_modules/**" + ] } } } diff --git a/packages/mosaic/turbo.json b/packages/mosaic/turbo.json index 65219661ea9..2109cb9f2cf 100644 --- a/packages/mosaic/turbo.json +++ b/packages/mosaic/turbo.json @@ -23,6 +23,28 @@ "!node_modules/**" ], "outputs": ["dist/**"] + }, + "build:inputs": { + "inputs": [ + "package.json", + "*.d.ts", + "src/**", + "tsconfig.json", + "tsconfig.base.json", + "tsdown.config.mts", + "tsdown.css.config.mts", + "stylex-lightningcss.config.mjs", + "scripts/**", + "!**/*.test.*", + "!**/test/**", + "!**/tests/**", + "!**/__tests__/**", + "!**/__mocks__/**", + "!**/__snapshots__/**", + "!coverage/**", + "!examples/**", + "!node_modules/**" + ] } } } diff --git a/packages/ui/turbo.json b/packages/ui/turbo.json index dc2b9940748..addda889245 100644 --- a/packages/ui/turbo.json +++ b/packages/ui/turbo.json @@ -28,6 +28,27 @@ ], "outputs": ["*/package.json", "dist/**"] }, + "build:inputs": { + "inputs": [ + "*.d.ts", + "src/**", + "tsconfig.json", + "tsconfig.base.json", + "tsconfig.src.json", + "rspack.config.js", + "tsdown.config.mts", + + "!**/*.test.*", + "!**/test/**", + "!**/tests/**", + "!**/__tests__/**", + "!**/__mocks__/**", + "!**/__snapshots__/**", + "!coverage/**", + "!examples/**", + "!node_modules/**" + ] + }, "test": { "inputs": [ "*.d.ts", diff --git a/scripts/integration-deps.test.mjs b/scripts/integration-deps.test.mjs index 7e12cc614c0..e18717374a1 100644 --- a/scripts/integration-deps.test.mjs +++ b/scripts/integration-deps.test.mjs @@ -52,6 +52,15 @@ describe('integration suite tasks in turbo.json', () => { for (const dir of packages) { const { scripts = {} } = JSON.parse(readFileSync(join(root, 'packages', dir, 'package.json'), 'utf8')); expect(scripts['build:inputs'], `packages/${dir} defines a build:inputs script`).toBeUndefined(); + const packageTurbo = join(root, 'packages', dir, 'turbo.json'); + if (!existsSync(packageTurbo)) { + continue; + } + const { tasks = {} } = JSON.parse(readFileSync(packageTurbo, 'utf8')); + expect( + tasks['build:inputs']?.inputs, + `packages/${dir}/turbo.json overrides build inputs, so its build:inputs must list the same inputs`, + ).toEqual(tasks.build?.inputs); } }); From a61edee9ab13e081af5fab027b83b8c43465d770 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Tue, 3 Nov 2026 23:04:21 -0700 Subject: [PATCH 16/17] ci(repo): simplify integration runner and its test --- integration/scripts/run-integration.mjs | 6 +++++- scripts/run-integration.test.mjs | 18 +++--------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/integration/scripts/run-integration.mjs b/integration/scripts/run-integration.mjs index baf3dbe8667..b9fa8807672 100644 --- a/integration/scripts/run-integration.mjs +++ b/integration/scripts/run-integration.mjs @@ -15,7 +15,11 @@ if (suite) { prepareCiRunner(suite); } -run(suite === 'electron' ? 'xvfb-run' : command, suite === 'electron' ? ['-a', command, ...args] : args); +if (suite === 'electron') { + run('xvfb-run', ['-a', command, ...args]); +} else { + run(command, args); +} function prepareCiRunner(suite) { const turboArgs = required('TURBO_ARGS').split(/\s+/).filter(Boolean); diff --git a/scripts/run-integration.test.mjs b/scripts/run-integration.test.mjs index c0212233482..cf37d1c28ef 100644 --- a/scripts/run-integration.test.mjs +++ b/scripts/run-integration.test.mjs @@ -1,5 +1,5 @@ import { spawnSync } from 'node:child_process'; -import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; @@ -36,20 +36,8 @@ function run(setup, fail = '', env = {}) { }, encoding: 'utf8', }); - const calls = (() => { - try { - return readFileSync(log, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse); - } catch { - return []; - } - })(); - const marker = (() => { - try { - return readFileSync(output, 'utf8'); - } catch { - return ''; - } - })(); + const calls = existsSync(log) ? readFileSync(log, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse) : []; + const marker = existsSync(output) ? readFileSync(output, 'utf8') : ''; rmSync(dir, { recursive: true, force: true }); return { result, calls, marker }; } From 2dbd626788a6bbf61ae52f20eaf7f477f16ce5a6 Mon Sep 17 00:00:00 2001 From: austincalvelage Date: Wed, 4 Nov 2026 00:03:58 -0700 Subject: [PATCH 17/17] ci(repo): include snapshots in test cache inputs --- packages/clerk-js/turbo.json | 2 +- packages/expo/turbo.json | 1 - packages/react/turbo.json | 1 - packages/ui/turbo.json | 1 - turbo.json | 11 +---------- 5 files changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/clerk-js/turbo.json b/packages/clerk-js/turbo.json index 7f03cd5639a..8f07a93e244 100644 --- a/packages/clerk-js/turbo.json +++ b/packages/clerk-js/turbo.json @@ -58,9 +58,9 @@ "tsconfig.json", "tsconfig.*.json", "tsup.config.ts", + "vitest.*.mts", "webpack.config.js", - "!**/__snapshots__/**", "!coverage/**", "!examples/**", "!node_modules/**" diff --git a/packages/expo/turbo.json b/packages/expo/turbo.json index de856a782d7..da404316192 100644 --- a/packages/expo/turbo.json +++ b/packages/expo/turbo.json @@ -21,7 +21,6 @@ "$TURBO_ROOT$/packages/expo-native-components/android/**", "$TURBO_ROOT$/packages/expo-native-components/ios/**", "!$TURBO_ROOT$/packages/expo-*/android/build/**", - "!**/__snapshots__/**", "!CHANGELOG.md", "!coverage/**", "!dist/**", diff --git a/packages/react/turbo.json b/packages/react/turbo.json index 71a56e92f14..cf090c6aa32 100644 --- a/packages/react/turbo.json +++ b/packages/react/turbo.json @@ -12,7 +12,6 @@ "tsconfig.json", "tsconfig.*.json", "tsup.config.ts", - "!**/__snapshots__/**", "!CHANGELOG.md", "!coverage/**", "!dist/**", diff --git a/packages/ui/turbo.json b/packages/ui/turbo.json index addda889245..c44707a73b3 100644 --- a/packages/ui/turbo.json +++ b/packages/ui/turbo.json @@ -59,7 +59,6 @@ "rspack.config.js", "vitest.*.mts", - "!**/__snapshots__/**", "!coverage/**", "!examples/**", "!node_modules/**" diff --git a/turbo.json b/turbo.json index c9218e07808..1c13fd074a5 100644 --- a/turbo.json +++ b/turbo.json @@ -4,15 +4,7 @@ "signature": true }, "ui": "tui", - "globalDependencies": [ - ".github/.cache-version", - ".npmrc", - "package.json", - "pnpm-lock.yaml", - "pnpm-workspace.yaml", - "tsconfig.json", - "tsconfig.*.json" - ], + "globalDependencies": [".github/.cache-version", ".npmrc", "pnpm-workspace.yaml", "tsconfig.json", "tsconfig.*.json"], "globalEnv": [ "CLERK_*", "EXPO_PUBLIC_CLERK_*", @@ -152,7 +144,6 @@ "tsdown.config.mts", "tsdown.config.ts", "tsup.config.ts", - "!**/__snapshots__/**", "!CHANGELOG.md", "!coverage/**", "!dist/**",