mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Merge pull request #139 from acelaya/feature/coverage-80
Created tags list reducer test
This commit is contained in:
commit
7c22713d7d
4 changed files with 253 additions and 6 deletions
|
@ -1,6 +1,5 @@
|
||||||
import { createAction, handleActions } from 'redux-actions';
|
import { createAction, handleActions } from 'redux-actions';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { pick } from 'ramda';
|
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START';
|
export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START';
|
||||||
|
@ -27,7 +26,7 @@ const initialState = {
|
||||||
export default handleActions({
|
export default handleActions({
|
||||||
[EDIT_SHORT_URL_TAGS_START]: (state) => ({ ...state, saving: true, error: false }),
|
[EDIT_SHORT_URL_TAGS_START]: (state) => ({ ...state, saving: true, error: false }),
|
||||||
[EDIT_SHORT_URL_TAGS_ERROR]: (state) => ({ ...state, saving: false, error: true }),
|
[EDIT_SHORT_URL_TAGS_ERROR]: (state) => ({ ...state, saving: false, error: true }),
|
||||||
[EDIT_SHORT_URL_TAGS]: (state, action) => ({ ...pick([ 'shortCode', 'tags' ], action), saving: false, error: false }),
|
[EDIT_SHORT_URL_TAGS]: (state, { shortCode, tags }) => ({ shortCode, tags, saving: false, error: false }),
|
||||||
[RESET_EDIT_SHORT_URL_TAGS]: () => initialState,
|
[RESET_EDIT_SHORT_URL_TAGS]: () => initialState,
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@ import { TAG_DELETED } from './tagDelete';
|
||||||
import { TAG_EDITED } from './tagEdit';
|
import { TAG_EDITED } from './tagEdit';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
const LIST_TAGS_START = 'shlink/tagsList/LIST_TAGS_START';
|
export const LIST_TAGS_START = 'shlink/tagsList/LIST_TAGS_START';
|
||||||
const LIST_TAGS_ERROR = 'shlink/tagsList/LIST_TAGS_ERROR';
|
export const LIST_TAGS_ERROR = 'shlink/tagsList/LIST_TAGS_ERROR';
|
||||||
const LIST_TAGS = 'shlink/tagsList/LIST_TAGS';
|
export const LIST_TAGS = 'shlink/tagsList/LIST_TAGS';
|
||||||
const FILTER_TAGS = 'shlink/tagsList/FILTER_TAGS';
|
export const FILTER_TAGS = 'shlink/tagsList/FILTER_TAGS';
|
||||||
/* eslint-enable padding-line-between-statements */
|
/* eslint-enable padding-line-between-statements */
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
|
104
test/short-urls/reducers/shortUrlTags.test.js
Normal file
104
test/short-urls/reducers/shortUrlTags.test.js
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import reducer, {
|
||||||
|
EDIT_SHORT_URL_TAGS,
|
||||||
|
EDIT_SHORT_URL_TAGS_ERROR,
|
||||||
|
EDIT_SHORT_URL_TAGS_START, editShortUrlTags,
|
||||||
|
RESET_EDIT_SHORT_URL_TAGS,
|
||||||
|
resetShortUrlsTags,
|
||||||
|
SHORT_URL_TAGS_EDITED,
|
||||||
|
shortUrlTagsEdited,
|
||||||
|
} from '../../../src/short-urls/reducers/shortUrlTags';
|
||||||
|
|
||||||
|
describe('shortUrlTagsReducer', () => {
|
||||||
|
const tags = [ 'foo', 'bar', 'baz' ];
|
||||||
|
const shortCode = 'abc123';
|
||||||
|
|
||||||
|
describe('reducer', () => {
|
||||||
|
it('returns loading on EDIT_SHORT_URL_TAGS_START', () => {
|
||||||
|
expect(reducer({}, { type: EDIT_SHORT_URL_TAGS_START })).toEqual({
|
||||||
|
saving: true,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns error on EDIT_SHORT_URL_TAGS_ERROR', () => {
|
||||||
|
expect(reducer({}, { type: EDIT_SHORT_URL_TAGS_ERROR })).toEqual({
|
||||||
|
saving: false,
|
||||||
|
error: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns provided tags and shortCode on EDIT_SHORT_URL_TAGS', () => {
|
||||||
|
expect(reducer({}, { type: EDIT_SHORT_URL_TAGS, tags, shortCode })).toEqual({
|
||||||
|
tags,
|
||||||
|
shortCode,
|
||||||
|
saving: false,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('goes back to initial state on RESET_EDIT_SHORT_URL_TAGS', () => {
|
||||||
|
expect(reducer({}, { type: RESET_EDIT_SHORT_URL_TAGS })).toEqual({
|
||||||
|
tags: [],
|
||||||
|
shortCode: null,
|
||||||
|
saving: false,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resetShortUrlsTags', () =>
|
||||||
|
it('creates expected action', () => expect(resetShortUrlsTags()).toEqual({ type: RESET_EDIT_SHORT_URL_TAGS })));
|
||||||
|
|
||||||
|
describe('shortUrlTagsEdited', () =>
|
||||||
|
it('creates expected action', () => expect(shortUrlTagsEdited(shortCode, tags)).toEqual({
|
||||||
|
tags,
|
||||||
|
shortCode,
|
||||||
|
type: SHORT_URL_TAGS_EDITED,
|
||||||
|
})));
|
||||||
|
|
||||||
|
describe('editShortUrlTags', () => {
|
||||||
|
const updateShortUrlTags = jest.fn();
|
||||||
|
const buildShlinkApiClient = jest.fn().mockResolvedValue({ updateShortUrlTags });
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
updateShortUrlTags.mockReset();
|
||||||
|
buildShlinkApiClient.mockClear();
|
||||||
|
dispatch.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches normalized tags on success', async () => {
|
||||||
|
const normalizedTags = [ 'bar', 'foo' ];
|
||||||
|
|
||||||
|
updateShortUrlTags.mockResolvedValue(normalizedTags);
|
||||||
|
|
||||||
|
await editShortUrlTags(buildShlinkApiClient)(shortCode, tags)(dispatch);
|
||||||
|
|
||||||
|
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
|
||||||
|
expect(updateShortUrlTags).toHaveBeenCalledTimes(1);
|
||||||
|
expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, tags);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_TAGS, tags: normalizedTags, shortCode });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches error on failure', async () => {
|
||||||
|
const error = new Error();
|
||||||
|
|
||||||
|
updateShortUrlTags.mockRejectedValue(error);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await editShortUrlTags(buildShlinkApiClient)(shortCode, tags)(dispatch);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBe(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
|
||||||
|
expect(updateShortUrlTags).toHaveBeenCalledTimes(1);
|
||||||
|
expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, tags);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_TAGS_ERROR });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
144
test/tags/reducers/tagsList.test.js
Normal file
144
test/tags/reducers/tagsList.test.js
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
import reducer, {
|
||||||
|
FILTER_TAGS,
|
||||||
|
filterTags,
|
||||||
|
LIST_TAGS,
|
||||||
|
LIST_TAGS_ERROR,
|
||||||
|
LIST_TAGS_START, listTags,
|
||||||
|
} from '../../../src/tags/reducers/tagsList';
|
||||||
|
import { TAG_DELETED } from '../../../src/tags/reducers/tagDelete';
|
||||||
|
import { TAG_EDITED } from '../../../src/tags/reducers/tagEdit';
|
||||||
|
|
||||||
|
describe('tagsListReducer', () => {
|
||||||
|
describe('reducer', () => {
|
||||||
|
it('returns loading on LIST_TAGS_START', () => {
|
||||||
|
expect(reducer({}, { type: LIST_TAGS_START })).toEqual({
|
||||||
|
loading: true,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns error on LIST_TAGS_ERROR', () => {
|
||||||
|
expect(reducer({}, { type: LIST_TAGS_ERROR })).toEqual({
|
||||||
|
loading: false,
|
||||||
|
error: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns provided tags as filtered and regular tags on LIST_TAGS', () => {
|
||||||
|
const tags = [ 'foo', 'bar', 'baz' ];
|
||||||
|
|
||||||
|
expect(reducer({}, { type: LIST_TAGS, tags })).toEqual({
|
||||||
|
tags,
|
||||||
|
filteredTags: tags,
|
||||||
|
loading: false,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes provided tag from filtered and regular tags on TAG_DELETED', () => {
|
||||||
|
const tags = [ 'foo', 'bar', 'baz' ];
|
||||||
|
const tag = 'foo';
|
||||||
|
const expectedTags = [ 'bar', 'baz' ];
|
||||||
|
|
||||||
|
expect(reducer({ tags, filteredTags: tags }, { type: TAG_DELETED, tag })).toEqual({
|
||||||
|
tags: expectedTags,
|
||||||
|
filteredTags: expectedTags,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renames provided tag from filtered and regular tags on TAG_EDITED', () => {
|
||||||
|
const tags = [ 'foo', 'bar', 'baz' ];
|
||||||
|
const oldName = 'bar';
|
||||||
|
const newName = 'renamed';
|
||||||
|
const expectedTags = [ 'foo', 'renamed', 'baz' ].sort();
|
||||||
|
|
||||||
|
expect(reducer({ tags, filteredTags: tags }, { type: TAG_EDITED, oldName, newName })).toEqual({
|
||||||
|
tags: expectedTags,
|
||||||
|
filteredTags: expectedTags,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filters original list of tags by provided search term on FILTER_TAGS', () => {
|
||||||
|
const tags = [ 'foo', 'bar', 'baz', 'foo2', 'fo' ];
|
||||||
|
const searchTerm = 'fo';
|
||||||
|
const filteredTags = [ 'foo', 'foo2', 'fo' ];
|
||||||
|
|
||||||
|
expect(reducer({ tags }, { type: FILTER_TAGS, searchTerm })).toEqual({
|
||||||
|
tags,
|
||||||
|
filteredTags,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('filterTags', () =>
|
||||||
|
it('creates expected action', () => expect(filterTags('foo')).toEqual({ type: FILTER_TAGS, searchTerm: 'foo' })));
|
||||||
|
|
||||||
|
describe('listTags', () => {
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
const getState = jest.fn(() => ({}));
|
||||||
|
const buildShlinkApiClient = jest.fn();
|
||||||
|
const listTagsMock = jest.fn();
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
dispatch.mockReset();
|
||||||
|
getState.mockClear();
|
||||||
|
buildShlinkApiClient.mockReset();
|
||||||
|
listTagsMock.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
const assertNoAction = async (tagsList) => {
|
||||||
|
getState.mockReturnValue({ tagsList });
|
||||||
|
|
||||||
|
await listTags(buildShlinkApiClient, false)()(dispatch, getState);
|
||||||
|
|
||||||
|
expect(buildShlinkApiClient).not.toHaveBeenCalled();
|
||||||
|
expect(dispatch).not.toHaveBeenCalled();
|
||||||
|
expect(getState).toHaveBeenCalledTimes(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('does nothing when loading', async () => await assertNoAction({ loading: true }));
|
||||||
|
it('does nothing when list is not empty', async () => await assertNoAction({ loading: false, tags: [ 'foo', 'bar' ] }));
|
||||||
|
|
||||||
|
it('dispatches loaded lists when no error occurs', async () => {
|
||||||
|
const tags = [ 'foo', 'bar', 'baz' ];
|
||||||
|
|
||||||
|
listTagsMock.mockResolvedValue(tags);
|
||||||
|
buildShlinkApiClient.mockResolvedValue({ listTags: listTagsMock });
|
||||||
|
|
||||||
|
await listTags(buildShlinkApiClient, true)()(dispatch, getState);
|
||||||
|
|
||||||
|
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getState).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: LIST_TAGS_START });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: LIST_TAGS, tags });
|
||||||
|
});
|
||||||
|
|
||||||
|
const assertErrorResult = async () => {
|
||||||
|
await listTags(buildShlinkApiClient, true)()(dispatch, getState);
|
||||||
|
|
||||||
|
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getState).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: LIST_TAGS_START });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: LIST_TAGS_ERROR });
|
||||||
|
};
|
||||||
|
|
||||||
|
it('dispatches error when error occurs on list call', async () => {
|
||||||
|
listTagsMock.mockRejectedValue(new Error());
|
||||||
|
buildShlinkApiClient.mockResolvedValue({ listTags: listTagsMock });
|
||||||
|
|
||||||
|
await assertErrorResult();
|
||||||
|
|
||||||
|
expect(listTagsMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches error when error occurs on build call', async () => {
|
||||||
|
buildShlinkApiClient.mockRejectedValue(new Error());
|
||||||
|
|
||||||
|
await assertErrorResult();
|
||||||
|
|
||||||
|
expect(listTagsMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue