@@ -7,6 +7,7 @@ import { Page, TestInfo } from '@playwright/test';
77import { Credentials } from './targets' ;
88import { BaseTarget } from './targets/base' ;
99import { MfaScope } from 'fxa-settings/src/lib/types' ;
10+ import { getTotpCode } from './totp' ;
1011
1112enum EmailPrefix {
1213 BLOCKED = 'blocked' ,
@@ -251,16 +252,31 @@ export class TestAccountTracker {
251252 sessionToken = credentials . sessionToken ;
252253 }
253254
254- // Helper function to verify session if needed
255- const verifySessionIfNeeded = async ( ) => {
255+ // Helper function to verify account or session if needed
256+ const verifyIfNeeded = async ( ) => {
256257 try {
257258 await this . target . authClient . sessionResendVerifyCode ( sessionToken ) ;
258- const code = await this . target . emailClient . getVerifyLoginCode (
259+ // Prefer short (signup) code to confirm account when unverified
260+ try {
261+ const shortCode =
262+ await this . target . emailClient . getVerifyShortCode ( account . email ) ;
263+ await this . target . authClient . sessionVerifyCode (
264+ sessionToken ,
265+ shortCode
266+ ) ;
267+ return ;
268+ } catch ( _ ) {
269+ // Fallback to login verification code for unverified session
270+ }
271+ const loginCode = await this . target . emailClient . getVerifyLoginCode (
259272 account . email
260273 ) ;
261- await this . target . authClient . sessionVerifyCode ( sessionToken , code ) ;
274+ await this . target . authClient . sessionVerifyCode (
275+ sessionToken ,
276+ loginCode
277+ ) ;
262278 } catch {
263- // Ignore verification errors - session might already be verified
279+ // Ignore verification errors - account/ session might already be verified
264280 }
265281 } ;
266282
@@ -275,24 +291,32 @@ export class TestAccountTracker {
275291 account . password
276292 ) ;
277293 sessionToken = credentials . sessionToken ;
278- await verifySessionIfNeeded ( ) ;
294+ await verifyIfNeeded ( ) ;
279295 return true ;
280296 }
281297 return false ;
282298 } ;
283299
300+ const elevateToAal2 = async ( secret : string ) => {
301+ const code = await getTotpCode ( secret ) ;
302+ await this . target . authClient . verifyTotpCode ( sessionToken , code ) ;
303+ } ;
304+
284305 // Check if account has 2FA enabled before attempting to delete TOTP
285306 try {
286307 const profile =
287308 await this . target . authClient . accountProfile ( sessionToken ) ;
288309 const has2FA = profile . authenticationMethods ?. includes ( 'otp' ) ;
289310
290311 if ( has2FA ) {
312+ if ( 'secret' in account && account . secret ) {
313+ await elevateToAal2 ( account . secret ) ;
314+ }
291315 try {
292316 await this . target . authClient . deleteTotpToken ( sessionToken ) ;
293317 } catch ( totpError ) {
294318 if ( totpError . message . includes ( 'Unconfirmed session' ) ) {
295- await verifySessionIfNeeded ( ) ;
319+ await verifyIfNeeded ( ) ;
296320 await this . target . authClient . deleteTotpToken ( sessionToken ) ;
297321 } else if ( await getFreshSessionIfNeeded ( totpError ) ) {
298322 await this . target . authClient . deleteTotpToken ( sessionToken ) ;
@@ -308,7 +332,7 @@ export class TestAccountTracker {
308332 await this . target . authClient . deleteTotpToken ( sessionToken ) ;
309333 } catch ( totpError ) {
310334 if ( totpError . message . includes ( 'Unconfirmed session' ) ) {
311- await verifySessionIfNeeded ( ) ;
335+ await verifyIfNeeded ( ) ;
312336 await this . target . authClient . deleteTotpToken ( sessionToken ) ;
313337 } else if ( await getFreshSessionIfNeeded ( totpError ) ) {
314338 await this . target . authClient . deleteTotpToken ( sessionToken ) ;
@@ -327,7 +351,7 @@ export class TestAccountTracker {
327351 ) ;
328352 } catch ( destroyError ) {
329353 if ( destroyError . message . includes ( 'Unconfirmed session' ) ) {
330- await verifySessionIfNeeded ( ) ;
354+ await verifyIfNeeded ( ) ;
331355 await this . target . authClient . accountDestroy (
332356 account . email ,
333357 account . password ,
0 commit comments