1+ import { createClerkClient } from '@clerk/backend' ;
12import type { Clerk , SignOutOptions } from '@clerk/types' ;
23import type { Page } from '@playwright/test' ;
34
@@ -15,36 +16,55 @@ type PlaywrightClerkLoadedParams = {
1516 page : Page ;
1617} ;
1718
19+ type PlaywrightClerkSignInParamsWithEmail = {
20+ page : Page ;
21+ emailAddress : string ;
22+ setupClerkTestingTokenOptions ?: SetupClerkTestingTokenOptions ;
23+ } ;
24+
1825type ClerkHelperParams = {
1926 /**
20- * Signs in a user using Clerk. This helper supports only password, phone_code and email_code first factor strategies.
27+ * Signs in a user using Clerk. This helper supports multiple sign-in strategies:
28+ * 1. Using signInParams object (password, phone_code, email_code strategies)
29+ * 2. Using emailAddress for automatic ticket-based sign-in
30+ *
2131 * Multi-factor is not supported.
2232 * This helper is using the `setupClerkTestingToken` internally.
2333 * It is required to call `page.goto` before calling this helper, and navigate to a not protected page that loads Clerk.
2434 *
35+ * For strategy-based sign-in:
2536 * If the strategy is password, the helper will sign in the user using the provided password and identifier.
2637 * If the strategy is phone_code, you are required to have a user with a test phone number as an identifier (e.g. +15555550100).
2738 * 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).
2839 *
29- * @param opts.signInParams.strategy - The sign in strategy. Supported strategies are 'password', 'phone_code' and 'email_code'.
30- * @param opts.signInParams.identifier - The user's identifier. Could be a username, a phone number or an email.
31- * @param opts.signInParams.password - The user's password. Required only if the strategy is 'password'.
32- * @param opts.page - The Playwright page object.
33- * @param opts.setupClerkTestingTokenOptions - The options for the `setupClerkTestingToken` function. Optional.
40+ * For email-based sign-in:
41+ * The helper finds the user by email, creates a sign-in token using Clerk's backend API, and uses the ticket strategy.
3442 *
35- * @example
43+ * @example Strategy-based sign-in
3644 * import { clerk } from "@clerk/testing/playwright";
3745 *
38- * test("sign in", async ({ page }) => {
46+ * test("sign in with strategy ", async ({ page }) => {
3947 * await page.goto("/");
4048 * await clerk.signIn({
4149 * page,
4250 * signInParams: { strategy: 'phone_code', identifier: '+15555550100' },
4351 * });
4452 * await page.goto("/protected");
4553 * });
54+ *
55+ * @example Email-based sign-in
56+ * import { clerk } from "@clerk/testing/playwright";
57+ *
58+ * test("sign in with email", async ({ page }) => {
59+ * await page.goto("/");
60+ * await clerk.signIn({ emailAddress: "bryce@clerk .dev", page });
61+ * await page.goto("/protected");
62+ * });
4663 */
47- signIn : ( opts : PlaywrightClerkSignInParams ) => Promise < void > ;
64+ signIn : {
65+ ( opts : PlaywrightClerkSignInParams ) : Promise < void > ;
66+ ( opts : PlaywrightClerkSignInParamsWithEmail ) : Promise < void > ;
67+ } ;
4868 /**
4969 * Signs out the current user using Clerk.
5070 * It is required to call `page.goto` before calling this helper, and navigate to a page that loads Clerk.
@@ -87,16 +107,56 @@ type PlaywrightClerkSignInParams = {
87107 setupClerkTestingTokenOptions ?: SetupClerkTestingTokenOptions ;
88108} ;
89109
90- const signIn = async ( { page , signInParams , setupClerkTestingTokenOptions } : PlaywrightClerkSignInParams ) => {
91- const context = page . context ( ) ;
110+ const signIn = async ( opts : PlaywrightClerkSignInParams | PlaywrightClerkSignInParamsWithEmail ) => {
111+ const context = opts . page . context ( ) ;
92112 if ( ! context ) {
93113 throw new Error ( 'Page context is not available. Make sure the page is properly initialized.' ) ;
94114 }
95115
96- await setupClerkTestingToken ( { context, options : setupClerkTestingTokenOptions } ) ;
97- await loaded ( { page } ) ;
116+ await setupClerkTestingToken ( {
117+ context,
118+ options : 'setupClerkTestingTokenOptions' in opts ? opts . setupClerkTestingTokenOptions : undefined ,
119+ } ) ;
120+ await loaded ( { page : opts . page } ) ;
121+
122+ if ( 'emailAddress' in opts ) {
123+ // Email-based sign-in using ticket strategy
124+ const { emailAddress, page } = opts ;
125+
126+ const secretKey = process . env . CLERK_SECRET_KEY ;
127+ if ( ! secretKey ) {
128+ throw new Error ( 'CLERK_SECRET_KEY environment variable is required for email-based sign-in' ) ;
129+ }
130+
131+ const clerkClient = createClerkClient ( { secretKey } ) ;
98132
99- await page . evaluate ( signInHelper , { signInParams } ) ;
133+ try {
134+ // Find user by email
135+ const userList = await clerkClient . users . getUserList ( { emailAddress : [ emailAddress ] } ) ;
136+ if ( ! userList . data || userList . data . length === 0 ) {
137+ throw new Error ( `No user found with email: ${ emailAddress } ` ) ;
138+ }
139+
140+ const user = userList . data [ 0 ] ;
141+
142+ const signInToken = await clerkClient . signInTokens . createSignInToken ( {
143+ userId : user . id ,
144+ expiresInSeconds : 300 , // 5 minutes
145+ } ) ;
146+
147+ await page . evaluate ( signInHelper , {
148+ signInParams : { strategy : 'ticket' as const , ticket : signInToken . token } ,
149+ } ) ;
150+
151+ await page . waitForFunction ( ( ) => window . Clerk ?. user !== null ) ;
152+ } catch ( err : any ) {
153+ throw new Error ( `Failed to sign in with email ${ emailAddress } : ${ err ?. message } ` ) ;
154+ }
155+ } else {
156+ // Strategy-based sign-in: signIn(opts)
157+ const { page, signInParams } = opts ;
158+ await page . evaluate ( signInHelper , { signInParams } ) ;
159+ }
100160} ;
101161
102162type PlaywrightClerkSignOutParams = {
@@ -113,7 +173,7 @@ const signOut = async ({ page, signOutOptions }: PlaywrightClerkSignOutParams) =
113173} ;
114174
115175export const clerk : ClerkHelperParams = {
116- signIn,
176+ signIn : signIn as ClerkHelperParams [ 'signIn' ] ,
117177 signOut,
118178 loaded,
119179} ;
0 commit comments