From 931f2e16d84d8250d144dc6d4ff5c68fdd46b686 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 14:37:55 -0400 Subject: [PATCH 1/9] Add tests for removeFromFollowers() action --- .../actions/__tests__/accounts.test.ts | 67 +++++++++++++++++++ app/soapbox/actions/accounts.ts | 13 ++-- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index 2ea60bd80..6a8d6ddd5 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -12,6 +12,7 @@ import { fetchAccountByUsername, followAccount, muteAccount, + removeFromFollowers, subscribeAccount, unblockAccount, unfollowAccount, @@ -921,3 +922,69 @@ describe('unsubscribeAccount()', () => { }); }); }); + +describe('removeFromFollowers()', () => { + const id = '1'; + + describe('when logged out', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', null); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(removeFromFollowers(id)); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', '123'); + store = mockStore(state); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onPost(`/api/v1/accounts/${id}/remove_from_followers`).reply(200, {}); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'ACCOUNT_REMOVE_FROM_FOLLOWERS_REQUEST', id }, + { + type: 'ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS', + relationship: {}, + }, + ]; + await store.dispatch(removeFromFollowers(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onPost(`/api/v1/accounts/${id}/remove_from_followers`).networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'ACCOUNT_REMOVE_FROM_FOLLOWERS_REQUEST', id }, + { type: 'ACCOUNT_REMOVE_FROM_FOLLOWERS_FAIL', id, error: new Error('Network Error') }, + ]; + await store.dispatch(removeFromFollowers(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); \ No newline at end of file diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index e5a92fae9..0249ac4f5 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -468,15 +468,14 @@ const unsubscribeAccountFail = (error: AxiosError) => ({ const removeFromFollowers = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return null; - dispatch(muteAccountRequest(id)); + dispatch(removeFromFollowersRequest(id)); - api(getState).post(`/api/v1/accounts/${id}/remove_from_followers`).then(response => { - dispatch(removeFromFollowersSuccess(response.data)); - }).catch(error => { - dispatch(removeFromFollowersFail(id, error)); - }); + return api(getState) + .post(`/api/v1/accounts/${id}/remove_from_followers`) + .then(response => dispatch(removeFromFollowersSuccess(response.data))) + .catch(error => dispatch(removeFromFollowersFail(id, error))); }; const removeFromFollowersRequest = (id: string) => ({ From 761d524fdb9fc6e3e5cbf6142ecaa80650c689dd Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 14:47:43 -0400 Subject: [PATCH 2/9] Add tests for fetchFollowers() action --- .../actions/__tests__/accounts.test.ts | 58 +++++++++++++++++++ app/soapbox/actions/accounts.ts | 19 +++--- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index 6a8d6ddd5..90de6544a 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -10,6 +10,7 @@ import { createAccount, fetchAccount, fetchAccountByUsername, + fetchFollowers, followAccount, muteAccount, removeFromFollowers, @@ -983,6 +984,63 @@ describe('removeFromFollowers()', () => { await store.dispatch(removeFromFollowers(id)); const actions = store.getActions(); + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); + +describe('fetchFollowers()', () => { + const id = '1'; + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}); + store = mockStore(state); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet(`/api/v1/accounts/${id}/followers`).reply(200, [], { + link: `; rel='prev'`, + }); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOWERS_FETCH_REQUEST', id }, + { type: 'ACCOUNTS_IMPORT', accounts: [] }, + { + type: 'FOLLOWERS_FETCH_SUCCESS', + id, + accounts: [], + next: null, + }, + ]; + await store.dispatch(fetchFollowers(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet(`/api/v1/accounts/${id}/followers`).networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOWERS_FETCH_REQUEST', id }, + { type: 'FOLLOWERS_FETCH_FAIL', id, error: new Error('Network Error') }, + ]; + await store.dispatch(fetchFollowers(id)); + const actions = store.getActions(); + expect(actions).toEqual(expectedActions); }); }); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index 0249ac4f5..75a2f41a9 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -498,15 +498,18 @@ const fetchFollowers = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(fetchFollowersRequest(id)); - api(getState).get(`/api/v1/accounts/${id}/followers`).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); + return api(getState) + .get(`/api/v1/accounts/${id}/followers`) + .then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedAccounts(response.data)); - dispatch(fetchFollowersSuccess(id, response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); - }).catch(error => { - dispatch(fetchFollowersFail(id, error)); - }); + dispatch(importFetchedAccounts(response.data)); + dispatch(fetchFollowersSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); + }) + .catch(error => { + dispatch(fetchFollowersFail(id, error)); + }); }; const fetchFollowersRequest = (id: string) => ({ From c2620b017b942779ab5c1fc7a35d89c32805da94 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 14:59:37 -0400 Subject: [PATCH 3/9] Add tests for expandFollowers() action --- .../actions/__tests__/accounts.test.ts | 90 ++++++++++++++++++- app/soapbox/actions/accounts.ts | 23 ++--- 2 files changed, 102 insertions(+), 11 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index 90de6544a..b1db9d916 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -8,6 +8,7 @@ import { normalizeAccount } from '../../normalizers'; import { blockAccount, createAccount, + expandFollowers, fetchAccount, fetchAccountByUsername, fetchFollowers, @@ -1045,4 +1046,91 @@ describe('fetchFollowers()', () => { }); }); }); -}); \ No newline at end of file +}); + +describe('expandFollowers()', () => { + const id = '1'; + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('user_lists', ImmutableMap({ + followers: ImmutableMap({ + [id]: ImmutableMap({ + next: 'next_url', + }), + }), + })) + .set('me', '123'); + store = mockStore(state); + }); + + describe('when the url is null', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('user_lists', ImmutableMap({ + followers: ImmutableMap({ + [id]: ImmutableMap({ + next: null, + }), + }), + })) + .set('me', '123'); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(expandFollowers(id)); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet('next_url').reply(200, [], { + link: `; rel='prev'`, + }); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOWERS_EXPAND_REQUEST', id }, + { type: 'ACCOUNTS_IMPORT', accounts: [] }, + { + type: 'FOLLOWERS_EXPAND_SUCCESS', + id, + accounts: [], + next: null, + }, + ]; + await store.dispatch(expandFollowers(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet('next_url').networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOWERS_EXPAND_REQUEST', id }, + { type: 'FOLLOWERS_EXPAND_FAIL', id, error: new Error('Network Error') }, + ]; + await store.dispatch(expandFollowers(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index 75a2f41a9..f1e3b277c 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -532,25 +532,28 @@ const fetchFollowersFail = (id: string, error: AxiosError) => ({ const expandFollowers = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return null; const url = getState().user_lists.getIn(['followers', id, 'next']); if (url === null) { - return; + return null; } dispatch(expandFollowersRequest(id)); - api(getState).get(url).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); + return api(getState) + .get(url) + .then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedAccounts(response.data)); - dispatch(expandFollowersSuccess(id, response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); - }).catch(error => { - dispatch(expandFollowersFail(id, error)); - }); + dispatch(importFetchedAccounts(response.data)); + dispatch(expandFollowersSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); + }) + .catch(error => { + dispatch(expandFollowersFail(id, error)); + }); }; const expandFollowersRequest = (id: string) => ({ From 04a2d6be990d5238cf9dd63e2b9e5216deb8dca6 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 15:03:48 -0400 Subject: [PATCH 4/9] Add tests for fetchFollowing() action --- .../actions/__tests__/accounts.test.ts | 60 ++++++++++++++++++- app/soapbox/actions/accounts.ts | 19 +++--- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index b1db9d916..1b63739a8 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -12,6 +12,7 @@ import { fetchAccount, fetchAccountByUsername, fetchFollowers, + fetchFollowing, followAccount, muteAccount, removeFromFollowers, @@ -996,7 +997,7 @@ describe('fetchFollowers()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootReducer(undefined, {}); + const state = rootReducer(undefined, {}).set('me', '123'); store = mockStore(state); }); @@ -1134,3 +1135,60 @@ describe('expandFollowers()', () => { }); }); }); + +describe('fetchFollowing()', () => { + const id = '1'; + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', '123'); + store = mockStore(state); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet(`/api/v1/accounts/${id}/following`).reply(200, [], { + link: `; rel='prev'`, + }); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOWING_FETCH_REQUEST', id }, + { type: 'ACCOUNTS_IMPORT', accounts: [] }, + { + type: 'FOLLOWING_FETCH_SUCCESS', + id, + accounts: [], + next: null, + }, + ]; + await store.dispatch(fetchFollowing(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet(`/api/v1/accounts/${id}/following`).networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOWING_FETCH_REQUEST', id }, + { type: 'FOLLOWING_FETCH_FAIL', id, error: new Error('Network Error') }, + ]; + await store.dispatch(fetchFollowing(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index f1e3b277c..becf822d6 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -578,15 +578,18 @@ const fetchFollowing = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(fetchFollowingRequest(id)); - api(getState).get(`/api/v1/accounts/${id}/following`).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); + return api(getState) + .get(`/api/v1/accounts/${id}/following`) + .then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedAccounts(response.data)); - dispatch(fetchFollowingSuccess(id, response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); - }).catch(error => { - dispatch(fetchFollowingFail(id, error)); - }); + dispatch(importFetchedAccounts(response.data)); + dispatch(fetchFollowingSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); + }) + .catch(error => { + dispatch(fetchFollowingFail(id, error)); + }); }; const fetchFollowingRequest = (id: string) => ({ From 2cc38554fa8918ea3fb93232d7f084c919fe8220 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 15:06:28 -0400 Subject: [PATCH 5/9] Add tests for expandFollowing() action --- .../actions/__tests__/accounts.test.ts | 116 ++++++++++++++++++ app/soapbox/actions/accounts.ts | 23 ++-- 2 files changed, 129 insertions(+), 10 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index 1b63739a8..ad7b55851 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -9,6 +9,7 @@ import { blockAccount, createAccount, expandFollowers, + expandFollowing, fetchAccount, fetchAccountByUsername, fetchFollowers, @@ -1052,6 +1053,20 @@ describe('fetchFollowers()', () => { describe('expandFollowers()', () => { const id = '1'; + describe('when logged out', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', null); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(expandFollowers(id)); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + describe('when logged in', () => { beforeEach(() => { const state = rootReducer(undefined, {}) @@ -1192,3 +1207,104 @@ describe('fetchFollowing()', () => { }); }); }); + +describe('expandFollowing()', () => { + const id = '1'; + + describe('when logged out', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', null); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(expandFollowing(id)); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('user_lists', ImmutableMap({ + following: ImmutableMap({ + [id]: ImmutableMap({ + next: 'next_url', + }), + }), + })) + .set('me', '123'); + store = mockStore(state); + }); + + describe('when the url is null', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('user_lists', ImmutableMap({ + following: ImmutableMap({ + [id]: ImmutableMap({ + next: null, + }), + }), + })) + .set('me', '123'); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(expandFollowing(id)); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet('next_url').reply(200, [], { + link: `; rel='prev'`, + }); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOWING_EXPAND_REQUEST', id }, + { type: 'ACCOUNTS_IMPORT', accounts: [] }, + { + type: 'FOLLOWING_EXPAND_SUCCESS', + id, + accounts: [], + next: null, + }, + ]; + await store.dispatch(expandFollowing(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet('next_url').networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOWING_EXPAND_REQUEST', id }, + { type: 'FOLLOWING_EXPAND_FAIL', id, error: new Error('Network Error') }, + ]; + await store.dispatch(expandFollowing(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index becf822d6..ca6f3b1d5 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -612,25 +612,28 @@ const fetchFollowingFail = (id: string, error: AxiosError) => ({ const expandFollowing = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return null; const url = getState().user_lists.getIn(['following', id, 'next']); if (url === null) { - return; + return null; } dispatch(expandFollowingRequest(id)); - api(getState).get(url).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); + return api(getState) + .get(url) + .then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedAccounts(response.data)); - dispatch(expandFollowingSuccess(id, response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); - }).catch(error => { - dispatch(expandFollowingFail(id, error)); - }); + dispatch(importFetchedAccounts(response.data)); + dispatch(expandFollowingSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); + }) + .catch(error => { + dispatch(expandFollowingFail(id, error)); + }); }; const expandFollowingRequest = (id: string) => ({ From ba595259c166b259110bff3bf5a8daddacd7d355 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 15:17:36 -0400 Subject: [PATCH 6/9] Add tests for fetchRelationships() action --- .../actions/__tests__/accounts.test.ts | 94 +++++++++++++++++++ app/soapbox/actions/accounts.ts | 13 ++- 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index ad7b55851..0a0a909e0 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -14,6 +14,7 @@ import { fetchAccountByUsername, fetchFollowers, fetchFollowing, + fetchRelationships, followAccount, muteAccount, removeFromFollowers, @@ -1308,3 +1309,96 @@ describe('expandFollowing()', () => { }); }); }); + +describe('fetchRelationships()', () => { + const id = '1'; + + describe('when logged out', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', null); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(fetchRelationships([id])); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('me', '123'); + store = mockStore(state); + }); + + describe('without newAccountIds', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('relationships', ImmutableMap({ [id]: {} })) + .set('me', '123'); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(fetchRelationships([id])); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('relationships', ImmutableMap({})) + .set('me', '123'); + store = mockStore(state); + + __stub((mock) => { + mock + .onGet(`/api/v1/accounts/relationships?${[id].map(id => `id[]=${id}`).join('&')}`) + .reply(200, []); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'RELATIONSHIPS_FETCH_REQUEST', ids: [id], skipLoading: true }, + { + type: 'RELATIONSHIPS_FETCH_SUCCESS', + relationships: [], + skipLoading: true, + }, + ]; + await store.dispatch(fetchRelationships([id])); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock + .onGet(`/api/v1/accounts/relationships?${[id].map(id => `id[]=${id}`).join('&')}`) + .networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'RELATIONSHIPS_FETCH_REQUEST', ids: [id], skipLoading: true }, + { type: 'RELATIONSHIPS_FETCH_FAIL', skipLoading: true, error: new Error('Network Error') }, + ]; + await store.dispatch(fetchRelationships([id])); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index ca6f3b1d5..74e95c5da 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -656,22 +656,21 @@ const expandFollowingFail = (id: string, error: AxiosError) => ({ const fetchRelationships = (accountIds: string[]) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return null; const loadedRelationships = getState().relationships; const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null); if (newAccountIds.length === 0) { - return; + return null; } dispatch(fetchRelationshipsRequest(newAccountIds)); - api(getState).get(`/api/v1/accounts/relationships?${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => { - dispatch(fetchRelationshipsSuccess(response.data)); - }).catch(error => { - dispatch(fetchRelationshipsFail(error)); - }); + return api(getState) + .get(`/api/v1/accounts/relationships?${newAccountIds.map(id => `id[]=${id}`).join('&')}`) + .then(response => dispatch(fetchRelationshipsSuccess(response.data))) + .catch(error => dispatch(fetchRelationshipsFail(error))); }; const fetchRelationshipsRequest = (ids: string[]) => ({ From 6775beba93851b008b348a4562c8322cb844a74c Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 15:24:31 -0400 Subject: [PATCH 7/9] Add tests for fetchFollowRequests() action --- .../actions/__tests__/accounts.test.ts | 75 +++++++++++++++++++ app/soapbox/actions/accounts.ts | 15 ++-- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index 0a0a909e0..4e26a677f 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -14,6 +14,7 @@ import { fetchAccountByUsername, fetchFollowers, fetchFollowing, + fetchFollowRequests, fetchRelationships, followAccount, muteAccount, @@ -1402,3 +1403,77 @@ describe('fetchRelationships()', () => { }); }); }); + +describe('fetchFollowRequests()', () => { + describe('when logged out', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', null); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(fetchFollowRequests()); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('me', '123'); + store = mockStore(state); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('relationships', ImmutableMap({})) + .set('me', '123'); + store = mockStore(state); + + __stub((mock) => { + mock.onGet('/api/v1/follow_requests').reply(200, [], { + link: '; rel=\'prev\'', + }); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOW_REQUESTS_FETCH_REQUEST' }, + { type: 'ACCOUNTS_IMPORT', accounts: [] }, + { + type: 'FOLLOW_REQUESTS_FETCH_SUCCESS', + accounts: [], + next: null, + }, + ]; + await store.dispatch(fetchFollowRequests()); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet('/api/v1/follow_requests').networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOW_REQUESTS_FETCH_REQUEST' }, + { type: 'FOLLOW_REQUESTS_FETCH_FAIL', error: new Error('Network Error') }, + ]; + await store.dispatch(fetchFollowRequests()); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index 74e95c5da..ee6790171 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -693,15 +693,18 @@ const fetchRelationshipsFail = (error: AxiosError) => ({ const fetchFollowRequests = () => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return null; dispatch(fetchFollowRequestsRequest()); - api(getState).get('/api/v1/follow_requests').then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedAccounts(response.data)); - dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null)); - }).catch(error => dispatch(fetchFollowRequestsFail(error))); + return api(getState) + .get('/api/v1/follow_requests') + .then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(importFetchedAccounts(response.data)); + dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null)); + }) + .catch(error => dispatch(fetchFollowRequestsFail(error))); }; const fetchFollowRequestsRequest = () => ({ From 7fc43f524a12f77bea79297286ba5db427488aa8 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 15:31:32 -0400 Subject: [PATCH 8/9] Add tests for expandFollowRequests() action --- .../actions/__tests__/accounts.test.ts | 95 +++++++++++++++++++ app/soapbox/actions/accounts.ts | 17 ++-- 2 files changed, 105 insertions(+), 7 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index 4e26a677f..c0af695cb 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -10,6 +10,7 @@ import { createAccount, expandFollowers, expandFollowing, + expandFollowRequests, fetchAccount, fetchAccountByUsername, fetchFollowers, @@ -1477,3 +1478,97 @@ describe('fetchFollowRequests()', () => { }); }); }); + +describe('expandFollowRequests()', () => { + describe('when logged out', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', null); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(expandFollowRequests()); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('user_lists', ImmutableMap({ + follow_requests: ImmutableMap({ + next: 'next_url', + }), + })) + .set('me', '123'); + store = mockStore(state); + }); + + describe('when the url is null', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}) + .set('user_lists', ImmutableMap({ + follow_requests: ImmutableMap({ + next: null, + }), + })) + .set('me', '123'); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(expandFollowRequests()); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet('next_url').reply(200, [], { + link: '; rel=\'prev\'', + }); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOW_REQUESTS_EXPAND_REQUEST' }, + { type: 'ACCOUNTS_IMPORT', accounts: [] }, + { + type: 'FOLLOW_REQUESTS_EXPAND_SUCCESS', + accounts: [], + next: null, + }, + ]; + await store.dispatch(expandFollowRequests()); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet('next_url').networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOW_REQUESTS_EXPAND_REQUEST' }, + { type: 'FOLLOW_REQUESTS_EXPAND_FAIL', error: new Error('Network Error') }, + ]; + await store.dispatch(expandFollowRequests()); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index ee6790171..a1544a27f 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -724,21 +724,24 @@ const fetchFollowRequestsFail = (error: AxiosError) => ({ const expandFollowRequests = () => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return null; const url = getState().user_lists.getIn(['follow_requests', 'next']); if (url === null) { - return; + return null; } dispatch(expandFollowRequestsRequest()); - api(getState).get(url).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedAccounts(response.data)); - dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null)); - }).catch(error => dispatch(expandFollowRequestsFail(error))); + return api(getState) + .get(url) + .then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(importFetchedAccounts(response.data)); + dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null)); + }) + .catch(error => dispatch(expandFollowRequestsFail(error))); }; const expandFollowRequestsRequest = () => ({ From 86d6c519f02b10d2e57d210b5d2fbc90b75e8e65 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 23 Jun 2022 15:37:21 -0400 Subject: [PATCH 9/9] Add tests for authorizeFollowRequest() action --- .../actions/__tests__/accounts.test.ts | 64 +++++++++++++++++++ app/soapbox/actions/accounts.ts | 4 +- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/app/soapbox/actions/__tests__/accounts.test.ts b/app/soapbox/actions/__tests__/accounts.test.ts index c0af695cb..9e5903f3b 100644 --- a/app/soapbox/actions/__tests__/accounts.test.ts +++ b/app/soapbox/actions/__tests__/accounts.test.ts @@ -6,6 +6,7 @@ import rootReducer from 'soapbox/reducers'; import { normalizeAccount } from '../../normalizers'; import { + authorizeFollowRequest, blockAccount, createAccount, expandFollowers, @@ -1572,3 +1573,66 @@ describe('expandFollowRequests()', () => { }); }); }); + +describe('authorizeFollowRequest()', () => { + const id = '1'; + + describe('when logged out', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', null); + store = mockStore(state); + }); + + it('should do nothing', async() => { + await store.dispatch(authorizeFollowRequest(id)); + const actions = store.getActions(); + + expect(actions).toEqual([]); + }); + }); + + describe('when logged in', () => { + beforeEach(() => { + const state = rootReducer(undefined, {}).set('me', '123'); + store = mockStore(state); + }); + + describe('with a successful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onPost(`/api/v1/follow_requests/${id}/authorize`).reply(200); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOW_REQUEST_AUTHORIZE_REQUEST', id }, + { type: 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS', id }, + ]; + await store.dispatch(authorizeFollowRequest(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + + describe('with an unsuccessful API request', () => { + beforeEach(() => { + __stub((mock) => { + mock.onPost(`/api/v1/follow_requests/${id}/authorize`).networkError(); + }); + }); + + it('should dispatch the correct actions', async() => { + const expectedActions = [ + { type: 'FOLLOW_REQUEST_AUTHORIZE_REQUEST', id }, + { type: 'FOLLOW_REQUEST_AUTHORIZE_FAIL', id, error: new Error('Network Error') }, + ]; + await store.dispatch(authorizeFollowRequest(id)); + const actions = store.getActions(); + + expect(actions).toEqual(expectedActions); + }); + }); + }); +}); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index a1544a27f..e1a4217e5 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -761,11 +761,11 @@ const expandFollowRequestsFail = (error: AxiosError) => ({ const authorizeFollowRequest = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return null; dispatch(authorizeFollowRequestRequest(id)); - api(getState) + return api(getState) .post(`/api/v1/follow_requests/${id}/authorize`) .then(() => dispatch(authorizeFollowRequestSuccess(id))) .catch(error => dispatch(authorizeFollowRequestFail(id, error)));