@@ -9,6 +9,42 @@ import { Url } from '../../core/common';
99export class OauthPageRecipe extends PageRecipe {
1010 private static longTimeout = 40 ;
1111
12+ /**
13+ * Check if an error is a "Navigating frame was detached" error.
14+ * This can happen when the OAuth page closes during navigation (e.g., successful auth).
15+ * @param e - The error to check
16+ * @returns true if this is a frame detached error that can be safely ignored
17+ */
18+ private static isFrameDetachedError ( e : unknown ) : boolean {
19+ if ( ! e ) {
20+ return false ;
21+ }
22+ // Check if it's an Error object with a message property
23+ if ( e instanceof Error ) {
24+ return e . message . includes ( 'Navigating frame was detached' ) ;
25+ }
26+ // Fallback: convert to string and check (for non-Error objects)
27+ const errorStr = String ( e ) ;
28+ return errorStr . includes ( 'Navigating frame was detached' ) || errorStr . includes ( 'frame was detached' ) ;
29+ }
30+
31+ /**
32+ * Safely navigate to a URL, ignoring "frame was detached" errors.
33+ * The OAuth page may close during navigation on successful auth.
34+ * @param oauthPage - The OAuth page to navigate
35+ * @param url - The URL to navigate to
36+ */
37+ private static async safeGoto ( oauthPage : ControllablePage , url : string ) : Promise < void > {
38+ try {
39+ await oauthPage . target . goto ( url ) ;
40+ } catch ( e : unknown ) {
41+ if ( ! OauthPageRecipe . isFrameDetachedError ( e ) ) {
42+ throw e ;
43+ }
44+ // Ignore frame detached errors - page closed successfully
45+ }
46+ }
47+
1248 public static mock = async (
1349 t : AvaContext ,
1450 oauthPage : ControllablePage ,
@@ -21,33 +57,28 @@ export class OauthPageRecipe extends PageRecipe {
2157 await oauthPage . close ( ) ;
2258 } else if ( action === 'login_with_invalid_state' ) {
2359 mockOauthUrl = Url . removeParamsFromUrl ( mockOauthUrl , [ 'login_hint' ] ) ;
24- await oauthPage . target . goto (
60+ await OauthPageRecipe . safeGoto (
61+ oauthPage ,
2562 mockOauthUrl . replace ( 'CRYPTUP_STATE' , 'INVALID_CRYPTUP_STATE' ) + '&login_hint=' + encodeURIComponent ( acctEmail ) + '&proceed=true'
2663 ) ;
2764 } else if ( action === 'missing_permission' ) {
2865 mockOauthUrl = Url . removeParamsFromUrl ( mockOauthUrl , [ 'scope' ] ) ;
2966 mockOauthUrl += '&scope=missing_scope' ;
30- await oauthPage . target . goto ( mockOauthUrl + '&proceed=true' ) ;
67+ await OauthPageRecipe . safeGoto ( oauthPage , mockOauthUrl + '&proceed=true' ) ;
3168 } else if ( ! login_hint ) {
32- await oauthPage . target . goto ( mockOauthUrl + '&login_hint=' + encodeURIComponent ( acctEmail ) + '&proceed=true' ) ;
69+ await OauthPageRecipe . safeGoto ( oauthPage , mockOauthUrl + '&login_hint=' + encodeURIComponent ( acctEmail ) + '&proceed=true' ) ;
3370 } else {
3471 if ( action === 'override_acct' ) {
3572 mockOauthUrl = Url . removeParamsFromUrl ( mockOauthUrl , [ 'login_hint' ] ) ;
3673 mockOauthUrl += '&login_hint=' + encodeURIComponent ( acctEmail ) ;
3774 }
38- await oauthPage . target . goto ( mockOauthUrl + '&proceed=true' ) ;
75+ await OauthPageRecipe . safeGoto ( oauthPage , mockOauthUrl + '&proceed=true' ) ;
3976 }
4077 } ;
4178
4279 public static customIdp = async ( t : AvaContext , oauthPage : ControllablePage ) : Promise < void > => {
4380 const mockOauthUrl = oauthPage . target . url ( ) ;
44- try {
45- await oauthPage . target . goto ( mockOauthUrl + '&proceed=true' ) ;
46- } catch ( e ) {
47- if ( ! e . message . includes ( 'Navigating frame was detached' ) ) {
48- throw e ;
49- }
50- }
81+ await OauthPageRecipe . safeGoto ( oauthPage , mockOauthUrl + '&proceed=true' ) ;
5182 } ;
5283
5384 public static google = async (
0 commit comments