Add tests for new Who To Follow panel
This commit is contained in:
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';
|
||||||
|
|
||||||
describe('<WhoToFollow />', () => {
|
const buildTruthSuggestion = (id: string) => ({
|
||||||
it('renders suggested accounts', () => {
|
account_avatar: 'avatar',
|
||||||
const store = {
|
account_id: id,
|
||||||
accounts: ImmutableMap({
|
acct: 'acct',
|
||||||
'1': normalizeAccount({
|
display_name: 'my name',
|
||||||
id: '1',
|
note: 'hello',
|
||||||
acct: 'username',
|
verified: true,
|
||||||
display_name: 'My name',
|
});
|
||||||
avatar: 'test.jpg',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
suggestions: {
|
|
||||||
items: ImmutableOrderedSet([{
|
|
||||||
source: 'staff',
|
|
||||||
account: '1',
|
|
||||||
}]),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
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 />', () => {
|
||||||
|
let store: any;
|
||||||
|
|
||||||
|
describe('using Truth Social software', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
store = rootState
|
||||||
|
.set('me', '1234')
|
||||||
|
.set('instance', normalizeInstance({
|
||||||
|
version: '3.4.1 (compatible; TruthSocial 1.0.0)',
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a single suggestion', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('/api/v1/truth/carousels/suggestions')
|
||||||
|
.reply(200, [buildTruthSuggestion('1')], {
|
||||||
|
link: '<https://example.com/api/v1/truth/carousels/suggestions?since_id=1>; rel=\'prev\'',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders suggested accounts', async () => {
|
||||||
render(<WhoToFollowPanel limit={1} />, undefined, store);
|
render(<WhoToFollowPanel limit={1} />, undefined, store);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.getByTestId('account')).toHaveTextContent(/my name/i);
|
expect(screen.getByTestId('account')).toHaveTextContent(/my name/i);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders multiple accounts', () => {
|
describe('with a multiple suggestion', () => {
|
||||||
const store = {
|
beforeEach(() => {
|
||||||
accounts: ImmutableMap({
|
__stub((mock) => {
|
||||||
'1': normalizeAccount({
|
mock.onGet('/api/v1/truth/carousels/suggestions')
|
||||||
id: '1',
|
.reply(200, [buildTruthSuggestion('1'), buildTruthSuggestion('2')], {
|
||||||
acct: 'username',
|
link: '<https://example.com/api/v1/truth/carousels/suggestions?since_id=1>; rel=\'prev\'',
|
||||||
display_name: 'My name',
|
});
|
||||||
avatar: 'test.jpg',
|
});
|
||||||
}),
|
});
|
||||||
'2': normalizeAccount({
|
|
||||||
id: '1',
|
|
||||||
acct: 'username2',
|
|
||||||
display_name: 'My other name',
|
|
||||||
avatar: 'test.jpg',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
suggestions: {
|
|
||||||
items: ImmutableOrderedSet([
|
|
||||||
{
|
|
||||||
source: 'staff',
|
|
||||||
account: '1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: 'staff',
|
|
||||||
account: '2',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
render(<WhoToFollowPanel limit={3} />, undefined, store);
|
it('renders suggested accounts', async () => {
|
||||||
|
render(<WhoToFollowPanel limit={2} />, undefined, store);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.queryAllByTestId('account')).toHaveLength(2);
|
expect(screen.queryAllByTestId('account')).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('respects the limit prop', () => {
|
describe('with a set limit', () => {
|
||||||
const store = {
|
beforeEach(() => {
|
||||||
accounts: ImmutableMap({
|
__stub((mock) => {
|
||||||
'1': normalizeAccount({
|
mock.onGet('/api/v1/truth/carousels/suggestions')
|
||||||
id: '1',
|
.reply(200, [buildTruthSuggestion('1'), buildTruthSuggestion('2')], {
|
||||||
acct: 'username',
|
link: '<https://example.com/api/v1/truth/carousels/suggestions?since_id=1>; rel=\'prev\'',
|
||||||
display_name: 'My name',
|
});
|
||||||
avatar: 'test.jpg',
|
});
|
||||||
}),
|
});
|
||||||
'2': normalizeAccount({
|
|
||||||
id: '1',
|
|
||||||
acct: 'username2',
|
|
||||||
display_name: 'My other name',
|
|
||||||
avatar: 'test.jpg',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
suggestions: {
|
|
||||||
items: ImmutableOrderedSet([
|
|
||||||
{
|
|
||||||
source: 'staff',
|
|
||||||
account: '1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: 'staff',
|
|
||||||
account: '2',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
it('respects the limit prop', async () => {
|
||||||
render(<WhoToFollowPanel limit={1} />, undefined, store);
|
render(<WhoToFollowPanel limit={1} />, undefined, store);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.queryAllByTestId('account')).toHaveLength(1);
|
expect(screen.queryAllByTestId('account')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders empty', () => {
|
describe('when the API returns an empty list', () => {
|
||||||
const store = {
|
beforeEach(() => {
|
||||||
accounts: ImmutableMap({
|
__stub((mock) => {
|
||||||
'1': normalizeAccount({
|
mock.onGet('/api/v1/truth/carousels/suggestions')
|
||||||
id: '1',
|
.reply(200, [], {
|
||||||
acct: 'username',
|
link: '',
|
||||||
display_name: 'My name',
|
});
|
||||||
avatar: 'test.jpg',
|
});
|
||||||
}),
|
});
|
||||||
'2': normalizeAccount({
|
|
||||||
id: '1',
|
|
||||||
acct: 'username2',
|
|
||||||
display_name: 'My other name',
|
|
||||||
avatar: 'test.jpg',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
suggestions: {
|
|
||||||
items: ImmutableOrderedSet([]),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
it('renders empty', async () => {
|
||||||
render(<WhoToFollowPanel limit={1} />, undefined, store);
|
render(<WhoToFollowPanel limit={1} />, undefined, store);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.queryAllByTestId('account')).toHaveLength(0);
|
expect(screen.queryAllByTestId('account')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('using Pleroma software', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
store = rootState.set('me', '1234');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a single suggestion', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('/api/v2/suggestions')
|
||||||
|
.reply(200, [buildSuggestion('1')], {
|
||||||
|
link: '<https://example.com/api/v2/suggestions?since_id=1>; rel=\'prev\'',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders suggested accounts', async () => {
|
||||||
|
render(<WhoToFollowPanel limit={1} />, undefined, store);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('account')).toHaveTextContent(/my name/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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…
Reference in New Issue