UNPKG

@clerk/testing

Version:

Utilities to help you create E2E test suites for apps using Clerk

1 lines 9.68 kB
{"version":3,"sources":["../../src/playwright/setup.ts","../../src/playwright/setupClerkTestingToken.ts","../../src/playwright/helpers.ts"],"sourcesContent":["import type { ClerkSetupOptions } from '../common';\nimport { fetchEnvVars } from '../common';\n\n/**\n * Sets up Clerk for testing by fetching the testing token from the Clerk Backend API.\n *\n * @param options.publishableKey - The publishable key for your Clerk dev instance.\n * @param options.frontendApiUrl - The frontend API URL for your Clerk dev instance, without the protocol. It overrides the Frontend API URL parsed from the publishable key.\n * @param options.debug - Enable debug logs.\n * @returns A promise that resolves when Clerk is set up.\n *\n * @throws An error if the publishable key or the secret key is not provided.\n * @throws An error if the secret key is from a production instance.\n * @throws An error if the testing token cannot be fetched from the Clerk Backend API.\n */\nexport const clerkSetup = async (options?: ClerkSetupOptions) => {\n const { CLERK_FAPI, CLERK_TESTING_TOKEN } = await fetchEnvVars(options);\n process.env.CLERK_FAPI = CLERK_FAPI;\n process.env.CLERK_TESTING_TOKEN = CLERK_TESTING_TOKEN;\n};\n","import type { Page } from '@playwright/test';\n\nimport type { SetupClerkTestingTokenOptions } from '../common';\nimport { ERROR_MISSING_FRONTEND_API_URL, TESTING_TOKEN_PARAM } from '../common';\n\ntype SetupClerkTestingTokenParams = {\n page: Page;\n options?: SetupClerkTestingTokenOptions;\n};\n\n/**\n * Bypasses bot protection by appending the testing token in the Frontend API requests.\n *\n * @param params.page - The Playwright page object.\n * @param params.options.frontendApiUrl - The frontend API URL for your Clerk dev instance, without the protocol.\n * @returns A promise that resolves when the bot protection bypass is set up.\n * @throws An error if the Frontend API URL is not provided.\n * @example\n * import { setupClerkTestingToken } from '@clerk/testing/playwright';\n *\n * test('should bypass bot protection', async ({ page }) => {\n * await setupClerkTestingToken({ page });\n * await page.goto('https://your-app.com');\n * // Continue with your test...\n * });\n */\nexport const setupClerkTestingToken = async ({ page, options }: SetupClerkTestingTokenParams) => {\n const fapiUrl = options?.frontendApiUrl || process.env.CLERK_FAPI;\n if (!fapiUrl) {\n throw new Error(ERROR_MISSING_FRONTEND_API_URL);\n }\n const apiUrl = `https://${fapiUrl}/v1/**/*`;\n\n await page.route(apiUrl, async route => {\n const originalUrl = new URL(route.request().url());\n const testingToken = process.env.CLERK_TESTING_TOKEN;\n\n if (testingToken) {\n originalUrl.searchParams.set(TESTING_TOKEN_PARAM, testingToken);\n }\n\n try {\n const response = await route.fetch({\n url: originalUrl.toString(),\n });\n\n const json = await response.json();\n\n // Override captcha_bypass in /v1/client\n if (json?.response?.captcha_bypass === false) {\n json.response.captcha_bypass = true;\n }\n\n // Override captcha_bypass in piggybacking\n if (json?.client?.captcha_bypass === false) {\n json.client.captcha_bypass = true;\n }\n\n await route.fulfill({\n response,\n json,\n });\n } catch {\n await route\n .continue({\n url: originalUrl.toString(),\n })\n .catch(console.error);\n }\n });\n};\n","import type { Clerk, SignOutOptions } from '@clerk/types';\nimport type { Page } from '@playwright/test';\n\nimport type { ClerkSignInParams, SetupClerkTestingTokenOptions } from '../common';\nimport { signInHelper } from '../common';\nimport { setupClerkTestingToken } from './setupClerkTestingToken';\n\ndeclare global {\n interface Window {\n Clerk: Clerk;\n }\n}\n\ntype PlaywrightClerkLoadedParams = {\n page: Page;\n};\n\ntype ClerkHelperParams = {\n /**\n * Signs in a user using Clerk. This helper supports only password, phone_code and email_code first factor strategies.\n * Multi-factor is not supported.\n * This helper is using the `setupClerkTestingToken` internally.\n * It is required to call `page.goto` before calling this helper, and navigate to a not protected page that loads Clerk.\n *\n * If the strategy is password, the helper will sign in the user using the provided password and identifier.\n * If the strategy is phone_code, you are required to have a user with a test phone number as an identifier (e.g. +15555550100).\n * If the strategy is email_code, you are required to have a user with a test email as an identifier (e.g. your_email+clerk_test@example.com).\n *\n * @param opts.signInParams.strategy - The sign in strategy. Supported strategies are 'password', 'phone_code' and 'email_code'.\n * @param opts.signInParams.identifier - The user's identifier. Could be a username, a phone number or an email.\n * @param opts.signInParams.password - The user's password. Required only if the strategy is 'password'.\n * @param opts.page - The Playwright page object.\n * @param opts.setupClerkTestingTokenOptions - The options for the `setupClerkTestingToken` function. Optional.\n *\n * @example\n * import { clerk } from \"@clerk/testing/playwright\";\n *\n * test(\"sign in\", async ({ page }) => {\n * await page.goto(\"/\");\n * await clerk.signIn({\n * page,\n * signInParams: { strategy: 'phone_code', identifier: '+15555550100' },\n * });\n * await page.goto(\"/protected\");\n * });\n */\n signIn: (opts: PlaywrightClerkSignInParams) => Promise<void>;\n /**\n * Signs out the current user using Clerk.\n * It is required to call `page.goto` before calling this helper, and navigate to a page that loads Clerk.\n * @param opts.signOutOptions - A SignOutOptions object.\n * @param opts.page - The Playwright page object.\n *\n * @example\n * import { clerk } from \"@clerk/testing/playwright\";\n *\n * test(\"sign out\", async ({ page }) => {\n * await page.goto(\"/\");\n * await clerk.signIn({\n * page,\n * signInParams: { strategy: 'phone_code', identifier: '+15555550100' },\n * });\n * await page.goto(\"/protected\");\n * await clerk.signOut({ page });\n * await page.goto(\"/protected\");\n * // should redirect to sign in page\n * });\n */\n signOut: (opts: PlaywrightClerkSignOutParams) => Promise<void>;\n /**\n * Asserts that Clerk has been loaded.\n * It is required to call `page.goto` before calling this helper, and navigate to a page that loads Clerk.\n *\n * @param opts.page - The Playwright page object.\n */\n loaded: (opts: PlaywrightClerkLoadedParams) => Promise<void>;\n};\n\nconst loaded = async ({ page }: PlaywrightClerkLoadedParams) => {\n await page.waitForFunction(() => window.Clerk !== undefined);\n await page.waitForFunction(() => window.Clerk.loaded);\n};\n\ntype PlaywrightClerkSignInParams = {\n page: Page;\n signInParams: ClerkSignInParams;\n setupClerkTestingTokenOptions?: SetupClerkTestingTokenOptions;\n};\n\nconst signIn = async ({ page, signInParams, setupClerkTestingTokenOptions }: PlaywrightClerkSignInParams) => {\n await setupClerkTestingToken({ page, options: setupClerkTestingTokenOptions });\n await loaded({ page });\n\n await page.evaluate(signInHelper, { signInParams });\n};\n\ntype PlaywrightClerkSignOutParams = {\n page: Page;\n signOutOptions?: SignOutOptions;\n};\n\nconst signOut = async ({ page, signOutOptions }: PlaywrightClerkSignOutParams) => {\n await loaded({ page });\n\n await page.evaluate(async options => {\n await window.Clerk.signOut(options);\n }, signOutOptions);\n};\n\nexport const clerk: ClerkHelperParams = {\n signIn,\n signOut,\n loaded,\n};\n"],"mappings":"+DAeO,IAAMA,EAAa,MAAOC,GAAgC,CAC/D,GAAM,CAAE,WAAAC,EAAY,oBAAAC,CAAoB,EAAI,MAAMC,EAAaH,CAAO,EACtE,QAAQ,IAAI,WAAaC,EACzB,QAAQ,IAAI,oBAAsBC,CACpC,ECOO,IAAME,EAAyB,MAAO,CAAE,KAAAC,EAAM,QAAAC,CAAQ,IAAoC,CAC/F,IAAMC,EAAUD,GAAS,gBAAkB,QAAQ,IAAI,WACvD,GAAI,CAACC,EACH,MAAM,IAAI,MAAMC,CAA8B,EAEhD,IAAMC,EAAS,WAAWF,CAAO,WAEjC,MAAMF,EAAK,MAAMI,EAAQ,MAAMC,GAAS,CACtC,IAAMC,EAAc,IAAI,IAAID,EAAM,QAAQ,EAAE,IAAI,CAAC,EAC3CE,EAAe,QAAQ,IAAI,oBAE7BA,GACFD,EAAY,aAAa,IAAIE,EAAqBD,CAAY,EAGhE,GAAI,CACF,IAAME,EAAW,MAAMJ,EAAM,MAAM,CACjC,IAAKC,EAAY,SAAS,CAC5B,CAAC,EAEKI,EAAO,MAAMD,EAAS,KAAK,EAG7BC,GAAM,UAAU,iBAAmB,KACrCA,EAAK,SAAS,eAAiB,IAI7BA,GAAM,QAAQ,iBAAmB,KACnCA,EAAK,OAAO,eAAiB,IAG/B,MAAML,EAAM,QAAQ,CAClB,SAAAI,EACA,KAAAC,CACF,CAAC,CACH,MAAQ,CACN,MAAML,EACH,SAAS,CACR,IAAKC,EAAY,SAAS,CAC5B,CAAC,EACA,MAAM,QAAQ,KAAK,CACxB,CACF,CAAC,CACH,ECQA,IAAMK,EAAS,MAAO,CAAE,KAAAC,CAAK,IAAmC,CAC9D,MAAMA,EAAK,gBAAgB,IAAM,OAAO,QAAU,MAAS,EAC3D,MAAMA,EAAK,gBAAgB,IAAM,OAAO,MAAM,MAAM,CACtD,EAQMC,EAAS,MAAO,CAAE,KAAAD,EAAM,aAAAE,EAAc,8BAAAC,CAA8B,IAAmC,CAC3G,MAAMC,EAAuB,CAAE,KAAAJ,EAAM,QAASG,CAA8B,CAAC,EAC7E,MAAMJ,EAAO,CAAE,KAAAC,CAAK,CAAC,EAErB,MAAMA,EAAK,SAASK,EAAc,CAAE,aAAAH,CAAa,CAAC,CACpD,EAOMI,EAAU,MAAO,CAAE,KAAAN,EAAM,eAAAO,CAAe,IAAoC,CAChF,MAAMR,EAAO,CAAE,KAAAC,CAAK,CAAC,EAErB,MAAMA,EAAK,SAAS,MAAMQ,GAAW,CACnC,MAAM,OAAO,MAAM,QAAQA,CAAO,CACpC,EAAGD,CAAc,CACnB,EAEaE,EAA2B,CACtC,OAAAR,EACA,QAAAK,EACA,OAAAP,CACF","names":["clerkSetup","options","CLERK_FAPI","CLERK_TESTING_TOKEN","fetchEnvVars","setupClerkTestingToken","page","options","fapiUrl","ERROR_MISSING_FRONTEND_API_URL","apiUrl","route","originalUrl","testingToken","TESTING_TOKEN_PARAM","response","json","loaded","page","signIn","signInParams","setupClerkTestingTokenOptions","setupClerkTestingToken","signInHelper","signOut","signOutOptions","options","clerk"]}