Add tests for new Who To Follow panel

environments/review-improve-pe-oqh7pz/deployments/1027
Justin 2 years ago
parent 63bd9a21fc
commit facd4e95f5

@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
import ScrollableList from 'soapbox/components/scrollable_list'; import ScrollableList from 'soapbox/components/scrollable_list';
import { Button, Card, CardBody, Stack, Text } from 'soapbox/components/ui'; import { Button, Card, CardBody, Stack, Text } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container'; import AccountContainer from 'soapbox/containers/account_container';
import useOnboardingSuggestions from 'soapbox/queries/suggestions'; import { useOnboardingSuggestions } from 'soapbox/queries/suggestions';
const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => { const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => {
const { data, fetchNextPage, hasNextPage, isFetching } = useOnboardingSuggestions(); const { data, fetchNextPage, hasNextPage, isFetching } = useOnboardingSuggestions();

@ -11,7 +11,7 @@ export default ({ limit }: { limit: number }) => {
return ( return (
<> <>
{new Array(limit).fill(undefined).map((_, idx) => ( {new Array(limit).fill(undefined).map((_, idx) => (
<HStack alignItems='center' space={2} className='animate-pulse'> <HStack key={idx} alignItems='center' space={2} className='animate-pulse'>
<Stack space={3} className='text-center'> <Stack space={3} className='text-center'>
<div <div
className='w-9 h-9 block mx-auto rounded-full bg-primary-200 dark:bg-primary-700' className='w-9 h-9 block mx-auto rounded-full bg-primary-200 dark:bg-primary-700'

@ -1,123 +1,201 @@
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
import React from 'react'; import React from 'react';
import { render, screen } from '../../../../jest/test-helpers'; import { __stub } from 'soapbox/api';
import { normalizeAccount } from '../../../../normalizers';
import { render, rootState, screen, waitFor } from '../../../../jest/test-helpers';
import { normalizeInstance } from '../../../../normalizers';
import WhoToFollowPanel from '../who-to-follow-panel'; import WhoToFollowPanel from '../who-to-follow-panel';
const buildTruthSuggestion = (id: string) => ({
account_avatar: 'avatar',
account_id: id,
acct: 'acct',
display_name: 'my name',
note: 'hello',
verified: true,
});
const buildSuggestion = (id: string) => ({
source: 'staff',
account: {
username: 'username',
verified: true,
id,
acct: 'acct',
avatar: 'avatar',
avatar_static: 'avatar',
display_name: 'my name',
},
});
describe('<WhoToFollow />', () => { describe('<WhoToFollow />', () => {
it('renders suggested accounts', () => { let store: any;
const store = {
accounts: ImmutableMap({
'1': normalizeAccount({
id: '1',
acct: 'username',
display_name: 'My name',
avatar: 'test.jpg',
}),
}),
suggestions: {
items: ImmutableOrderedSet([{
source: 'staff',
account: '1',
}]),
},
};
render(<WhoToFollowPanel limit={1} />, undefined, store);
expect(screen.getByTestId('account')).toHaveTextContent(/my name/i);
});
it('renders multiple accounts', () => { describe('using Truth Social software', () => {
const store = { beforeEach(() => {
accounts: ImmutableMap({ store = rootState
'1': normalizeAccount({ .set('me', '1234')
id: '1', .set('instance', normalizeInstance({
acct: 'username', version: '3.4.1 (compatible; TruthSocial 1.0.0)',
display_name: 'My name', }));
avatar: 'test.jpg', });
}),
'2': normalizeAccount({ describe('with a single suggestion', () => {
id: '1', beforeEach(() => {
acct: 'username2', __stub((mock) => {
display_name: 'My other name', mock.onGet('/api/v1/truth/carousels/suggestions')
avatar: 'test.jpg', .reply(200, [buildTruthSuggestion('1')], {
}), link: '<https://example.com/api/v1/truth/carousels/suggestions?since_id=1>; rel=\'prev\'',
}), });
suggestions: { });
items: ImmutableOrderedSet([ });
{
source: 'staff', it('renders suggested accounts', async () => {
account: '1', render(<WhoToFollowPanel limit={1} />, undefined, store);
},
{ await waitFor(() => {
source: 'staff', expect(screen.getByTestId('account')).toHaveTextContent(/my name/i);
account: '2', });
}, });
]), });
},
}; describe('with a multiple suggestion', () => {
beforeEach(() => {
render(<WhoToFollowPanel limit={3} />, undefined, store); __stub((mock) => {
expect(screen.queryAllByTestId('account')).toHaveLength(2); mock.onGet('/api/v1/truth/carousels/suggestions')
}); .reply(200, [buildTruthSuggestion('1'), buildTruthSuggestion('2')], {
link: '<https://example.com/api/v1/truth/carousels/suggestions?since_id=1>; rel=\'prev\'',
});
});
});
it('renders suggested accounts', async () => {
render(<WhoToFollowPanel limit={2} />, undefined, store);
await waitFor(() => {
expect(screen.queryAllByTestId('account')).toHaveLength(2);
});
});
});
describe('with a set limit', () => {
beforeEach(() => {
__stub((mock) => {
mock.onGet('/api/v1/truth/carousels/suggestions')
.reply(200, [buildTruthSuggestion('1'), buildTruthSuggestion('2')], {
link: '<https://example.com/api/v1/truth/carousels/suggestions?since_id=1>; rel=\'prev\'',
});
});
});
it('respects the limit prop', async () => {
render(<WhoToFollowPanel limit={1} />, undefined, store);
it('respects the limit prop', () => { await waitFor(() => {
const store = { expect(screen.queryAllByTestId('account')).toHaveLength(1);
accounts: ImmutableMap({ });
'1': normalizeAccount({ });
id: '1', });
acct: 'username',
display_name: 'My name', describe('when the API returns an empty list', () => {
avatar: 'test.jpg', beforeEach(() => {
}), __stub((mock) => {
'2': normalizeAccount({ mock.onGet('/api/v1/truth/carousels/suggestions')
id: '1', .reply(200, [], {
acct: 'username2', link: '',
display_name: 'My other name', });
avatar: 'test.jpg', });
}), });
}),
suggestions: { it('renders empty', async () => {
items: ImmutableOrderedSet([ render(<WhoToFollowPanel limit={1} />, undefined, store);
{
source: 'staff', await waitFor(() => {
account: '1', expect(screen.queryAllByTestId('account')).toHaveLength(0);
}, });
{ });
source: 'staff', });
account: '2',
},
]),
},
};
render(<WhoToFollowPanel limit={1} />, undefined, store);
expect(screen.queryAllByTestId('account')).toHaveLength(1);
}); });
it('renders empty', () => { describe('using Pleroma software', () => {
const store = { beforeEach(() => {
accounts: ImmutableMap({ store = rootState.set('me', '1234');
'1': normalizeAccount({ });
id: '1',
acct: 'username', describe('with a single suggestion', () => {
display_name: 'My name', beforeEach(() => {
avatar: 'test.jpg', __stub((mock) => {
}), mock.onGet('/api/v2/suggestions')
'2': normalizeAccount({ .reply(200, [buildSuggestion('1')], {
id: '1', link: '<https://example.com/api/v2/suggestions?since_id=1>; rel=\'prev\'',
acct: 'username2', });
display_name: 'My other name', });
avatar: 'test.jpg', });
}),
}), it('renders suggested accounts', async () => {
suggestions: { render(<WhoToFollowPanel limit={1} />, undefined, store);
items: ImmutableOrderedSet([]),
}, await waitFor(() => {
}; expect(screen.getByTestId('account')).toHaveTextContent(/my name/i);
});
render(<WhoToFollowPanel limit={1} />, undefined, store); });
expect(screen.queryAllByTestId('account')).toHaveLength(0); });
describe('with a multiple suggestion', () => {
beforeEach(() => {
__stub((mock) => {
mock.onGet('/api/v2/suggestions')
.reply(200, [buildSuggestion('1'), buildSuggestion('2')], {
link: '<https://example.com/api/v2/suggestions?since_id=1>; rel=\'prev\'',
});
});
});
it('renders suggested accounts', async () => {
render(<WhoToFollowPanel limit={2} />, undefined, store);
await waitFor(() => {
expect(screen.queryAllByTestId('account')).toHaveLength(2);
});
});
});
describe('with a set limit', () => {
beforeEach(() => {
__stub((mock) => {
mock.onGet('/api/v2/suggestions')
.reply(200, [buildSuggestion('1'), buildSuggestion('2')], {
link: '<https://example.com/api/v2/suggestions?since_id=1>; rel=\'prev\'',
});
});
});
it('respects the limit prop', async () => {
render(<WhoToFollowPanel limit={1} />, undefined, store);
await waitFor(() => {
expect(screen.queryAllByTestId('account')).toHaveLength(1);
});
});
});
describe('when the API returns an empty list', () => {
beforeEach(() => {
__stub((mock) => {
mock.onGet('/api/v2/suggestions')
.reply(200, [], {
link: '',
});
});
});
it('renders empty', async () => {
render(<WhoToFollowPanel limit={1} />, undefined, store);
await waitFor(() => {
expect(screen.queryAllByTestId('account')).toHaveLength(0);
});
});
});
}); });
}); });

@ -1,7 +1,7 @@
import { __stub } from 'soapbox/api'; import { __stub } from 'soapbox/api';
import { renderHook, waitFor } from 'soapbox/jest/test-helpers'; import { renderHook, waitFor } from 'soapbox/jest/test-helpers';
import useOnboardingSuggestions from '../suggestions'; import { useOnboardingSuggestions } from '../suggestions';
describe('useCarouselAvatars', () => { describe('useCarouselAvatars', () => {
describe('with a successful query', () => { describe('with a successful query', () => {
@ -17,7 +17,7 @@ describe('useCarouselAvatars', () => {
}); });
}); });
it('is successful', async() => { it('is successful', async () => {
const { result } = renderHook(() => useOnboardingSuggestions()); const { result } = renderHook(() => useOnboardingSuggestions());
await waitFor(() => expect(result.current.isFetching).toBe(false)); await waitFor(() => expect(result.current.isFetching).toBe(false));
@ -33,7 +33,7 @@ describe('useCarouselAvatars', () => {
}); });
}); });
it('is successful', async() => { it('is successful', async () => {
const { result } = renderHook(() => useOnboardingSuggestions()); const { result } = renderHook(() => useOnboardingSuggestions());
await waitFor(() => expect(result.current.isFetching).toBe(false)); await waitFor(() => expect(result.current.isFetching).toBe(false));

@ -4,7 +4,7 @@ import { fetchRelationships } from 'soapbox/actions/accounts';
import { importFetchedAccounts } from 'soapbox/actions/importer'; import { importFetchedAccounts } from 'soapbox/actions/importer';
import { SuggestedProfile } from 'soapbox/actions/suggestions'; import { SuggestedProfile } from 'soapbox/actions/suggestions';
import { getLinks } from 'soapbox/api'; import { getLinks } from 'soapbox/api';
import { useApi, useAppDispatch, useFeatures, useOwnAccount } from 'soapbox/hooks'; import { useApi, useAppDispatch, useFeatures } from 'soapbox/hooks';
import { PaginatedResult, removePageItem } from '../utils/queries'; import { PaginatedResult, removePageItem } from '../utils/queries';
@ -47,6 +47,10 @@ type TruthSuggestion = {
verified: boolean verified: boolean
} }
type Result = TruthSuggestion | {
account: string
}
type PageParam = { type PageParam = {
link?: string link?: string
} }
@ -66,12 +70,11 @@ const mapSuggestedProfileToAccount = (suggestedProfile: SuggestedProfile) => ({
}); });
const useSuggestions = () => { const useSuggestions = () => {
const account = useOwnAccount();
const api = useApi(); const api = useApi();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
const getV2Suggestions = async(pageParam: PageParam): Promise<PaginatedResult<TruthSuggestion | Suggestion>> => { const getV2Suggestions = async (pageParam: PageParam): Promise<PaginatedResult<Result>> => {
const endpoint = pageParam?.link || '/api/v2/suggestions'; const endpoint = pageParam?.link || '/api/v2/suggestions';
const response = await api.get<Suggestion[]>(endpoint); const response = await api.get<Suggestion[]>(endpoint);
const hasMore = !!response.headers.link; const hasMore = !!response.headers.link;
@ -83,13 +86,13 @@ const useSuggestions = () => {
dispatch(fetchRelationships(accountIds)); dispatch(fetchRelationships(accountIds));
return { return {
result: response.data, result: response.data.map(x => ({ ...x, account: x.account.id })),
link: nextLink, link: nextLink,
hasMore, hasMore,
}; };
}; };
const getTruthSuggestions = async(pageParam: PageParam): Promise<PaginatedResult<TruthSuggestion | Suggestion>> => { const getTruthSuggestions = async (pageParam: PageParam): Promise<PaginatedResult<Result>> => {
const endpoint = pageParam?.link || '/api/v1/truth/carousels/suggestions'; const endpoint = pageParam?.link || '/api/v1/truth/carousels/suggestions';
const response = await api.get<TruthSuggestion[]>(endpoint); const response = await api.get<TruthSuggestion[]>(endpoint);
const hasMore = !!response.headers.link; const hasMore = !!response.headers.link;
@ -118,7 +121,6 @@ const useSuggestions = () => {
({ pageParam }: any) => getSuggestions(pageParam), ({ pageParam }: any) => getSuggestions(pageParam),
{ {
keepPreviousData: true, keepPreviousData: true,
enabled: !!account,
getNextPageParam: (config) => { getNextPageParam: (config) => {
if (config?.hasMore) { if (config?.hasMore) {
return { nextLink: config?.link }; return { nextLink: config?.link };
@ -153,7 +155,7 @@ function useOnboardingSuggestions() {
const api = useApi(); const api = useApi();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const getV2Suggestions = async(pageParam: any): Promise<{ data: Suggestion[], link: string | undefined, hasMore: boolean }> => { const getV2Suggestions = async (pageParam: any): Promise<{ data: Suggestion[], link: string | undefined, hasMore: boolean }> => {
const link = pageParam?.link || '/api/v2/suggestions'; const link = pageParam?.link || '/api/v2/suggestions';
const response = await api.get<Suggestion[]>(link); const response = await api.get<Suggestion[]>(link);
const hasMore = !!response.headers.link; const hasMore = !!response.headers.link;
@ -193,4 +195,4 @@ function useOnboardingSuggestions() {
}; };
} }
export { useOnboardingSuggestions as default, useSuggestions, useDismissSuggestion }; export { useOnboardingSuggestions, useSuggestions, useDismissSuggestion };
Loading…
Cancel
Save