import { QueryClientProvider, useQuery } from '@tanstack/react-query' import { act, renderHook, waitFor } from '@testing-library/react-native' import React from 'react' import { Alert } from 'react-native' import { buildTestQueryClient } from '../../__utils__/query_client' import { getConversationsRequestArgs } from '../../utils/request/conversation' import { getConversationRequestArgs, useConversationArchive } from '../use_conversation' import { currentPersonQueryKey } from '../use_current_person' import { getRequestQueryKey } from '../use_suspense_api' const mockPost = jest.fn() jest.mock('../use_api_client', () => ({ useApiClient: () => ({ chat: { post: mockPost } }), })) const conversation_id = 7 const conversationKey = getRequestQueryKey(getConversationRequestArgs({ conversation_id })) const conversationsKey = getRequestQueryKey(getConversationsRequestArgs({ gids: 'all' })) const buildQueryClient = () => buildTestQueryClient({ defaultOptions: { queries: { gcTime: Infinity } } }) let queryClient = buildQueryClient() const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const isInvalidated = (queryKey: unknown[]) => queryClient.getQueryCache().find({ queryKey, exact: true })?.state.isInvalidated describe('useConversationArchive', () => { beforeEach(() => { queryClient = buildQueryClient() queryClient.setQueryData(conversationKey, { data: { id: conversation_id } }) queryClient.setQueryData(conversationsKey, { data: [] }) queryClient.setQueryData(currentPersonQueryKey, { data: { id: 1, canBrowseArchivedConversations: false }, }) mockPost.mockReset() jest.spyOn(Alert, 'alert').mockImplementation(() => {}) }) afterEach(() => { jest.restoreAllMocks() }) it.each([ [true, 'archive'], [false, 'unarchive'], ])( 'posts archived=%s to the %s action, then invalidates the conversation and list', async (archived, action) => { mockPost.mockResolvedValue({ data: { id: conversation_id } }) const { result } = renderHook(() => useConversationArchive({ conversation_id }), { wrapper }) await act(() => result.current.mutateAsync(archived)) expect(mockPost).toHaveBeenCalledWith({ url: `/me/conversations/7/${action}`, data: { data: { type: 'Conversation', attributes: {} } }, }) expect(isInvalidated(conversationKey)).toBe(true) expect(isInvalidated(conversationsKey)).toBe(true) expect(isInvalidated(currentPersonQueryKey)).toBe(false) } ) it.each([ [true, true], [false, false], ])( 'sets archive access in the person cache to %s after archived=%s', async (archived, canBrowse) => { mockPost.mockResolvedValue({ data: { id: conversation_id } }) const { result } = renderHook(() => useConversationArchive({ conversation_id }), { wrapper }) await act(() => result.current.mutateAsync(archived)) expect(queryClient.getQueryData(currentPersonQueryKey)).toEqual({ data: { id: 1, canBrowseArchivedConversations: canBrowse }, }) } ) it('stays pending until the conversation refetch finishes', async () => { mockPost.mockResolvedValue({ data: { id: conversation_id } }) let finishRefetch = () => {} const { result } = renderHook( () => { useQuery({ queryKey: conversationKey, queryFn: () => new Promise(resolve => { finishRefetch = () => resolve({ data: { id: conversation_id } }) }), staleTime: Infinity, }) return useConversationArchive({ conversation_id }) }, { wrapper } ) let archiving = Promise.resolve() await act(async () => { archiving = result.current.mutateAsync(true).then(() => {}) }) await waitFor(() => expect(result.current.isPending).toBe(true)) await act(async () => { finishRefetch() await archiving }) await waitFor(() => expect(result.current.isPending).toBe(false)) }) it("doesn't wait on the list refetch", async () => { mockPost.mockResolvedValue({ data: { id: conversation_id } }) const { result } = renderHook( () => { useQuery({ queryKey: conversationsKey, queryFn: () => new Promise(() => {}), staleTime: Infinity, }) return useConversationArchive({ conversation_id }) }, { wrapper } ) await act(() => result.current.mutateAsync(true)) expect(result.current.isPending).toBe(false) }) it.each([ [true, 'archive'], [false, 'unarchive'], ])('alerts when archived=%s fails', async (archived, action) => { mockPost.mockRejectedValue(new Error('nope')) const { result } = renderHook(() => useConversationArchive({ conversation_id }), { wrapper }) await act(() => result.current.mutateAsync(archived).catch(() => {})) expect(Alert.alert).toHaveBeenCalledWith( 'Oops', `Couldn't ${action} this conversation. Please try again.` ) }) })