UNPKG

@sanity/sdk

Version:
259 lines (218 loc) 8.3 kB
import {NEVER} from 'rxjs' import {beforeEach, describe, it} from 'vitest' import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance' import {AuthStateType} from './authStateType' import {getAuthState} from './authStore' import {handleAuthCallback} from './handleAuthCallback' import {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser' import {subscribeToStorageEventsAndSetToken} from './subscribeToStorageEventsAndSetToken' import {getAuthCode, getTokenFromLocation, getTokenFromStorage} from './utils' vi.mock('./utils', async (importOriginal) => { const original = await importOriginal<typeof import('./utils')>() return { ...original, getTokenFromStorage: vi.fn(), getAuthCode: vi.fn(), getTokenFromLocation: vi.fn(), } }) vi.mock('./subscribeToStateAndFetchCurrentUser') vi.mock('./subscribeToStorageEventsAndSetToken') let instance: SanityInstance | undefined describe('handleCallback', () => { beforeEach(() => { vi.clearAllMocks() vi.mocked(subscribeToStateAndFetchCurrentUser).mockImplementation(() => NEVER.subscribe()) vi.mocked(subscribeToStorageEventsAndSetToken).mockImplementation(() => NEVER.subscribe()) }) afterEach(() => { instance?.dispose() }) it('trades the auth code for a token, sets the state to logged in, and sets the token in storage', async () => { const request = vi.fn().mockResolvedValue({token: 'new-token'}) const clientFactory = vi.fn().mockReturnValue({request}) const setItem = vi.fn() vi.mocked(getTokenFromStorage).mockReturnValue(null) const authCode = 'auth-code' vi.mocked(getAuthCode).mockReturnValue(authCode) instance = createSanityInstance({ projectId: 'p', dataset: 'd', auth: { clientFactory, storageArea: {setItem} as unknown as Storage, }, }) const authState = getAuthState(instance) expect(authState.getCurrent()).toMatchObject({isExchangingToken: false}) const resultPromise = handleAuthCallback( instance, 'https://example.com/callback?foo=bar#withSid=code', ) expect(authState.getCurrent()).toMatchObject({isExchangingToken: true}) const result = await resultPromise expect(result).toBe('https://example.com/callback?foo=bar') expect(clientFactory).toHaveBeenCalledWith({ apiVersion: '2021-06-07', requestTagPrefix: 'sanity.sdk.auth', useProjectHostname: false, useCdn: false, }) expect(request).toHaveBeenLastCalledWith({ method: 'GET', query: {sid: authCode}, tag: 'fetch-token', uri: '/auth/fetch', }) expect(setItem).toHaveBeenCalledWith('__sanity_auth_token', '{"token":"new-token"}') }) it('returns early if there is a provided token', async () => { const mockRequest = vi.fn().mockResolvedValue({token: 'new-token'}) const clientFactory = vi.fn().mockReturnValue({request: mockRequest}) const setItem = vi.fn() vi.mocked(getTokenFromStorage).mockReturnValue(null) const authCode = 'auth-code' vi.mocked(getAuthCode).mockReturnValue(authCode) instance = createSanityInstance({ projectId: 'p', dataset: 'd', auth: { clientFactory, storageArea: {setItem} as unknown as Storage, token: 'provided-token', }, }) const result = await handleAuthCallback( instance, 'https://example.com/callback?foo=bar#withSid=code', ) expect(result).toBe(false) expect(clientFactory).not.toHaveBeenCalled() expect(setItem).not.toHaveBeenCalled() }) it('returns early if already exchanging the the token', async () => { let resolveRequest!: (value: {token: string; label: string}) => void const requestPromise = new Promise<{token: string; label: string}>((resolve) => { resolveRequest = resolve }) vi.mocked(getAuthCode).mockReturnValue('code') const request = vi.fn().mockReturnValue(requestPromise) const clientFactory = vi.fn().mockReturnValue({request}) const setItem = vi.fn() instance = createSanityInstance({ projectId: 'p', dataset: 'd', auth: { clientFactory, storageArea: {setItem: setItem as Storage['setItem']} as Storage, }, }) const authState = getAuthState(instance) expect(authState.getCurrent()).toMatchObject({type: AuthStateType.LOGGING_IN}) const locationHref = 'https://example.com/callback?foo=bar#withSid=code' const originalResultPromise = handleAuthCallback(instance, locationHref) expect(authState.getCurrent()).toMatchObject({ type: AuthStateType.LOGGING_IN, isExchangingToken: true, }) // ensures mock calls are reset to zero clientFactory.mockClear() setItem.mockClear() // notice how this resolves first const earlyResult = await handleAuthCallback(instance, locationHref) expect(earlyResult).toBe(false) expect(clientFactory).not.toHaveBeenCalled() expect(setItem).not.toHaveBeenCalled() // this will resolve resolveRequest({token: 'token', label: 'label'}) expect(await originalResultPromise).toBe('https://example.com/callback?foo=bar') // expect(result).toBe(false) }) it('returns early if there is no auth code present', async () => { const clientFactory = vi.fn() const setItem = vi.fn() instance = createSanityInstance({ projectId: 'p', dataset: 'd', auth: { clientFactory, storageArea: {setItem} as unknown as Storage, }, }) vi.mocked(getAuthCode).mockReturnValue(null) const result = await handleAuthCallback( instance, 'https://example.com/callback?foo=bar#withSid=code', ) expect(result).toBe(false) expect(clientFactory).not.toHaveBeenCalled() expect(setItem).not.toHaveBeenCalled() }) it('sets an auth error and returns the cleaned URL if exchanging the token fails', async () => { const error = new Error('test error') const mockRequest = vi.fn().mockRejectedValue(error) const clientFactory = vi.fn().mockReturnValue({request: mockRequest}) const setItem = vi.fn() vi.mocked(getTokenFromStorage).mockReturnValue(null) const authCode = 'auth-code' vi.mocked(getAuthCode).mockReturnValue(authCode) instance = createSanityInstance({ projectId: 'p', dataset: 'd', auth: { clientFactory, storageArea: {setItem} as unknown as Storage, }, }) const authState = getAuthState(instance) const result = await handleAuthCallback( instance, 'https://example.com/callback?foo=bar#withSid=code', ) expect(result).toBe('https://example.com/callback?foo=bar') expect(authState.getCurrent()).toMatchObject({type: AuthStateType.ERROR, error}) expect(clientFactory).toHaveBeenCalledWith({ apiVersion: '2021-06-07', requestTagPrefix: 'sanity.sdk.auth', useProjectHostname: false, useCdn: false, }) expect(mockRequest).toHaveBeenLastCalledWith({ method: 'GET', query: {sid: authCode}, tag: 'fetch-token', uri: '/auth/fetch', }) }) it('sets the auth state to logged in when token is found in URL hash', async () => { const clientFactory = vi.fn() const setItem = vi.fn() vi.mocked(getTokenFromStorage).mockReturnValue(null) vi.mocked(getAuthCode).mockReturnValue(null) vi.mocked(getTokenFromLocation).mockReturnValue('hash-token') instance = createSanityInstance({ projectId: 'p', dataset: 'd', auth: { clientFactory, storageArea: {setItem} as unknown as Storage, }, }) const authState = getAuthState(instance) const result = await handleAuthCallback( instance, 'https://example.com/callback?foo=bar#token=hash-token', ) expect(result).toBe('https://example.com/callback?foo=bar') expect(getTokenFromLocation).toHaveBeenCalledWith( 'https://example.com/callback?foo=bar#token=hash-token', ) expect(authState.getCurrent()).toMatchObject({ type: AuthStateType.LOGGED_IN, token: 'hash-token', currentUser: null, }) expect(clientFactory).not.toHaveBeenCalled() expect(setItem).not.toHaveBeenCalled() }) })