Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
908f6b3
feat(integration): add E2E_STAGING flag for staging env auto-swap
jacekradko Mar 13, 2026
b2daf97
ci: temporarily allow jacek/* branches in e2e-staging ref validation
jacekradko Mar 13, 2026
4526919
fix(integration): return empty instead of throwing when no staging ap…
jacekradko Mar 13, 2026
fbc6171
fix(integration): use marker instead of null for missing staging keys
jacekradko Mar 13, 2026
bfc0234
fix(integration): use CLERK_API_URL presence instead of env var marker
jacekradko Mar 13, 2026
478a85a
fix(ci): revert temporary jacek/* ref allowlist in e2e-staging
jacekradko Mar 13, 2026
b099c4f
fix: address code review feedback from PR #8060
jacekradko Mar 13, 2026
d41ca3c
fix: format files with prettier
jacekradko Mar 13, 2026
53a7c0c
fix(e2e): guard against missing sk in handshake test mock JWKS server
jacekradko Mar 13, 2026
8e2c064
chore: add empty changeset
jacekradko Mar 13, 2026
664d80e
feat(ci): add 6 staging test suites to e2e-staging workflow matrix
jacekradko Mar 13, 2026
f7bdc57
feat(ci): add fastify turbo tasks and staging script
jacekradko Mar 13, 2026
daee773
refactor(ci): remove duplicate staging scripts, add INTEGRATION_STAGI…
jacekradko Mar 13, 2026
acd0a0c
Merge branch 'main' into jacek/staging-env-swap
jacekradko Mar 13, 2026
4bb2d27
Merge branch 'main' into jacek/staging-env-swap
jacekradko Mar 13, 2026
76c8d09
Merge branch 'main' into jacek/staging-env-swap
jacekradko Mar 13, 2026
ed9dc5f
fix(e2e): skip broken cache-components tests
jacekradko Mar 13, 2026
61dd852
Merge branch 'main' into jacek/staging-env-swap
jacekradko Mar 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/staging-env-swap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
41 changes: 25 additions & 16 deletions .github/workflows/e2e-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ on:
workflow_dispatch:
inputs:
ref:
description: "Branch to test against"
description: 'Branch to test against'
required: false
default: "main"
default: 'main'
type: string
clerk-go-commit-sha:
description: "clerk_go commit SHA for status reporting"
description: 'clerk_go commit SHA for status reporting'
required: false
type: string
sdk-source:
description: "SDK source: 'latest' uses published @latest from npm, 'ref' builds from the checked-out branch"
required: false
default: "latest"
default: 'latest'
type: choice
options:
- latest
- ref
notify-slack:
description: "Send Slack notification on failure"
description: 'Send Slack notification on failure'
required: false
default: true
type: boolean
Expand All @@ -39,7 +39,7 @@ concurrency:
jobs:
integration-tests:
name: Integration Tests (${{ matrix.test-name }}, ${{ matrix.test-project }})
runs-on: "blacksmith-8vcpu-ubuntu-2204"
runs-on: 'blacksmith-8vcpu-ubuntu-2204'
defaults:
run:
shell: bash
Expand All @@ -49,9 +49,16 @@ jobs:
fail-fast: false
matrix:
test-name:
- "sessions:staging"
- "handshake:staging"
test-project: ["chrome"]
- 'sessions:staging'
- 'handshake:staging'
- 'generic'
- 'cache-components'
- 'express'
- 'hono'
- 'quickstart'
- 'react-router'
- 'tanstack-react-start'
test-project: ['chrome']

steps:
- name: Normalize inputs
Expand Down Expand Up @@ -96,7 +103,7 @@ jobs:
ref: ${{ steps.inputs.outputs.ref }}
fetch-depth: 1
fetch-tags: false
filter: "blob:none"
filter: 'blob:none'
show-progress: false

- name: Setup
Expand Down Expand Up @@ -164,8 +171,8 @@ jobs:
- name: Write all ENV certificates to files in integration/certs
uses: actions/github-script@v7
env:
INTEGRATION_CERTS: "${{ secrets.INTEGRATION_CERTS }}"
INTEGRATION_ROOT_CA: "${{ secrets.INTEGRATION_ROOT_CA }}"
INTEGRATION_CERTS: '${{ secrets.INTEGRATION_CERTS }}'
INTEGRATION_ROOT_CA: '${{ secrets.INTEGRATION_ROOT_CA }}'
with:
script: |
const fs = require('fs');
Expand All @@ -186,14 +193,16 @@ jobs:
timeout-minutes: 25
run: pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS
env:
E2E_DEBUG: "1"
E2E_DEBUG: '1'
E2E_STAGING: '1'
E2E_SDK_SOURCE: ${{ steps.inputs.outputs.sdk-source }}
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_CLERK_JS_VERSION: 'latest'
E2E_CLERK_UI_VERSION: 'latest'
E2E_PROJECT: ${{ matrix.test-project }}
INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }}
INTEGRATION_STAGING_INSTANCE_KEYS: ${{ secrets.INTEGRATION_STAGING_INSTANCE_KEYS }}
NODE_EXTRA_CA_CERTS: ${{ github.workspace }}/integration/certs/rootCA.pem

- name: Upload test-results
Expand All @@ -208,7 +217,7 @@ jobs:
name: Report Results
needs: [integration-tests]
if: always()
runs-on: "blacksmith-8vcpu-ubuntu-2204"
runs-on: 'blacksmith-8vcpu-ubuntu-2204'
defaults:
run:
shell: bash
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ playground/*/yarn.lock

# integration testing
.keys.json
.keys.staging.json
.env.json
.temp_integration
playwright-report
Expand Down
1 change: 1 addition & 0 deletions integration/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,5 @@ export const constants = {
* PK and SK pairs from the env to use for integration tests.
*/
INTEGRATION_INSTANCE_KEYS: process.env.INTEGRATION_INSTANCE_KEYS,
INTEGRATION_STAGING_INSTANCE_KEYS: process.env.INTEGRATION_STAGING_INSTANCE_KEYS,
} as const;
100 changes: 100 additions & 0 deletions integration/presets/__tests__/longRunningApps.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

// Create a Proxy that returns a mock object for any property access (nested)
const deepProxy = (): any =>
new Proxy(
{},
{
get: () => ({}),
},
);

// Mock all preset modules to avoid loading real configs
vi.mock('../astro', () => ({ astro: deepProxy() }));
vi.mock('../expo', () => ({ expo: deepProxy() }));
vi.mock('../express', () => ({ express: deepProxy() }));
vi.mock('../hono', () => ({ hono: deepProxy() }));
vi.mock('../next', () => ({ next: deepProxy() }));
vi.mock('../nuxt', () => ({ nuxt: deepProxy() }));
vi.mock('../react', () => ({ react: deepProxy() }));
vi.mock('../react-router', () => ({ reactRouter: deepProxy() }));
vi.mock('../tanstack', () => ({ tanstack: deepProxy() }));
vi.mock('../vue', () => ({ vue: deepProxy() }));

// Mock longRunningApplication to pass through config as-is
vi.mock('../../models/longRunningApplication', () => ({
longRunningApplication: (params: any) => ({ id: params.id, env: params.env }),
}));

// Mock envs — use a Proxy so any envs.* property returns a unique mock env
const mockIsStagingReady = vi.fn(() => true);
vi.mock('../envs', () => {
const envProxy = new Proxy(
{},
{
get: (_target, prop: string) => ({ __mockEnvId: prop }),
},
);
return {
envs: envProxy,
isStagingReady: (...args: any[]) => mockIsStagingReady(...args),
};
});

describe('createLongRunningApps', () => {
let createLongRunningApps: typeof import('../longRunningApps').createLongRunningApps;

beforeEach(async () => {
vi.resetModules();
mockIsStagingReady.mockImplementation(() => true);
const mod = await import('../longRunningApps');
createLongRunningApps = mod.createLongRunningApps;
});

afterEach(() => {
delete process.env.E2E_STAGING;
});

describe('getByPattern', () => {
it('returns matching apps for a valid exact pattern', () => {
const apps = createLongRunningApps();
const result = apps.getByPattern(['react.vite.withEmailCodes']);
expect(result).toHaveLength(1);
expect(result[0].id).toBe('react.vite.withEmailCodes');
});

it('returns matching apps for a valid glob pattern', () => {
const apps = createLongRunningApps();
const result = apps.getByPattern(['react.vite.*']);
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result.every((r: any) => r.id.startsWith('react.vite.'))).toBe(true);
});

it('throws for an invalid pattern (typo) in normal mode', () => {
const apps = createLongRunningApps();
expect(() => apps.getByPattern(['react.vite.withEmailCodez'])).toThrow(/Could not find long running app with id/);
});

it('throws for an invalid pattern (typo) even when E2E_STAGING=1', () => {
process.env.E2E_STAGING = '1';
const apps = createLongRunningApps();
expect(() => apps.getByPattern(['react.vite.withEmailCodez'])).toThrow(/Could not find long running app with id/);
});

it('returns [] for a known app filtered by isStagingReady when E2E_STAGING=1', () => {
process.env.E2E_STAGING = '1';
// Filter out all apps (simulates no staging keys)
mockIsStagingReady.mockImplementation(() => false);
const apps = createLongRunningApps();
const result = apps.getByPattern(['react.vite.withEmailCodes']);
expect(result).toEqual([]);
});

it('throws for a known app filtered by isStagingReady without E2E_STAGING', () => {
// Filter out all apps
mockIsStagingReady.mockImplementation(() => false);
const apps = createLongRunningApps();
expect(() => apps.getByPattern(['react.vite.withEmailCodes'])).toThrow(/Could not find long running app with id/);
});
});
});
Loading
Loading