From 78c34a342d8ff9e700872fc5af68f8b0c46191f2 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 28 Apr 2019 12:40:50 +0200 Subject: [PATCH] Added tests for new use cases --- src/common/Home.js | 2 +- src/servers/reducers/server.js | 8 ++--- src/servers/services/ServersService.js | 7 ++-- test/common/Home.test.js | 22 +++++++++---- test/servers/ServersDropdown.test.js | 24 ++++++++++---- test/servers/reducers/server.test.js | 44 ++++++++++++++++++++------ 6 files changed, 76 insertions(+), 31 deletions(-) diff --git a/src/common/Home.js b/src/common/Home.js index f021de55..e670e20e 100644 --- a/src/common/Home.js +++ b/src/common/Home.js @@ -28,7 +28,7 @@ export default class Home extends React.Component {
{!loading && hasServers && Please, select a server.} {!loading && !hasServers && Please, add a server.} - {loading && Trying to load servers....} + {loading && Trying to load servers...}
{!loading && hasServers && ( diff --git a/src/servers/reducers/server.js b/src/servers/reducers/server.js index a5d3b3b4..17ac2662 100644 --- a/src/servers/reducers/server.js +++ b/src/servers/reducers/server.js @@ -13,7 +13,7 @@ const initialState = { loading: false, }; -const assocId = (server) => assoc('id', uuid(), server); +const assocId = (server) => assoc('id', server.id || uuid(), server); export default handleActions({ [FETCH_SERVERS_START]: (state) => ({ ...state, loading: true }), @@ -22,8 +22,6 @@ export default handleActions({ export const listServers = ({ listServers, createServers }, { get }) => () => async (dispatch) => { dispatch({ type: FETCH_SERVERS_START }); - - // Fetch list from local storage. const localList = listServers(); if (!isEmpty(localList)) { @@ -32,12 +30,12 @@ export const listServers = ({ listServers, createServers }, { get }) => () => as return; } - // If local list is empty, try to fetch it remotely, calculate IDs for every server, and use it + // If local list is empty, try to fetch it remotely and calculate IDs for every server const { data: remoteList } = await get(`${homepage}/servers.json`); const listWithIds = map(assocId, remoteList); createServers(listWithIds); - dispatch({ type: FETCH_SERVERS, list: listWithIds }); + dispatch({ type: FETCH_SERVERS, list: listWithIds.reduce((map, server) => ({ ...map, [server.id]: server }), {}) }); }; export const createServer = ({ createServer }, listServersAction) => pipe(createServer, listServersAction); diff --git a/src/servers/services/ServersService.js b/src/servers/services/ServersService.js index aee850d5..e8ffdb68 100644 --- a/src/servers/services/ServersService.js +++ b/src/servers/services/ServersService.js @@ -1,11 +1,10 @@ -import { assoc, curry, dissoc, reduce } from 'ramda'; +import { assoc, dissoc, reduce } from 'ramda'; const SERVERS_STORAGE_KEY = 'servers'; export default class ServersService { constructor(storage) { this.storage = storage; - this.setServers = curry(this.storage.set)(SERVERS_STORAGE_KEY); } listServers = () => this.storage.get(SERVERS_STORAGE_KEY) || {}; @@ -21,9 +20,9 @@ export default class ServersService { servers ); - this.setServers(allServers); + this.storage.set(SERVERS_STORAGE_KEY, allServers); }; deleteServer = ({ id }) => - this.setServers(dissoc(id, this.listServers())); + this.storage.set(SERVERS_STORAGE_KEY, dissoc(id, this.listServers())); } diff --git a/test/common/Home.test.js b/test/common/Home.test.js index 6fb094a5..249b3e5d 100644 --- a/test/common/Home.test.js +++ b/test/common/Home.test.js @@ -6,10 +6,8 @@ import Home from '../../src/common/Home'; describe('', () => { let wrapped; const defaultProps = { - resetSelectedServer() { - return ''; - }, - servers: {}, + resetSelectedServer: () => '', + servers: { loading: false, list: {} }, }; const createComponent = (props) => { const actualProps = { ...defaultProps, ...props }; @@ -41,10 +39,22 @@ describe('', () => { expect(wrapped.find('ListGroup')).toHaveLength(0); }); + it('shows message when loading servers', () => { + const wrapped = createComponent({ servers: { loading: true } }); + const span = wrapped.find('span'); + + expect(span).toHaveLength(1); + expect(span.text()).toContain('Trying to load servers...'); + expect(wrapped.find('ListGroup')).toHaveLength(0); + }); + it('shows servers list when list of servers is not empty', () => { const servers = { - 1: { name: 'foo', id: '123' }, - 2: { name: 'bar', id: '456' }, + loading: false, + list: { + 1: { name: 'foo', id: '123' }, + 2: { name: 'bar', id: '456' }, + }, }; const wrapped = createComponent({ servers }); diff --git a/test/servers/ServersDropdown.test.js b/test/servers/ServersDropdown.test.js index 0f194f3e..c5bc0dd4 100644 --- a/test/servers/ServersDropdown.test.js +++ b/test/servers/ServersDropdown.test.js @@ -8,9 +8,12 @@ describe('', () => { let wrapped; let ServersDropdown; const servers = { - '1a': { name: 'foo', id: 1 }, - '2b': { name: 'bar', id: 2 }, - '3c': { name: 'baz', id: 3 }, + list: { + '1a': { name: 'foo', id: 1 }, + '2b': { name: 'bar', id: 2 }, + '3c': { name: 'baz', id: 3 }, + }, + loading: false, }; beforeEach(() => { @@ -20,7 +23,7 @@ describe('', () => { afterEach(() => wrapped.unmount()); it('contains the list of servers', () => - expect(wrapped.find(DropdownItem).filter('[to]')).toHaveLength(values(servers).length)); + expect(wrapped.find(DropdownItem).filter('[to]')).toHaveLength(values(servers.list).length)); it('contains a toggle with proper title', () => expect(wrapped.find(DropdownToggle)).toHaveLength(1)); @@ -32,12 +35,21 @@ describe('', () => { expect(items.filter('.servers-dropdown__export-item')).toHaveLength(1); }); - it('contains a message when no servers exist yet', () => { - wrapped = shallow(); + it('shows a message when no servers exist yet', () => { + wrapped = shallow(); const item = wrapped.find(DropdownItem); expect(item).toHaveLength(1); expect(item.prop('disabled')).toEqual(true); expect(item.find('i').text()).toEqual('Add a server first...'); }); + + it('shows a message when loading', () => { + wrapped = shallow(); + const item = wrapped.find(DropdownItem); + + expect(item).toHaveLength(1); + expect(item.prop('disabled')).toEqual(true); + expect(item.find('i').text()).toEqual('Trying to load servers...'); + }); }); diff --git a/test/servers/reducers/server.test.js b/test/servers/reducers/server.test.js index 6645f9e0..14d204bb 100644 --- a/test/servers/reducers/server.test.js +++ b/test/servers/reducers/server.test.js @@ -4,17 +4,17 @@ import reducer, { deleteServer, listServers, createServers, - FETCH_SERVERS, + FETCH_SERVERS, FETCH_SERVERS_START, } from '../../../src/servers/reducers/server'; describe('serverReducer', () => { - const payload = { + const list = { abc123: { id: 'abc123' }, def456: { id: 'def456' }, }; - const expectedFetchServersResult = { type: FETCH_SERVERS, payload }; + const expectedFetchServersResult = { type: FETCH_SERVERS, list }; const ServersServiceMock = { - listServers: jest.fn(() => payload), + listServers: jest.fn(() => list), createServer: jest.fn(), deleteServer: jest.fn(), createServers: jest.fn(), @@ -22,7 +22,7 @@ describe('serverReducer', () => { describe('reducer', () => { it('returns servers when action is FETCH_SERVERS', () => - expect(reducer({}, { type: FETCH_SERVERS, payload })).toEqual(payload)); + expect(reducer({}, { type: FETCH_SERVERS, list })).toEqual({ loading: false, list })); }); describe('action creators', () => { @@ -34,14 +34,40 @@ describe('serverReducer', () => { }); describe('listServers', () => { - it('fetches servers and returns them as part of the action', () => { - const result = listServers(ServersServiceMock)(); + const axios = { get: jest.fn().mockResolvedValue({ data: [] }) }; + const dispatch = jest.fn(); - expect(result).toEqual(expectedFetchServersResult); + beforeEach(() => { + axios.get.mockClear(); + dispatch.mockReset(); + }); + + it('fetches servers from local storage when found', async () => { + await listServers(ServersServiceMock, axios)()(dispatch); + + expect(dispatch).toHaveBeenCalledTimes(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: FETCH_SERVERS_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, expectedFetchServersResult); expect(ServersServiceMock.listServers).toHaveBeenCalledTimes(1); expect(ServersServiceMock.createServer).not.toHaveBeenCalled(); expect(ServersServiceMock.deleteServer).not.toHaveBeenCalled(); expect(ServersServiceMock.createServers).not.toHaveBeenCalled(); + expect(axios.get).not.toHaveBeenCalled(); + }); + + it('tries to fetch servers from remote when not found locally', async () => { + const NoListServersServiceMock = { ...ServersServiceMock, listServers: jest.fn(() => ({})) }; + + await listServers(NoListServersServiceMock, axios)()(dispatch); + + expect(dispatch).toHaveBeenCalledTimes(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: FETCH_SERVERS_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: FETCH_SERVERS, list: {} }); + expect(NoListServersServiceMock.listServers).toHaveBeenCalledTimes(1); + expect(NoListServersServiceMock.createServer).not.toHaveBeenCalled(); + expect(NoListServersServiceMock.deleteServer).not.toHaveBeenCalled(); + expect(NoListServersServiceMock.createServers).toHaveBeenCalledTimes(1); + expect(axios.get).toHaveBeenCalledTimes(1); }); }); @@ -75,7 +101,7 @@ describe('serverReducer', () => { describe('createServer', () => { it('creates multiple servers and then fetches servers again', () => { - const serversToCreate = values(payload); + const serversToCreate = values(list); const result = createServers(ServersServiceMock, () => expectedFetchServersResult)(serversToCreate); expect(result).toEqual(expectedFetchServersResult);