import {act, fireEvent, render, waitFor} from '@testing-library/react-native' import {type Shadow} from '#/state/cache/post-shadow' import {type app} from '#/lexicons' import {PostMenuControlItems} from './PostMenuControlItems' const mockQueueRepost = jest.fn() const mockQueueUnrepost = jest.fn() const mockAutoLike = jest.fn() jest.mock('#/alf', () => ({ atoms: {}, useTheme: () => ({palette: {}, atoms: {}}), })) jest.mock('@lingui/react', () => ({useLingui: () => ({_: () => ''})})) jest.mock('#/state/queries/post', () => ({ usePostRepostMutationQueue: () => [mockQueueRepost, mockQueueUnrepost], usePostLikeMutationQueue: () => [jest.fn()], })) jest.mock('../useAutoLikeAfterRepost', () => ({ useAutoLikeAfterRepost: () => mockAutoLike, })) jest.mock('../BookmarkButton', () => ({usePostBookmark: () => ({})})) jest.mock('#/state/session', () => ({ useRequireAuth: () => (action: () => void) => action(), })) jest.mock('#/state/feed-feedback', () => ({ useFeedFeedbackContext: () => ({sendInteraction: jest.fn()}), })) jest.mock('#/lib/hooks/useOpenComposer', () => ({ useOpenComposer: () => ({openComposer: jest.fn()}), })) jest.mock('#/lib/haptics', () => ({useHaptics: () => ({confirm: jest.fn()})})) jest.mock('#/analytics', () => ({useAnalytics: () => ({metric: jest.fn()})})) jest.mock('#/components/Toast', () => ({show: jest.fn()})) jest.mock('#/components/Menu', () => { const {Pressable, View} = jest.requireActual('react-native') return { Group: View, Divider: () => null, ItemIcon: () => null, ItemText: () => null, Item: ({testID, onPress}: {testID: string; onPress: () => void}) => ( ), } }) function mount(reposted = false) { const post = { uri: 'at://did:plc:author/app.bsky.feed.post/abc', author: {did: 'did:plc:author'}, viewer: { repost: reposted ? 'at://did:plc:me/app.bsky.feed.repost/abc' : undefined, }, } as unknown as Shadow return render( , ) } beforeEach(() => { jest.clearAllMocks() mockQueueRepost.mockResolvedValue(undefined) mockQueueUnrepost.mockResolvedValue(undefined) mockAutoLike.mockResolvedValue(undefined) }) it('runs auto-like after a successful compact-menu repost', async () => { const {getByTestId} = mount() fireEvent.press(getByTestId('postDropdownRepostBtn')) await waitFor(() => expect(mockAutoLike).toHaveBeenCalledTimes(1)) expect(mockQueueRepost.mock.invocationCallOrder[0]).toBeLessThan( mockAutoLike.mock.invocationCallOrder[0], ) }) it('does not auto-like when undoing a repost', async () => { const {getByTestId} = mount(true) fireEvent.press(getByTestId('postDropdownRepostBtn')) await waitFor(() => expect(mockQueueUnrepost).toHaveBeenCalledTimes(1)) expect(mockAutoLike).not.toHaveBeenCalled() }) it('ignores duplicate activation while a repost is pending', async () => { let finish!: () => void const pending = new Promise(resolve => { finish = resolve }) mockQueueRepost.mockReturnValueOnce(pending) const {getByTestId} = mount() fireEvent.press(getByTestId('postDropdownRepostBtn')) fireEvent.press(getByTestId('postDropdownRepostBtn')) expect(mockQueueRepost).toHaveBeenCalledTimes(1) await act(async () => { finish() await pending }) expect(mockAutoLike).toHaveBeenCalledTimes(1) })