@@ -193,32 +193,47 @@ final class StoreAccountDeregistrationCombinedTests: XCTestCase {
// / 两 步 页 面 的 实 际 U I K i t 布 局 和 交 互 测 试 ; 独 立 窗 口 不 触 碰 用 户 真 实 会 话 。
@ MainActor
final class StoreAccountDeregistrationRedesignTests : XCTestCase {
// / 明 确 受 理 后 立 即 调 用 一 次 退 出 , 不 等 待 后 续 G E T ; 在 途 连 点 及 成 功 后 再 次 点 击 均 不 重 发 。
func testAcceptedSubmissionExitsOnceWithoutPostSubmissionQuery ( ) async throws {
override func setUp ( ) {
super . setUp ( )
GlobalLoadingManager . shared . hideAll ( )
}
override func tearDown ( ) {
GlobalLoadingManager . shared . hideAll ( )
super . tearDown ( )
}
// / 明 确 受 理 后 只 读 获 取 截 止 时 间 并 调 用 一 次 退 出 ; 在 途 连 点 及 成 功 后 再 次 点 击 均 不 重 发 。
func testAcceptedSubmissionCapturesCoolingDeadlineAndExitsOnce ( ) async throws {
let service = CombinedDeregistrationService ( )
service . walletWaived = true
service . pointsWaived = true
service . holdApply = true
service . onApply = { service . readError = APIError . networkFailed ( " 成功后不应再查询 " ) }
let coolingStatus = try StoreAccountDeregistrationFixtures . lifecycleStatus ( )
service . onApply = { service . statusOverride = coolingStatus }
let suite = " DeregistrationLogout. \( UUID ( ) . uuidString ) "
let defaults = UserDefaults ( suiteName : suite ) !
let store = StoreAccountDeregistrationSubmissionStore ( storeUserID : " 101 " , environment : . testing , defaults : defaults )
defer { defaults . removePersistentDomain ( forName : suite ) }
let model = StoreAccountDeregistrationViewModel ( storeUserID : 101 , submissionStore : store )
var logoutCount = 0
var acceptedDeadline : String ?
var unresolvedCount = 0
let controller = StoreAccountDeregistrationViewController (
identityName : " 测试门店 " , viewModel : model , api : service ,
onUnresolvedSubmission : { unresolvedCount += 1 } , onSubmissionAccepted : { logoutCount += 1 } )
onUnresolvedSubmission : { unresolvedCount += 1 } , onSubmissionAccepted : { deadline in
acceptedDeadline = deadline
logoutCount += 1
} )
let window = makeWindow ( controller , size : UIScreen . main . bounds . size )
defer { service . finishApply ( ) ; close ( window ) }
await waitUntil { model . canContinue }
model . beginVerification ( )
let initialStatusCount = service . statusCount
let initialEligibilityCount = service . eligibilityCount
controller . submitConfirmedApplication ( smsCode : " 654321 " , reason : " 不再使用 " )
controller . submitConfirmedApplication ( smsCode : " 654321 " , reason : " 不再使用 " )
await waitUntil { service . applyContinuation != nil }
controller . submitConfirmedApplication ( smsCode : " 654321 " , reason : " 不再使用 " )
controller . submitConfirmedApplication ( smsCode : " 654321 " , reason : " 不再使用 " )
XCTAssertEqual ( logoutCount , 0 )
XCTAssertTrue ( model . isBusy )
XCTAssertTrue ( GlobalLoadingManager . shared . isShowing )
@@ -226,13 +241,16 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
capture ( window , name : " deregister-submitting-global-loading " )
service . finishApply ( )
await waitUntil { logoutCount = = 1 }
controller . submitConfirmedApplication ( smsCode : " 654321 " , reason : " 不再使用 " )
controller . submitConfirmedApplication ( smsCode : " 654321 " , reason : " 不再使用 " )
XCTAssertEqual ( logoutCount , 1 )
XCTAssertEqual ( unresolvedCount , 0 )
XCTAssertEqual ( service . mutations , [ " apply " ] )
XCTAssertEqual ( service . statusCount , initialStatusCount + 1 , " 只保留提交前校验 " )
XCTAssertEqual ( service . lastReason , " 不再使用 " )
XCTAssertEqual ( service . statusCount , initialStatusCount + 2 , " 提交前校验并在成功后只读获取截止时间 " )
XCTAssertEqual ( service . eligibilityCount , initialEligibilityCount + 1 )
XCTAssertTrue ( model . submissionAccepted )
XCTAssertEqual ( model . submittedCoolingUntil , coolingStatus . coolingUntil )
XCTAssertEqual ( acceptedDeadline , coolingStatus . coolingUntil )
XCTAssertNil ( model . errorMessage )
XCTAssertTrue ( store . hasUnresolvedSubmission , " 保留意图供下次登录核实 " )
XCTAssertFalse ( GlobalLoadingManager . shared . isShowing )
@@ -250,17 +268,29 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
var unresolvedCount = 0
let controller = StoreAccountDeregistrationViewController (
identityName : " 测试门店 " , viewModel : model , api : service ,
onUnresolvedSubmission : { unresolvedCount += 1 } , onSubmissionAccepted : { logoutCount += 1 } )
onUnresolvedSubmission : { unresolvedCount += 1 } , onSubmissionAccepted : { _ in logoutCount += 1 } )
let window = makeWindow ( controller , size : UIScreen . main . bounds . size )
defer { close ( window ) }
await waitUntil { model . canContinue }
model . beginVerification ( )
let codeField = try XCTUnwrap ( find ( controller . view , " deregister.code " ) as ? UITextField )
let reasonField = try XCTUnwrap ( find ( controller . view , " deregister.reason " ) as ? UITextView )
codeField . text = " 654321 "
reasonField . text = " 不再使用 "
controller . submitConfirmedApplication ( smsCode : " 654321 " , reason : " 不再使用 " )
await waitUntil { model . errorMessage != nil && ! model . isBusy }
XCTAssertEqual ( logoutCount , 0 )
XCTAssertEqual ( unresolvedCount , uncertain ? 1 : 0 )
XCTAssertEqual ( model . submissionAttempted , uncertain )
XCTAssertFalse ( model . submissionAccepted )
XCTAssertEqual ( model . step , uncertain ? . unresolvedRequest : . verification )
XCTAssertEqual ( model . canContinue , ! uncertain )
let submit = try XCTUnwrap ( find ( controller . view , " deregister.submit " ) as ? UIButton )
XCTAssertEqual ( submit . isHidden , uncertain )
if ! uncertain {
XCTAssertEqual ( codeField . text , " 654321 " )
XCTAssertEqual ( reasonField . text , " 不再使用 " )
}
XCTAssertEqual ( service . mutations , [ " apply " ] )
XCTAssertFalse ( GlobalLoadingManager . shared . isShowing )
}
@@ -281,7 +311,7 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
next . sendActions ( for : . touchUpInside )
await waitUntil { model . step = = . verification }
let code = try XCTUnwrap ( find ( controller . view , " deregister.code " ) as ? UITextField )
let reason = try XCTUnwrap ( find ( controller . view , " deregister.reason " ) as ? UITextField )
let reason = try XCTUnwrap ( find ( controller . view , " deregister.reason " ) as ? UITextView )
code . text = " 654321 "
reason . text = " 不再使用 "
let scroll = try XCTUnwrap ( allViews ( controller . view ) . first { $0 is UIScrollView } as ? UIScrollView )
@@ -324,13 +354,16 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
await waitUntil { service . statusCount = = count + 1 && ! model . isBusy }
let texts = allViews ( controller . view ) . compactMap { ( $0 as ? UILabel ) ? . text } . joined ( separator : " \n " )
XCTAssertFalse ( texts . contains ( " 景区 " ) )
XCTAssertTrue ( texts . contains ( " 同手机号的其他身份不受影响 " ) )
XCTAssertTrue ( texts . contains ( " 仅注销当前身份 " ) )
XCTAssertFalse ( texts . contains ( " 同手机号的其他身份不受影响 " ) )
XCTAssertFalse ( texts . contains ( " 到期由服务端复核 " ) )
XCTAssertFalse ( texts . contains ( " 历史订单、财务及审计记录 " ) )
XCTAssertFalse ( StoreAccountDeregistrationError . unsupportedIdentity . localizedDescription . contains ( " 景区 " ) )
XCTAssertTrue ( service . mutations . isEmpty )
XCTAssertFalse ( refresh . isRefreshing )
}
// / 点 击 提 交 仅 显 示 最 终 确 认 , 明 确 退 出 登 录 和 冷 静 期 , 不 自 动 申 请 或 发 送 短 信 。
// / 点 击 提 交 仅 显 示 高 保 真 最 终 确 认 ; 原 因 必 填 并 在 确 认 弹 层 展 示 规 范 化 内 容 。
func testSubmitRequiresFinalConfirmationWithLogoutNotice ( ) async throws {
let service = CombinedDeregistrationService ( )
service . walletWaived = true
@@ -338,7 +371,8 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
let model = StoreAccountDeregistrationViewModel ( storeUserID : 101 )
var logoutCount = 0
let controller = StoreAccountDeregistrationViewController (
identityName : " 当前测试身份 " , viewModel : model , api : service , onSubmissionAccepted : { logoutCount += 1 } )
identityName : " 当前测试身份 " , viewModel : model , api : service ,
onSubmissionAccepted : { _ in logoutCount += 1 } )
let window = makeWindow ( controller , size : UIScreen . main . bounds . size )
defer { close ( window ) }
await waitUntil { model . canContinue }
@@ -346,23 +380,107 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
next . sendActions ( for : . touchUpInside )
await waitUntil { model . step = = . verification }
let code = try XCTUnwrap ( find ( controller . view , " deregister.code " ) as ? UITextField )
let reason = try XCTUnwrap ( find ( controller . view , " deregister.reason " ) as ? UITextField )
let reason = try XCTUnwrap ( find ( controller . view , " deregister.reason " ) as ? UITextView )
code . text = " 654321 "
reason . text = " 不再使用 "
reason . sendActions ( for : . editingChanged )
code . sendActions ( for : . editingChanged )
let submit = try XCTUnwrap ( find ( controller . view , " deregister.submit " ) as ? UIButton )
XCTAssertFalse ( submit . isEnabled , " 验证码和注销原因均为必填项 " )
reason . text = " 门店停业 "
controller . textViewDidChange ( reason )
XCTAssertTrue ( submit . isEnabled )
submit . sendActions ( for : . touchUpInside )
await waitUntil { controller . presentedViewController is UIAlertController }
let alert = try XCTUnwrap ( controller . presentedViewController as ? UIAlertController )
XCTAssertTrue ( alert . message ? . contains ( " 提交成功后将退出登录 " ) = = true )
XCTAssertTrue ( alert . message ? . contains ( " 当前测试身份 " ) = = true )
XCTAssertTrue ( alert . message ? . contains ( " 7 天冷静期 " ) = = true )
XCTAssertTrue ( alert . message ? . contains ( " 正式完成不可恢复 " ) = = true )
XCTAssertEqual ( alert . actions . map ( \ . title ) , [ " 暂不提交 " , " 确认提交申请 " ] )
await waitUntil { controller . presentedViewController is StoreAccountDeregistrationConfirmationSheetViewController }
let sheet = try XCTUnwrap ( controller . presentedViewController as ? StoreAccountDeregistrationConfirmationSheetViewController )
sheet . loadViewIfNeeded ( )
let texts = allViews ( sheet . view ) . compactMap { ( $0 as ? UILabel ) ? . text } . joined ( separator : " \n " )
XCTAssertTrue ( texts . contains ( " 确认提交注销申请? " ) )
XCTAssertTrue ( texts . contains ( " 当前测试身份 " ) )
XCTAssertTrue ( texts . contains ( " 7 天 " ) )
XCTAssertTrue ( texts . contains ( " 注销原因 " ) )
XCTAssertTrue ( texts . contains ( " 门店停业 " ) )
XCTAssertTrue ( texts . contains ( " 正式完成后不可恢复 " ) )
XCTAssertEqual ( ( find ( sheet . view , " deregister.sheet.cancel " ) as ? UIButton ) ? . configuration ? . title , " 我再想想 " )
XCTAssertEqual ( ( find ( sheet . view , " deregister.sheet.confirm " ) as ? UIButton ) ? . configuration ? . title , " 确认提交 " )
try ? await Task . sleep ( nanoseconds : 350_000_000 )
try assertSheetHugsContent ( sheet )
capture ( window , name : " redesign-submit-confirmation " )
XCTAssertTrue ( service . mutations . isEmpty )
XCTAssertEqual ( logoutCount , 0 )
}
// / 申 请 受 理 后 的 退 出 态 结 果 页 只 保 留 “ 完 成 ” , 不 能 返 回 或 继 续 操 作 注 销 接 口 。
func testSubmittedPageOnlyAllowsCompletingToLogin ( ) throws {
var doneCount = 0
let controller = StoreAccountDeregistrationSubmittedViewController (
identityName : " 当前测试身份 " ,
coolingUntil : " 2026-09-07 18:30:00 "
) {
doneCount += 1
}
let window = makeWindow ( controller , size : UIScreen . main . bounds . size )
defer { close ( window ) }
window . layoutIfNeeded ( )
let title = try XCTUnwrap ( find ( controller . view , " deregister.submitted.title " ) as ? UILabel )
let identity = try XCTUnwrap ( find ( controller . view , " deregister.submitted.identity " ) as ? UILabel )
let deadline = try XCTUnwrap ( find ( controller . view , " deregister.submitted.deadline " ) as ? UILabel )
let done = try XCTUnwrap ( find ( controller . view , " deregister.submitted.done " ) as ? UIButton )
XCTAssertEqual ( title . text , " 注销申请已提交 " )
XCTAssertEqual ( identity . text , " 当前身份:当前测试身份 " )
XCTAssertEqual ( deadline . text , " 2026-09-07 18:30:00 " )
XCTAssertEqual ( done . configuration ? . title , " 完成 " )
XCTAssertNil ( find ( controller . view , " deregister.access.logout " ) )
XCTAssertNil ( find ( controller . view , " deregister.access.cancel " ) )
XCTAssertTrue ( controller . navigationItem . hidesBackButton )
XCTAssertFalse ( controller . navigationController ? . interactivePopGestureRecognizer ? . isEnabled ? ? true )
XCTAssertTrue ( window . bounds . contains ( done . convert ( done . bounds , to : window ) ) )
capture ( window , name : " redesign-submitted-signed-out " )
done . sendActions ( for : . touchUpInside )
done . sendActions ( for : . touchUpInside )
XCTAssertEqual ( doneCount , 1 )
}
// / i P h o n e 1 1 画 布 按 最 终 设 计 稿 渲 染 资 产 页 和 资 产 确 认 弹 层 ; 步 骤 标 题 必 须 保 持 横 排 。
func testDesignReferenceAssetScreensOnPhysicalDevice ( ) async throws {
let service = CombinedDeregistrationService ( )
service . walletFen = 1260
service . points = 20
let model = StoreAccountDeregistrationViewModel ( storeUserID : 101 )
let controller = StoreAccountDeregistrationViewController ( identityName : " 示例门店 " , viewModel : model , api : service )
let window = makeWindow ( controller , size : UIScreen . main . bounds . size )
defer { close ( window ) }
await waitUntil { model . canConfirmAssetsAndContinue }
window . layoutIfNeeded ( )
let labels = allViews ( controller . view ) . compactMap { $0 as ? UILabel }
for title in [ " 确认资产 " , " 手机验证 " ] {
let label = try XCTUnwrap ( labels . first { $0 . text = = title } )
XCTAssertEqual ( label . numberOfLines , 1 )
XCTAssertLessThan ( label . bounds . height , 28 )
}
let assetStep = try XCTUnwrap ( find ( controller . view , " deregister.step.assets " ) )
let verificationStep = try XCTUnwrap ( find ( controller . view , " deregister.step.verification " ) )
XCTAssertEqual ( assetStep . bounds . width , verificationStep . bounds . width , accuracy : 0.5 )
XCTAssertEqual ( assetStep . bounds . height , 46 , accuracy : 0.5 )
XCTAssertEqual ( verificationStep . bounds . height , 46 , accuracy : 0.5 )
XCTAssertEqual ( assetStep . backgroundColor , StoreAccountDeregistrationStyle . primary )
XCTAssertNotEqual ( assetStep . backgroundColor , verificationStep . backgroundColor )
XCTAssertTrue ( assetStep . accessibilityTraits . contains ( . selected ) )
let pageTexts = labels . compactMap ( \ . text ) . joined ( separator : " \n " )
XCTAssertFalse ( pageTexts . contains ( " 同手机号的其他身份不受影响 " ) )
XCTAssertFalse ( pageTexts . contains ( " 到期由服务端复核 " ) )
XCTAssertFalse ( pageTexts . contains ( " 历史订单、财务及审计记录 " ) )
capture ( window , name : " redesign-assets-iphone11 " )
let next = try XCTUnwrap ( find ( controller . view , " deregister.continue " ) as ? UIButton )
next . sendActions ( for : . touchUpInside )
await waitUntil { controller . presentedViewController is StoreAccountDeregistrationConfirmationSheetViewController }
try ? await Task . sleep ( nanoseconds : 350_000_000 )
let sheet = try XCTUnwrap ( controller . presentedViewController as ? StoreAccountDeregistrationConfirmationSheetViewController )
try assertSheetHugsContent ( sheet )
capture ( window , name : " redesign-asset-confirmation " )
XCTAssertTrue ( service . mutations . isEmpty )
}
// / 3 7 5 p t 小 屏 上 仅 一 个 主 按 钮 , 零 资 产 确 认 也 先 弹 窗 , 没 有 自 动 提 交 。
func testSingleAssetButtonShowsOneExplicitConfirmation ( ) async throws {
let service = CombinedDeregistrationService ( )
@@ -383,16 +501,21 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
XCTAssertTrue ( window . bounds . contains ( next . convert ( next . bounds , to : window ) ) )
capture ( window , name : " redesign-assets-375 " )
next . sendActions ( for : . touchUpInside )
await waitUntil { controller . presentedViewController is UIAlertController }
let alert = try XCTUnwrap ( controller . presentedViewController as ? UIAlertController )
XCTAssertEqual ( alert . actions . map ( \ . title ) , [ " 暂不确认 " , " 确认并继续 " ] )
XCTAssertTrue ( alert . message ? . contains ( " 现金余额:¥0.00 " ) = = true )
XCTAssertTrue ( alert . message ? . contains ( " 积分:0 " ) = = true )
XCTAssertTrue ( alert . message ? . contains ( " 确认自愿放弃以上现金余额,并确认自愿放弃以上积分 " ) = = true )
await waitUntil { controller . presentedViewController is StoreAccountDeregistrationConfirmationSheetViewController }
let sheet = try XCTUnwrap ( controller . presentedViewController as ? StoreAccountDeregistrationConfirmationSheetViewController )
sheet . loadViewIfNeeded ( )
let sheetTexts = allViews ( sheet . view ) . compactMap { ( $0 as ? UILabel ) ? . text } . joined ( separator : " \n " )
XCTAssertTrue ( sheetTexts . contains ( " 确认放弃账号资产 " ) )
XCTAssertTrue ( sheetTexts . contains ( " ¥0.00 " ) )
XCTAssertTrue ( sheetTexts . contains ( " 积分 \n 0 " ) )
XCTAssertFalse ( sheetTexts . contains ( " 自愿放弃以上资产 " ) )
XCTAssertFalse ( sheetTexts . contains ( " 零余额也需要确认 " ) )
XCTAssertEqual ( ( find ( sheet . view , " deregister.sheet.cancel " ) as ? UIButton ) ? . configuration ? . title , " 暂不确认 " )
XCTAssertEqual ( ( find ( sheet . view , " deregister.sheet.confirm " ) as ? UIButton ) ? . configuration ? . title , " 确认放弃 " )
XCTAssertTrue ( service . mutations . isEmpty )
}
// / 已 确 认 资 产 不 再 弹 窗 ; 验 证 码 与 获 取 按 钮 同 行 , 输 入 完 整 前 禁 用 提 交 , 键 盘 不 遮 挡 操 作 区 。
// / 已 确 认 资 产 不 再 弹 窗 ; 验 证 码 和 原 因 均 必 填 , 多 行 输 入 与 错 误 提 示 不 被 键 盘 遮 挡 。
func testVerificationLayoutInputAndKeyboard ( ) async throws {
let service = CombinedDeregistrationService ( )
service . walletWaived = true
@@ -406,24 +529,37 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
next . sendActions ( for : . touchUpInside )
await waitUntil { model . step = = . verification }
window . layoutIfNeeded ( )
let assetStep = try XCTUnwrap ( find ( controller . view , " deregister.step.assets " ) )
let verificationStep = try XCTUnwrap ( find ( controller . view , " deregister.step.verification " ) )
XCTAssertNotEqual ( assetStep . backgroundColor , verificationStep . backgroundColor )
XCTAssertTrue ( verificationStep . accessibilityTraits . contains ( . selected ) )
XCTAssertFalse ( assetStep . accessibilityTraits . contains ( . selected ) )
let code = try XCTUnwrap ( find ( controller . view , " deregister.code " ) as ? UITextField )
let reason = try XCTUnwrap ( find ( controller . view , " deregister.reason " ) as ? UITextField )
let reason = try XCTUnwrap ( find ( controller . view , " deregister.reason " ) as ? UITextView )
let sms = try XCTUnwrap ( find ( controller . view , " deregister.sms " ) as ? UIButton )
let submit = try XCTUnwrap ( find ( controller . view , " deregister.submit " ) as ? UIButton )
XCTAssertEqual ( code . textContentType , . oneTimeCode )
XCTAssertEqual ( code . keyboardType , . numberPad )
XCTAssertEqual ( code . superview , sms . superview )
XCTAssertEqual ( find ( controller . view , " deregister.reason.title " ) ? . accessibilityLabel , " 注销原因,必填 " )
XCTAssertFalse ( submit . isEnabled )
capture ( window , name : " redesign-verification " )
code . text = " 123456 "
code . sendActions ( for : . editingChanged )
XCTAssertFalse ( submit . isEnabled )
reason . becomeFirstResponder ( )
reason . resignFirstResponder ( )
controller . textViewDidEndEditing ( reason )
let reasonError = try XCTUnwrap ( find ( controller . view , " deregister.reason.error " ) as ? UILabel )
XCTAssertFalse ( reasonError . isHidden )
XCTAssertEqual ( reasonError . text , " 请填写注销原因 " )
reason . text = " 测试 "
reason . sendActions ( for : . editingChanged )
controller . textViewDidChange ( reason )
XCTAssertTrue ( submit . isEnabled )
XCTAssertTrue ( reasonError . isHidden )
let navigationFrame = controller . navigationController ? . view . frame
code . becomeFirstResponder ( )
await waitUntil { controller . view . keyboardLayoutGuide . layoutFrame . height > controller . view . safeAreaInsets . bottom + 80 }
XCTAssertTrue ( code . becomeFirstResponder ( ) )
try ? await Task . sleep ( nanoseconds : 100_000_000 )
window . layoutIfNeeded ( )
let frame = submit . convert ( submit . bounds , to : controller . view )
XCTAssertLessThanOrEqual ( frame . maxY , controller . view . keyboardLayoutGuide . layoutFrame . minY + 1 )
@@ -515,6 +651,14 @@ final class StoreAccountDeregistrationRedesignTests: XCTestCase {
}
private func allViews ( _ view : UIView ) -> [ UIView ] { view . subviews . flatMap { [ $0 ] + allViews ( $0 ) } }
private func find ( _ view : UIView , _ id : String ) -> UIView ? { allViews ( view ) . first { $0 . accessibilityIdentifier = = id } }
private func assertSheetHugsContent ( _ sheet : StoreAccountDeregistrationConfirmationSheetViewController ) throws {
sheet . view . layoutIfNeeded ( )
let confirm = try XCTUnwrap ( find ( sheet . view , " deregister.sheet.confirm " ) )
let buttonBottom = confirm . convert ( confirm . bounds , to : sheet . view ) . maxY
let visualGap = sheet . view . bounds . height - sheet . view . safeAreaInsets . bottom - buttonBottom
XCTAssertGreaterThanOrEqual ( visualGap , 10 )
XCTAssertLessThanOrEqual ( visualGap , 18 , " 弹层底部只应保留 12pt 操作间距 " )
}
private func waitUntil ( _ condition : ( ) -> Bool ) async {
for _ in 0. . < 150 {
if condition ( ) { return }
@@ -559,6 +703,7 @@ private final class CombinedDeregistrationService: StoreAccountDeregistrationSer
var applyContinuation : CheckedContinuation < Void , Never > ?
var holdNextStatus = false
var statusContinuation : CheckedContinuation < Void , Never > ?
var lastReason : String ?
func eligibility ( ) async throws -> StoreAccountDeregistrationEligibility {
eligibilityCount += 1
@@ -600,6 +745,7 @@ private final class CombinedDeregistrationService: StoreAccountDeregistrationSer
func sendSMS ( ) async throws { mutations . append ( " sms " ) }
func apply ( smsCode : String , reason : String ) async throws {
mutations . append ( " apply " )
lastReason = reason
if holdApply { await withCheckedContinuation { applyContinuation = $0 } }
if let applyError { throw applyError }
onApply ? ( )