-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36 from ucsb-cs156-s24/aaron-edit2
added school edit pages, utils, tests, stories
- Loading branch information
Showing
10 changed files
with
365 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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({})); | ||
}), | ||
], | ||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"})); | ||
|
||
}); | ||
|
||
|
||
}); | ||
}); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.