Combine API props, update tests for redux hooks

This commit is contained in:
Nathan Barber
2021-04-08 18:28:49 -04:00
parent 21f4988f24
commit 51deaa36f3
18 changed files with 347 additions and 222 deletions

1
.gitignore vendored
View File

@@ -29,3 +29,4 @@ htmlcov
pip-wheel-metadata
docs/source/reference/metrics.rst
oldest-requirements.txt
jupyterhub-proxy.pid

View File

@@ -1,7 +1,7 @@
{
"extends": ["plugin:react/recommended"],
"parserOptions": {
"ecmaVersion": 6,
"ecmaVersion": 2018,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true

View File

@@ -2,11 +2,11 @@ import React, { Component, useEffect } from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import { Button } from "react-bootstrap";
import { compose } from "recompose";
import { initialState, reducers } from "./Store";
import { jhapiRequest } from "./util/jhapiUtil";
import withAPI from "./util/withAPI";
import { HashRouter, Switch, Route, Link } from "react-router-dom";
import { createBrowserHistory } from "history";
import ServerDashboard from "./components/ServerDashboard/ServerDashboard";
import Groups from "./components/Groups/Groups";
@@ -37,12 +37,32 @@ const App = (props) => {
<Provider store={store}>
<HashRouter>
<Switch>
<Route exact path="/" component={ServerDashboard} />
<Route exact path="/groups" component={Groups} />
<Route exact path="/group-edit" component={GroupEdit} />
<Route exact path="/create-group" component={CreateGroup} />
<Route exact path="/add-users" component={AddUser} />
<Route exact path="/edit-user" component={EditUser} />
<Route
exact
path="/"
component={compose(withAPI)(ServerDashboard)}
/>
<Route exact path="/groups" component={compose(withAPI)(Groups)} />
<Route
exact
path="/group-edit"
component={compose(withAPI)(GroupEdit)}
/>
<Route
exact
path="/create-group"
component={compose(withAPI)(CreateGroup)}
/>
<Route
exact
path="/add-users"
component={compose(withAPI)(AddUser)}
/>
<Route
exact
path="/edit-user"
component={compose(withAPI)(EditUser)}
/>
</Switch>
</HashRouter>
</Provider>

View File

@@ -107,17 +107,4 @@ AddUser.propTypes = {
}),
};
const withUserAPI = withProps((props) => ({
addUsers: (usernames, admin) =>
jhapiRequest("/users", "POST", { usernames, admin }),
failRegexEvent: () =>
alert(
"Removed " +
JSON.stringify(removed_users) +
" for either containing special characters or being too short."
),
refreshUserData: () =>
jhapiRequest("/users", "GET").then((data) => data.json()),
}));
export default compose(withUserAPI)(AddUser);
export default AddUser;

View File

@@ -1,31 +1,53 @@
import React from "react";
import Enzyme, { shallow } from "enzyme";
import AddUser from "./AddUser.pre";
import Enzyme, { mount } from "enzyme";
import AddUser from "./AddUser";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { Provider, useDispatch } from "react-redux";
import { createStore } from "redux";
import { HashRouter } from "react-router-dom";
Enzyme.configure({ adapter: new Adapter() });
jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useDispatch: jest.fn(),
}));
describe("AddUser Component: ", () => {
var mockAsync = () =>
jest.fn().mockImplementation(() => Promise.resolve({ key: "value" }));
var addUserJsx = (callbackSpy) => (
<AddUser
addUsers={callbackSpy}
failRegexEvent={callbackSpy}
refreshUserData={callbackSpy}
history={{ push: (a) => {} }}
/>
<Provider store={createStore(() => {}, {})}>
<HashRouter>
<AddUser
addUsers={callbackSpy}
failRegexEvent={callbackSpy}
refreshUserData={callbackSpy}
history={{ push: (a) => {} }}
/>
</HashRouter>
</Provider>
);
beforeEach(() => {
useDispatch.mockImplementation((callback) => {
return () => {};
});
});
afterEach(() => {
useDispatch.mockClear();
});
it("Renders", () => {
let component = shallow(addUserJsx(mockAsync()));
let component = mount(addUserJsx(mockAsync()));
expect(component.find(".container").length).toBe(1);
});
it("Removes users when they fail Regex", () => {
let callbackSpy = mockAsync(),
component = shallow(addUserJsx(callbackSpy)),
component = mount(addUserJsx(callbackSpy)),
textarea = component.find("textarea").first();
textarea.simulate("blur", { target: { value: "foo\nbar\n!!*&*" } });
let submit = component.find("#submit");
@@ -35,7 +57,7 @@ describe("AddUser Component: ", () => {
it("Correctly submits admin", () => {
let callbackSpy = mockAsync(),
component = shallow(addUserJsx(callbackSpy)),
component = mount(addUserJsx(callbackSpy)),
input = component.find("input").first();
input.simulate("change", { target: { checked: true } });
let submit = component.find("#submit");

View File

@@ -81,16 +81,4 @@ CreateGroup.propTypes = {
}),
};
const withGroupsAPI = withProps((props) => ({
createGroup: (groupName) => jhapiRequest("/groups/" + groupName, "POST"),
failRegexEvent: () =>
alert(
"Removed " +
JSON.stringify(removed_users) +
" for either containing special characters or being too short."
),
refreshGroupsData: () =>
jhapiRequest("/groups", "GET").then((data) => data.json()),
}));
export default compose(withGroupsAPI)(CreateGroup);
export default CreateGroup;

View File

@@ -1,30 +1,52 @@
import React from "react";
import Enzyme, { mount, shallow } from "enzyme";
import CreateGroup from "./CreateGroup.pre";
import Enzyme, { mount } from "enzyme";
import CreateGroup from "./CreateGroup";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { Provider, useDispatch } from "react-redux";
import { createStore } from "redux";
import { HashRouter } from "react-router-dom";
Enzyme.configure({ adapter: new Adapter() });
jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useDispatch: jest.fn(),
}));
describe("CreateGroup Component: ", () => {
var mockAsync = () =>
jest.fn().mockImplementation(() => Promise.resolve({ key: "value" }));
var createGroupJsx = (callbackSpy) => (
<CreateGroup
createGroup={callbackSpy}
refreshGroupsData={callbackSpy}
history={{ push: () => {} }}
/>
<Provider store={createStore(() => {}, {})}>
<HashRouter>
<CreateGroup
createGroup={callbackSpy}
refreshGroupsData={callbackSpy}
history={{ push: () => {} }}
/>
</HashRouter>
</Provider>
);
beforeEach(() => {
useDispatch.mockImplementation((callback) => {
return () => {};
});
});
afterEach(() => {
useDispatch.mockClear();
});
it("Renders", () => {
let component = shallow(createGroupJsx());
let component = mount(createGroupJsx());
expect(component.find(".container").length).toBe(1);
});
it("Calls createGroup and refreshGroupsData on submit", () => {
let callbackSpy = mockAsync(),
component = shallow(createGroupJsx(callbackSpy)),
component = mount(createGroupJsx(callbackSpy)),
input = component.find("input").first(),
submit = component.find("#submit").first();
input.simulate("change", { target: { value: "" } });

View File

@@ -1,12 +1,8 @@
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { compose, withProps } from "recompose";
import { useDispatch } from "react-redux";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
import { jhapiRequest } from "../../util/jhapiUtil";
const EditUser = (props) => {
var dispatch = useDispatch();
@@ -160,22 +156,4 @@ EditUser.propTypes = {
refreshUserData: PropTypes.func,
};
const withUserAPI = withProps((props) => ({
editUser: (username, updated_username, admin) =>
jhapiRequest("/users/" + username, "PATCH", {
name: updated_username,
admin,
}),
deleteUser: (username) => jhapiRequest("/users/" + username, "DELETE"),
failRegexEvent: () =>
alert(
"Cannot change username - either contains special characters or is too short."
),
noChangeEvent: () => {
returns;
},
refreshUserData: () =>
jhapiRequest("/users", "GET").then((data) => data.json()),
}));
export default compose(withUserAPI)(EditUser);
export default EditUser;

View File

@@ -1,30 +1,54 @@
import React from "react";
import Enzyme, { shallow } from "enzyme";
import EditUser from "./EditUser.pre";
import Enzyme, { mount } from "enzyme";
import EditUser from "./EditUser";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { Provider, useDispatch } from "react-redux";
import { createStore } from "redux";
import { HashRouter } from "react-router-dom";
Enzyme.configure({ adapter: new Adapter() });
jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useDispatch: jest.fn(),
}));
describe("EditUser Component: ", () => {
var mockAsync = () =>
jest.fn().mockImplementation(() => Promise.resolve({ key: "value" }));
var mockSync = () => jest.fn();
var editUserJsx = (callbackSpy) => (
<EditUser
location={{ state: { username: "foo", has_admin: false } }}
deleteUser={callbackSpy}
editUser={callbackSpy}
refreshUserData={mockSync()}
history={{ push: (a) => {} }}
failRegexEvent={callbackSpy}
noChangeEvent={callbackSpy}
/>
var editUserJsx = (callbackSpy, empty) => (
<Provider store={createStore(() => {}, {})}>
<HashRouter>
<EditUser
location={
empty ? {} : { state: { username: "foo", has_admin: false } }
}
deleteUser={callbackSpy}
editUser={callbackSpy}
refreshUserData={callbackSpy}
history={{ push: (a) => {} }}
failRegexEvent={callbackSpy}
noChangeEvent={callbackSpy}
/>
</HashRouter>
</Provider>
);
beforeEach(() => {
useDispatch.mockImplementation((callback) => {
return () => {};
});
});
afterEach(() => {
useDispatch.mockClear();
});
it("Calls the delete user function when the button is pressed", () => {
let callbackSpy = mockAsync(),
component = shallow(editUserJsx(callbackSpy)),
component = mount(editUserJsx(callbackSpy)),
deleteUser = component.find("#delete-user");
deleteUser.simulate("click");
expect(callbackSpy).toHaveBeenCalled();
@@ -32,9 +56,15 @@ describe("EditUser Component: ", () => {
it("Submits the edits when the button is pressed", () => {
let callbackSpy = mockSync(),
component = shallow(editUserJsx(callbackSpy)),
component = mount(editUserJsx(callbackSpy)),
submit = component.find("#submit");
submit.simulate("click");
expect(callbackSpy).toHaveBeenCalled();
});
it("Doesn't render when no data is provided", () => {
let callbackSpy = mockSync(),
component = mount(editUserJsx(callbackSpy, true));
expect(component.find(".container").length).toBe(0);
});
});

View File

@@ -137,14 +137,4 @@ GroupEdit.propTypes = {
refreshGroupsData: PropTypes.func,
};
const withGroupsAPI = withProps((props) => ({
addToGroup: (users, groupname) =>
jhapiRequest("/groups/" + groupname + "/users", "POST", { users }),
removeFromGroup: (users, groupname) =>
jhapiRequest("/groups/" + groupname + "/users", "DELETE", { users }),
deleteGroup: (name) => jhapiRequest("/groups/" + name, "DELETE"),
refreshGroupsData: () =>
jhapiRequest("/groups", "GET").then((data) => data.json()),
}));
export default compose(withGroupsAPI)(GroupEdit);
export default GroupEdit;

View File

@@ -1,39 +1,62 @@
import React from "react";
import Enzyme, { mount, shallow } from "enzyme";
import GroupEdit from "./GroupEdit.pre";
import GroupEdit from "./GroupEdit";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { Provider, useSelector } from "react-redux";
import { createStore } from "redux";
import { HashRouter } from "react-router-dom";
Enzyme.configure({ adapter: new Adapter() });
jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useSelector: jest.fn(),
}));
describe("GroupEdit Component: ", () => {
var mockAsync = () =>
jest.fn().mockImplementation(() => Promise.resolve({ key: "value" }));
var groupEditJsx = (callbackSpy) => (
<GroupEdit
location={{
state: {
user_data: [{ name: "foo" }, { name: "bar" }],
group_data: { users: ["foo"], name: "group" },
callback: () => {},
},
}}
addToGroup={callbackSpy}
removeFromGroup={callbackSpy}
deleteGroup={callbackSpy}
history={{ push: (a) => callbackSpy }}
refreshGroupsData={() => {}}
/>
<Provider store={createStore(() => {}, {})}>
<HashRouter>
<GroupEdit
location={{
state: {
user_data: [{ name: "foo" }, { name: "bar" }],
group_data: { users: ["foo"], name: "group" },
callback: () => {},
},
}}
addToGroup={callbackSpy}
removeFromGroup={callbackSpy}
deleteGroup={callbackSpy}
history={{ push: (a) => callbackSpy }}
refreshGroupsData={callbackSpy}
/>
</HashRouter>
</Provider>
);
var deepGroupEditJsx = (callbackSpy) => (
<HashRouter>{groupEditJsx(callbackSpy)}</HashRouter>
);
var mockAppState = () => ({
user_data: JSON.parse(
'[{"kind":"user","name":"foo","admin":true,"groups":[],"server":"/user/foo/","pending":null,"created":"2020-12-07T18:46:27.112695Z","last_activity":"2020-12-07T21:00:33.336354Z","servers":{"":{"name":"","last_activity":"2020-12-07T20:58:02.437408Z","started":"2020-12-07T20:58:01.508266Z","pending":null,"ready":true,"state":{"pid":28085},"url":"/user/foo/","user_options":{},"progress_url":"/hub/api/users/foo/server/progress"}}},{"kind":"user","name":"bar","admin":false,"groups":[],"server":null,"pending":null,"created":"2020-12-07T18:46:27.115528Z","last_activity":"2020-12-07T20:43:51.013613Z","servers":{}}]'
),
});
beforeEach(() => {
useSelector.mockImplementation((callback) => {
return callback(mockAppState());
});
});
afterEach(() => {
useSelector.mockClear();
});
it("Adds a newly selected user to group on submit", () => {
let callbackSpy = mockAsync(),
component = mount(deepGroupEditJsx(callbackSpy)),
component = mount(groupEditJsx(callbackSpy)),
unselected = component.find(".unselected"),
submit = component.find("#submit");
unselected.simulate("click");
@@ -43,7 +66,7 @@ describe("GroupEdit Component: ", () => {
it("Removes a user from group on submit", () => {
let callbackSpy = mockAsync(),
component = mount(deepGroupEditJsx(callbackSpy)),
component = mount(groupEditJsx(callbackSpy)),
selected = component.find(".selected"),
submit = component.find("#submit");
selected.simulate("click");
@@ -53,9 +76,10 @@ describe("GroupEdit Component: ", () => {
it("Calls deleteGroup on button click", () => {
let callbackSpy = mockAsync(),
component = shallow(groupEditJsx(callbackSpy)),
component = mount(groupEditJsx(callbackSpy)),
deleteGroup = component.find("#delete-group").first();
deleteGroup.simulate("click");
expect(callbackSpy).toHaveBeenCalled();
expect(callbackSpy).toHaveBeenNthCalledWith(1, "group");
expect(callbackSpy).toHaveBeenNthCalledWith(2);
});
});

View File

@@ -102,21 +102,4 @@ Groups.propTypes = {
}),
};
const withGroupsAPI = withProps((props) => ({
refreshGroupsData: () =>
jhapiRequest("/groups", "GET").then((data) => data.json()),
refreshUserData: () =>
jhapiRequest("/users", "GET").then((data) => data.json()),
addUsersToGroup: (name, new_users) =>
jhapiRequest("/groups/" + name + "/users", "POST", {
body: { users: new_users },
json: true,
}),
removeUsersFromGroup: (name, removed_users) =>
jhapiRequest("/groups/" + name + "/users", "DELETE", {
body: { users: removed_users },
json: true,
}),
}));
export default compose(withGroupsAPI)(Groups);
export default Groups;

View File

@@ -1,30 +1,58 @@
import React from "react";
import Enzyme, { shallow } from "enzyme";
import Groups from "./Groups.pre";
import Enzyme, { mount } from "enzyme";
import Groups from "./Groups";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { Provider, useSelector } from "react-redux";
import { createStore } from "redux";
import { HashRouter } from "react-router-dom";
Enzyme.configure({ adapter: new Adapter() });
jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useSelector: jest.fn(),
useDispatch: jest.fn(),
}));
describe("Groups Component: ", () => {
var groupsJsx = () => (
<Groups
user_data={JSON.parse(
'[{"kind":"user","name":"foo","admin":true,"groups":[],"server":"/user/foo/","pending":null,"created":"2020-12-07T18:46:27.112695Z","last_activity":"2020-12-07T21:00:33.336354Z","servers":{"":{"name":"","last_activity":"2020-12-07T20:58:02.437408Z","started":"2020-12-07T20:58:01.508266Z","pending":null,"ready":true,"state":{"pid":28085},"url":"/user/foo/","user_options":{},"progress_url":"/hub/api/users/foo/server/progress"}}},{"kind":"user","name":"bar","admin":false,"groups":[],"server":null,"pending":null,"created":"2020-12-07T18:46:27.115528Z","last_activity":"2020-12-07T20:43:51.013613Z","servers":{}}]'
)}
groups_data={JSON.parse(
'[{"kind":"group","name":"testgroup","users":[]}, {"kind":"group","name":"testgroup2","users":["foo", "bar"]}]'
)}
/>
<Provider store={createStore(() => {}, {})}>
<HashRouter>
<Groups />
</HashRouter>
</Provider>
);
var mockAppState = () => ({
user_data: JSON.parse(
'[{"kind":"user","name":"foo","admin":true,"groups":[],"server":"/user/foo/","pending":null,"created":"2020-12-07T18:46:27.112695Z","last_activity":"2020-12-07T21:00:33.336354Z","servers":{"":{"name":"","last_activity":"2020-12-07T20:58:02.437408Z","started":"2020-12-07T20:58:01.508266Z","pending":null,"ready":true,"state":{"pid":28085},"url":"/user/foo/","user_options":{},"progress_url":"/hub/api/users/foo/server/progress"}}},{"kind":"user","name":"bar","admin":false,"groups":[],"server":null,"pending":null,"created":"2020-12-07T18:46:27.115528Z","last_activity":"2020-12-07T20:43:51.013613Z","servers":{}}]'
),
groups_data: JSON.parse(
'[{"kind":"group","name":"testgroup","users":[]}, {"kind":"group","name":"testgroup2","users":["foo", "bar"]}]'
),
});
beforeEach(() => {
useSelector.mockImplementation((callback) => {
return callback(mockAppState());
});
});
afterEach(() => {
useSelector.mockClear();
});
it("Renders groups_data prop into links", () => {
let component = shallow(groupsJsx()),
let component = mount(groupsJsx()),
links = component.find(".group-edit-link");
expect(links.length).toBe(2);
});
it("Renders nothing if required data is not available", () => {
let component = shallow(<Groups />);
useSelector.mockImplementation((callback) => {
return callback({});
});
let component = mount(groupsJsx());
expect(component.html()).toBe("<div></div>");
});
});

View File

@@ -155,7 +155,7 @@ const ServerDashboard = (props) => {
{/* Shutdown Jupyterhub */}
<Button
variant="danger"
className="shutdown-button"
id="shutdown-button"
onClick={shutdownHub}
>
Shutdown Hub
@@ -288,15 +288,4 @@ SortHandler.propTypes = {
callback: PropTypes.func,
};
const withHubActions = withProps((props) => ({
updateUsers: (cb) => jhapiRequest("/users", "GET"),
shutdownHub: () => jhapiRequest("/shutdown", "POST"),
startServer: (name) => jhapiRequest("/users/" + name + "/server", "POST"),
stopServer: (name) => jhapiRequest("/users/" + name + "/server", "DELETE"),
startAll: (names) =>
names.map((e) => jhapiRequest("/users/" + e + "/server", "POST")),
stopAll: (names) =>
names.map((e) => jhapiRequest("/users/" + e + "/server", "DELETE")),
}));
export default compose(withHubActions)(ServerDashboard);
export default ServerDashboard;

View File

@@ -1,44 +1,34 @@
import React from "react";
import Enzyme, { shallow, mount } from "enzyme";
import ServerDashboard from "./ServerDashboard.pre";
import ServerDashboard from "./ServerDashboard";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { HashRouter, Switch } from "react-router-dom";
import { Provider, useSelector } from "react-redux";
import { createStore } from "redux";
Enzyme.configure({ adapter: new Adapter() });
jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useSelector: jest.fn(),
}));
describe("ServerDashboard Component: ", () => {
var serverDashboardJsx = (callbackSpy) => (
<ServerDashboard
user_data={JSON.parse(
'[{"kind":"user","name":"foo","admin":true,"groups":[],"server":"/user/foo/","pending":null,"created":"2020-12-07T18:46:27.112695Z","last_activity":"2020-12-07T21:00:33.336354Z","servers":{"":{"name":"","last_activity":"2020-12-07T20:58:02.437408Z","started":"2020-12-07T20:58:01.508266Z","pending":null,"ready":true,"state":{"pid":28085},"url":"/user/foo/","user_options":{},"progress_url":"/hub/api/users/foo/server/progress"}}},{"kind":"user","name":"bar","admin":false,"groups":[],"server":null,"pending":null,"created":"2020-12-07T18:46:27.115528Z","last_activity":"2020-12-07T20:43:51.013613Z","servers":{}}]'
)}
updateUsers={callbackSpy}
shutdownHub={callbackSpy}
startServer={callbackSpy}
stopServer={callbackSpy}
startAll={callbackSpy}
stopAll={callbackSpy}
dispatch={callbackSpy}
/>
);
var deepServerDashboardJsx = (callbackSpy) => (
<HashRouter>
<Switch>
<ServerDashboard
user_data={JSON.parse(
'[{"kind":"user","name":"foo","admin":true,"groups":[],"server":"/user/foo/","pending":null,"created":"2020-12-07T18:46:27.112695Z","last_activity":"2020-12-07T21:00:33.336354Z","servers":{"":{"name":"","last_activity":"2020-12-07T20:58:02.437408Z","started":"2020-12-07T20:58:01.508266Z","pending":null,"ready":true,"state":{"pid":28085},"url":"/user/foo/","user_options":{},"progress_url":"/hub/api/users/foo/server/progress"}}},{"kind":"user","name":"bar","admin":false,"groups":[],"server":null,"pending":null,"created":"2020-12-07T18:46:27.115528Z","last_activity":"2020-12-07T20:43:51.013613Z","servers":{}}]'
)}
updateUsers={callbackSpy}
shutdownHub={callbackSpy}
startServer={callbackSpy}
stopServer={callbackSpy}
startAll={callbackSpy}
stopAll={callbackSpy}
dispatch={callbackSpy}
/>
</Switch>
</HashRouter>
<Provider store={createStore(() => {}, {})}>
<HashRouter>
<Switch>
<ServerDashboard
updateUsers={callbackSpy}
shutdownHub={callbackSpy}
startServer={callbackSpy}
stopServer={callbackSpy}
startAll={callbackSpy}
stopAll={callbackSpy}
/>
</Switch>
</HashRouter>
</Provider>
);
var mockAsync = () =>
@@ -48,14 +38,30 @@ describe("ServerDashboard Component: ", () => {
Promise.resolve({ json: () => Promise.resolve({ k: "v" }) })
);
var mockAppState = () => ({
user_data: JSON.parse(
'[{"kind":"user","name":"foo","admin":true,"groups":[],"server":"/user/foo/","pending":null,"created":"2020-12-07T18:46:27.112695Z","last_activity":"2020-12-07T21:00:33.336354Z","servers":{"":{"name":"","last_activity":"2020-12-07T20:58:02.437408Z","started":"2020-12-07T20:58:01.508266Z","pending":null,"ready":true,"state":{"pid":28085},"url":"/user/foo/","user_options":{},"progress_url":"/hub/api/users/foo/server/progress"}}},{"kind":"user","name":"bar","admin":false,"groups":[],"server":null,"pending":null,"created":"2020-12-07T18:46:27.115528Z","last_activity":"2020-12-07T20:43:51.013613Z","servers":{}}]'
),
});
beforeEach(() => {
useSelector.mockImplementation((callback) => {
return callback(mockAppState());
});
});
afterEach(() => {
useSelector.mockClear();
});
it("Renders users from props.user_data into table", () => {
let component = shallow(serverDashboardJsx(jest.fn())),
let component = mount(serverDashboardJsx(jest.fn())),
userRows = component.find(".user-row");
expect(userRows.length).toBe(2);
});
it("Renders correctly the status of a single-user server", () => {
let component = shallow(serverDashboardJsx(jest.fn())),
let component = mount(serverDashboardJsx(jest.fn())),
userRows = component.find(".user-row");
// Renders .stop-button when server is started
// Should be 1 since user foo is started
@@ -67,7 +73,7 @@ describe("ServerDashboard Component: ", () => {
it("Invokes the startServer event on button click", () => {
let callbackSpy = mockAsync(),
component = shallow(serverDashboardJsx(callbackSpy)),
component = mount(serverDashboardJsx(callbackSpy)),
startBtn = component.find(".start-button");
startBtn.simulate("click");
expect(callbackSpy).toHaveBeenCalled();
@@ -75,7 +81,7 @@ describe("ServerDashboard Component: ", () => {
it("Invokes the stopServer event on button click", () => {
let callbackSpy = mockAsync(),
component = shallow(serverDashboardJsx(callbackSpy)),
component = mount(serverDashboardJsx(callbackSpy)),
stopBtn = component.find(".stop-button");
stopBtn.simulate("click");
expect(callbackSpy).toHaveBeenCalled();
@@ -83,14 +89,14 @@ describe("ServerDashboard Component: ", () => {
it("Invokes the shutdownHub event on button click", () => {
let callbackSpy = mockAsync(),
component = shallow(serverDashboardJsx(callbackSpy)),
shutdownBtn = component.find(".shutdown-button");
component = mount(serverDashboardJsx(callbackSpy)),
shutdownBtn = component.find("#shutdown-button").first();
shutdownBtn.simulate("click");
expect(callbackSpy).toHaveBeenCalled();
});
it("Sorts according to username", () => {
let component = mount(deepServerDashboardJsx(jest.fn())).find(
let component = mount(serverDashboardJsx(jest.fn())).find(
"ServerDashboard"
),
handler = component.find("SortHandler").first();
@@ -103,7 +109,7 @@ describe("ServerDashboard Component: ", () => {
});
it("Sorts according to admin", () => {
let component = mount(deepServerDashboardJsx(jest.fn())).find(
let component = mount(serverDashboardJsx(jest.fn())).find(
"ServerDashboard"
),
handler = component.find("SortHandler").at(1);
@@ -116,7 +122,7 @@ describe("ServerDashboard Component: ", () => {
});
it("Sorts according to last activity", () => {
let component = mount(deepServerDashboardJsx(jest.fn())).find(
let component = mount(serverDashboardJsx(jest.fn())).find(
"ServerDashboard"
),
handler = component.find("SortHandler").at(2);
@@ -131,7 +137,7 @@ describe("ServerDashboard Component: ", () => {
});
it("Sorts according to server status (running/not running)", () => {
let component = mount(deepServerDashboardJsx(jest.fn())).find(
let component = mount(serverDashboardJsx(jest.fn())).find(
"ServerDashboard"
),
handler = component.find("SortHandler").at(3);
@@ -146,7 +152,10 @@ describe("ServerDashboard Component: ", () => {
});
it("Renders nothing if required data is not available", () => {
let component = shallow(<ServerDashboard />);
useSelector.mockImplementation((callback) => {
return callback({});
});
let component = mount(serverDashboardJsx(jest.fn()));
expect(component.html()).toBe("<div></div>");
});
});

40
jsx/src/util/withAPI.js Normal file
View File

@@ -0,0 +1,40 @@
import { withProps } from "recompose";
import { jhapiRequest } from "./jhapiUtil";
const withAPI = withProps((props) => ({
updateUsers: (cb) => jhapiRequest("/users", "GET"),
shutdownHub: () => jhapiRequest("/shutdown", "POST"),
startServer: (name) => jhapiRequest("/users/" + name + "/server", "POST"),
stopServer: (name) => jhapiRequest("/users/" + name + "/server", "DELETE"),
startAll: (names) =>
names.map((e) => jhapiRequest("/users/" + e + "/server", "POST")),
stopAll: (names) =>
names.map((e) => jhapiRequest("/users/" + e + "/server", "DELETE")),
addToGroup: (users, groupname) =>
jhapiRequest("/groups/" + groupname + "/users", "POST", { users }),
removeFromGroup: (users, groupname) =>
jhapiRequest("/groups/" + groupname + "/users", "DELETE", { users }),
createGroup: (groupName) => jhapiRequest("/groups/" + groupName, "POST"),
deleteGroup: (name) => jhapiRequest("/groups/" + name, "DELETE"),
addUsers: (usernames, admin) =>
jhapiRequest("/users", "POST", { usernames, admin }),
editUser: (username, updated_username, admin) =>
jhapiRequest("/users/" + username, "PATCH", {
name: updated_username,
admin,
}),
deleteUser: (username) => jhapiRequest("/users/" + username, "DELETE"),
failRegexEvent: () =>
alert(
"Cannot change username - either contains special characters or is too short."
),
noChangeEvent: () => {
returns;
},
refreshGroupsData: () =>
jhapiRequest("/groups", "GET").then((data) => data.json()),
refreshUserData: () =>
jhapiRequest("/users", "GET").then((data) => data.json()),
}));
export default withAPI;

View File

@@ -1 +0,0 @@
38441

File diff suppressed because one or more lines are too long