Convert blocks action to TypeScript
This commit is contained in:
parent
ff7753cb42
commit
a335c06f13
5 changed files with 306 additions and 96 deletions
8
app/soapbox/__fixtures__/blocks.json
Normal file
8
app/soapbox/__fixtures__/blocks.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "22",
|
||||||
|
"username": "twoods",
|
||||||
|
"acct": "twoods",
|
||||||
|
"display_name": "Tiger Woods"
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,6 +1,7 @@
|
||||||
import { jest } from '@jest/globals';
|
import { jest } from '@jest/globals';
|
||||||
import { AxiosInstance } from 'axios';
|
import { AxiosInstance, AxiosResponse } from 'axios';
|
||||||
import MockAdapter from 'axios-mock-adapter';
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import LinkHeader from 'http-link-header';
|
||||||
|
|
||||||
const api = jest.requireActual('../api') as Record<string, Function>;
|
const api = jest.requireActual('../api') as Record<string, Function>;
|
||||||
let mocks: Array<Function> = [];
|
let mocks: Array<Function> = [];
|
||||||
|
@ -15,6 +16,10 @@ const setupMock = (axios: AxiosInstance) => {
|
||||||
|
|
||||||
export const staticClient = api.staticClient;
|
export const staticClient = api.staticClient;
|
||||||
|
|
||||||
|
export const getLinks = (response: AxiosResponse): LinkHeader => {
|
||||||
|
return new LinkHeader(response.headers?.link);
|
||||||
|
};
|
||||||
|
|
||||||
export const baseClient = (...params: any[]) => {
|
export const baseClient = (...params: any[]) => {
|
||||||
const axios = api.baseClient(...params);
|
const axios = api.baseClient(...params);
|
||||||
setupMock(axios);
|
setupMock(axios);
|
||||||
|
|
183
app/soapbox/actions/__tests__/blocks.test.ts
Normal file
183
app/soapbox/actions/__tests__/blocks.test.ts
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
import { __stub } from 'soapbox/api';
|
||||||
|
import { mockStore } from 'soapbox/jest/test-helpers';
|
||||||
|
import rootReducer from 'soapbox/reducers';
|
||||||
|
|
||||||
|
import { expandBlocks, fetchBlocks } from '../blocks';
|
||||||
|
|
||||||
|
const account = {
|
||||||
|
acct: 'twoods',
|
||||||
|
display_name: 'Tiger Woods',
|
||||||
|
id: '22',
|
||||||
|
username: 'twoods',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('fetchBlocks()', () => {
|
||||||
|
let store;
|
||||||
|
|
||||||
|
describe('if logged out', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const state = rootReducer(undefined, {}).set('me', null);
|
||||||
|
store = mockStore(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing', async() => {
|
||||||
|
await store.dispatch(fetchBlocks());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if logged in', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const state = rootReducer(undefined, {}).set('me', '1234');
|
||||||
|
store = mockStore(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a successful API request', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const blocks = require('soapbox/__fixtures__/blocks.json');
|
||||||
|
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('/api/v1/blocks').reply(200, blocks, {
|
||||||
|
link: '<https://example.com/api/v1/blocks?since_id=1>; rel=\'prev\'',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch blocks from the API', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: 'BLOCKS_FETCH_REQUEST' },
|
||||||
|
{ type: 'ACCOUNTS_IMPORT', accounts: [account] },
|
||||||
|
{ type: 'BLOCKS_FETCH_SUCCESS', accounts: [account], next: null },
|
||||||
|
{
|
||||||
|
type: 'RELATIONSHIPS_FETCH_REQUEST',
|
||||||
|
ids: ['22'],
|
||||||
|
skipLoading: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await store.dispatch(fetchBlocks());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with an unsuccessful API request', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('/api/v1/blocks').networkError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dispatch failed action', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: 'BLOCKS_FETCH_REQUEST' },
|
||||||
|
{ type: 'BLOCKS_FETCH_FAIL', error: new Error('Network Error') },
|
||||||
|
];
|
||||||
|
await store.dispatch(fetchBlocks());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('expandBlocks()', () => {
|
||||||
|
let store;
|
||||||
|
|
||||||
|
describe('if logged out', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const state = rootReducer(undefined, {}).set('me', null);
|
||||||
|
store = mockStore(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing', async() => {
|
||||||
|
await store.dispatch(expandBlocks());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if logged in', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const state = rootReducer(undefined, {}).set('me', '1234');
|
||||||
|
store = mockStore(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('without a url', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const state = rootReducer(undefined, {})
|
||||||
|
.set('me', '1234')
|
||||||
|
.set('user_lists', { blocks: { next: null } });
|
||||||
|
store = mockStore(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing', async() => {
|
||||||
|
await store.dispatch(expandBlocks());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a url', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const state = rootReducer(undefined, {})
|
||||||
|
.set('me', '1234')
|
||||||
|
.set('user_lists', { blocks: { next: 'example' } });
|
||||||
|
store = mockStore(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a successful API request', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const blocks = require('soapbox/__fixtures__/blocks.json');
|
||||||
|
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('example').reply(200, blocks, {
|
||||||
|
link: '<https://example.com/api/v1/blocks?since_id=1>; rel=\'prev\'',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch blocks from the url', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: 'BLOCKS_EXPAND_REQUEST' },
|
||||||
|
{ type: 'ACCOUNTS_IMPORT', accounts: [account] },
|
||||||
|
{ type: 'BLOCKS_EXPAND_SUCCESS', accounts: [account], next: null },
|
||||||
|
{
|
||||||
|
type: 'RELATIONSHIPS_FETCH_REQUEST',
|
||||||
|
ids: ['22'],
|
||||||
|
skipLoading: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await store.dispatch(expandBlocks());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with an unsuccessful API request', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('example').networkError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dispatch failed action', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: 'BLOCKS_EXPAND_REQUEST' },
|
||||||
|
{ type: 'BLOCKS_EXPAND_FAIL', error: new Error('Network Error') },
|
||||||
|
];
|
||||||
|
await store.dispatch(expandBlocks());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,95 +0,0 @@
|
||||||
import { isLoggedIn } from 'soapbox/utils/auth';
|
|
||||||
import { getNextLinkName } from 'soapbox/utils/quirks';
|
|
||||||
|
|
||||||
import api, { getLinks } from '../api';
|
|
||||||
|
|
||||||
import { fetchRelationships } from './accounts';
|
|
||||||
import { importFetchedAccounts } from './importer';
|
|
||||||
|
|
||||||
export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST';
|
|
||||||
export const BLOCKS_FETCH_SUCCESS = 'BLOCKS_FETCH_SUCCESS';
|
|
||||||
export const BLOCKS_FETCH_FAIL = 'BLOCKS_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const BLOCKS_EXPAND_REQUEST = 'BLOCKS_EXPAND_REQUEST';
|
|
||||||
export const BLOCKS_EXPAND_SUCCESS = 'BLOCKS_EXPAND_SUCCESS';
|
|
||||||
export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL';
|
|
||||||
|
|
||||||
export function fetchBlocks() {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
const nextLinkName = getNextLinkName(getState);
|
|
||||||
|
|
||||||
dispatch(fetchBlocksRequest());
|
|
||||||
|
|
||||||
api(getState).get('/api/v1/blocks').then(response => {
|
|
||||||
const next = getLinks(response).refs.find(link => link.rel === nextLinkName);
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
|
||||||
dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null));
|
|
||||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
|
||||||
}).catch(error => dispatch(fetchBlocksFail(error)));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchBlocksRequest() {
|
|
||||||
return {
|
|
||||||
type: BLOCKS_FETCH_REQUEST,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchBlocksSuccess(accounts, next) {
|
|
||||||
return {
|
|
||||||
type: BLOCKS_FETCH_SUCCESS,
|
|
||||||
accounts,
|
|
||||||
next,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchBlocksFail(error) {
|
|
||||||
return {
|
|
||||||
type: BLOCKS_FETCH_FAIL,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandBlocks() {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
const nextLinkName = getNextLinkName(getState);
|
|
||||||
|
|
||||||
const url = getState().getIn(['user_lists', 'blocks', 'next']);
|
|
||||||
|
|
||||||
if (url === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(expandBlocksRequest());
|
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
|
||||||
const next = getLinks(response).refs.find(link => link.rel === nextLinkName);
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
|
||||||
dispatch(expandBlocksSuccess(response.data, next ? next.uri : null));
|
|
||||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
|
||||||
}).catch(error => dispatch(expandBlocksFail(error)));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandBlocksRequest() {
|
|
||||||
return {
|
|
||||||
type: BLOCKS_EXPAND_REQUEST,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandBlocksSuccess(accounts, next) {
|
|
||||||
return {
|
|
||||||
type: BLOCKS_EXPAND_SUCCESS,
|
|
||||||
accounts,
|
|
||||||
next,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandBlocksFail(error) {
|
|
||||||
return {
|
|
||||||
type: BLOCKS_EXPAND_FAIL,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
109
app/soapbox/actions/blocks.ts
Normal file
109
app/soapbox/actions/blocks.ts
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import { AnyAction } from '@reduxjs/toolkit';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
import { isLoggedIn } from 'soapbox/utils/auth';
|
||||||
|
import { getNextLinkName } from 'soapbox/utils/quirks';
|
||||||
|
|
||||||
|
import api, { getLinks } from '../api';
|
||||||
|
|
||||||
|
import { fetchRelationships } from './accounts';
|
||||||
|
import { importFetchedAccounts } from './importer';
|
||||||
|
|
||||||
|
const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST';
|
||||||
|
const BLOCKS_FETCH_SUCCESS = 'BLOCKS_FETCH_SUCCESS';
|
||||||
|
const BLOCKS_FETCH_FAIL = 'BLOCKS_FETCH_FAIL';
|
||||||
|
|
||||||
|
const BLOCKS_EXPAND_REQUEST = 'BLOCKS_EXPAND_REQUEST';
|
||||||
|
const BLOCKS_EXPAND_SUCCESS = 'BLOCKS_EXPAND_SUCCESS';
|
||||||
|
const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL';
|
||||||
|
|
||||||
|
const fetchBlocks = () => (dispatch: React.Dispatch<AnyAction>, getState: any) => {
|
||||||
|
if (!isLoggedIn(getState)) return null;
|
||||||
|
const nextLinkName = getNextLinkName(getState);
|
||||||
|
|
||||||
|
dispatch(fetchBlocksRequest());
|
||||||
|
|
||||||
|
return api(getState)
|
||||||
|
.get('/api/v1/blocks')
|
||||||
|
.then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === nextLinkName);
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null));
|
||||||
|
dispatch(fetchRelationships(response.data.map((item: any) => item.id)) as any);
|
||||||
|
})
|
||||||
|
.catch(error => dispatch(fetchBlocksFail(error)));
|
||||||
|
};
|
||||||
|
|
||||||
|
function fetchBlocksRequest() {
|
||||||
|
return { type: BLOCKS_FETCH_REQUEST };
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchBlocksSuccess(accounts: any, next: any) {
|
||||||
|
return {
|
||||||
|
type: BLOCKS_FETCH_SUCCESS,
|
||||||
|
accounts,
|
||||||
|
next,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchBlocksFail(error: AxiosError) {
|
||||||
|
return {
|
||||||
|
type: BLOCKS_FETCH_FAIL,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandBlocks = () => (dispatch: React.Dispatch<AnyAction>, getState: any) => {
|
||||||
|
if (!isLoggedIn(getState)) return null;
|
||||||
|
const nextLinkName = getNextLinkName(getState);
|
||||||
|
|
||||||
|
const url = getState().getIn(['user_lists', 'blocks', 'next']);
|
||||||
|
|
||||||
|
if (url === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(expandBlocksRequest());
|
||||||
|
|
||||||
|
return api(getState)
|
||||||
|
.get(url)
|
||||||
|
.then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === nextLinkName);
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(expandBlocksSuccess(response.data, next ? next.uri : null));
|
||||||
|
dispatch(fetchRelationships(response.data.map((item: any) => item.id)) as any);
|
||||||
|
})
|
||||||
|
.catch(error => dispatch(expandBlocksFail(error)));
|
||||||
|
};
|
||||||
|
|
||||||
|
function expandBlocksRequest() {
|
||||||
|
return {
|
||||||
|
type: BLOCKS_EXPAND_REQUEST,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandBlocksSuccess(accounts: any, next: any) {
|
||||||
|
return {
|
||||||
|
type: BLOCKS_EXPAND_SUCCESS,
|
||||||
|
accounts,
|
||||||
|
next,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandBlocksFail(error: AxiosError) {
|
||||||
|
return {
|
||||||
|
type: BLOCKS_EXPAND_FAIL,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
fetchBlocks,
|
||||||
|
expandBlocks,
|
||||||
|
BLOCKS_FETCH_REQUEST,
|
||||||
|
BLOCKS_FETCH_SUCCESS,
|
||||||
|
BLOCKS_FETCH_FAIL,
|
||||||
|
BLOCKS_EXPAND_REQUEST,
|
||||||
|
BLOCKS_EXPAND_SUCCESS,
|
||||||
|
BLOCKS_EXPAND_FAIL,
|
||||||
|
};
|
Loading…
Reference in a new issue