mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-10 18:27:25 +03:00
Merge pull request #137 from acelaya/feature/pre-configure-servers
Feature/pre configure servers
This commit is contained in:
commit
95462b0c1d
16 changed files with 188 additions and 80 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@ npm-debug.log*
|
|||
|
||||
docker-compose.override.yml
|
||||
home
|
||||
public/servers.json*
|
||||
|
|
|
@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
#### Added
|
||||
|
||||
* [#101](https://github.com/shlinkio/shlink-web-client/issues/101) Added checkbox to short URL creation form that allows to determine the value of the `findIfExists` flag introduced in Shlink v1.16.0.
|
||||
* [#105](https://github.com/shlinkio/shlink-web-client/issues/105) Added support to pre-configure servers.
|
||||
|
||||
#### Changed
|
||||
|
||||
|
|
57
README.md
57
README.md
|
@ -14,9 +14,9 @@ A ReactJS-based progressive web application for [Shlink](https://shlink.io).
|
|||
|
||||
There are three ways in which you can use this application.
|
||||
|
||||
* The easiest way to use shlink-web-client is by just going to https://app.shlink.io.
|
||||
* The easiest way to use shlink-web-client is by just going to <https://app.shlink.io>.
|
||||
|
||||
The application runs 100% in the browser, so you can use that instance and access any shlink instance from it.
|
||||
The application runs 100% in the browser, so you can safely access any shlink instance from there.
|
||||
|
||||
* Self hosting the application yourself.
|
||||
|
||||
|
@ -24,27 +24,60 @@ There are three ways in which you can use this application.
|
|||
|
||||
The package contains static files only, so just put it in a folder and serve it with the web server of your choice.
|
||||
|
||||
Provided dist files are configured to be served from the root of your domain. If you need to serve shlink-web-client from a subpath, you will have to build it yourself following [these simple steps](#serve-shlink-in-subpath).
|
||||
Provided dist files are configured to be served from the root of your domain. If you need to serve shlink-web-client from a subpath, you will have to build it yourself following [these steps](#serve-shlink-in-subpath).
|
||||
|
||||
* Use the official [docker image](https://hub.docker.com/r/shlinkio/shlink-web-client/)
|
||||
* Using the official [docker image](https://hub.docker.com/r/shlinkio/shlink-web-client/)
|
||||
|
||||
If you want to deploy shlink-web-client in a container-based cluster (kubernetes, docker swarm, etc), just pick the image and do it.
|
||||
If you want to deploy shlink-web-client in a container-based cluster (kubernetes, docker swarm, etc), just pick the `shlinkio/shlink-web-client` image and do it.
|
||||
|
||||
It's a lightweight [nginx:alpine](https://hub.docker.com/r/library/nginx/) image serving the assets on port 80.
|
||||
It's a lightweight [nginx:alpine](https://hub.docker.com/r/library/nginx/) image serving the static app on port 80.
|
||||
|
||||
## Pre-configuring servers
|
||||
|
||||
The first time you access shlink-web-client from a browser, you will have to configure the list of shlink servers you want to manage, and they will be saved in the local storage.
|
||||
|
||||
Those servers can be exported and imported in other browsers, but if for some reason you need some servers to be there from the beginning, starting with shlink-web-client 2.1.0, you can provide a `servers.json` file in the project root folder (the same containing the `index.html`, `favicon.ico`, etc) with a structure like this:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "Main server",
|
||||
"url": "https://doma.in",
|
||||
"apiKey": "09c972b7-506b-49f1-a19a-d729e22e599c"
|
||||
},
|
||||
{
|
||||
"name": "Local",
|
||||
"url": "http://localhost:8080",
|
||||
"apiKey": "580d0b42-4dea-419a-96bf-6c876b901451"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
> The list can contain as many servers as you need.
|
||||
|
||||
If you are using the shlink-web-client docker image, you can mount the `servers.json` file in a volume inside `/usr/share/nginx/html`, which is the app's document root inside the container.
|
||||
|
||||
docker run --name shlink-web-client -p 8000:80 -v ${PWD}/servers.json:/usr/share/nginx/html/servers.json shlinkio/shlink-web-client
|
||||
|
||||
## Serve project in subpath
|
||||
|
||||
Official distributable files have been build so that they are served from the root of a domain.
|
||||
Official distributable files have been built so that they are served from the root of a domain.
|
||||
|
||||
If you need to host shlink-web-client yourself and serve it from a subpath, follow these steps:
|
||||
|
||||
* Download [node](https://nodejs.org/en/download/package-manager/) 10.15 or later (if you don't have it yet).
|
||||
* Download shlink-web-client source files for the version you want to build.
|
||||
* Download shlink-web-client source code for the version you want to build.
|
||||
* For example, if you want to build `v1.0.1`, use this link https://github.com/shlinkio/shlink-web-client/archive/v1.0.1.zip
|
||||
* Replace the `v1.0.1` part in the link with the one of the version you want to build.
|
||||
* Decompress the file and `cd` into the resulting folder.
|
||||
* Install project dependencies by running `npm install`.
|
||||
* Open the `package.json` file in the root of the project, locate the `homepage` property and replace the value (which should be an empty string) by the path from which you want to serve shlink-web-client.
|
||||
* For example: `"homepage": "/my-projects/shlink-web-client",`.
|
||||
* Build the distributable contents by running `npm run build`.
|
||||
* Once the command finishes, you will have a `build` folder with all the static assets you need to run shlink-web-client. Just place them wherever you want them to be served from.
|
||||
* Build the project:
|
||||
* For classic hosting:
|
||||
* Download [node](https://nodejs.org/en/download/package-manager/) 10.15 or later.
|
||||
* Install project dependencies by running `npm install`.
|
||||
* Build the project by running `npm run build`.
|
||||
* Once the command finishes, you will have a `build` folder with all the static assets you need to run shlink-web-client. Just place them wherever you want them to be served from.
|
||||
* For docker image:
|
||||
* Download [docker](https://docs.docker.com/install/).
|
||||
* Build the docker image by running `docker build . -t shlink-web-client`.
|
||||
* Once the command finishes, you will have an image with the name `shlink-web-client`.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"description": "A React-based progressive web application for shlink",
|
||||
"version": "1.0.0",
|
||||
"private": false,
|
||||
"homepage": "https://shlink.io",
|
||||
"homepage": "",
|
||||
"scripts": {
|
||||
"lint": "npm run lint:js && npm run lint:css",
|
||||
"lint:js": "eslint src test scripts config",
|
||||
|
|
|
@ -18,18 +18,20 @@ export default class Home extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const servers = values(this.props.servers);
|
||||
const { servers: { list, loading } } = this.props;
|
||||
const servers = values(list);
|
||||
const hasServers = !isEmpty(servers);
|
||||
|
||||
return (
|
||||
<div className="home">
|
||||
<h1 className="home__title">Welcome to Shlink</h1>
|
||||
<h5 className="home__intro">
|
||||
{hasServers && <span>Please, select a server.</span>}
|
||||
{!hasServers && <span>Please, <Link to="/server/create">add a server</Link>.</span>}
|
||||
{!loading && hasServers && <span>Please, select a server.</span>}
|
||||
{!loading && !hasServers && <span>Please, <Link to="/server/create">add a server</Link>.</span>}
|
||||
{loading && <span>Trying to load servers...</span>}
|
||||
</h5>
|
||||
|
||||
{hasServers && (
|
||||
{!loading && hasServers && (
|
||||
<ListGroup className="home__servers-list">
|
||||
{servers.map(({ name, id }) => (
|
||||
<ListGroupItem
|
||||
|
|
|
@ -14,7 +14,12 @@ const ServersDropdown = (serversExporter) => class ServersDropdown extends React
|
|||
};
|
||||
|
||||
renderServers = () => {
|
||||
const { servers, selectedServer, selectServer } = this.props;
|
||||
const { servers: { list, loading }, selectedServer, selectServer } = this.props;
|
||||
const servers = values(list);
|
||||
|
||||
if (loading) {
|
||||
return <DropdownItem disabled><i>Trying to load servers...</i></DropdownItem>;
|
||||
}
|
||||
|
||||
if (isEmpty(servers)) {
|
||||
return <DropdownItem disabled><i>Add a server first...</i></DropdownItem>;
|
||||
|
@ -22,7 +27,7 @@ const ServersDropdown = (serversExporter) => class ServersDropdown extends React
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{values(servers).map(({ name, id }) => (
|
||||
{servers.map(({ name, id }) => (
|
||||
<DropdownItem
|
||||
key={id}
|
||||
tag={Link}
|
||||
|
@ -46,18 +51,14 @@ const ServersDropdown = (serversExporter) => class ServersDropdown extends React
|
|||
);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.listServers();
|
||||
}
|
||||
componentDidMount = this.props.listServers;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<UncontrolledDropdown nav inNavbar>
|
||||
<DropdownToggle nav caret>Servers</DropdownToggle>
|
||||
<DropdownMenu right>{this.renderServers()}</DropdownMenu>
|
||||
</UncontrolledDropdown>
|
||||
);
|
||||
}
|
||||
render = () => (
|
||||
<UncontrolledDropdown nav inNavbar>
|
||||
<DropdownToggle nav caret>Servers</DropdownToggle>
|
||||
<DropdownMenu right>{this.renderServers()}</DropdownMenu>
|
||||
</UncontrolledDropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServersDropdown;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
import { UncontrolledTooltip } from 'reactstrap';
|
||||
import { assoc, map } from 'ramda';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ImportServersBtn = (serversImporter) => class ImportServersBtn extends React.Component {
|
||||
|
@ -22,10 +20,8 @@ const ImportServersBtn = (serversImporter) => class ImportServersBtn extends Rea
|
|||
render() {
|
||||
const { importServersFromFile } = serversImporter;
|
||||
const { onImport, createServers } = this.props;
|
||||
const assocId = (server) => assoc('id', uuid(), server);
|
||||
const onChange = ({ target }) =>
|
||||
importServersFromFile(target.files[0])
|
||||
.then(map(assocId))
|
||||
.then(createServers)
|
||||
.then(onImport)
|
||||
.then(() => {
|
||||
|
|
|
@ -10,10 +10,10 @@ const initialState = null;
|
|||
|
||||
export const resetSelectedServer = createAction(RESET_SELECTED_SERVER);
|
||||
|
||||
export const selectServer = (serversService) => (serverId) => (dispatch) => {
|
||||
export const selectServer = ({ findServerById }) => (serverId) => (dispatch) => {
|
||||
dispatch(resetShortUrlParams());
|
||||
|
||||
const selectedServer = serversService.findServerById(serverId);
|
||||
const selectedServer = findServerById(serverId);
|
||||
|
||||
dispatch({
|
||||
type: SELECT_SERVER,
|
||||
|
|
|
@ -1,16 +1,51 @@
|
|||
import { createAction, handleActions } from 'redux-actions';
|
||||
import { pipe } from 'ramda';
|
||||
import { handleActions } from 'redux-actions';
|
||||
import { pipe, isEmpty, assoc, map, prop } from 'ramda';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { homepage } from '../../../package.json';
|
||||
|
||||
/* eslint-disable padding-line-between-statements */
|
||||
export const FETCH_SERVERS_START = 'shlink/servers/FETCH_SERVERS_START';
|
||||
export const FETCH_SERVERS = 'shlink/servers/FETCH_SERVERS';
|
||||
/* eslint-enable padding-line-between-statements */
|
||||
|
||||
export const listServers = ({ listServers }) => createAction(FETCH_SERVERS, () => listServers());
|
||||
const initialState = {
|
||||
list: {},
|
||||
loading: false,
|
||||
};
|
||||
|
||||
export const createServer = ({ createServer }, listServers) => pipe(createServer, listServers);
|
||||
|
||||
export const deleteServer = ({ deleteServer }, listServers) => pipe(deleteServer, listServers);
|
||||
|
||||
export const createServers = ({ createServers }, listServers) => pipe(createServers, listServers);
|
||||
const assocId = (server) => assoc('id', server.id || uuid(), server);
|
||||
|
||||
export default handleActions({
|
||||
[FETCH_SERVERS]: (state, { payload }) => payload,
|
||||
}, {});
|
||||
[FETCH_SERVERS_START]: (state) => ({ ...state, loading: true }),
|
||||
[FETCH_SERVERS]: (state, { list }) => ({ list, loading: false }),
|
||||
}, initialState);
|
||||
|
||||
export const listServers = ({ listServers, createServers }, { get }) => () => async (dispatch) => {
|
||||
dispatch({ type: FETCH_SERVERS_START });
|
||||
const localList = listServers();
|
||||
|
||||
if (!isEmpty(localList)) {
|
||||
dispatch({ type: FETCH_SERVERS, list: localList });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If local list is empty, try to fetch it remotely and calculate IDs for every server
|
||||
const remoteList = await get(`${homepage}/servers.json`)
|
||||
.then(prop('data'))
|
||||
.then(map(assocId))
|
||||
.catch(() => []);
|
||||
|
||||
createServers(remoteList);
|
||||
dispatch({ type: FETCH_SERVERS, list: remoteList.reduce((map, server) => ({ ...map, [server.id]: server }), {}) });
|
||||
};
|
||||
|
||||
export const createServer = ({ createServer }, listServersAction) => pipe(createServer, listServersAction);
|
||||
|
||||
export const deleteServer = ({ deleteServer }, listServersAction) => pipe(deleteServer, listServersAction);
|
||||
|
||||
export const createServers = ({ createServers }, listServersAction) => pipe(
|
||||
map(assocId),
|
||||
createServers,
|
||||
listServersAction
|
||||
);
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
export const serversImporterType = PropTypes.shape({
|
||||
importServersFromFile: PropTypes.func,
|
||||
});
|
||||
|
||||
export default class ServersImporter {
|
||||
constructor(csvjson) {
|
||||
this.csvjson = csvjson;
|
||||
|
|
|
@ -23,9 +23,6 @@ export default class ServersService {
|
|||
this.storage.set(SERVERS_STORAGE_KEY, allServers);
|
||||
};
|
||||
|
||||
deleteServer = (server) =>
|
||||
this.storage.set(
|
||||
SERVERS_STORAGE_KEY,
|
||||
dissoc(server.id, this.listServers())
|
||||
);
|
||||
deleteServer = ({ id }) =>
|
||||
this.storage.set(SERVERS_STORAGE_KEY, dissoc(id, this.listServers()));
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ const provideServices = (bottle, connect, withRouter) => {
|
|||
bottle.serviceFactory('createServer', createServer, 'ServersService', 'listServers');
|
||||
bottle.serviceFactory('createServers', createServers, 'ServersService', 'listServers');
|
||||
bottle.serviceFactory('deleteServer', deleteServer, 'ServersService', 'listServers');
|
||||
bottle.serviceFactory('listServers', listServers, 'ServersService');
|
||||
bottle.serviceFactory('listServers', listServers, 'ServersService', 'axios');
|
||||
|
||||
bottle.serviceFactory('resetSelectedServer', () => resetSelectedServer);
|
||||
};
|
||||
|
|
|
@ -52,4 +52,4 @@ export const useToggle = (initialValue = false) => {
|
|||
return [ flag, () => setFlag(!flag) ];
|
||||
};
|
||||
|
||||
export const wait = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds));
|
||||
export const wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
||||
|
|
|
@ -6,10 +6,8 @@ import Home from '../../src/common/Home';
|
|||
describe('<Home />', () => {
|
||||
let wrapped;
|
||||
const defaultProps = {
|
||||
resetSelectedServer() {
|
||||
return '';
|
||||
},
|
||||
servers: {},
|
||||
resetSelectedServer: () => '',
|
||||
servers: { loading: false, list: {} },
|
||||
};
|
||||
const createComponent = (props) => {
|
||||
const actualProps = { ...defaultProps, ...props };
|
||||
|
@ -41,10 +39,22 @@ describe('<Home />', () => {
|
|||
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 });
|
||||
|
||||
|
|
|
@ -8,9 +8,12 @@ describe('<ServersDropdown />', () => {
|
|||
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('<ServersDropdown />', () => {
|
|||
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('<ServersDropdown />', () => {
|
|||
expect(items.filter('.servers-dropdown__export-item')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('contains a message when no servers exist yet', () => {
|
||||
wrapped = shallow(<ServersDropdown servers={{}} listServers={identity} />);
|
||||
it('shows a message when no servers exist yet', () => {
|
||||
wrapped = shallow(<ServersDropdown servers={{ loading: false, list: {} }} listServers={identity} />);
|
||||
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(<ServersDropdown servers={{ loading: true, list: {} }} listServers={identity} />);
|
||||
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...');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue