From 416c1606cb7e27635c24e344ec8a1bbe5e58fadc Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Wed, 15 Jul 2026 09:04:29 +0700 Subject: [PATCH 1/2] test: stabilize phone resend countdown --- .../auth/account-phones-list/account-phones-list.test.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/blocks/src/blocks/auth/account-phones-list/account-phones-list.test.tsx b/apps/blocks/src/blocks/auth/account-phones-list/account-phones-list.test.tsx index f05dacb..286225f 100644 --- a/apps/blocks/src/blocks/auth/account-phones-list/account-phones-list.test.tsx +++ b/apps/blocks/src/blocks/auth/account-phones-list/account-phones-list.test.tsx @@ -430,7 +430,10 @@ describe('AccountPhonesList', () => { const resendButton = screen.getByTestId('otp-resend-p5'); expect(resendButton).toHaveTextContent('Resend in 60s'); expect(resendButton).toBeDisabled(); - expect(vi.getTimerCount()).toBe(1); + // Form/UI internals may briefly schedule an additional timer on the first + // click. The countdown timer itself is verified after that transient work + // flushes on the first tick. + expect(vi.getTimerCount()).toBeGreaterThanOrEqual(1); await act(async () => { vi.advanceTimersByTime(1000); From 66ec6fdcbfe282b0ed0468900e6df4909cfbb849 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Wed, 15 Jul 2026 09:07:11 +0700 Subject: [PATCH 2/2] test: isolate resend timer cleanup --- .../account-phones-list.test.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/blocks/src/blocks/auth/account-phones-list/account-phones-list.test.tsx b/apps/blocks/src/blocks/auth/account-phones-list/account-phones-list.test.tsx index 286225f..b2e4551 100644 --- a/apps/blocks/src/blocks/auth/account-phones-list/account-phones-list.test.tsx +++ b/apps/blocks/src/blocks/auth/account-phones-list/account-phones-list.test.tsx @@ -415,6 +415,8 @@ describe('AccountPhonesList', () => { it('resend countdown decrements to zero, restarts, and cleans up on unmount', async () => { vi.useFakeTimers(); + const setTimeoutSpy = vi.spyOn(globalThis, 'setTimeout'); + const clearTimeoutSpy = vi.spyOn(globalThis, 'clearTimeout'); const onSubmitSendOtp = vi.fn().mockResolvedValue(undefined); phonesQueryDataMock.mockReturnValue({ phoneNumbers: { @@ -430,16 +432,11 @@ describe('AccountPhonesList', () => { const resendButton = screen.getByTestId('otp-resend-p5'); expect(resendButton).toHaveTextContent('Resend in 60s'); expect(resendButton).toBeDisabled(); - // Form/UI internals may briefly schedule an additional timer on the first - // click. The countdown timer itself is verified after that transient work - // flushes on the first tick. - expect(vi.getTimerCount()).toBeGreaterThanOrEqual(1); await act(async () => { vi.advanceTimersByTime(1000); }); expect(resendButton).toHaveTextContent('Resend in 59s'); - expect(vi.getTimerCount()).toBe(1); for (let second = 0; second < 59; second += 1) { await act(async () => { @@ -448,17 +445,25 @@ describe('AccountPhonesList', () => { } expect(resendButton).toHaveTextContent('Resend code'); expect(resendButton).not.toBeDisabled(); - expect(vi.getTimerCount()).toBe(0); await act(async () => { fireEvent.click(resendButton); }); expect(onSubmitSendOtp).toHaveBeenCalledTimes(2); expect(resendButton).toHaveTextContent('Resend in 60s'); - expect(vi.getTimerCount()).toBe(1); + + const countdownTimerIndex = setTimeoutSpy.mock.calls.reduce( + (latestIndex, call, index) => (call[1] === 1000 ? index : latestIndex), + -1 + ); + expect(countdownTimerIndex).toBeGreaterThanOrEqual(0); + const countdownTimer = setTimeoutSpy.mock.results[countdownTimerIndex]?.value; + clearTimeoutSpy.mockClear(); unmount(); - expect(vi.getTimerCount()).toBe(0); + expect(clearTimeoutSpy).toHaveBeenCalledWith(countdownTimer); + setTimeoutSpy.mockRestore(); + clearTimeoutSpy.mockRestore(); }); it('verify inline without onSubmitSendOtp: surfaces PROCEDURE_NOT_FOUND', async () => {