From 3256be822c6fc50c8a3de41f41eda7b7dcad79bb Mon Sep 17 00:00:00 2001 From: karolina-siemieniuk-morawska Date: Mon, 27 Nov 2023 13:50:01 +0100 Subject: [PATCH] ui: set up and write unit tests * ref: cern-sis/issues-scoap3#228 --- ui/README.md | 12 +- ui/jest.config.mjs | 13 + ui/jest.setup.ts | 26 + ui/package.json | 10 +- .../{shared => detail}/JsonPreview.tsx | 2 +- .../detail/__tests__/DetailPageInfo.test.js | 34 + .../detail/__tests__/JsonPreview.test.js | 62 + .../__snapshots__/DetailPageInfo.test.js.snap | 162 ++ .../home/__tests__/TabContent.test.js | 37 + ui/src/components/search/CheckboxFacet.tsx | 4 +- .../search/__tests__/Checkboxfacet.test.js | 106 + .../search/__tests__/ResultItem.test.js | 33 + .../search/__tests__/SearchPagination.test.js | 40 + .../search/__tests__/SearchResults.test.js | 34 + .../search/__tests__/YearFacet.test.js | 44 + ui/src/components/shared/PublicationInfo.tsx | 2 +- .../shared/__tests__/Authors.test.js | 63 + .../shared/__tests__/Footer.test.js | 12 + .../shared/__tests__/FulltextFiles.test.js | 41 + .../shared/__tests__/Header.test.js | 12 + .../shared/__tests__/Layout.test.js | 12 + .../shared/__tests__/PublicationInfo.test.js | 41 + .../shared/__tests__/SearchBar.test.js | 42 + .../__snapshots__/Footer.test.js.snap | 90 + .../__snapshots__/Header.test.js.snap | 204 ++ .../__snapshots__/Layout.test.js.snap | 293 ++ ui/src/mocks/authors.ts | 44 + ui/src/mocks/facets.ts | 35 + ui/src/mocks/index.ts | 5 + ui/src/mocks/query.ts | 7 + ui/src/mocks/record.ts | 104 + ui/src/mocks/results.ts | 827 ++++++ ui/src/pages/500.tsx | 4 +- ui/src/pages/__tests__/404.test.js | 12 + ui/src/pages/__tests__/500.test.js | 32 + .../__tests__/__snapshots__/404.test.js.snap | 35 + .../__tests__/__snapshots__/500.test.js.snap | 45 + .../__snapshots__/index.test.js.snap | 206 ++ .../__snapshots__/record.test.js.snap | 281 ++ .../__snapshots__/search.test.js.snap | 1908 +++++++++++++ ui/src/pages/__tests__/index.test.js | 33 + ui/src/pages/__tests__/record.test.js | 23 + ui/src/pages/__tests__/search.test.js | 28 + ui/src/pages/index.tsx | 18 +- ui/src/pages/records/[recordId].tsx | 2 +- ui/src/types.ts | 2 +- ui/tsconfig.json | 2 +- ui/yarn.lock | 2393 ++++++++++++++++- 48 files changed, 7358 insertions(+), 119 deletions(-) create mode 100644 ui/jest.config.mjs create mode 100644 ui/jest.setup.ts rename ui/src/components/{shared => detail}/JsonPreview.tsx (87%) create mode 100644 ui/src/components/detail/__tests__/DetailPageInfo.test.js create mode 100644 ui/src/components/detail/__tests__/JsonPreview.test.js create mode 100644 ui/src/components/detail/__tests__/__snapshots__/DetailPageInfo.test.js.snap create mode 100644 ui/src/components/home/__tests__/TabContent.test.js create mode 100644 ui/src/components/search/__tests__/Checkboxfacet.test.js create mode 100644 ui/src/components/search/__tests__/ResultItem.test.js create mode 100644 ui/src/components/search/__tests__/SearchPagination.test.js create mode 100644 ui/src/components/search/__tests__/SearchResults.test.js create mode 100644 ui/src/components/search/__tests__/YearFacet.test.js create mode 100644 ui/src/components/shared/__tests__/Authors.test.js create mode 100644 ui/src/components/shared/__tests__/Footer.test.js create mode 100644 ui/src/components/shared/__tests__/FulltextFiles.test.js create mode 100644 ui/src/components/shared/__tests__/Header.test.js create mode 100644 ui/src/components/shared/__tests__/Layout.test.js create mode 100644 ui/src/components/shared/__tests__/PublicationInfo.test.js create mode 100644 ui/src/components/shared/__tests__/SearchBar.test.js create mode 100644 ui/src/components/shared/__tests__/__snapshots__/Footer.test.js.snap create mode 100644 ui/src/components/shared/__tests__/__snapshots__/Header.test.js.snap create mode 100644 ui/src/components/shared/__tests__/__snapshots__/Layout.test.js.snap create mode 100644 ui/src/mocks/authors.ts create mode 100644 ui/src/mocks/facets.ts create mode 100644 ui/src/mocks/index.ts create mode 100644 ui/src/mocks/query.ts create mode 100644 ui/src/mocks/record.ts create mode 100644 ui/src/mocks/results.ts create mode 100644 ui/src/pages/__tests__/404.test.js create mode 100644 ui/src/pages/__tests__/500.test.js create mode 100644 ui/src/pages/__tests__/__snapshots__/404.test.js.snap create mode 100644 ui/src/pages/__tests__/__snapshots__/500.test.js.snap create mode 100644 ui/src/pages/__tests__/__snapshots__/index.test.js.snap create mode 100644 ui/src/pages/__tests__/__snapshots__/record.test.js.snap create mode 100644 ui/src/pages/__tests__/__snapshots__/search.test.js.snap create mode 100644 ui/src/pages/__tests__/index.test.js create mode 100644 ui/src/pages/__tests__/record.test.js create mode 100644 ui/src/pages/__tests__/search.test.js diff --git a/ui/README.md b/ui/README.md index ff41fa7be..475a08566 100644 --- a/ui/README.md +++ b/ui/README.md @@ -1,4 +1,10 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +# SCOAP# Repository + +www.repo.scoap3.org + +SCOAP3 is a one-of-its-kind partnership of over three-thousand libraries, key funding agencies and research centers in 43 countries and 3 intergovernmental organizations. Working with leading publishers, SCOAP3 has converted key journals in the field of high-energy physics to open access at no cost for authors. SCOAP3 centrally pays publishers for costs involved in providing their services, publishers, in turn, reduce subscription fees to all their customers, who can redirect these funds to contribute to SCOAP3. Each country contributes in a way commensurate to its scientific output in the field. In addition, existing open access journals are also centrally supported, removing any existing financial barrier for authors. + +SCOAP3 journals are open for any scientist to publish without any financial barriers. Copyright stays with authors, and a permissive CC-BY license allows text- and data-mining. SCOAP3 addresses open access mandates at no burden for authors. All articles appear in the SCOAP3 repository for further distribution, as well as being open access on publishers’ websites. ## Getting Started @@ -15,7 +21,3 @@ bun dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. diff --git a/ui/jest.config.mjs b/ui/jest.config.mjs new file mode 100644 index 000000000..0448fb0c4 --- /dev/null +++ b/ui/jest.config.mjs @@ -0,0 +1,13 @@ +import nextJest from 'next/jest.js' + +const createJestConfig = nextJest({ + dir: './', +}) + +/** @type {import('jest').Config} */ +const config = { + setupFilesAfterEnv: ['./jest.setup.ts'], + testEnvironment: 'jest-environment-jsdom', +} + +export default createJestConfig(config) diff --git a/ui/jest.setup.ts b/ui/jest.setup.ts new file mode 100644 index 000000000..09a5a99b2 --- /dev/null +++ b/ui/jest.setup.ts @@ -0,0 +1,26 @@ +import '@testing-library/jest-dom'; + +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +jest.mock("next/navigation", () => { + return { + useRouter: jest.fn(() => ({ + push: jest.fn(), + })), + useSearchParams: jest.fn(() => ({ + get: jest.fn(), + })), + }; +}); diff --git a/ui/package.json b/ui/package.json index f67e24d8c..010792359 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,14 +2,17 @@ "name": "ui", "version": "0.1.0", "private": true, + "license": "GPL-2.0", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "test": "yarn lint && jest" }, "dependencies": { "@ant-design/cssinjs": "^1.17.2", + "@testing-library/user-event": "^14.5.1", "antd": "^5.10.1", "lodash.isequal": "^4.5.0", "moment": "^2.29.4", @@ -21,12 +24,17 @@ "react-vis": "^1.12.1" }, "devDependencies": { + "@testing-library/jest-dom": "^6.1.4", + "@testing-library/react": "^14.1.2", + "@types/jest": "^29.5.10", "@types/lodash.isequal": "^4.5.8", "@types/node": "^20", "@types/react-html-parser": "^2.0.4", "autoprefixer": "^10", "eslint": "^8", "eslint-config-next": "13.5.5", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "postcss": "^8", "tailwindcss": "^3", "typescript": "^5" diff --git a/ui/src/components/shared/JsonPreview.tsx b/ui/src/components/detail/JsonPreview.tsx similarity index 87% rename from ui/src/components/shared/JsonPreview.tsx rename to ui/src/components/detail/JsonPreview.tsx index 54cf39d11..77d44f85b 100644 --- a/ui/src/components/shared/JsonPreview.tsx +++ b/ui/src/components/detail/JsonPreview.tsx @@ -15,7 +15,7 @@ export const JsonPreview = ({ article }: JsonPreviewProps) => { key: "1", label: "Metadata preview. Preview of JSON metadata for this article.", children: ( -

+

                 {JSON.stringify(article, undefined, 2)}
               
diff --git a/ui/src/components/detail/__tests__/DetailPageInfo.test.js b/ui/src/components/detail/__tests__/DetailPageInfo.test.js new file mode 100644 index 000000000..7d0e31f78 --- /dev/null +++ b/ui/src/components/detail/__tests__/DetailPageInfo.test.js @@ -0,0 +1,34 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; + +import DetailPageInfo from "../DetailPageInfo"; +import { record } from '@/mocks/record'; + +describe("DetailPageInfo", () => { + it("renders detail page info correctly", () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + expect(screen.getByText("Published on:")).toBeInTheDocument(); + expect(screen.getByText("25 June 2015")).toBeInTheDocument(); + expect(screen.getByText("Created on:")).toBeInTheDocument(); + expect(screen.getByText("30 April 2018")).toBeInTheDocument(); + expect(screen.getByText("Springer/SISSA")).toBeInTheDocument(); + expect(screen.getByText("Published in:")).toBeInTheDocument(); + expect(screen.getByText("Journal of High Energy Physics")).toBeInTheDocument(); + expect(screen.getByText("(2015)")).toBeInTheDocument(); + expect(screen.getByText("Page 171")).toBeInTheDocument(); + expect(screen.getByText("DOI:")).toBeInTheDocument(); + expect(screen.getByText("10.1007/JHEP06(2015)171")).toBeInTheDocument(); + expect(screen.getByText("arXiv:")).toBeInTheDocument(); + expect(screen.getByText("hep-th")).toBeInTheDocument(); + expect(screen.getByText("1504.07579")).toBeInTheDocument(); + expect(screen.getByText("Copyrights:")).toBeInTheDocument(); + expect(screen.getByText("The Author(s)")).toBeInTheDocument(); + expect(screen.getByText("Licence:")).toBeInTheDocument(); + expect(screen.getByText("CC-BY-4.0")).toBeInTheDocument(); + expect(screen.getByText("Fulltext files:")).toBeInTheDocument(); + expect(screen.getByText("XML")).toBeInTheDocument(); + expect(screen.getByText("PDF/A")).toBeInTheDocument(); + }); +}); diff --git a/ui/src/components/detail/__tests__/JsonPreview.test.js b/ui/src/components/detail/__tests__/JsonPreview.test.js new file mode 100644 index 000000000..c6e648b80 --- /dev/null +++ b/ui/src/components/detail/__tests__/JsonPreview.test.js @@ -0,0 +1,62 @@ +import React from "react"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; + +import { JsonPreview } from "../JsonPreview"; +import { record } from "../../../mocks/index"; + +describe("JsonPreview", () => { + it("renders JSON preview correctly", async () => { + render(); + + await waitFor(() => + userEvent.click( + screen.getByText( + "Metadata preview. Preview of JSON metadata for this article." + ) + ) + ); + + expect( + screen.getByText( + "Metadata preview. Preview of JSON metadata for this article." + ) + ).toBeInTheDocument(); + expect(screen.getByTestId("json-preview-content")?.textContent).toContain( + "Strings from 3D gravity: asymptotic dynamics of AdS 3 gravity with free boundary conditions" + ); + expect(screen.getByTestId("json-preview-content")?.textContent).toContain( + "Sundborg" + ); + expect(screen.getByTestId("json-preview-content")?.textContent).toContain( + "2015-06-25" + ); + expect(screen.getByTestId("json-preview-content")?.textContent).toContain( + "Springer/SISSA" + ); + expect(screen.getByTestId("json-preview-content")?.textContent).toContain( + "Journal of High Energy Physics" + ); + expect(screen.getByTestId("json-preview-content")?.textContent).toContain( + "10.1007/JHEP06(2015)171" + ); + }); + + it("collapses and expands the content correctly", async () => { + render(); + + expect(screen.queryByTestId("json-preview-content")).toBeNull(); + + userEvent.click( + screen.getByText( + "Metadata preview. Preview of JSON metadata for this article." + ) + ); + + await waitFor(() => + expect(screen.getByTestId("json-preview-content")?.textContent).toContain( + "Strings from 3D gravity: asymptotic dynamics of AdS 3 gravity with free boundary conditions" + ) + ); + }); +}); diff --git a/ui/src/components/detail/__tests__/__snapshots__/DetailPageInfo.test.js.snap b/ui/src/components/detail/__tests__/__snapshots__/DetailPageInfo.test.js.snap new file mode 100644 index 000000000..b9b8166ef --- /dev/null +++ b/ui/src/components/detail/__tests__/__snapshots__/DetailPageInfo.test.js.snap @@ -0,0 +1,162 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DetailPageInfo renders detail page info correctly 1`] = ` +
+
+
+ Published on: +
+
+ 25 June 2015 +
+
+ Created on: +
+
+ 30 April 2018 +
+
+ Publisher: +
+
+ Springer/SISSA +
+
+ Published in: +
+
+

+ + Journal of High Energy Physics + + + (2015) + + + Page 171 + +

+
+
+
+ DOI + : +
+
+ + 10.1007/JHEP06(2015)171 + +
+
+
+
+ arXiv + : +
+
+ hep-th +
+
+ + 1504.07579 + +
+
+
+ Copyrights: +
+
+ The Author(s) +
+
+ Licence: +
+
+ + CC-BY-4.0 + +
+
+
+`; diff --git a/ui/src/components/home/__tests__/TabContent.test.js b/ui/src/components/home/__tests__/TabContent.test.js new file mode 100644 index 000000000..1a518b67c --- /dev/null +++ b/ui/src/components/home/__tests__/TabContent.test.js @@ -0,0 +1,37 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; + +import TabContent from "../TabContent"; + +describe("TabContent", () => { + const mockData = [ + { key: "Item 1", doc_count: 5 }, + { key: "Item 2", doc_count: 10 }, + ]; + + it("renders tab content correctly", () => { + render(); + + expect(screen.getByText("Item 1")).toBeInTheDocument(); + expect(screen.getByText("Item 2")).toBeInTheDocument(); + expect(screen.getByText("5")).toBeInTheDocument(); + expect(screen.getByText("10")).toBeInTheDocument(); + }); + + it("triggers the correct journal link when clicking on an item", () => { + render(); + + userEvent.click(screen.getByText("Item 1")); + + expect(screen.getByText("Item 1")).toHaveAttribute("href", "/search?page=1&page_size=20&journal=Item 1"); + }); + + it("triggers the correct country link when clicking on an item", () => { + render(); + + userEvent.click(screen.getByText("Item 1")); + + expect(screen.getByText("Item 1")).toHaveAttribute("href", "/search?page=1&page_size=20&country=Item 1"); + }); +}); diff --git a/ui/src/components/search/CheckboxFacet.tsx b/ui/src/components/search/CheckboxFacet.tsx index d2b8cc82f..6582011b5 100644 --- a/ui/src/components/search/CheckboxFacet.tsx +++ b/ui/src/components/search/CheckboxFacet.tsx @@ -59,7 +59,7 @@ const CheckboxFacet: React.FC = ({
{displayedData?.map((item) => (
- + + {item?.doc_count}
))} diff --git a/ui/src/components/search/__tests__/Checkboxfacet.test.js b/ui/src/components/search/__tests__/Checkboxfacet.test.js new file mode 100644 index 000000000..8ad09070b --- /dev/null +++ b/ui/src/components/search/__tests__/Checkboxfacet.test.js @@ -0,0 +1,106 @@ +import React from "react"; +import { render, screen, fireEvent } from "@testing-library/react"; + +import CheckboxFacet from "../CheckboxFacet"; + +describe("CheckboxFacet", () => { + const mockData = [ + { key: "Item 1", doc_count: 5 }, + { key: "Item 2", doc_count: 10 }, + { key: "Item 3", doc_count: 15 }, + { key: "Item 4", doc_count: 20 }, + { key: "Item 5", doc_count: 25 }, + { key: "Item 6", doc_count: 30 }, + { key: "Item 7", doc_count: 35 }, + { key: "Item 8", doc_count: 40 }, + { key: "Item 9", doc_count: 45 }, + { key: "Item 10", doc_count: 50 }, + { key: "Item 11", doc_count: 55 }, + { key: "Item 12", doc_count: 60 }, + { key: "Item 13", doc_count: 65 }, + { key: "Item 14", doc_count: 70 }, + ]; + + const mockParams = { + country: "SelectedCountry", + }; + + it("renders checkbox facet correctly", () => { + render( + + ); + + expect(screen.getByText("Country")).toBeInTheDocument(); + expect(screen.getByText("Item 1")).toBeInTheDocument(); + expect(screen.getByText("Item 2")).toBeInTheDocument(); + expect(screen.getByText("5")).toBeInTheDocument(); + expect(screen.getByText("10")).toBeInTheDocument(); + }); + + it("triggers the correct state change when clicking on a checkbox ", () => { + render( + + ); + + const checkbox = screen.getByRole("checkbox", { name: "Item 1" }); + + fireEvent.click(checkbox); + + expect(checkbox).toBeChecked(); + + fireEvent.click(checkbox); + + expect(checkbox).not.toBeChecked(); + }); + + + it("displays additional items when clicking 'Show More' ", () => { + render( + + ); + + expect(screen.getByText("Item 1")).toBeInTheDocument(); + expect(screen.queryByText("Item 14")).not.toBeInTheDocument(); + + fireEvent.click(screen.getByText("Show More")); + + expect(screen.getByText("Item 1")).toBeInTheDocument(); + expect(screen.getByText("Item 14")).toBeInTheDocument(); + }); + + it("hides additional items when clicking 'Show Less'", () => { + render( + + ); + + fireEvent.click(screen.getByText("Show More")); + + expect(screen.getByText("Item 1")).toBeInTheDocument(); + expect(screen.getByText("Item 14")).toBeInTheDocument(); + + fireEvent.click(screen.getByText("Show Less")); + + expect(screen.getByText("Item 1")).toBeInTheDocument(); + expect(screen.queryByText("Item 14")).not.toBeInTheDocument(); + }); +}); diff --git a/ui/src/components/search/__tests__/ResultItem.test.js b/ui/src/components/search/__tests__/ResultItem.test.js new file mode 100644 index 000000000..a80db0c67 --- /dev/null +++ b/ui/src/components/search/__tests__/ResultItem.test.js @@ -0,0 +1,33 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; + +import ResultItem from "../ResultItem"; +import { record } from "../../../mocks/index"; + +describe("ResultItem", () => { + it("renders result item correctly", () => { + render(); + + expect( + screen.getByText( + "Strings from 3D gravity: asymptotic dynamics of AdS 3 gravity with free boundary conditions" + ) + ).toBeInTheDocument(); + expect(screen.getByText("Apolo, Luis")).toBeInTheDocument(); + expect( + screen.getByText("Journal of High Energy Physics") + ).toBeInTheDocument(); + expect(screen.getByText("10.1007/JHEP06(2015)171")).toBeInTheDocument(); + expect(screen.getByText("XML")).toBeInTheDocument(); + expect(screen.getByText("PDF/A")).toBeInTheDocument(); + }); + + it("renders link to the record page correctly", () => { + render(); + + const link = screen.getByRole("link", { + name: /Strings from 3D gravity: asymptotic dynamics of AdS 3 gravity with free boundary conditions/i, + }); + expect(link).toHaveAttribute("href", "/records/10913"); + }); +}); diff --git a/ui/src/components/search/__tests__/SearchPagination.test.js b/ui/src/components/search/__tests__/SearchPagination.test.js new file mode 100644 index 000000000..7e6b56575 --- /dev/null +++ b/ui/src/components/search/__tests__/SearchPagination.test.js @@ -0,0 +1,40 @@ +import React from "react"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { useRouter } from "next/navigation"; + +import SearchPagination from "../SearchPagination"; + +const mockParams = { + page: 2, + page_size: 10, +}; + +const mockCount = 100; + +jest.mock("next/navigation", () => ({ + useRouter: jest.fn(), +})); + +const mockPush = jest.fn(); +useRouter.mockImplementation(() => ({ + push: mockPush, +})); + +describe("SearchPagination", () => { + it("renders pagination correctly", () => { + const { container } = render(); + + expect(container.querySelector('.ant-pagination')).toBeInTheDocument(); + expect(screen.getAllByRole('listitem').find(listitem => listitem.textContent === '2')).toHaveClass( + "ant-pagination-item-active" + ); + }); + + it("triggers the correct navigation when clicking on a page", () => { + render(); + + fireEvent.click(screen.getAllByRole('listitem').find(listitem => listitem.textContent === '3')); + + expect(mockPush).toHaveBeenCalledWith("?page=3&page_size=10"); + }); +}); diff --git a/ui/src/components/search/__tests__/SearchResults.test.js b/ui/src/components/search/__tests__/SearchResults.test.js new file mode 100644 index 000000000..42a0e4d74 --- /dev/null +++ b/ui/src/components/search/__tests__/SearchResults.test.js @@ -0,0 +1,34 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import SearchResults from "../SearchResults"; + +const mockResults = [ + { id: "1", title: "Result 1" }, + { id: "2", title: "Result 2" }, +]; + +const mockCount = 20; +const mockParams = { + page: 1, + page_size: 10, +}; + +jest.mock("../ResultItem", () => ({ article }) => ( +
{article.title}
+)); + +jest.mock("../SearchPagination", () => ({ + count, + params, +}) =>
{`${count} results, page ${params.page}`}
); + +describe("SearchResults", () => { + it("renders search results correctly", () => { + const { container } = render(); + + expect(screen.getByText("Found 20 results.")).toBeInTheDocument(); + expect(screen.getByTestId("result-item-1")).toBeInTheDocument(); + expect(screen.getByTestId("result-item-2")).toBeInTheDocument(); + expect(container.querySelector("[data-testid='search-pagination']")).toBeInTheDocument(); + }); +}); diff --git a/ui/src/components/search/__tests__/YearFacet.test.js b/ui/src/components/search/__tests__/YearFacet.test.js new file mode 100644 index 000000000..27c02e614 --- /dev/null +++ b/ui/src/components/search/__tests__/YearFacet.test.js @@ -0,0 +1,44 @@ +import React from "react"; +import { render, screen, fireEvent } from "@testing-library/react"; + +import YearFacet from "../YearFacet"; + +describe("YearFacet", () => { + const mockData = [ + { key: "2021", doc_count: 5 }, + { key: "2022", doc_count: 10 }, + { key: "2023", doc_count: 15 }, + ]; + + const mockParams = { + publication_year__range: "2021__2023", + }; + + it("renders year facet correctly", () => { + render(); + + expect(screen.getByText("Year")).toBeInTheDocument(); + expect(screen.getByText("2021")).toBeInTheDocument(); + expect(screen.getByText("2023")).toBeInTheDocument(); + }); + + it("displays the correct hint when hovering on a bar", () => { + const { container } = render(); + + fireEvent.mouseOver(container.querySelector('rect')); + + expect(screen.getByText("5")).toBeInTheDocument(); + + fireEvent.mouseOut(container.querySelector('rect')); + + expect(screen.queryByText("5")).not.toBeInTheDocument(); + }); + + it("shows reset button after clicking on a bar", () => { + const { container } = render(); + + fireEvent.click(container.querySelector('rect')); + + expect(screen.getByText("Reset")).toBeInTheDocument(); + }); +}); diff --git a/ui/src/components/shared/PublicationInfo.tsx b/ui/src/components/shared/PublicationInfo.tsx index bae14189e..79437e87c 100644 --- a/ui/src/components/shared/PublicationInfo.tsx +++ b/ui/src/components/shared/PublicationInfo.tsx @@ -26,7 +26,7 @@ const PublicationInfo: React.FC = ({ data, page }) => { } if (journal_volume) { - publicationText += `, Volume ${journal_volume}, Volume ${journal_volume}`; } if (volume_year) { diff --git a/ui/src/components/shared/__tests__/Authors.test.js b/ui/src/components/shared/__tests__/Authors.test.js new file mode 100644 index 000000000..af9f2f56a --- /dev/null +++ b/ui/src/components/shared/__tests__/Authors.test.js @@ -0,0 +1,63 @@ +import React from "react"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; + +import Authors from "../Authors"; +import { authors } from "../../../mocks/index"; + + +describe("Authors", () => { + it("renders authors correctly in search page", () => { + render(); + + expect(screen.getByText("Doe, John")).toBeInTheDocument(); + expect(screen.getByText("Smith, Jane")).toBeInTheDocument(); + expect(screen.getByTitle("1234-5678-9101-1126")).toBeInTheDocument(); + expect(screen.getByTitle("9876-5432-1098-7658")).toBeInTheDocument(); + }); + + it("renders authors correctly in detail page", () => { + render(); + + expect(screen.getByText("John Doe")).toBeInTheDocument(); + expect(screen.getByText("Jane Smith")).toBeInTheDocument(); + expect(screen.getByText("Affiliation 1, Affiliation 2")).toBeInTheDocument(); + expect(screen.getByText("Affiliation 3")).toBeInTheDocument(); + expect(screen.getByTitle("1234-5678-9101-1126")).toBeInTheDocument(); + expect(screen.getByTitle("9876-5432-1098-7658")).toBeInTheDocument(); + }); + + it("renders affiliations link correctly", () => { + render(); + + expect(screen.getByRole("link", { name: /Affiliation 1, Affiliation 2/i })).toBeInTheDocument(); + expect(screen.getByRole("link", { name: /Affiliation 3/i })).toBeInTheDocument(); + }); + + it("renders Orcid links correctly", () => { + render(); + + expect(screen.getByTitle("1234-5678-9101-1126")).toHaveAttribute("href", "https://orcid.org/1234-5678-9101-1126"); + expect(screen.getByTitle("9876-5432-1098-7658")).toHaveAttribute("href", "https://orcid.org/9876-5432-1098-7658"); + }); + + it("renders modal button correctly", () => { + render(); + + expect(screen.getByText(/Show all 7 authors/i)).toBeInTheDocument(); + }); + + it("opens and closes modal correctly", async () => { + render(); + + expect(screen.getByText("et al")).toBeInTheDocument(); + expect(screen.queryByText("Michael Smith")).toBeNull(); + expect(screen.queryByText("Tom Jones")).toBeNull(); + + fireEvent.click(screen.getByText(/Show all 7 authors/i)); + await waitFor(() => expect(screen.getByText("Michael Smith")).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText("Tom Jones")).toBeInTheDocument()); + + fireEvent.click(screen.getByRole("button", { name: /Close/i })); + await waitFor(() => expect(screen.queryByRole("button", { name: /Close/i })).toBeNull()); + }); +}); diff --git a/ui/src/components/shared/__tests__/Footer.test.js b/ui/src/components/shared/__tests__/Footer.test.js new file mode 100644 index 000000000..ba0bfa319 --- /dev/null +++ b/ui/src/components/shared/__tests__/Footer.test.js @@ -0,0 +1,12 @@ +import React from "react"; +import { render } from "@testing-library/react"; + +import Footer from "../Footer"; + +describe("Footer", () => { + it("renders footer correctly", () => { + const { container } = render(