@@ -395,7 +395,7 @@ test("Guardian flow: propose and confirm guardian", async ({ page, context: _con
395395 await guardianPage . waitForTimeout ( 5000 ) ;
396396
397397 // Check for success message
398- const successIndicator = guardianPage . getByText ( / G u a r d i a n .* c o n f i r m e d | S u c c e s s | C o n f i r m e d / i) ;
398+ const successIndicator = guardianPage . getByText ( / G u a r d i a n .* c o n f i r m e d | S u c c e s s | C o n f i r m e d / i) . first ( ) ;
399399 if ( await successIndicator . isVisible ( { timeout : 5000 } ) ) {
400400 console . log ( "✅ Guardian confirmation success message visible" ) ;
401401 } else {
@@ -678,7 +678,7 @@ test("Guardian flow: propose guardian with paymaster", async ({ page }) => {
678678
679679 // Verify success
680680 await guardianPage . waitForTimeout ( 5000 ) ;
681- const successIndicator = guardianPage . getByText ( / G u a r d i a n .* c o n f i r m e d | S u c c e s s | C o n f i r m e d / i) ;
681+ const successIndicator = guardianPage . getByText ( / G u a r d i a n .* c o n f i r m e d | S u c c e s s | C o n f i r m e d / i) . first ( ) ;
682682 if ( await successIndicator . isVisible ( { timeout : 5000 } ) ) {
683683 console . log ( "✅ Guardian confirmation success message visible" ) ;
684684 } else {
@@ -844,20 +844,141 @@ test("Guardian flow: full recovery execution", async ({ page, context: baseConte
844844 await guardianPage . waitForTimeout ( 2000 ) ;
845845 }
846846
847- // Check if confirmation text appeared
848- const confirmingVisible = await guardianPage . getByText ( / c o n f i r m i n g / i) . isVisible ( { timeout : 2000 } ) . catch ( ( ) => false ) ;
849- if ( ! confirmingVisible ) {
850- const finalState = await stateElement . textContent ( ) . catch ( ( ) => "unknown" ) ;
851- console . log ( "⚠️ 'Confirming' text not visible after waiting. Final state:" , finalState ) ;
852- throw new Error ( `Guardian confirmation did not show progress. Final state: ${ finalState } ` ) ;
847+ // Check for successful completion
848+ const finalState = await stateElement . textContent ( ) . catch ( ( ) => "unknown" ) ;
849+ if ( finalState !== "complete" && finalState !== "confirm_guardian_completed" ) {
850+ console . log ( "⚠️ Guardian confirmation did not complete. Final state:" , finalState ) ;
851+ throw new Error ( `Guardian confirmation failed. Final state: ${ finalState } ` ) ;
853852 }
854- console . log ( "✅ Guardian confirmed successfully" ) ;
853+ console . log ( "✅ Guardian confirmed successfully. Final state:" , finalState ) ;
855854
856- // NOTE: Recovery execution steps 5-10 would follow similar patterns
857- // but are skipped until recovery UI pages are updated with proper test IDs
855+ // Step 5: Owner initiates recovery with new passkey
856+ console . log ( "\nStep 5: Owner initiating account recovery..." ) ;
858857
859- console . log ( "\n=== Full Recovery Execution E2E Test Complete (Skipped) ===\n" ) ;
858+ // Create new recovery credential
859+ const recoveryContext = await baseContext . browser ( ) ! . newContext ( ) ;
860+ const recoveryPage = await recoveryContext . newPage ( ) ;
861+
862+ // Navigate directly to guardian recovery page
863+ await recoveryPage . goto ( "http://localhost:3002/recovery/guardian" ) ;
864+ await recoveryPage . waitForTimeout ( 2000 ) ;
865+
866+ // Enter owner account address - find the input inside the ZkInput component
867+ const accountInput = recoveryPage . locator ( "#address input" ) ;
868+ await expect ( accountInput ) . toBeVisible ( { timeout : 10000 } ) ;
869+ await accountInput . fill ( ownerAddress ) ;
870+ await recoveryPage . waitForTimeout ( 1000 ) ;
871+
872+ // Click Continue button
873+ const continueBtn = recoveryPage . getByRole ( "button" , { name : / C o n t i n u e / i } ) ;
874+ await expect ( continueBtn ) . toBeEnabled ( { timeout : 5000 } ) ;
875+ await continueBtn . click ( ) ;
876+ await recoveryPage . waitForTimeout ( 2000 ) ;
877+
878+ // Set up WebAuthn mock for recovery passkey
879+ await setupWebAuthn ( recoveryPage ) ;
880+
881+ const createPasskeyBtn = recoveryPage . getByRole ( "button" , { name : / G e n e r a t e P a s s k e y / i } ) ;
882+ await expect ( createPasskeyBtn ) . toBeVisible ( { timeout : 10000 } ) ;
883+ await createPasskeyBtn . click ( ) ;
884+ await recoveryPage . waitForTimeout ( 3000 ) ;
885+
886+ // Click "Confirm Later" to see the recovery URL
887+ const confirmLaterBtn = recoveryPage . getByRole ( "button" , { name : / C o n f i r m L a t e r / i } ) ;
888+ await expect ( confirmLaterBtn ) . toBeVisible ( { timeout : 10000 } ) ;
889+ await confirmLaterBtn . click ( ) ;
890+ await recoveryPage . waitForTimeout ( 2000 ) ;
891+
892+ // Wait for the recovery URL link to be visible and extract it
893+ const recoveryLink = recoveryPage . locator ( "a[href*='confirm-recovery']" ) ;
894+ await expect ( recoveryLink ) . toBeVisible ( { timeout : 10000 } ) ;
895+
896+ const confirmRecoveryUrl = await recoveryLink . getAttribute ( "href" ) ;
897+
898+ console . log ( `✅ Recovery initiated. Confirmation URL: ${ confirmRecoveryUrl } ` ) ;
899+
900+ // Step 6: Guardian confirms the recovery
901+ console . log ( "\nStep 6: Guardian confirming recovery request..." ) ;
902+
903+ if ( ! confirmRecoveryUrl ) {
904+ throw new Error ( "Failed to get recovery confirmation URL" ) ;
905+ }
906+
907+ // The URL is already a full URL, use it directly
908+ await guardianPage . goto ( confirmRecoveryUrl ) ;
909+ console . log ( "Navigated to recovery confirmation page" ) ;
910+
911+ // Wait for page to load
912+ await guardianPage . waitForLoadState ( "networkidle" ) ;
913+ await guardianPage . waitForTimeout ( 2000 ) ;
914+
915+ // Take a screenshot to see what's on the page
916+ await guardianPage . screenshot ( { path : "test-results/recovery-page-debug.png" } ) ;
917+
918+ // Check what's actually on the page
919+ const bodyText = await guardianPage . locator ( "body" ) . textContent ( ) ;
920+ console . log ( `Page content (first 1000 chars): ${ bodyText ?. substring ( 0 , 1000 ) } ` ) ;
921+
922+ // Check if there's an error message first (before trying to find h1)
923+ const errorCard = guardianPage . locator ( "[title='Error']" ) ;
924+ const hasError = await errorCard . isVisible ( ) . catch ( ( ) => false ) ;
925+ if ( hasError ) {
926+ const errorMsg = await guardianPage . locator ( "body" ) . textContent ( ) ;
927+ console . log ( `❌ Error on recovery page: ${ errorMsg ?. substring ( 0 , 500 ) } ` ) ;
928+ throw new Error ( `Recovery page shows error. credentialPublicKey is empty in URL: ${ confirmRecoveryUrl } ` ) ;
929+ }
930+
931+ // Now try to find h1
932+ const h1Element = guardianPage . locator ( "h1" ) ;
933+ const h1Visible = await h1Element . isVisible ( ) . catch ( ( ) => false ) ;
934+ if ( ! h1Visible ) {
935+ throw new Error ( `No h1 found on page. This likely means the page failed to load due to missing credentialPublicKey in URL: ${ confirmRecoveryUrl } ` ) ;
936+ }
937+ const pageTitle = await h1Element . textContent ( ) ;
938+ console . log ( `Recovery page title: ${ pageTitle } ` ) ;
939+
940+ // Select guardian from dropdown (it's a native select element inside account-recovery-account-select)
941+ const guardianSelect = guardianPage . locator ( "select" ) ;
942+ await expect ( guardianSelect ) . toBeVisible ( { timeout : 10000 } ) ;
943+ await guardianSelect . selectOption ( guardianAddress ) ;
944+ await guardianPage . waitForTimeout ( 1000 ) ;
945+
946+ // Confirm the recovery
947+ const confirmRecoveryBtn = guardianPage . getByRole ( "button" , { name : / C o n f i r m R e c o v e r y / i } ) ;
948+ await expect ( confirmRecoveryBtn ) . toBeVisible ( { timeout : 10000 } ) ;
949+ await confirmRecoveryBtn . click ( ) ;
950+ await guardianPage . waitForTimeout ( 5000 ) ;
951+
952+ // Verify recovery was initiated
953+ const recoveryCompletedMsg = guardianPage . getByText ( / 2 4 h r s / i) ;
954+ const recoveryCompleted = await recoveryCompletedMsg . isVisible ( { timeout : 10000 } ) . catch ( ( ) => false ) ;
955+
956+ if ( ! recoveryCompleted ) {
957+ // Take a screenshot for debugging
958+ await guardianPage . screenshot ( { path : "test-results/recovery-confirmation-debug.png" } ) ;
959+ const bodyText = await guardianPage . locator ( "body" ) . textContent ( ) ;
960+ console . log ( "Page content:" , bodyText ?. substring ( 0 , 500 ) ) ;
961+ throw new Error ( "Recovery confirmation failed - completion message not visible" ) ;
962+ }
963+
964+ console . log ( "✅ Guardian confirmed recovery successfully" ) ;
965+
966+ // Step 7: Verify recovery is pending (skip execution for now as it requires time travel)
967+ console . log ( "\nStep 7: Verifying recovery is in pending state..." ) ;
968+ console . log ( "Note: Full recovery execution requires EVM time manipulation" ) ;
969+ console . log ( "The recovery would need to wait 24 hours before it can be finalized" ) ;
970+
971+ console . log ( "\n=== Full Recovery E2E Flow Complete ===\n" ) ;
972+ console . log ( "Summary:" ) ;
973+ console . log ( " ✅ Owner account created" ) ;
974+ console . log ( " ✅ Guardian account created" ) ;
975+ console . log ( " ✅ Guardian proposed by owner" ) ;
976+ console . log ( " ✅ Guardian confirmed their role" ) ;
977+ console . log ( " ✅ Owner initiated recovery with new passkey" ) ;
978+ console . log ( " ✅ Guardian confirmed the recovery request" ) ;
979+ console . log ( " ⏳ Recovery pending (24hr delay before finalization)" ) ;
860980
861981 // Cleanup
862982 await guardianContext . close ( ) ;
983+ await recoveryContext . close ( ) ;
863984} ) ;
0 commit comments