Updated CreateGroup, EditUser to testing-library. Added tests

This commit is contained in:
Nathan Barber
2021-11-30 23:23:52 -05:00
parent 9e245379e8
commit c048ad4aac
5 changed files with 274 additions and 161 deletions

View File

@@ -1,16 +1,16 @@
import React from "react"; import React from "react";
import "@testing-library/jest-dom"; import "@testing-library/jest-dom";
import { act } from "react-dom/test-utils"; import { act } from "react-dom/test-utils";
import { render, screen, fireEvent } from '@testing-library/react' import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from '@testing-library/user-event' import userEvent from "@testing-library/user-event";
import { Provider, useDispatch, useSelector } from "react-redux"; import { Provider, useDispatch, useSelector } from "react-redux";
import { createStore } from "redux"; import { createStore } from "redux";
import { HashRouter } from "react-router-dom"; import { HashRouter } from "react-router-dom";
import AddUser from "./AddUser";
// eslint-disable-next-line // eslint-disable-next-line
import regeneratorRuntime from 'regenerator-runtime' import regeneratorRuntime from 'regenerator-runtime'
import AddUser from "./AddUser";
jest.mock("react-redux", () => ({ jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"), ...jest.requireActual("react-redux"),
useDispatch: jest.fn(), useDispatch: jest.fn(),
@@ -18,11 +18,7 @@ jest.mock("react-redux", () => ({
})); }));
var mockAsync = (result) => var mockAsync = (result) =>
jest jest.fn().mockImplementation(() => Promise.resolve(result));
.fn()
.mockImplementation(() =>
Promise.resolve(result)
);
var mockAsyncRejection = () => var mockAsyncRejection = () =>
jest.fn().mockImplementation(() => Promise.reject()); jest.fn().mockImplementation(() => Promise.reject());
@@ -59,25 +55,25 @@ afterEach(() => {
test("Renders", async () => { test("Renders", async () => {
await act(async () => { await act(async () => {
render(addUserJsx()) render(addUserJsx());
}) });
expect(screen.getByTestId("container")).toBeVisible() expect(screen.getByTestId("container")).toBeVisible();
}); });
test("Removes users when they fail Regex", async () => { test("Removes users when they fail Regex", async () => {
let callbackSpy = mockAsync(); let callbackSpy = mockAsync();
await act(async () => { await act(async () => {
render(addUserJsx(callbackSpy)) render(addUserJsx(callbackSpy));
}) });
let textarea = screen.getByTestId("user-textarea") let textarea = screen.getByTestId("user-textarea");
let submit = screen.getByTestId("submit") let submit = screen.getByTestId("submit");
fireEvent.blur(textarea, { target: { value: "foo\nbar\n!!*&*" }}) fireEvent.blur(textarea, { target: { value: "foo\nbar\n!!*&*" } });
await act(async () => { await act(async () => {
fireEvent.click(submit) fireEvent.click(submit);
}) });
expect(callbackSpy).toHaveBeenCalledWith(["foo", "bar"], false); expect(callbackSpy).toHaveBeenCalledWith(["foo", "bar"], false);
}); });
@@ -86,18 +82,18 @@ test("Correctly submits admin", async () => {
let callbackSpy = mockAsync(); let callbackSpy = mockAsync();
await act(async () => { await act(async () => {
render(addUserJsx(callbackSpy)) render(addUserJsx(callbackSpy));
}) });
let textarea = screen.getByTestId("user-textarea") let textarea = screen.getByTestId("user-textarea");
let submit = screen.getByTestId("submit") let submit = screen.getByTestId("submit");
let check = screen.getByTestId("check") let check = screen.getByTestId("check");
userEvent.click(check) userEvent.click(check);
fireEvent.blur(textarea, { target: { value: "foo" }}) fireEvent.blur(textarea, { target: { value: "foo" } });
await act(async () => { await act(async () => {
fireEvent.click(submit) fireEvent.click(submit);
}) });
expect(callbackSpy).toHaveBeenCalledWith(["foo"], true); expect(callbackSpy).toHaveBeenCalledWith(["foo"], true);
}); });
@@ -106,18 +102,18 @@ test("Shows a UI error dialogue when user creation fails", async () => {
let callbackSpy = mockAsyncRejection(); let callbackSpy = mockAsyncRejection();
await act(async () => { await act(async () => {
render(addUserJsx(callbackSpy)) render(addUserJsx(callbackSpy));
}) });
let submit = screen.getByTestId("submit") let submit = screen.getByTestId("submit");
await act(async () => { await act(async () => {
fireEvent.click(submit) fireEvent.click(submit);
}) });
let errorDialog = screen.getByText("Failed to create user.") let errorDialog = screen.getByText("Failed to create user.");
expect(errorDialog).toBeVisible() expect(errorDialog).toBeVisible();
expect(callbackSpy).toHaveBeenCalled(); expect(callbackSpy).toHaveBeenCalled();
}); });
@@ -125,17 +121,19 @@ test("Shows a more specific UI error dialogue when user creation returns an impr
let callbackSpy = mockAsync({ status: 409 }); let callbackSpy = mockAsync({ status: 409 });
await act(async () => { await act(async () => {
render(addUserJsx(callbackSpy)) render(addUserJsx(callbackSpy));
}) });
let submit = screen.getByTestId("submit") let submit = screen.getByTestId("submit");
await act(async () => { await act(async () => {
fireEvent.click(submit) fireEvent.click(submit);
}) });
let errorDialog = screen.getByText("Failed to create user. User already exists.") let errorDialog = screen.getByText(
"Failed to create user. User already exists."
);
expect(errorDialog).toBeVisible() expect(errorDialog).toBeVisible();
expect(callbackSpy).toHaveBeenCalled(); expect(callbackSpy).toHaveBeenCalled();
}); });

View File

@@ -24,7 +24,7 @@ const CreateGroup = (props) => {
return ( return (
<> <>
<div className="container"> <div className="container" data-testid="container">
{errorAlert != null ? ( {errorAlert != null ? (
<div className="row"> <div className="row">
<div className="col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2"> <div className="col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
@@ -53,6 +53,7 @@ const CreateGroup = (props) => {
<div className="input-group"> <div className="input-group">
<input <input
className="group-name-input" className="group-name-input"
data-testid="group-input"
type="text" type="text"
id="group-name" id="group-name"
value={groupName} value={groupName}
@@ -70,6 +71,7 @@ const CreateGroup = (props) => {
<span> </span> <span> </span>
<button <button
id="submit" id="submit"
data-testid="submit"
className="btn btn-primary" className="btn btn-primary"
onClick={() => { onClick={() => {
createGroup(groupName) createGroup(groupName)
@@ -89,7 +91,7 @@ const CreateGroup = (props) => {
}` }`
); );
}) })
.catch((err) => setErrorAlert(`Could not create group.`)); .catch((err) => setErrorAlert(`Failed to create group.`));
}} }}
> >
Create Create

View File

@@ -1,13 +1,14 @@
import React from "react"; import React from "react";
import Enzyme, { mount } from "enzyme"; import "@testing-library/jest-dom";
import CreateGroup from "./CreateGroup"; import { act } from "react-dom/test-utils";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Provider, useDispatch, useSelector } from "react-redux"; import { Provider, useDispatch, useSelector } from "react-redux";
import { createStore } from "redux"; import { createStore } from "redux";
import { HashRouter } from "react-router-dom"; import { HashRouter } from "react-router-dom";
import regeneratorRuntime from "regenerator-runtime"; // eslint-disable-line // eslint-disable-next-line
import regeneratorRuntime from "regenerator-runtime";
Enzyme.configure({ adapter: new Adapter() }); import CreateGroup from "./CreateGroup";
jest.mock("react-redux", () => ({ jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"), ...jest.requireActual("react-redux"),
@@ -15,10 +16,12 @@ jest.mock("react-redux", () => ({
useSelector: jest.fn(), useSelector: jest.fn(),
})); }));
describe("CreateGroup Component: ", () => {
var mockAsync = (result) => var mockAsync = (result) =>
jest.fn().mockImplementation(() => Promise.resolve(result)); jest.fn().mockImplementation(() => Promise.resolve(result));
var mockAsyncRejection = () =>
jest.fn().mockImplementation(() => Promise.reject());
var createGroupJsx = (callbackSpy) => ( var createGroupJsx = (callbackSpy) => (
<Provider store={createStore(() => {}, {})}> <Provider store={createStore(() => {}, {})}>
<HashRouter> <HashRouter>
@@ -48,19 +51,65 @@ describe("CreateGroup Component: ", () => {
useDispatch.mockClear(); useDispatch.mockClear();
}); });
it("Renders", () => { test("Renders", async () => {
let component = mount(createGroupJsx()); await act(async () => {
expect(component.find(".container").length).toBe(1); render(createGroupJsx());
});
expect(screen.getByTestId("container")).toBeVisible();
}); });
it("Calls createGroup on submit", () => { test("Calls createGroup on submit", async () => {
let callbackSpy = mockAsync({ status: 200 }), let callbackSpy = mockAsync({ status: 200 });
component = mount(createGroupJsx(callbackSpy)),
input = component.find("input").first(), await act(async () => {
submit = component.find("#submit").first(); render(createGroupJsx(callbackSpy));
input.simulate("change", { target: { value: "" } });
submit.simulate("click");
expect(callbackSpy).toHaveBeenNthCalledWith(1, "");
expect(component.find(".alert.alert-danger").length).toBe(0);
}); });
let input = screen.getByTestId("group-input");
let submit = screen.getByTestId("submit");
userEvent.type(input, "groupname");
await act(async () => fireEvent.click(submit));
expect(callbackSpy).toHaveBeenNthCalledWith(1, "groupname");
});
test("Shows a UI error dialogue when group creation fails", async () => {
let callbackSpy = mockAsyncRejection();
await act(async () => {
render(createGroupJsx(callbackSpy));
});
let submit = screen.getByTestId("submit");
await act(async () => {
fireEvent.click(submit);
});
let errorDialog = screen.getByText("Failed to create group.");
expect(errorDialog).toBeVisible();
expect(callbackSpy).toHaveBeenCalled();
});
test("Shows a more specific UI error dialogue when user creation returns an improper status code", async () => {
let callbackSpy = mockAsync({ status: 409 });
await act(async () => {
render(createGroupJsx(callbackSpy));
});
let submit = screen.getByTestId("submit");
await act(async () => {
fireEvent.click(submit);
});
let errorDialog = screen.getByText(
"Failed to create group. Group already exists."
);
expect(errorDialog).toBeVisible();
expect(callbackSpy).toHaveBeenCalled();
}); });

View File

@@ -40,7 +40,7 @@ const EditUser = (props) => {
return ( return (
<> <>
<div className="container"> <div className="container" data-testid="container">
{errorAlert != null ? ( {errorAlert != null ? (
<div className="row"> <div className="row">
<div className="col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2"> <div className="col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
@@ -70,6 +70,7 @@ const EditUser = (props) => {
<div className="form-group"> <div className="form-group">
<textarea <textarea
className="form-control" className="form-control"
data-testid="edit-username-input"
id="exampleFormControlTextarea1" id="exampleFormControlTextarea1"
rows="3" rows="3"
placeholder="updated username" placeholder="updated username"
@@ -90,8 +91,10 @@ const EditUser = (props) => {
<br></br> <br></br>
<button <button
id="delete-user" id="delete-user"
data-testid="delete-user"
className="btn btn-danger btn-sm" className="btn btn-danger btn-sm"
onClick={() => { onClick={(e) => {
e.preventDefault();
deleteUser(username) deleteUser(username)
.then((data) => { .then((data) => {
data.status < 300 data.status < 300
@@ -122,8 +125,10 @@ const EditUser = (props) => {
<span> </span> <span> </span>
<button <button
id="submit" id="submit"
data-testid="submit"
className="btn btn-primary" className="btn btn-primary"
onClick={() => { onClick={(e) => {
e.preventDefault();
if (updatedUsername == "" && admin == has_admin) { if (updatedUsername == "" && admin == has_admin) {
noChangeEvent(); noChangeEvent();
return; return;

View File

@@ -1,12 +1,14 @@
import React from "react"; import React from "react";
import Enzyme, { mount } from "enzyme"; import "@testing-library/jest-dom";
import EditUser from "./EditUser"; import { act } from "react-dom/test-utils";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; import { render, screen, fireEvent } from "@testing-library/react";
import { Provider, useDispatch, useSelector } from "react-redux"; import { Provider, useDispatch, useSelector } from "react-redux";
import { createStore } from "redux"; import { createStore } from "redux";
import { HashRouter } from "react-router-dom"; import { HashRouter } from "react-router-dom";
// eslint-disable-next-line
import regeneratorRuntime from 'regenerator-runtime'
Enzyme.configure({ adapter: new Adapter() }); import EditUser from "./EditUser";
jest.mock("react-redux", () => ({ jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"), ...jest.requireActual("react-redux"),
@@ -14,20 +16,17 @@ jest.mock("react-redux", () => ({
useSelector: jest.fn(), useSelector: jest.fn(),
})); }));
describe("EditUser Component: ", () => { var mockAsync = (data) =>
var mockAsync = () => jest.fn().mockImplementation(() => Promise.resolve(data));
jest
.fn() var mockAsyncRejection = () =>
.mockImplementation(() => Promise.resolve({ key: "value", status: 200 })); jest.fn().mockImplementation(() => Promise.reject());
var mockSync = () => jest.fn();
var editUserJsx = (callbackSpy, empty) => ( var editUserJsx = (callbackSpy, empty) => (
<Provider store={createStore(() => {}, {})}> <Provider store={createStore(() => {}, {})}>
<HashRouter> <HashRouter>
<EditUser <EditUser
location={ location={empty ? {} : { state: { username: "foo", has_admin: false } }}
empty ? {} : { state: { username: "foo", has_admin: false } }
}
deleteUser={callbackSpy} deleteUser={callbackSpy}
editUser={callbackSpy} editUser={callbackSpy}
updateUsers={callbackSpy} updateUsers={callbackSpy}
@@ -56,25 +55,85 @@ describe("EditUser Component: ", () => {
useDispatch.mockClear(); useDispatch.mockClear();
}); });
it("Calls the delete user function when the button is pressed", () => { test("Renders", async () => {
let callbackSpy = mockAsync(), let callbackSpy = mockAsync({ key: "value", status: 200 });
component = mount(editUserJsx(callbackSpy)),
deleteUser = component.find("#delete-user"); await act(async () => {
deleteUser.simulate("click"); render(editUserJsx(callbackSpy));
});
expect(screen.getByTestId("container")).toBeVisible();
});
test("Calls the delete user function when the button is pressed", async () => {
let callbackSpy = mockAsync({ key: "value", status: 200 });
await act(async () => {
render(editUserJsx(callbackSpy));
});
let deleteUser = screen.getByTestId("delete-user");
await act(async () => {
fireEvent.click(deleteUser);
});
expect(callbackSpy).toHaveBeenCalled(); expect(callbackSpy).toHaveBeenCalled();
}); });
it("Submits the edits when the button is pressed", () => { test("Submits the edits when the button is pressed", async () => {
let callbackSpy = mockSync(), let callbackSpy = mockAsync({ key: "value", status: 200 });
component = mount(editUserJsx(callbackSpy)),
submit = component.find("#submit"); await act(async () => {
submit.simulate("click"); render(editUserJsx(callbackSpy));
});
let submit = screen.getByTestId("submit");
await act(async () => {
fireEvent.click(submit);
});
expect(callbackSpy).toHaveBeenCalled(); expect(callbackSpy).toHaveBeenCalled();
}); });
it("Doesn't render when no data is provided", () => { test("Shows a UI error dialogue when user edit fails", async () => {
let callbackSpy = mockSync(), let callbackSpy = mockAsyncRejection();
component = mount(editUserJsx(callbackSpy, true));
expect(component.find(".container").length).toBe(0); await act(async () => {
render(editUserJsx(callbackSpy));
}); });
let submit = screen.getByTestId("submit");
let usernameInput = screen.getByTestId("edit-username-input");
fireEvent.blur(usernameInput, { target: { value: "whatever" } });
await act(async () => {
fireEvent.click(submit);
});
let errorDialog = screen.getByText("Failed to edit user.");
expect(errorDialog).toBeVisible();
expect(callbackSpy).toHaveBeenCalled();
});
test("Shows a UI error dialogue when user edit returns an improper status code", async () => {
let callbackSpy = mockAsync({ status: 409 });
await act(async () => {
render(editUserJsx(callbackSpy));
});
let submit = screen.getByTestId("submit");
let usernameInput = screen.getByTestId("edit-username-input");
fireEvent.blur(usernameInput, { target: { value: "whatever" } });
await act(async () => {
fireEvent.click(submit);
});
let errorDialog = screen.getByText("Failed to edit user.");
expect(errorDialog).toBeVisible();
expect(callbackSpy).toHaveBeenCalled();
}); });