diff --git a/.changeset/cute-berries-design.md b/.changeset/cute-berries-design.md new file mode 100644 index 0000000000..0aaa32eec5 --- /dev/null +++ b/.changeset/cute-berries-design.md @@ -0,0 +1,6 @@ +--- +'@clerk/testing': patch +--- + + +Update `setupClerk` to handle the explict setting of secret keys via `setupClerk({ secretKey: 'sk_test_***' })` \ No newline at end of file diff --git a/.changeset/rich-breads-play.md b/.changeset/rich-breads-play.md new file mode 100644 index 0000000000..c8ebf13388 --- /dev/null +++ b/.changeset/rich-breads-play.md @@ -0,0 +1,5 @@ +--- +'@clerk/testing': patch +--- + +Update `setupClerk` to enable skipping of the automatic loading of dot env files via `setupClerk({ dotenv: false })` \ No newline at end of file diff --git a/integration/models/longRunningApplication.ts b/integration/models/longRunningApplication.ts index 605266dbd9..52c4ac4b6d 100644 --- a/integration/models/longRunningApplication.ts +++ b/integration/models/longRunningApplication.ts @@ -1,3 +1,6 @@ +import { parsePublishableKey } from '@clerk/shared/keys'; +import { clerkSetup } from '@clerk/testing/playwright'; + import { awaitableTreekill, fs } from '../scripts'; import type { Application } from './application'; import type { ApplicationConfig } from './applicationConfig'; @@ -56,6 +59,25 @@ export const longRunningApplication = (params: LongRunningApplicationParams) => // will be called by global.setup.ts and by the test runner // the first time this is called, the app starts and the state is persisted in the state file init: async () => { + try { + const publishableKey = params.env.publicVariables.get('CLERK_PUBLISHABLE_KEY'); + const secretKey = params.env.privateVariables.get('CLERK_SECRET_KEY'); + const { instanceType, frontendApi: frontendApiUrl } = parsePublishableKey(publishableKey); + + if (instanceType !== 'development') { + console.log('Clerk: skipping setup of testing tokens for non-development instance'); + } else { + await clerkSetup({ + publishableKey, + frontendApiUrl, + secretKey, + dotenv: false, + }); + } + } catch (error) { + console.error('Error setting up testing tokens:', error); + throw error; + } try { app = await config.commit(); } catch (error) { diff --git a/integration/testUtils/appPageObject.ts b/integration/testUtils/appPageObject.ts index 627e231029..f27e1e1c34 100644 --- a/integration/testUtils/appPageObject.ts +++ b/integration/testUtils/appPageObject.ts @@ -1,19 +1,27 @@ +import { setupClerkTestingToken } from '@clerk/testing/playwright'; import type { Page } from '@playwright/test'; import type { Application } from '../models/application'; -export const createAppPageObject = (testArgs: { page: Page }, app: Application) => { - const { page } = testArgs; +export const createAppPageObject = (testArgs: { page: Page; useTestingToken?: boolean }, app: Application) => { + const { page, useTestingToken = true } = testArgs; const appPage = Object.create(page) as Page; const helpers = { goToAppHome: async () => { try { + if (useTestingToken) { + await setupClerkTestingToken({ page }); + } + await page.goto(app.serverUrl); } catch { // do not fail the test if interstitial is returned (401) } }, - goToRelative: (path: string, opts: { waitUntil?: any; searchParams?: URLSearchParams; timeout?: number } = {}) => { + goToRelative: async ( + path: string, + opts: { waitUntil?: any; searchParams?: URLSearchParams; timeout?: number } = {}, + ) => { let url: URL; try { @@ -35,6 +43,11 @@ export const createAppPageObject = (testArgs: { page: Page }, app: Application) if (opts.searchParams) { url.search = opts.searchParams.toString(); } + + if (useTestingToken) { + await setupClerkTestingToken({ page }); + } + return page.goto(url.toString(), { timeout: opts.timeout ?? 20000, waitUntil: opts.waitUntil }); }, waitForClerkJsLoaded: async () => { diff --git a/integration/testUtils/index.ts b/integration/testUtils/index.ts index d04da00cc2..82d3128269 100644 --- a/integration/testUtils/index.ts +++ b/integration/testUtils/index.ts @@ -1,4 +1,5 @@ import { createClerkClient as backendCreateClerkClient } from '@clerk/backend'; +import { setupClerkTestingToken } from '@clerk/testing/playwright'; import type { Browser, BrowserContext, Page, Response } from '@playwright/test'; import { expect } from '@playwright/test'; @@ -75,55 +76,65 @@ const createClerkUtils = ({ page }: TestArgs) => { }; }; +const createTestingTokenUtils = ({ page }: TestArgs) => { + return { + setup: async () => setupClerkTestingToken({ page }), + }; +}; + export type CreateAppPageObjectArgs = { page: Page; context: BrowserContext; browser: Browser }; export const createTestUtils = < - Params extends { app: Application } & Partial, + Params extends { app: Application; useTestingToken?: boolean } & Partial, Services = typeof services, PO = typeof pageObjects, BH = typeof browserHelpers, - FullReturn = { services: Services; po: PO; tabs: BH; page: EnchancedPage; nexJsVersion: string }, + FullReturn = { services: Services; po: PO; tabs: BH; page: EnchancedPage; nextJsVersion: string }, OnlyAppReturn = { services: Services }, >( params: Params, ): Params extends Partial ? FullReturn : OnlyAppReturn => { - const { app, context, browser } = params || {}; + const { app, context, browser, useTestingToken = true } = params || {}; const clerkClient = createClerkClient(app); const services = { + clerk: clerkClient, email: createEmailService(), users: createUserService(clerkClient), invitations: createInvitationService(clerkClient), organizations: createOrganizationsService(clerkClient), - clerk: clerkClient, }; if (!params.page) { return { services } as any; } - const page = createAppPageObject({ page: params.page }, app); + const page = createAppPageObject({ page: params.page, useTestingToken }, app); const testArgs = { page, context, browser }; const pageObjects = { + clerk: createClerkUtils(testArgs), + expect: createExpectPageObject(testArgs), keylessPopover: createKeylessPopoverPageObject(testArgs), - signUp: createSignUpComponentPageObject(testArgs), - signIn: createSignInComponentPageObject(testArgs), - userProfile: createUserProfileComponentPageObject(testArgs), organizationSwitcher: createOrganizationSwitcherComponentPageObject(testArgs), + sessionTask: createSessionTaskComponentPageObject(testArgs), + signIn: createSignInComponentPageObject(testArgs), + signUp: createSignUpComponentPageObject(testArgs), + testingToken: createTestingTokenUtils(testArgs), userButton: createUserButtonPageObject(testArgs), + userProfile: createUserProfileComponentPageObject(testArgs), userVerification: createUserVerificationComponentPageObject(testArgs), waitlist: createWaitlistComponentPageObject(testArgs), - sessionTask: createSessionTaskComponentPageObject(testArgs), - expect: createExpectPageObject(testArgs), - clerk: createClerkUtils(testArgs), }; const browserHelpers = { runInNewTab: async ( cb: (u: { services: Services; po: PO; page: EnchancedPage }, context: BrowserContext) => Promise, ) => { - const u = createTestUtils({ app, page: createAppPageObject({ page: await context.newPage() }, app) }); + const u = createTestUtils({ + app, + page: createAppPageObject({ page: await context.newPage(), useTestingToken }, app), + }); await cb(u as any, context); return u; }, @@ -134,7 +145,10 @@ export const createTestUtils = < throw new Error('Browser is not defined. Did you forget to pass it to createPageObjects?'); } const context = await browser.newContext(); - const u = createTestUtils({ app, page: createAppPageObject({ page: await context.newPage() }, app) }); + const u = createTestUtils({ + app, + page: createAppPageObject({ page: await context.newPage(), useTestingToken }, app), + }); await cb(u as any, context); return u; }, @@ -146,7 +160,7 @@ export const createTestUtils = < po: pageObjects, tabs: browserHelpers, // eslint-disable-next-line turbo/no-undeclared-env-vars - nexJsVersion: process.env.E2E_NEXTJS_VERSION, + nextJsVersion: process.env.E2E_NEXTJS_VERSION, } as any; }; diff --git a/integration/tests/email-link.test.ts b/integration/tests/email-link.test.ts index 67d029aca0..0f29aa6fe1 100644 --- a/integration/tests/email-link.test.ts +++ b/integration/tests/email-link.test.ts @@ -87,6 +87,8 @@ const performSignUpVerificationLinkSameDevice = async ( await u.po.signUp.waitForEmailVerificationScreen(); await u.tabs.runInNewTab(async u => { const verificationLink = await u.services.email.getVerificationLinkForEmailAddress(fakeUser.email); + + await u.po.testingToken.setup(); await u.page.goto(verificationLink); await u.po.expect.toBeSignedIn(); await u.page.close(); @@ -109,6 +111,8 @@ const performSignUpVerificationLinkDifferentDevice = async ( await u.po.signUp.waitForEmailVerificationScreen(); await u.tabs.runInNewBrowser(async u => { const verificationLink = await u.services.email.getVerificationLinkForEmailAddress(fakeUser.email); + + await u.po.testingToken.setup(); await u.page.goto(verificationLink); await u.po.expect.toBeSignedOut(); await u.page.pause(); diff --git a/integration/tests/localhost/localhost-different-port-different-instance.test.ts b/integration/tests/localhost/localhost-different-port-different-instance.test.ts index 717c4ae75a..418cfc08fb 100644 --- a/integration/tests/localhost/localhost-different-port-different-instance.test.ts +++ b/integration/tests/localhost/localhost-different-port-different-instance.test.ts @@ -41,8 +41,8 @@ test.describe('multiple apps running on localhost using different Clerk instance test('sessions are independent between the different apps', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; await u[0].po.signIn.goTo(); @@ -86,8 +86,8 @@ test.describe('multiple apps running on localhost using different Clerk instance test('signing out from the root domains does not affect the sub domain', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; // signin in tab0 diff --git a/integration/tests/localhost/localhost-different-port-same-instance.test.ts b/integration/tests/localhost/localhost-different-port-same-instance.test.ts index 0a9d0eccce..8fa4af1dda 100644 --- a/integration/tests/localhost/localhost-different-port-same-instance.test.ts +++ b/integration/tests/localhost/localhost-different-port-same-instance.test.ts @@ -39,8 +39,8 @@ test.describe('multiple apps running on localhost using same Clerk instance @loc test('the cookies are aligned for the root and sub domains', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; await u[0].po.signIn.goTo(); @@ -81,8 +81,8 @@ test.describe('multiple apps running on localhost using same Clerk instance @loc test('signing out from the root domain affects the sub domain', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; // sign tab0 diff --git a/integration/tests/localhost/localhost-switch-instance.test.ts b/integration/tests/localhost/localhost-switch-instance.test.ts index d89218c426..4410591860 100644 --- a/integration/tests/localhost/localhost-switch-instance.test.ts +++ b/integration/tests/localhost/localhost-switch-instance.test.ts @@ -29,7 +29,7 @@ test.describe('switching instances on localhost same port @localhost', () => { // Create app and user for the 1st app const { app } = await prepareApplication('sessions-dev-1', port); let page = await context.newPage(); - let u = createTestUtils({ app, page, context }); + let u = createTestUtils({ app, page, context, useTestingToken: false }); let fakeUser = u.services.users.createFakeUser(); fakeUsers.push(fakeUser); @@ -46,7 +46,7 @@ test.describe('switching instances on localhost same port @localhost', () => { await app.dev({ port }); page = await context.newPage(); - u = createTestUtils({ app, page, context }); + u = createTestUtils({ app, page, context, useTestingToken: false }); fakeUser = u.services.users.createFakeUser(); fakeUsers.push(fakeUser); await u.services.users.createBapiUser(fakeUser); diff --git a/integration/tests/next-account-portal/common.ts b/integration/tests/next-account-portal/common.ts index 50ad8010b7..9ce7dad158 100644 --- a/integration/tests/next-account-portal/common.ts +++ b/integration/tests/next-account-portal/common.ts @@ -16,7 +16,7 @@ type TestParams = { }; export const testSignIn = async ({ app, page, context, fakeUser }: TestParams) => { - const u = createTestUtils({ app, page, context }); + const u = createTestUtils({ app, page, context, useTestingToken: false }); // Begin in localhost await u.page.goToAppHome(); await u.page.waitForClerkJsLoaded(); @@ -79,7 +79,7 @@ export const testSignIn = async ({ app, page, context, fakeUser }: TestParams) = }; export const testSignUp = async ({ app, page, context }: TestParams) => { - const u = createTestUtils({ app, page, context }); + const u = createTestUtils({ app, page, context, useTestingToken: false }); const tempUser = u.services.users.createFakeUser({ fictionalEmail: true }); // Begin in localhost @@ -148,7 +148,7 @@ export const testSignUp = async ({ app, page, context }: TestParams) => { }; export const testSSR = async ({ app, page, context, fakeUser }: TestParams) => { - const u = createTestUtils({ app, page, context }); + const u = createTestUtils({ app, page, context, useTestingToken: false }); // Begin in localhost await u.page.goToAppHome(); diff --git a/integration/tests/non-secure-context.test.ts b/integration/tests/non-secure-context.test.ts index 0e49f7af7d..92f717c57d 100644 --- a/integration/tests/non-secure-context.test.ts +++ b/integration/tests/non-secure-context.test.ts @@ -53,6 +53,8 @@ testAgainstRunningApps({ withPattern: ['next.appRouter.withEmailCodes'] })( test('sign-in flow', async ({ page }) => { const u = createTestUtils({ app, page }); + + await u.po.testingToken.setup(); await u.page.goto(`http://${APP_HOST}`, { timeout: 50000 }); await u.po.signIn.goTo(); await u.po.signIn.signInWithEmailAndInstantPassword(fakeUser); diff --git a/integration/tests/resiliency.test.ts b/integration/tests/resiliency.test.ts index b807372128..3bb426a29d 100644 --- a/integration/tests/resiliency.test.ts +++ b/integration/tests/resiliency.test.ts @@ -81,7 +81,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('resilienc await u.po.expect.toBeSignedIn(); }); - test('resiliency to not break devBroswer - dummy client and is not created on `/client` 4xx errors', async ({ + test('resiliency to not break devBrowser - dummy client and is not created on `/client` 4xx errors', async ({ page, context, }) => { @@ -100,6 +100,9 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('resilienc clerk_trace_id: 'some-trace-id', }), }; + + const u = createTestUtils({ app, page, context, useTestingToken: false }); + await page.route('**/v1/client?**', route => { return route.fulfill(response); }); @@ -108,8 +111,6 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('resilienc return route.fulfill(response); }); - const u = createTestUtils({ app, page, context }); - const waitForClientImmediately = page.waitForResponse( response => response.url().includes('/client?') && response.status() === 401, { timeout: 3_000 }, diff --git a/integration/tests/restricted-mode.test.ts b/integration/tests/restricted-mode.test.ts index 947f8a516a..fa4fd8c7e8 100644 --- a/integration/tests/restricted-mode.test.ts +++ b/integration/tests/restricted-mode.test.ts @@ -116,6 +116,7 @@ export default function Page() { const invitation = await u.services.invitations.createBapiInvitation(invitedUser.email); + await u.po.testingToken.setup(); await u.page.goto(invitation.url); await u.po.signUp.waitForMounted(); await expect(u.page.getByText(/Create your account/i).first()).toBeVisible(); diff --git a/integration/tests/sessions/prod-app-migration.test.ts b/integration/tests/sessions/prod-app-migration.test.ts index 0ccf75b421..86660c8bb5 100644 --- a/integration/tests/sessions/prod-app-migration.test.ts +++ b/integration/tests/sessions/prod-app-migration.test.ts @@ -58,6 +58,7 @@ test.describe('root and subdomain production apps @manual-run', () => { fakeUsers.push(fakeUser); await u.services.users.createBapiUser(fakeUser); + await u.po.testingToken.setup(); await u.page.goto(`https://${host}`); await u.po.signIn.goTo({ timeout: 30000 }); await u.po.signIn.signInWithEmailAndInstantPassword(fakeUser); diff --git a/integration/tests/sessions/root-subdomain-prod-instances.test.ts b/integration/tests/sessions/root-subdomain-prod-instances.test.ts index 66b09e2874..82c673625c 100644 --- a/integration/tests/sessions/root-subdomain-prod-instances.test.ts +++ b/integration/tests/sessions/root-subdomain-prod-instances.test.ts @@ -82,8 +82,8 @@ test.describe('root and subdomain production apps @sessions', () => { test('the cookies are aligned for the root and sub domains', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; await u[0].page.goto(`https://${hosts[0]}`); @@ -138,8 +138,8 @@ test.describe('root and subdomain production apps @sessions', () => { test('signing out from the sub domains signs out the user from the root domain as well', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; await u[0].page.goto(`https://${hosts[0]}`); @@ -219,8 +219,8 @@ test.describe('root and subdomain production apps @sessions', () => { test('the cookies are independent for the root and sub domains', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; await u[0].page.goto(`https://${hosts[0]}`); @@ -246,7 +246,7 @@ test.describe('root and subdomain production apps @sessions', () => { // The client_uat cookie should always be set on etld+1 expect(tab0Cookies.get('__client_uat_*').domain).toEqual('.' + hosts[0].split(':')[0]); - u[1].po.expect.toBeHandshake(await u[1].page.goto(`https://${hosts[1]}`)); + await u[1].po.expect.toBeHandshake(await u[1].page.goto(`https://${hosts[1]}`)); await u[1].po.expect.toBeSignedOut(); expect((await u[1].page.evaluate(() => fetch('/api/me').then(r => r.json()))).userId).toBe(null); @@ -277,8 +277,8 @@ test.describe('root and subdomain production apps @sessions', () => { test('signing out from the root domains does not affect the sub domain', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; // signin in tab0 @@ -351,8 +351,8 @@ test.describe('root and subdomain production apps @sessions', () => { test('the cookies are independent for the root and sub domains', async ({ context }) => { const pages = await Promise.all([context.newPage(), context.newPage()]); const u = [ - createTestUtils({ app: apps[0].app, page: pages[0], context }), - createTestUtils({ app: apps[1].app, page: pages[1], context }), + createTestUtils({ app: apps[0].app, page: pages[0], context, useTestingToken: false }), + createTestUtils({ app: apps[1].app, page: pages[1], context, useTestingToken: false }), ]; await u[0].page.goto(`https://${hosts[0]}`); @@ -363,7 +363,7 @@ test.describe('root and subdomain production apps @sessions', () => { // make sure that the backend user now matches the user we signed in with on the client expect((await u[0].page.evaluate(() => fetch('/api/me').then(r => r.json()))).userId).toBe(tab0User.id); - u[1].po.expect.toBeHandshake(await u[1].page.goto(`https://${hosts[1]}`)); + await u[1].po.expect.toBeHandshake(await u[1].page.goto(`https://${hosts[1]}`)); await u[1].po.expect.toBeSignedOut(); expect((await u[1].page.evaluate(() => fetch('/api/me').then(r => r.json()))).userId).toBe(null); diff --git a/integration/tests/sign-in-or-up-email-links-flow.test.ts b/integration/tests/sign-in-or-up-email-links-flow.test.ts index f5c828cafe..e6950f3d31 100644 --- a/integration/tests/sign-in-or-up-email-links-flow.test.ts +++ b/integration/tests/sign-in-or-up-email-links-flow.test.ts @@ -33,6 +33,8 @@ testAgainstRunningApps({ withEnv: [] })('sign-in-or-up email links flow', ({ app await u.po.signUp.waitForEmailVerificationScreen(); await u.tabs.runInNewTab(async u => { const verificationLink = await u.services.email.getVerificationLinkForEmailAddress(fakeUser.email); + + await u.po.testingToken.setup(); await u.page.goto(verificationLink); await u.po.expect.toBeSignedIn(); await u.page.close(); @@ -52,6 +54,7 @@ testAgainstRunningApps({ withEnv: [] })('sign-in-or-up email links flow', ({ app await page.getByRole('heading', { name: /Check your email/i }).waitFor(); await u.tabs.runInNewTab(async u => { const verificationLink = await u.services.email.getVerificationLinkForEmailAddress(fakeUser.email); + await u.po.testingToken.setup(); await u.page.goto(verificationLink); await u.po.expect.toBeSignedIn(); await u.page.close(); diff --git a/integration/tests/waitlist-mode.test.ts b/integration/tests/waitlist-mode.test.ts index a494c44284..3880f898c4 100644 --- a/integration/tests/waitlist-mode.test.ts +++ b/integration/tests/waitlist-mode.test.ts @@ -129,6 +129,7 @@ test.describe('Waitlist mode', () => { const invitation = await u.services.invitations.createBapiInvitation(invitedUser.email); + await u.po.testingToken.setup(); await u.page.goto(invitation.url); await u.po.signUp.waitForMounted(); await expect(u.page.getByText(/Create your account/i).first()).toBeVisible(); diff --git a/package.json b/package.json index 372aa92fb7..807a08937f 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@changesets/get-github-info": "^0.6.0", "@clerk/backend": "workspace:*", "@clerk/shared": "workspace:*", + "@clerk/testing": "workspace:*", "@commitlint/cli": "^19.7.1", "@commitlint/config-conventional": "^19.7.1", "@emotion/jest": "^11.13.0", diff --git a/packages/testing/src/common/setup.ts b/packages/testing/src/common/setup.ts index d6486ccaa4..90397df525 100644 --- a/packages/testing/src/common/setup.ts +++ b/packages/testing/src/common/setup.ts @@ -5,24 +5,29 @@ import dotenv from 'dotenv'; import type { ClerkSetupOptions, ClerkSetupReturn } from './types'; export const fetchEnvVars = async (options?: ClerkSetupOptions): Promise => { + const { debug = false, dotenv: loadDotEnv = true, ...rest } = options || {}; + const log = (msg: string) => { - if (options?.debug) { + if (debug) { console.log(`Clerk: ${msg}`); } }; log('Setting up Clerk...'); - dotenv.config({ path: ['.env.local', '.env'] }); + + if (loadDotEnv) { + dotenv.config({ path: ['.env.local', '.env'] }); + } const publishableKey = - options?.publishableKey || + rest.publishableKey || process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || process.env.VITE_CLERK_PUBLISHABLE_KEY || process.env.CLERK_PUBLISHABLE_KEY || process.env.REACT_APP_CLERK_PUBLISHABLE_KEY || process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY; - const secretKey = process.env.CLERK_SECRET_KEY; + const secretKey = rest.secretKey || process.env.CLERK_SECRET_KEY; let testingToken = process.env.CLERK_TESTING_TOKEN; if (!publishableKey) { diff --git a/packages/testing/src/common/types.ts b/packages/testing/src/common/types.ts index 621162f193..fc86f5d129 100644 --- a/packages/testing/src/common/types.ts +++ b/packages/testing/src/common/types.ts @@ -1,4 +1,16 @@ export type ClerkSetupOptions = { + /* + * Enable debug mode. + */ + debug?: boolean; + + /* + * The frontend API URL for your Clerk dev instance, without the protocol. + * If provided, it overrides the Frontend API URL parsed from the publishable key. + * Example: 'relieved-chamois-66.clerk.accounts.dev' + */ + frontendApiUrl?: string; + /* * The publishable key for your Clerk dev instance. * If not provided, the library will look for the key in the following environment variables: @@ -9,16 +21,19 @@ export type ClerkSetupOptions = { * - EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY */ publishableKey?: string; + /* - * The frontend API URL for your Clerk dev instance, without the protocol. - * If provided, it overrides the Frontend API URL parsed from the publishable key. - * Example: 'relieved-chamois-66.clerk.accounts.dev' + * The secret key for your Clerk dev instance. + * If not provided, the library will look for the key in the following environment variables: + * - CLERK_SECRET_KEY */ - frontendApiUrl?: string; + secretKey?: string; + /* - * Enable debug mode. + * Automatic loading of environment variables from .env files. + * Default: true */ - debug?: boolean; + dotenv?: boolean; }; export type ClerkSetupReturn = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a3b8316a6..6f18159151 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,6 +70,9 @@ importers: '@clerk/shared': specifier: workspace:* version: link:packages/shared + '@clerk/testing': + specifier: workspace:* + version: link:packages/testing '@commitlint/cli': specifier: ^19.7.1 version: 19.7.1(@types/node@22.14.0)(typescript@5.8.2)