@thepalaceproject/circulation-admin 1.43.0-post.7 → 1.43.0-post.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/package.json +1 -1
  2. package/tests/jest/components/AccountPage.test.tsx +41 -0
  3. package/tests/jest/components/AdvancedSearchBooleanFilter.test.tsx +231 -0
  4. package/tests/jest/components/AdvancedSearchFilter.test.tsx +142 -0
  5. package/tests/jest/components/AdvancedSearchFilterInput.test.tsx +212 -0
  6. package/tests/jest/components/AdvancedSearchFilterViewer.test.tsx +52 -0
  7. package/tests/jest/components/AdvancedSearchValueFilter.test.tsx +123 -0
  8. package/tests/jest/components/BookDetails.test.tsx +133 -0
  9. package/tests/jest/components/BookDetailsContainer.test.tsx +116 -0
  10. package/tests/jest/components/CatalogServices.test.tsx +63 -0
  11. package/tests/jest/components/CirculationEventsDownloadForm.test.tsx +41 -0
  12. package/tests/jest/components/ConfigPage.test.tsx +58 -0
  13. package/tests/jest/components/CustomListPage.test.tsx +57 -0
  14. package/tests/jest/components/CustomListSearch.test.tsx +275 -0
  15. package/tests/jest/components/CustomListsSidebar.test.tsx +251 -0
  16. package/tests/jest/components/DashboardPage.test.tsx +76 -0
  17. package/tests/jest/components/DebugAuthentication.test.tsx +1 -5
  18. package/tests/jest/components/DiagnosticsTabContainer.test.tsx +147 -0
  19. package/tests/jest/components/EditableConfigList.test.tsx +8 -2
  20. package/tests/jest/components/EntryPointsContainer.test.tsx +97 -0
  21. package/tests/jest/components/EntryPointsTabs.test.tsx +88 -0
  22. package/tests/jest/components/ErrorMessage.test.tsx +97 -0
  23. package/tests/jest/components/Footer.test.tsx +141 -0
  24. package/tests/jest/components/InventoryReportRequestModal.test.tsx +16 -42
  25. package/tests/jest/components/LaneEditor.test.tsx +2 -6
  26. package/tests/jest/components/LanePage.test.tsx +68 -0
  27. package/tests/jest/components/LanguageField.test.tsx +40 -0
  28. package/tests/jest/components/LoadButton.test.tsx +49 -0
  29. package/tests/jest/components/ManagePatrons.test.tsx +73 -0
  30. package/tests/jest/components/ManagePatronsForm.test.tsx +180 -0
  31. package/tests/jest/components/ManagePatronsTabContainer.test.tsx +143 -0
  32. package/tests/jest/components/MetadataServices.test.tsx +59 -0
  33. package/tests/jest/components/PatronBlockingRulesEditor.test.tsx +3 -1
  34. package/tests/jest/components/PatronInfo.test.tsx +54 -0
  35. package/tests/jest/components/SelfTestResult.test.tsx +164 -0
  36. package/tests/jest/components/SelfTestsCategory.test.tsx +200 -0
  37. package/tests/jest/components/ServiceWithRegistrationsEditForm.test.tsx +110 -0
  38. package/tests/jest/components/SetupPage.test.tsx +41 -0
  39. package/tests/jest/components/SingleStatListItem.test.tsx +62 -0
  40. package/tests/jest/components/Stats.test.tsx +4 -8
  41. package/tests/jest/components/TabContainer.test.tsx +68 -0
  42. package/tests/jest/components/Timestamp.test.tsx +74 -0
  43. package/tests/jest/components/TroubleshootingTabContainer.test.tsx +68 -0
  44. package/tests/jest/components/WelcomePage.test.tsx +43 -0
  45. package/tests/jest/components/WithEditButton.test.tsx +55 -0
  46. package/tests/jest/components/WithRemoveButton.test.tsx +95 -0
  47. package/tests/jest/index.test.tsx +50 -0
  48. package/tests/jest/components/AdvancedSearchBuilder.test.tsx +0 -38
@@ -0,0 +1,123 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { DndProvider } from "react-dnd";
5
+ import { HTML5Backend } from "react-dnd-html5-backend";
6
+ import AdvancedSearchValueFilter from "../../../src/components/AdvancedSearchValueFilter";
7
+ import {
8
+ fields,
9
+ operators,
10
+ } from "../../../src/components/AdvancedSearchBuilder";
11
+
12
+ describe("AdvancedSearchValueFilter", () => {
13
+ let onMove;
14
+ let onRemove;
15
+ let onSelect;
16
+ let query;
17
+
18
+ const renderFilter = (props = {}) =>
19
+ render(
20
+ <DndProvider backend={HTML5Backend}>
21
+ <AdvancedSearchValueFilter
22
+ query={query}
23
+ onMove={onMove}
24
+ onSelect={onSelect}
25
+ onRemove={onRemove}
26
+ {...props}
27
+ />
28
+ </DndProvider>
29
+ );
30
+
31
+ beforeEach(() => {
32
+ onMove = jest.fn();
33
+ onRemove = jest.fn();
34
+ onSelect = jest.fn();
35
+
36
+ query = {
37
+ id: "91",
38
+ key: "title",
39
+ op: "eq",
40
+ value: "foo",
41
+ };
42
+ });
43
+
44
+ it("should render the field label, operator symbol, and value of the query", () => {
45
+ renderFilter();
46
+
47
+ const expectedField = `${
48
+ fields.find((field) => field.value === "title")?.label
49
+ }`;
50
+ const expectedOperator = `${
51
+ operators.find((op) => op.value === "eq")?.symbol
52
+ }`;
53
+ const filterLabelText = `${expectedField} ${expectedOperator} foo`;
54
+
55
+ expect(screen.getByText(filterLabelText)).toBeInTheDocument();
56
+ });
57
+
58
+ it("should apply the selected class when selected is true", () => {
59
+ const { container } = renderFilter({ selected: true });
60
+
61
+ expect(
62
+ container.querySelector(".advanced-search-value-filter")
63
+ ).toHaveClass("selected");
64
+ });
65
+
66
+ it("should not apply the selected class when readOnly is true", () => {
67
+ const { container } = renderFilter({ readOnly: true, selected: true });
68
+
69
+ expect(
70
+ container.querySelector(".advanced-search-value-filter")
71
+ ).not.toHaveClass("selected");
72
+ });
73
+
74
+ it("should render a remove button", () => {
75
+ renderFilter();
76
+
77
+ const button = screen.getByRole("button");
78
+
79
+ expect(button).toHaveTextContent("×");
80
+ });
81
+
82
+ it("should not render a remove button when readOnly is true", () => {
83
+ renderFilter({ readOnly: true });
84
+
85
+ expect(screen.queryByRole("button")).not.toBeInTheDocument();
86
+ });
87
+
88
+ it("should call onRemove when the remove button is clicked", async () => {
89
+ const user = userEvent.setup();
90
+ renderFilter();
91
+
92
+ await user.click(screen.getByRole("button"));
93
+
94
+ expect(onRemove).toHaveBeenCalledTimes(1);
95
+ expect(onRemove).toHaveBeenCalledWith("91");
96
+ });
97
+
98
+ it("should call onSelect when clicked", async () => {
99
+ const user = userEvent.setup();
100
+ const { container } = renderFilter();
101
+
102
+ await user.click(container.querySelector(".advanced-search-value-filter"));
103
+
104
+ expect(onSelect).toHaveBeenCalledTimes(1);
105
+ expect(onSelect).toHaveBeenCalledWith("91");
106
+ });
107
+
108
+ it("should call onSelect when the space bar is depressed", async () => {
109
+ const user = userEvent.setup();
110
+ const { container } = renderFilter();
111
+
112
+ // The filter is a focusable treeitem; focus it and press space, as a
113
+ // keyboard user would.
114
+ const item = container.querySelector(
115
+ ".advanced-search-value-filter"
116
+ ) as HTMLElement;
117
+ item.focus();
118
+ await user.keyboard(" ");
119
+
120
+ expect(onSelect).toHaveBeenCalledTimes(1);
121
+ expect(onSelect).toHaveBeenCalledWith("91");
122
+ });
123
+ });
@@ -0,0 +1,133 @@
1
+ import * as React from "react";
2
+ import { render } from "@testing-library/react";
3
+
4
+ import BookDetails from "../../../src/components/BookDetails";
5
+ import { BookData } from "@thepalaceproject/web-opds-client/lib/interfaces";
6
+
7
+ const book: BookData = {
8
+ id: "urn:librarysimplified.org/terms/id/3M%20ID/crrmnr9",
9
+ url: "http://circulation.librarysimplified.org/works/3M/crrmnr9",
10
+ title: "The Mayan Secrets",
11
+ authors: ["Clive Cussler", "Thomas Perry"],
12
+ contributors: ["contributor 1"],
13
+ summary:
14
+ "&lt;b&gt;Sam and Remi Fargo race for treasure&#8212;and survival&#8212;in this lightning-paced new adventure from #1&lt;i&gt; New York Times&lt;/i&gt; bestselling author Clive Cussler.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Husband-and-wife team Sam and Remi Fargo are in Mexico when they come upon a remarkable discovery&#8212;the mummified remainsof a man clutching an ancient sealed pot. Within the pot is a Mayan book larger than any known before.&lt;br /&gt;&lt;br /&gt;The book contains astonishing information about the Mayans, their cities, and about mankind itself. The secrets are so powerful that some people would do anything to possess them&#8212;as the Fargos are about to find out. Many men and women are going to die for that book.",
15
+ imageUrl: "https://dlotdqc6pnwqb.cloudfront.net/3M/crrmnr9/cover.jpg",
16
+ borrowUrl: "borrow url",
17
+ openAccessLinks: [{ url: "secrets.epub", type: "application/epub+zip" }],
18
+ publisher: "Penguin Publishing Group",
19
+ published: "February 29, 2016",
20
+ categories: ["Children", "10-12", "Fiction", "Adventure", "Fantasy"],
21
+ raw: {
22
+ category: [
23
+ {
24
+ $: {
25
+ scheme: { value: "http://schema.org/audience" },
26
+ label: { value: "Children" },
27
+ },
28
+ },
29
+ {
30
+ $: {
31
+ scheme: { value: "http://schema.org/typicalAgeRange" },
32
+ label: { value: "10-12" },
33
+ },
34
+ },
35
+ {
36
+ $: {
37
+ scheme: { value: "http://librarysimplified.org/terms/fiction/" },
38
+ label: { value: "Fiction" },
39
+ },
40
+ },
41
+ {
42
+ $: {
43
+ scheme: {
44
+ value: "http://librarysimplified.org/terms/genres/Simplified/",
45
+ },
46
+ label: { value: "Adventure" },
47
+ },
48
+ },
49
+ {
50
+ $: {
51
+ scheme: {
52
+ value: "http://librarysimplified.org/terms/genres/Simplified/",
53
+ },
54
+ label: { value: "Fantasy" },
55
+ },
56
+ },
57
+ ],
58
+ "bibframe:distribution": [
59
+ {
60
+ $: {
61
+ "bibframe:ProviderName": {
62
+ value: "Overdrive",
63
+ },
64
+ },
65
+ },
66
+ ],
67
+ link: [
68
+ {
69
+ $: {
70
+ rel: { value: "issues" },
71
+ href: { value: "http://example.com/report" },
72
+ },
73
+ },
74
+ {
75
+ $: {
76
+ rel: { value: "http://librarysimplified.org/terms/rel/revoke" },
77
+ href: { value: "http://example.com/revoke" },
78
+ },
79
+ },
80
+ ],
81
+ },
82
+ };
83
+
84
+ describe("BookDetails", () => {
85
+ const updateBook = jest.fn();
86
+
87
+ it("shows audience and target age", () => {
88
+ const { container } = render(
89
+ <BookDetails book={book} updateBook={updateBook} />
90
+ );
91
+ const audience = container.querySelector(".audience");
92
+ expect(audience).toBeInTheDocument();
93
+ expect(audience).toHaveTextContent("Audience: Children (age 10-12)");
94
+ });
95
+
96
+ it("shows categories", () => {
97
+ const { container } = render(
98
+ <BookDetails book={book} updateBook={updateBook} />
99
+ );
100
+ const categories = container.querySelector(".categories");
101
+ expect(categories).toBeInTheDocument();
102
+ expect(categories).toHaveTextContent("Categories: Adventure, Fantasy");
103
+ });
104
+
105
+ it("doesn't show categories when there aren't any", () => {
106
+ const bookCopy: BookData = {
107
+ ...book,
108
+ raw: { category: [], link: [] },
109
+ };
110
+ const { container } = render(
111
+ <BookDetails book={bookCopy} updateBook={updateBook} />
112
+ );
113
+ expect(container.querySelector(".categories")).not.toBeInTheDocument();
114
+ });
115
+
116
+ it("shows distributor", () => {
117
+ const { container } = render(
118
+ <BookDetails book={book} updateBook={updateBook} />
119
+ );
120
+ const distributor = container.querySelector(".distributed-by");
121
+ expect(distributor).toBeInTheDocument();
122
+ expect(distributor).toHaveTextContent("Distributed By: Overdrive");
123
+ });
124
+
125
+ it("doesn't render any circulation link content", () => {
126
+ const { container } = render(
127
+ <BookDetails book={book} updateBook={updateBook} />
128
+ );
129
+ const circulationLinks = container.querySelector(".circulation-links");
130
+ expect(circulationLinks).toBeInTheDocument();
131
+ expect(circulationLinks).toBeEmptyDOMElement();
132
+ });
133
+ });
@@ -0,0 +1,116 @@
1
+ import * as React from "react";
2
+ import * as PropTypes from "prop-types";
3
+ import { render, screen } from "@testing-library/react";
4
+
5
+ import buildStore from "../../../src/store";
6
+ import { BookData } from "@thepalaceproject/web-opds-client/lib/interfaces";
7
+
8
+ // BookDetailsTabContainer is a connected TabContainer that mounts every one of its
9
+ // tab panels on render (the metadata editor, classifications, cover, and lists
10
+ // editors), each of which fetches over the network on mount. That machinery is
11
+ // incidental to what BookDetailsContainer composes, so mock it to a marker that
12
+ // echoes the props it receives and renders the book child it is handed.
13
+ jest.mock("../../../src/components/BookDetailsTabContainer", () => ({
14
+ __esModule: true,
15
+ default: (props: any) => (
16
+ <div
17
+ data-testid="book-details-tab-container"
18
+ data-book-url={props.bookUrl}
19
+ data-collection-url={props.collectionUrl}
20
+ data-csrf-token={props.csrfToken}
21
+ data-has-store={String(!!props.store)}
22
+ data-has-library={String(!!props.library)}
23
+ data-has-refresh-catalog={String(!!props.refreshCatalog)}
24
+ >
25
+ {props.children}
26
+ </div>
27
+ ),
28
+ }));
29
+
30
+ import BookDetailsContainer from "../../../src/components/BookDetailsContainer";
31
+ import BookDetails from "../../../src/components/BookDetails";
32
+
33
+ // BookDetailsContainer reads its configuration from the legacy React context that
34
+ // the OPDS web client supplies in production. `renderWithProviders` does not supply
35
+ // the `library` (nor `tab`) keys it reads, so wrap it in a small legacy context
36
+ // provider that hands it everything it needs.
37
+ class LegacyContextProvider extends React.Component<{ context: any }> {
38
+ static childContextTypes = {
39
+ editorStore: PropTypes.object,
40
+ csrfToken: PropTypes.string,
41
+ tab: PropTypes.string,
42
+ library: PropTypes.func,
43
+ admin: PropTypes.object,
44
+ };
45
+ getChildContext() {
46
+ return this.props.context;
47
+ }
48
+ render() {
49
+ return this.props.children;
50
+ }
51
+ }
52
+
53
+ const bookData: BookData = {
54
+ id: "book id",
55
+ url: "book url",
56
+ title: "book title",
57
+ raw: { category: [], link: [] },
58
+ };
59
+
60
+ describe("BookDetailsContainer", () => {
61
+ let store;
62
+ let context;
63
+ let refreshCatalog;
64
+
65
+ beforeEach(() => {
66
+ store = buildStore();
67
+ refreshCatalog = jest.fn();
68
+ context = {
69
+ editorStore: store,
70
+ csrfToken: "token",
71
+ library: jest.fn(),
72
+ admin: { isLibraryManager: () => true },
73
+ };
74
+ });
75
+
76
+ const renderContainer = () =>
77
+ render(
78
+ <LegacyContextProvider context={context}>
79
+ <BookDetailsContainer
80
+ book={bookData}
81
+ bookUrl="book url"
82
+ collectionUrl="collection url"
83
+ refreshCatalog={refreshCatalog}
84
+ >
85
+ <BookDetails book={bookData} updateBook={jest.fn()} />
86
+ </BookDetailsContainer>
87
+ </LegacyContextProvider>
88
+ );
89
+
90
+ it("renders BookDetails with its child's props", () => {
91
+ renderContainer();
92
+ // The container recreates the real BookDetails from the child element's props,
93
+ // so the book it was handed shows up (its title is rendered as the heading).
94
+ expect(
95
+ screen.getByRole("heading", { name: "book title" })
96
+ ).toBeInTheDocument();
97
+ });
98
+
99
+ it("shows a tab container with initial tab", () => {
100
+ renderContainer();
101
+ const tabContainer = screen.getByTestId("book-details-tab-container");
102
+ expect(tabContainer).toBeInTheDocument();
103
+ expect(tabContainer).toHaveAttribute("data-book-url", "book url");
104
+ expect(tabContainer).toHaveAttribute(
105
+ "data-collection-url",
106
+ "collection url"
107
+ );
108
+ expect(tabContainer).toHaveAttribute("data-csrf-token", "token");
109
+ // The library function, redux store, and refreshCatalog callback are forwarded
110
+ // from context/props; they are not serializable to the DOM, so assert they were
111
+ // passed through.
112
+ expect(tabContainer).toHaveAttribute("data-has-store", "true");
113
+ expect(tabContainer).toHaveAttribute("data-has-library", "true");
114
+ expect(tabContainer).toHaveAttribute("data-has-refresh-catalog", "true");
115
+ });
116
+ });
@@ -0,0 +1,63 @@
1
+ import * as React from "react";
2
+ import { screen, within } from "@testing-library/react";
3
+ import { CatalogServices } from "../../../src/components/CatalogServices";
4
+ import renderWithContext from "../testUtils/renderWithContext";
5
+ import { defaultFeatureFlags } from "../../../src/utils/featureFlags";
6
+
7
+ describe("CatalogServices", () => {
8
+ const data = {
9
+ catalog_services: [
10
+ {
11
+ id: 2,
12
+ protocol: "test protocol",
13
+ settings: {
14
+ test_setting: "test setting",
15
+ },
16
+ libraries: [
17
+ {
18
+ short_name: "nypl",
19
+ test_library_setting: "test library setting",
20
+ },
21
+ ],
22
+ name: "nypl catalog",
23
+ },
24
+ ],
25
+ protocols: [
26
+ {
27
+ name: "test protocol",
28
+ label: "test protocol label",
29
+ settings: [],
30
+ },
31
+ ],
32
+ allLibraries: [
33
+ {
34
+ short_name: "nypl",
35
+ },
36
+ ],
37
+ };
38
+
39
+ const config = { csrfToken: "token", featureFlags: defaultFeatureFlags };
40
+
41
+ it("shows catalog service list", () => {
42
+ renderWithContext(
43
+ <CatalogServices
44
+ data={data}
45
+ fetchData={jest.fn()}
46
+ editItem={jest.fn().mockResolvedValue(undefined)}
47
+ csrfToken="token"
48
+ isFetching={false}
49
+ />,
50
+ config
51
+ );
52
+
53
+ const items = screen.getAllByRole("listitem");
54
+ expect(items).toHaveLength(1);
55
+ expect(items[0]).toHaveTextContent("nypl catalog: test protocol label");
56
+
57
+ const editLink = within(items[0]).getByRole("link");
58
+ expect(editLink).toHaveAttribute(
59
+ "href",
60
+ "/admin/web/config/catalogServices/edit/2"
61
+ );
62
+ });
63
+ });
@@ -0,0 +1,41 @@
1
+ import * as React from "react";
2
+ import { render, screen, within } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+
5
+ import CirculationEventsDownloadForm from "../../../src/components/CirculationEventsDownloadForm";
6
+
7
+ describe("CirculationEventsDownloadForm", () => {
8
+ let hide: jest.Mock;
9
+
10
+ beforeEach(() => {
11
+ hide = jest.fn();
12
+ render(<CirculationEventsDownloadForm show={true} hide={hide} />);
13
+ });
14
+
15
+ it("renders start date and end date inputs", () => {
16
+ // The modal renders into a portal, so query the whole document.
17
+ const dates =
18
+ document.querySelectorAll<HTMLInputElement>("input[type='date']");
19
+ expect(dates).toHaveLength(2);
20
+ expect(dates[0].name).toBe("dateStart");
21
+ expect(dates[1].name).toBe("dateEnd");
22
+ });
23
+
24
+ it("renders download and close buttons", async () => {
25
+ const user = userEvent.setup();
26
+
27
+ expect(
28
+ screen.getByRole("button", { name: "Download" })
29
+ ).toBeInTheDocument();
30
+
31
+ // The modal header's dismiss control is also labeled "Close" (its visible
32
+ // "×" is aria-hidden), so scope to the footer for the reusable Close button.
33
+ const footer = document.querySelector<HTMLElement>(".modal-footer");
34
+ const closeButton = within(footer).getByRole("button", { name: "Close" });
35
+ expect(closeButton).toBeInTheDocument();
36
+
37
+ await user.click(closeButton);
38
+
39
+ expect(hide).toHaveBeenCalledTimes(1);
40
+ });
41
+ });
@@ -0,0 +1,58 @@
1
+ import * as React from "react";
2
+ import { screen } from "@testing-library/react";
3
+
4
+ import { renderWithProviders } from "../testUtils/withProviders";
5
+
6
+ // Header and Footer are page chrome; Header pulls in the react-router v3 context
7
+ // that `renderWithProviders` does not supply. Mock them to markers.
8
+ jest.mock("../../../src/components/Header", () => ({
9
+ __esModule: true,
10
+ default: () => <div data-testid="header" />,
11
+ }));
12
+ jest.mock("../../../src/components/Footer", () => ({
13
+ __esModule: true,
14
+ default: () => <div data-testid="footer" />,
15
+ }));
16
+
17
+ // ConfigTabContainer mounts the connected configuration sections (Libraries,
18
+ // Collections, Individual Admins, ...) which fetch over the network on mount. That
19
+ // is incidental to what ConfigPage composes — the page's job is to hand the tab
20
+ // container the params and context it needs — so mock it to a marker that echoes
21
+ // the props it received.
22
+ jest.mock("../../../src/components/ConfigTabContainer", () => ({
23
+ __esModule: true,
24
+ default: (props: any) => (
25
+ <div
26
+ data-testid="config-tab-container"
27
+ data-tab={props.tab}
28
+ data-edit-or-create={props.editOrCreate}
29
+ data-identifier={props.identifier}
30
+ data-csrf-token={props.csrfToken}
31
+ data-has-store={String(!!props.store)}
32
+ />
33
+ ),
34
+ }));
35
+
36
+ import ConfigPage from "../../../src/components/ConfigPage";
37
+
38
+ describe("ConfigPage", () => {
39
+ const params = {
40
+ tab: "libraries",
41
+ editOrCreate: "edit",
42
+ identifier: "identifier",
43
+ };
44
+
45
+ it("shows a tab container with tab from params", () => {
46
+ renderWithProviders(<ConfigPage params={params} />, {
47
+ appConfigSettings: { csrfToken: "token" },
48
+ });
49
+
50
+ const tabContainer = screen.getByTestId("config-tab-container");
51
+ expect(tabContainer).toHaveAttribute("data-tab", "libraries");
52
+ expect(tabContainer).toHaveAttribute("data-edit-or-create", "edit");
53
+ expect(tabContainer).toHaveAttribute("data-identifier", "identifier");
54
+ expect(tabContainer).toHaveAttribute("data-csrf-token", "token");
55
+ // The redux store is forwarded from context; assert it was passed through.
56
+ expect(tabContainer).toHaveAttribute("data-has-store", "true");
57
+ });
58
+ });
@@ -0,0 +1,57 @@
1
+ import * as React from "react";
2
+ import { screen } from "@testing-library/react";
3
+
4
+ import { renderWithProviders } from "../testUtils/withProviders";
5
+
6
+ // Header and Footer are page chrome; Header pulls in the react-router v3 context
7
+ // that `renderWithProviders` does not supply. Mock them to markers.
8
+ jest.mock("../../../src/components/Header", () => ({
9
+ __esModule: true,
10
+ default: () => <div data-testid="header" />,
11
+ }));
12
+ jest.mock("../../../src/components/Footer", () => ({
13
+ __esModule: true,
14
+ default: () => <div data-testid="footer" />,
15
+ }));
16
+
17
+ // CustomLists is a connected component that fetches lists, collections, libraries,
18
+ // and languages over the network on mount. That is incidental to what
19
+ // CustomListPage composes — the page's job is to hand the editor the params and
20
+ // context it needs — so mock it to a marker that echoes the props it received.
21
+ jest.mock("../../../src/components/CustomLists", () => ({
22
+ __esModule: true,
23
+ default: (props: any) => (
24
+ <div
25
+ data-testid="custom-lists"
26
+ data-library={props.library}
27
+ data-edit-or-create={props.editOrCreate}
28
+ data-identifier={props.identifier}
29
+ data-csrf-token={props.csrfToken}
30
+ data-has-store={String(!!props.store)}
31
+ />
32
+ ),
33
+ }));
34
+
35
+ import CustomListPage from "../../../src/components/CustomListPage";
36
+
37
+ describe("CustomListPage", () => {
38
+ const params = {
39
+ library: "library",
40
+ editOrCreate: "edit",
41
+ identifier: "identifier",
42
+ };
43
+
44
+ it("shows custom lists with info from params", () => {
45
+ renderWithProviders(<CustomListPage params={params} />, {
46
+ appConfigSettings: { csrfToken: "token" },
47
+ });
48
+
49
+ const customLists = screen.getByTestId("custom-lists");
50
+ expect(customLists).toHaveAttribute("data-library", "library");
51
+ expect(customLists).toHaveAttribute("data-edit-or-create", "edit");
52
+ expect(customLists).toHaveAttribute("data-identifier", "identifier");
53
+ expect(customLists).toHaveAttribute("data-csrf-token", "token");
54
+ // The redux store is forwarded from context; assert it was passed through.
55
+ expect(customLists).toHaveAttribute("data-has-store", "true");
56
+ });
57
+ });