diff --git a/.babelrc.json b/.babelrc.json index cd3b7cc4..4dfe13cb 100644 --- a/.babelrc.json +++ b/.babelrc.json @@ -1,4 +1,4 @@ { "presets": ["@babel/preset-react", "@babel/preset-typescript"], - "plugins": ["transform-class-properties", "lodash", "@babel/plugin-transform-runtime"] + "plugins": ["transform-class-properties", "lodash", "@babel/plugin-transform-runtime", "dynamic-import-node"] } diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 35cc0392..b29d3294 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -32,3 +32,17 @@ jobs: eslint_extensions: js,jsx,ts,tsx prettier: true prettier_dir: csm_web/frontend/src + test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [16.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm run build --if-present + - run: npm run test diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000..a4d61bc3 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,4 @@ +// babel config for jest +module.exports = { + presets: ["@babel/preset-env", ["@babel/preset-react", { runtime: "automatic" }]] +}; diff --git a/csm_web/frontend/src/components/Modal.tsx b/csm_web/frontend/src/components/Modal.tsx index efe0d7d2..b5f3f0d7 100644 --- a/csm_web/frontend/src/components/Modal.tsx +++ b/csm_web/frontend/src/components/Modal.tsx @@ -19,8 +19,8 @@ export default function Modal({ children, closeModal, className = "" }: ModalPro
-
{children} diff --git a/csm_web/frontend/src/components/section/StudentDropper.tsx b/csm_web/frontend/src/components/section/StudentDropper.tsx index 5091c1d6..24787d75 100644 --- a/csm_web/frontend/src/components/section/StudentDropper.tsx +++ b/csm_web/frontend/src/components/section/StudentDropper.tsx @@ -26,7 +26,7 @@ export default function StudentDropper({ id, sectionId, name }: StudentDropperPr return ( setShowDropPrompt(true)}> - × + × {showDropPrompt && ( setShowDropPrompt(false)}> diff --git a/csm_web/frontend/src/tests/LoadingSpinner.test.tsx b/csm_web/frontend/src/tests/LoadingSpinner.test.tsx new file mode 100644 index 00000000..b8e95218 --- /dev/null +++ b/csm_web/frontend/src/tests/LoadingSpinner.test.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import LoadingSpinner from "../components/LoadingSpinner"; + +describe("LoadingSpinner", () => { + it("should render correctly", () => { + const component = render(); + expect(component.asFragment()).toMatchSnapshot(); + }); +}); diff --git a/csm_web/frontend/src/tests/Modal.test.tsx b/csm_web/frontend/src/tests/Modal.test.tsx new file mode 100644 index 00000000..6456bbb8 --- /dev/null +++ b/csm_web/frontend/src/tests/Modal.test.tsx @@ -0,0 +1,85 @@ +import React from "react"; +import { fireEvent, render } from "@testing-library/react"; +import Modal, { ModalCloser } from "../components/Modal"; +import { act } from "react-dom/test-utils"; + +/** + * Returns a test Modal component. + */ +const getTestModal = (handleCloseModal: React.MouseEventHandler, className = ""): React.ReactElement => { + if (className === "") { + return ( + +
Hello World
+
+ ); + } else { + return ( + +
Hello World
+
+ ); + } +}; + +describe("Modal", () => { + it("should render correctly", () => { + const handleCloseModal = jest.fn(); + const component = render(getTestModal(handleCloseModal)); + expect(component.asFragment()).toMatchSnapshot(); + }); + + it("should render correctly with className", () => { + const handleCloseModal = jest.fn(); + const component = render(getTestModal(handleCloseModal, "test-modal")); + expect(component.asFragment()).toMatchSnapshot(); + }); + + describe("should close correctly", () => { + test("when the close button is clicked", () => { + const handleCloseModal = jest.fn(); + const component = render(getTestModal(handleCloseModal)); + + // find and click the close button + const closeButton = component.getByRole("button", { name: /close/i }); + act(() => { + fireEvent.click(closeButton); + }); + + expect(handleCloseModal).toHaveBeenCalled(); + }); + + test("when the overlay is clicked", () => { + const handleCloseModal = jest.fn(); + const component = render(getTestModal(handleCloseModal)); + + // find and click the overlay + const overlay = component.container.querySelector(".modal-overlay")!; + act(() => { + fireEvent.click(overlay); + }); + + expect(handleCloseModal).toHaveBeenCalled(); + }); + + test("when an external modal closer is clicked", () => { + // create a modal + const handleCloseModal = jest.fn(); + const component = render( + +
Hello World
+ Close +
+ ); + expect(component.asFragment()).toMatchSnapshot(); + + // find and click the external modal closer + const closer = component.container.querySelector(".modal-close")!; + act(() => { + fireEvent.click(closer); + }); + + expect(handleCloseModal).toHaveBeenCalled(); + }); + }); +}); diff --git a/csm_web/frontend/src/tests/__snapshots__/LoadingSpinner.test.tsx.snap b/csm_web/frontend/src/tests/__snapshots__/LoadingSpinner.test.tsx.snap new file mode 100644 index 00000000..317bab1e --- /dev/null +++ b/csm_web/frontend/src/tests/__snapshots__/LoadingSpinner.test.tsx.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LoadingSpinner should render correctly 1`] = ` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +`; diff --git a/csm_web/frontend/src/tests/__snapshots__/Modal.test.tsx.snap b/csm_web/frontend/src/tests/__snapshots__/Modal.test.tsx.snap new file mode 100644 index 00000000..af19476c --- /dev/null +++ b/csm_web/frontend/src/tests/__snapshots__/Modal.test.tsx.snap @@ -0,0 +1,99 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Modal should close correctly when an external modal closer is clicked 1`] = ` + +