Skip to content

Commit

Permalink
Merge pull request #36 from ucsb-cs156-s24/aaron-edit2
Browse files Browse the repository at this point in the history
added school edit pages, utils, tests, stories
  • Loading branch information
pconrad committed May 31, 2024
2 parents 6aae341 + 1d2561b commit 3e91808
Show file tree
Hide file tree
Showing 10 changed files with 365 additions and 4 deletions.
2 changes: 2 additions & 0 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import CoursesEditPage from "main/pages/CoursesEditPage";
import AdminUsersPage from "main/pages/AdminUsersPage";
import AdminJobsPage from "main/pages/AdminJobsPage";
import SchoolIndexPage from "main/pages/SchoolIndexPage";
import SchoolEditPage from "main/pages/SchoolEditPage";

import CoursesCreatePage from "main/pages/CoursesCreatePage";
import CourseIndexPage from "main/pages/CourseIndexPage";
Expand All @@ -26,6 +27,7 @@ function App() {
const adminRoutes = hasRole(currentUser, "ROLE_ADMIN") ? (
<>
<Route path="/admin/schools" element={<SchoolIndexPage />} />
<Route path="/admin/schools/edit/:abbrev" element={<SchoolEditPage />} />
<Route path="/admin/users" element={<AdminUsersPage />} />
<Route path="/admin/jobs" element={<AdminJobsPage />} />
</>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/main/components/School/SchoolTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import React from "react";
const navigate = useNavigate();

const editCallback = (cell) => {
navigate(`/schools/edit/${cell.row.values.abbrev}`);
navigate(`/admin/schools/edit/${cell.row.values.abbrev}`);
};

// Stryker disable all : hard to test for query caching
Expand Down
70 changes: 70 additions & 0 deletions frontend/src/main/pages/SchoolEditPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import BasicLayout from "main/layouts/BasicLayout/BasicLayout";
import { useParams } from "react-router-dom";
import SchoolForm from "main/components/School/SchoolForm";
import { Navigate } from 'react-router-dom'
import { useBackend, useBackendMutation } from "main/utils/useBackend";
import { toast } from "react-toastify";

export default function SchoolEditPage({storybook=false}) {
let { abbrev } = useParams();

const { data: school, _error, _status } =
useBackend(
// Stryker disable next-line all : don't test internal caching of React Query
[`/api/schools?abbrev=${abbrev}`],
{ // Stryker disable next-line all : GET is the default, so changing this to "" doesn't introduce a bug
method: "GET",
url: `/api/schools`,
params: {
abbrev
}
}
);


const objectToAxiosPutParams = (school) => ({
url: "/api/schools/update",
method: "PUT",
params: {
abbrev: school.abbrev,
},
data: {
name: school.name,
termRegex: school.termRegex,
termDescription: school.termDescription,
termError: school.termError
},
});

const onSuccess = (school) => {
toast(`School Updated - abbrev: ${school.abbrev} name: ${school.name}`);
}

const mutation = useBackendMutation(
objectToAxiosPutParams,
{ onSuccess },
// Stryker disable next-line all : hard to set up test for caching
[`/api/schools/update?abbrev=${abbrev}`]
);

const { isSuccess } = mutation

const onSubmit = async (data) => {
mutation.mutate(data);
}

if (isSuccess && !storybook) {
return <Navigate to="/admin/schools" />
}

return (
<BasicLayout>
<div className="pt-2">
<h1>Edit School</h1>
{
school && <SchoolForm initialContents={school} submitAction={onSubmit} buttonLabel="Update" />
}
</div>
</BasicLayout>
)
}
2 changes: 1 addition & 1 deletion frontend/src/main/pages/SchoolIndexPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function SchoolIndexPage() {
return (
<Button
variant="primary"
href="/schools/create"
href="/admin/schools/create"
style={{ float: "right" }}
>
Create School
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/main/utils/schoolsUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { toast } from "react-toastify";

export function onDeleteSuccess(message) {
console.log(message);
toast(message);
}

export function cellToAxiosParamsDelete(cell) {
return {
url: "/api/schools",
method: "DELETE",
params: {
abbrev: cell.row.values.abbrev
}
}
}

37 changes: 37 additions & 0 deletions frontend/src/stories/pages/SchoolEditPage.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { apiCurrentUserFixtures } from "fixtures/currentUserFixtures";
import { systemInfoFixtures } from "fixtures/systemInfoFixtures";
import { schoolsFixtures } from 'fixtures/schoolsFixtures';
import { rest } from "msw";

import SchoolEditPage from 'main/pages/SchoolEditPage';

export default {
title: 'pages/SchoolEditPage',
component: SchoolEditPage
};

const Template = () => <SchoolEditPage storybook={true}/>;

export const Default = Template.bind({});
Default.parameters = {
msw: [
rest.get('/api/currentUser', (_req, res, ctx) => {
return res( ctx.json(apiCurrentUserFixtures.adminUser));
}),
rest.get('/api/systemInfo', (_req, res, ctx) => {
return res(ctx.json(systemInfoFixtures.showingNeither));
}),
rest.get('/api/schools', (_req, res, ctx) => {
return res(ctx.json(schoolsFixtures.threeSchools[0]));
}),
rest.put('/api/schools', async (req, res, ctx) => {
var reqBody = await req.text();
window.alert("PUT: " + req.url + " and body: " + reqBody);
return res(ctx.status(200),ctx.json({}));
}),
],
}



2 changes: 1 addition & 1 deletion frontend/src/tests/components/School/SchoolTable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe("UserTable tests", () => {

fireEvent.click(editButton);

await waitFor(() => expect(mockedNavigate).toHaveBeenCalledWith('/schools/edit/ucsb'));
await waitFor(() => expect(mockedNavigate).toHaveBeenCalledWith('/admin/schools/edit/ucsb'));

});

Expand Down
177 changes: 177 additions & 0 deletions frontend/src/tests/pages/School/SchoolEditPage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { fireEvent, render, waitFor, screen } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "react-query";
import { MemoryRouter } from "react-router-dom";
import SchoolEditPage from "main/pages/SchoolEditPage";

import { apiCurrentUserFixtures } from "fixtures/currentUserFixtures";
import { systemInfoFixtures } from "fixtures/systemInfoFixtures";
import axios from "axios";
import AxiosMockAdapter from "axios-mock-adapter";
import mockConsole from "jest-mock-console";

const mockToast = jest.fn();
jest.mock('react-toastify', () => {
const originalModule = jest.requireActual('react-toastify');
return {
__esModule: true,
...originalModule,
toast: (x) => mockToast(x)
};
});

const mockNavigate = jest.fn();
jest.mock('react-router-dom', () => {
const originalModule = jest.requireActual('react-router-dom');
return {
__esModule: true,
...originalModule,
useParams: () => ({
abbrev: "ucsb"
}),
Navigate: (x) => { mockNavigate(x); return null; }
};
});


describe("SchoolEditPage tests", () => {

describe("when the backend doesn't return data", () => {

const axiosMock = new AxiosMockAdapter(axios);

beforeEach(() => {
axiosMock.reset();
axiosMock.resetHistory();
axiosMock.onGet("/api/currentUser").reply(200, apiCurrentUserFixtures.userOnly);
axiosMock.onGet("/api/systemInfo").reply(200, systemInfoFixtures.showingNeither);
axiosMock.onGet("/api/schools", { params: { abbrev: "ucsb" } }).timeout();
});

const queryClient = new QueryClient();
test("renders header but table is not present", async () => {

const restoreConsole = mockConsole();

render(
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<SchoolEditPage />
</MemoryRouter>
</QueryClientProvider>
);
await screen.findByText("Edit School");
expect(screen.queryByTestId("SchoolsForm-name")).not.toBeInTheDocument();
restoreConsole();
});
});

describe("tests where backend is working normally", () => {

const axiosMock = new AxiosMockAdapter(axios);

beforeEach(() => {
axiosMock.reset();
axiosMock.resetHistory();
axiosMock.onGet("/api/currentUser").reply(200, apiCurrentUserFixtures.userOnly);
axiosMock.onGet("/api/systemInfo").reply(200, systemInfoFixtures.showingNeither);
axiosMock.onGet("/api/schools", { params: { abbrev: "ucsb" } }).reply(200, {
abbrev: "ucsb",
name: "University of California, Santa Barbara",
termRegex: "regexTest",
termDescription: "descriptionTest",
termError: "errorTest",
});
axiosMock.onPut('/api/schools/update').reply(200, {
abbrev: "ucsb",
name: "University of California, Sha Bi",
termRegex: "regexTest2",
termDescription: "descText2",
termError: "errTest2",
});
});

const queryClient = new QueryClient();
test("renders without crashing", () => {
render(
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<SchoolEditPage />
</MemoryRouter>
</QueryClientProvider>
);
});

test("Is populated with the data provided", async () => {

render(
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<SchoolEditPage />
</MemoryRouter>
</QueryClientProvider>
);

await screen.findByTestId("SchoolForm-abbrev");

const nameField = screen.getByTestId("SchoolForm-name");
const abbrevField = screen.getByTestId("SchoolForm-abbrev");
const termRegexField = screen.getByTestId("SchoolForm-termRegex");
const termDescriptionField = screen.getByTestId("SchoolForm-termDescription");
const termErrorField = screen.getByTestId("SchoolForm-termError");
const submitButton = screen.getByTestId("SchoolForm-submit");

expect(nameField).toHaveValue("University of California, Santa Barbara");
expect(abbrevField).toHaveValue("ucsb");
expect(termRegexField).toHaveValue("regexTest");
expect(termDescriptionField).toHaveValue("descriptionTest");
expect(termErrorField).toHaveValue("errorTest");
expect(submitButton).toBeInTheDocument();
});

test("Changes when you click Update", async () => {

render(
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<SchoolEditPage />
</MemoryRouter>
</QueryClientProvider>
);

await screen.findByTestId("SchoolForm-abbrev");

const nameField = screen.getByTestId("SchoolForm-name");
const abbrevField = screen.getByTestId("SchoolForm-abbrev");
const termRegexField = screen.getByTestId("SchoolForm-termRegex");
const termDescriptionField = screen.getByTestId("SchoolForm-termDescription");
const termErrorField = screen.getByTestId("SchoolForm-termError");
const submitButton = screen.getByTestId("SchoolForm-submit");

expect(nameField).toHaveValue("University of California, Santa Barbara");
expect(abbrevField).toHaveValue("ucsb");
expect(termRegexField).toHaveValue("regexTest");
expect(termDescriptionField).toHaveValue("descriptionTest");
expect(termErrorField).toHaveValue("errorTest");
expect(submitButton).toBeInTheDocument();

fireEvent.change(nameField, { target: { value: "University of California, Sha Bi" } })
fireEvent.change(termRegexField, { target: { value: "regexTest2" } })
fireEvent.change(termDescriptionField, { target: { value: "descTest2" } })
fireEvent.change(termErrorField, { target: { value: "errTest2" } })

fireEvent.click(submitButton);

await waitFor(() => expect(mockToast).toBeCalled());
expect(mockToast).toBeCalledWith("School Updated - abbrev: ucsb name: University of California, Sha Bi");
expect(mockNavigate).toBeCalledWith({ "to": "/admin/schools" });

expect(axiosMock.history.put.length).toBe(1);
expect(axiosMock.history.put[0].params).toEqual({ abbrev: "ucsb"});
expect(axiosMock.history.put[0].data).toEqual(JSON.stringify({ name: "University of California, Sha Bi", termRegex: "regexTest2", termDescription: "descTest2", termError: "errTest2"}));

});


});
});

Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe("SchoolIndexPage tests", () => {
expect(screen.getByText(/Create School/)).toBeInTheDocument();
});
const button = screen.getByText(/Create School/);
expect(button).toHaveAttribute("href", "/schools/create"); //fixme could have bug because plural
expect(button).toHaveAttribute("href", "/admin/schools/create");
expect(button).toHaveAttribute("style", "float: right;");
});

Expand Down
Loading

0 comments on commit 3e91808

Please sign in to comment.