@@ -9,35 +9,54 @@ async function setupWebAuthn(page: any) {
99 const result = await client . send ( 'WebAuthn.addVirtualAuthenticator' , {
1010 options : {
1111 protocol : 'ctap2' ,
12- transport : 'internal' ,
12+ transport : 'usb' ,
13+ hasResidentKey : true ,
1314 hasUserVerification : true ,
1415 isUserVerified : true ,
16+ automaticPresenceSimulation : true ,
1517 } ,
1618 } )
1719 return { client, authenticatorId : result . authenticatorId }
1820}
1921
22+ async function waitOnce (
23+ client : CDPSession ,
24+ event : Parameters < CDPSession [ 'once' ] > [ 0 ] ,
25+ ) {
26+ let resolve : ( ) => void
27+ client . once ( event , ( ) => resolve ( ) )
28+ return new Promise < void > ( ( r ) => {
29+ resolve = r
30+ } )
31+ }
32+
2033async function simulateSuccessfulPasskeyInput (
2134 client : CDPSession ,
22- authenticatorId : string ,
2335 operationTrigger : ( ) => Promise < void > ,
2436) {
2537 // initialize event listeners to wait for a successful passkey input event
26- const operationCompleted = new Promise < void > ( ( resolve ) => {
27- client . on ( 'WebAuthn.credentialAdded' , ( ) => resolve ( ) )
28- client . on ( 'WebAuthn.credentialAsserted' , ( ) => resolve ( ) )
38+ let resolve : ( ) => void
39+ const credentialAddedHandler = ( ) => resolve ( )
40+ const credentialAssertedHandler = ( ) => resolve ( )
41+ const operationCompleted = new Promise < void > ( ( r ) => {
42+ resolve = r
43+ client . on ( 'WebAuthn.credentialAdded' , credentialAddedHandler )
44+ client . on ( 'WebAuthn.credentialAsserted' , credentialAssertedHandler )
2945 } )
3046
3147 // perform a user action that triggers passkey prompt
3248 await operationTrigger ( )
3349
3450 // wait to receive the event that the passkey was successfully registered or verified
3551 await operationCompleted
52+
53+ // clean up event listeners
54+ client . off ( 'WebAuthn.credentialAdded' , credentialAddedHandler )
55+ client . off ( 'WebAuthn.credentialAsserted' , credentialAssertedHandler )
3656}
3757
3858test ( 'Users can register and use passkeys' , async ( { page, login } ) => {
39- const password = faker . internet . password ( )
40- const user = await login ( { password } )
59+ const user = await login ( )
4160
4261 const { client, authenticatorId } = await setupWebAuthn ( page )
4362
@@ -51,9 +70,9 @@ test('Users can register and use passkeys', async ({ page, login }) => {
5170
5271 await page . goto ( '/settings/profile/passkeys' )
5372
54- await simulateSuccessfulPasskeyInput ( client , authenticatorId , async ( ) => {
55- await page . getByRole ( 'button' , { name : / r e g i s t e r n e w p a s s k e y / i } ) . click ( )
56- } )
73+ const passkeyRegisteredPromise = waitOnce ( client , 'WebAuthn.credentialAdded' )
74+ await page . getByRole ( 'button' , { name : / r e g i s t e r n e w p a s s k e y / i } ) . click ( )
75+ await passkeyRegisteredPromise
5776
5877 // Verify the passkey appears in the UI
5978 await expect ( page . getByRole ( 'list' , { name : / p a s s k e y s / i } ) ) . toBeVisible ( )
@@ -77,10 +96,18 @@ test('Users can register and use passkeys', async ({ page, login }) => {
7796 await page . goto ( '/login' )
7897 const signCount1 = afterRegistrationCredentials . credentials [ 0 ] . signCount
7998
80- await simulateSuccessfulPasskeyInput ( client , authenticatorId , async ( ) => {
81- await page . getByRole ( 'button' , { name : / l o g i n w i t h a p a s s k e y / i } ) . click ( )
99+ const passkeyAssertedPromise = waitOnce ( client , 'WebAuthn.credentialAsserted' )
100+
101+ await page . getByRole ( 'button' , { name : / l o g i n w i t h a p a s s k e y / i } ) . click ( )
102+
103+ // Check for error message before waiting for completion
104+ const errorLocator = page . getByText ( / f a i l e d t o a u t h e n t i c a t e / i)
105+ const errorPromise = errorLocator . waitFor ( { timeout : 1000 } ) . then ( ( ) => {
106+ throw new Error ( 'Passkey authentication failed' )
82107 } )
83108
109+ await Promise . race ( [ passkeyAssertedPromise , errorPromise ] )
110+
84111 // Verify successful login
85112 await expect (
86113 page . getByRole ( 'link' , { name : user . name ?? user . username } ) ,
@@ -116,7 +143,7 @@ test('Users can register and use passkeys', async ({ page, login }) => {
116143
117144 // Try logging in with the deleted passkey
118145 await page . goto ( '/login' )
119- await simulateSuccessfulPasskeyInput ( client , authenticatorId , async ( ) => {
146+ await simulateSuccessfulPasskeyInput ( client , async ( ) => {
120147 await page . getByRole ( 'button' , { name : / l o g i n w i t h a p a s s k e y / i } ) . click ( )
121148 } )
122149
0 commit comments