login.test.tsx78 lines · main
1import { waitFor } from '@testing-library/dom'
2import { expect, test, vi } from 'vitest'
3
4import * as cliLogin from '@/data/cli/login'
5import { ProfileContextType } from '@/lib/profile'
6import { CliLoginScreen } from '@/pages/cli/login'
7import { customRender } from '@/tests/lib/custom-render'
8
9const DEFAULT_PROFILE_CONTEXT: ProfileContextType = {
10 profile: {
11 id: 1,
12 auth0_id: 'auth0|test',
13 gotrue_id: 'gotrue-test',
14 username: 'testuser',
15 primary_email: 'test@example.com',
16 first_name: null,
17 last_name: null,
18 mobile: null,
19 is_alpha_user: false,
20 is_sso_user: false,
21 disabled_features: [],
22 free_project_limit: null,
23 },
24 error: null,
25 isLoading: false,
26 isError: false,
27 isSuccess: true,
28}
29
30test('still navigates after parent re-renders during an in-flight POST', async () => {
31 // Resolve manually so we can inject a re-render while the POST is in flight.
32 let resolveSession: (value: { nonce: string }) => void = () => {}
33 const createCliLoginSessionMock = vi.spyOn(cliLogin, 'createCliLoginSession').mockImplementation(
34 () =>
35 new Promise<{ nonce: string }>((resolve) => {
36 resolveSession = resolve
37 })
38 )
39 const initialNavigate = vi.fn()
40 const { rerender } = customRender(
41 <CliLoginScreen
42 isLoggedIn
43 routerReady
44 sessionId="session-test"
45 publicKey="public-key-test"
46 tokenName="local-dev"
47 navigate={initialNavigate}
48 />,
49 { profileContext: DEFAULT_PROFILE_CONTEXT }
50 )
51
52 await waitFor(() => {
53 expect(createCliLoginSessionMock).toHaveBeenCalledTimes(1)
54 })
55
56 // Parent re-renders mid-POST with a brand-new navigate ref. This was the
57 // production hang: the cleanup would invalidate the success handler and
58 // the ref guard would skip the retry, leaving the screen on the loader.
59 const laterNavigate = vi.fn()
60 rerender(
61 <CliLoginScreen
62 isLoggedIn
63 routerReady
64 sessionId="session-test"
65 publicKey="public-key-test"
66 tokenName="local-dev"
67 navigate={laterNavigate}
68 />
69 )
70
71 resolveSession({ nonce: 'ABCDEFGH12345678' })
72
73 await waitFor(() => {
74 expect(laterNavigate).toHaveBeenCalledWith('/cli/login?device_code=ABCDEFGH')
75 })
76 expect(initialNavigate).not.toHaveBeenCalled()
77 expect(createCliLoginSessionMock).toHaveBeenCalledTimes(1)
78})