@@ -14,6 +14,19 @@ interface CreateUserOptions {
1414 pluginData ?: Record < string , unknown >
1515}
1616
17+ type OAuthProvider = 'google' | 'github' | 'apple' | 'microsoft' | 'facebook' | 'twitter' | 'discord' | 'gitlab'
18+
19+ interface CreateOAuthUserOptions {
20+ /** OAuth provider to simulate (e.g. 'google', 'github') */
21+ provider : OAuthProvider
22+ email ?: string
23+ name ?: string
24+ /** Provider account ID. Auto-generated if omitted. */
25+ providerAccountId ?: string
26+ /** Plugin-specific options, keyed by plugin ID */
27+ pluginData ?: Record < string , unknown >
28+ }
29+
1730interface TestUser {
1831 id : string
1932 email : string
@@ -23,6 +36,10 @@ interface TestUser {
2336 plugins : Record < string , unknown >
2437}
2538
39+ interface TestOAuthUser extends TestUser {
40+ account : { provider : OAuthProvider , providerAccountId : string }
41+ }
42+
2643interface TestAuth {
2744 /**
2845 * Create a test user and set session cookies on the current browser context.
@@ -33,6 +50,16 @@ interface TestAuth {
3350 */
3451 createUser : ( options ?: CreateUserOptions ) => Promise < TestUser >
3552
53+ /**
54+ * Create a test OAuth user (Google, GitHub, etc) without going through the
55+ * real provider's auth flow. Uses internalAdapter.createOAuthUser, which
56+ * exercises the same database hooks as a real OAuth signup — including
57+ * databaseHooks.user.create.after AND databaseHooks.account.create.after
58+ * with the correct providerId. Use for testing OAuth-specific behavior in
59+ * your app's auth hooks.
60+ */
61+ createOAuthUser : ( options : CreateOAuthUserOptions ) => Promise < TestOAuthUser >
62+
3663 /**
3764 * Delete a test user by email. Called automatically in teardown
3865 * for all users created during the test.
@@ -44,7 +71,7 @@ interface TestAuthFixtures {
4471 auth : TestAuth
4572}
4673
47- export type { CreateUserOptions , TestAuth , TestAuthFixtures , TestUser }
74+ export type { CreateOAuthUserOptions , CreateUserOptions , OAuthProvider , TestAuth , TestAuthFixtures , TestOAuthUser , TestUser }
4875
4976/**
5077 * Create Playwright fixtures configured for your Better Auth app.
@@ -87,10 +114,33 @@ export function createTestFixtures(config: {
87114 throw new Error ( 'baseURL must be configured in Playwright' )
88115 }
89116
90- const origin = baseURL . replace ( / \/ + $ / , '' )
117+ const verifiedBaseURL = baseURL
118+ const origin = verifiedBaseURL . replace ( / \/ + $ / , '' )
91119 const created : string [ ] = [ ]
92120 const context = page . context ( )
93121
122+ // Apply Set-Cookie headers from a fetch response onto the browser context
123+ async function applyCookiesFromResponse ( res : Response ) : Promise < void > {
124+ const domain = new URL ( verifiedBaseURL ) . hostname
125+ const setCookieHeaders = res . headers . getSetCookie ( )
126+ const cookies = setCookieHeaders . map ( ( header ) => {
127+ const [ nameValue , ...attrs ] = header . split ( ';' )
128+ const [ name , ...valueParts ] = nameValue ! . split ( '=' )
129+ const value = valueParts . join ( '=' )
130+ return {
131+ name : name ! . trim ( ) ,
132+ value,
133+ domain,
134+ path : '/' ,
135+ httpOnly : attrs . some ( a => a . trim ( ) . toLowerCase ( ) === 'httponly' ) ,
136+ secure : attrs . some ( a => a . trim ( ) . toLowerCase ( ) === 'secure' ) ,
137+ }
138+ } ) . filter ( c => c . name && c . value )
139+ if ( cookies . length > 0 ) {
140+ await context . addCookies ( cookies )
141+ }
142+ }
143+
94144 const auth : TestAuth = {
95145 async createUser ( options = { } ) {
96146 const email
@@ -124,33 +174,59 @@ export function createTestFixtures(config: {
124174 plugins : Record < string , unknown >
125175 }
126176 created . push ( email )
177+ await applyCookiesFromResponse ( res )
127178
128- // Extract Set-Cookie headers from the response and set them on the browser context
129- const domain = new URL ( baseURL ) . hostname
130- const setCookieHeaders = res . headers . getSetCookie ( )
131- const cookies = setCookieHeaders . map ( ( header ) => {
132- const [ nameValue , ...attrs ] = header . split ( ';' )
133- const [ name , ...valueParts ] = nameValue ! . split ( '=' )
134- const value = valueParts . join ( '=' )
135- return {
136- name : name ! . trim ( ) ,
137- value,
138- domain,
139- path : '/' ,
140- // Preserve httpOnly/secure from original cookie attributes
141- httpOnly : attrs . some ( a => a . trim ( ) . toLowerCase ( ) === 'httponly' ) ,
142- secure : attrs . some ( a => a . trim ( ) . toLowerCase ( ) === 'secure' ) ,
143- }
144- } ) . filter ( c => c . name && c . value )
145- if ( cookies . length > 0 ) {
146- await context . addCookies ( cookies )
179+ return {
180+ id : data . user . id ,
181+ email : data . user . email ,
182+ name : data . user . name ,
183+ session : data . session ,
184+ plugins : data . plugins ,
185+ }
186+ } ,
187+
188+ async createOAuthUser ( options ) {
189+ const email
190+ = options . email
191+ ?? `test-oauth-${ crypto . randomUUID ( ) . slice ( 0 , 8 ) } @test.local`
192+
193+ const res = await fetch ( `${ origin } ${ basePath } /test-data/oauth-user` , {
194+ method : 'POST' ,
195+ headers : {
196+ 'Content-Type' : 'application/json' ,
197+ 'X-Test-Secret' : config . secret ,
198+ } ,
199+ body : JSON . stringify ( {
200+ email,
201+ name : options . name ,
202+ provider : options . provider ,
203+ providerAccountId : options . providerAccountId ,
204+ pluginData : options . pluginData ,
205+ } ) ,
206+ } )
207+
208+ if ( ! res . ok ) {
209+ const error = await res . text ( )
210+ throw new Error (
211+ `better-auth-playwright: createOAuthUser failed (${ res . status } ): ${ error } ` ,
212+ )
147213 }
148214
215+ const data = ( await res . json ( ) ) as {
216+ user : { id : string , email : string , name : string }
217+ session : { id : string , token : string }
218+ account : { provider : OAuthProvider , providerAccountId : string }
219+ plugins : Record < string , unknown >
220+ }
221+ created . push ( email )
222+ await applyCookiesFromResponse ( res )
223+
149224 return {
150225 id : data . user . id ,
151226 email : data . user . email ,
152227 name : data . user . name ,
153228 session : data . session ,
229+ account : data . account ,
154230 plugins : data . plugins ,
155231 }
156232 } ,
0 commit comments