@thepalaceproject/circulation-admin 1.43.0-post.8 → 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 (27) hide show
  1. package/package.json +1 -1
  2. package/tests/jest/components/AdvancedSearchBooleanFilter.test.tsx +231 -0
  3. package/tests/jest/components/AdvancedSearchFilter.test.tsx +142 -0
  4. package/tests/jest/components/AdvancedSearchFilterInput.test.tsx +212 -0
  5. package/tests/jest/components/AdvancedSearchFilterViewer.test.tsx +52 -0
  6. package/tests/jest/components/AdvancedSearchValueFilter.test.tsx +123 -0
  7. package/tests/jest/components/CatalogServices.test.tsx +63 -0
  8. package/tests/jest/components/CirculationEventsDownloadForm.test.tsx +41 -0
  9. package/tests/jest/components/CustomListSearch.test.tsx +275 -0
  10. package/tests/jest/components/CustomListsSidebar.test.tsx +251 -0
  11. package/tests/jest/components/DiagnosticsTabContainer.test.tsx +147 -0
  12. package/tests/jest/components/ErrorMessage.test.tsx +97 -0
  13. package/tests/jest/components/Footer.test.tsx +141 -0
  14. package/tests/jest/components/LanguageField.test.tsx +40 -0
  15. package/tests/jest/components/LoadButton.test.tsx +49 -0
  16. package/tests/jest/components/ManagePatronsForm.test.tsx +180 -0
  17. package/tests/jest/components/ManagePatronsTabContainer.test.tsx +143 -0
  18. package/tests/jest/components/MetadataServices.test.tsx +59 -0
  19. package/tests/jest/components/SelfTestResult.test.tsx +164 -0
  20. package/tests/jest/components/SelfTestsCategory.test.tsx +200 -0
  21. package/tests/jest/components/ServiceWithRegistrationsEditForm.test.tsx +110 -0
  22. package/tests/jest/components/TabContainer.test.tsx +68 -0
  23. package/tests/jest/components/Timestamp.test.tsx +74 -0
  24. package/tests/jest/components/WithEditButton.test.tsx +55 -0
  25. package/tests/jest/components/WithRemoveButton.test.tsx +95 -0
  26. package/tests/jest/index.test.tsx +50 -0
  27. package/tests/jest/components/AdvancedSearchBuilder.test.tsx +0 -38
@@ -0,0 +1,200 @@
1
+ import * as React from "react";
2
+ import { screen } from "@testing-library/react";
3
+
4
+ import { renderWithProviders } from "../testUtils/withProviders";
5
+ import buildStore from "../../../src/store";
6
+
7
+ // `SelfTests` is a connected component that fetches self-test results on mount.
8
+ // It is incidental to what `SelfTestsCategory` itself does (build one panel per
9
+ // item, color-code them, decide `openByDefault`, and compute `sortByCollection`),
10
+ // so mock it to a marker that exposes the props the category passes down.
11
+ jest.mock("../../../src/components/SelfTests", () => ({
12
+ __esModule: true,
13
+ default: (props: any) => (
14
+ <div
15
+ data-testid="self-tests"
16
+ data-type={props.type}
17
+ data-item-id={props.item?.id}
18
+ data-item-name={props.item?.name}
19
+ data-sort-by-collection={String(props.sortByCollection)}
20
+ />
21
+ ),
22
+ }));
23
+
24
+ import SelfTestsCategory from "../../../src/components/SelfTestsCategory";
25
+
26
+ const initialSetupResult = {
27
+ duration: 0.000119,
28
+ end: "2018-08-07T19:34:54Z",
29
+ exception: null,
30
+ name: "Initial setup.",
31
+ result: null,
32
+ start: "2018-08-07T19:34:54Z",
33
+ success: true,
34
+ };
35
+
36
+ const collections: any[] = [
37
+ {
38
+ id: 1,
39
+ name: "collection with success",
40
+ protocol: "protocol",
41
+ self_test_results: {
42
+ duration: 1.75,
43
+ start: "2018-08-07T19:34:54Z",
44
+ end: "2018-08-07T19:34:55Z",
45
+ results: [initialSetupResult],
46
+ },
47
+ },
48
+ {
49
+ id: 2,
50
+ name: "collection with failure",
51
+ protocol: "protocol",
52
+ self_test_results: {
53
+ duration: 1.75,
54
+ start: "2018-08-07T19:34:54Z",
55
+ end: "2018-08-07T19:34:55Z",
56
+ results: [
57
+ initialSetupResult,
58
+ {
59
+ duration: 0,
60
+ end: "2018-08-07T19:34:55Z",
61
+ exception: {
62
+ class: "IntegrationException",
63
+ debug_message:
64
+ "Add the collection to a library that has a patron authentication service.",
65
+ message: "Collection is not associated with any libraries.",
66
+ },
67
+ name: "Acquiring test patron credentials.",
68
+ result: null,
69
+ start: "2018-08-07T19:34:55Z",
70
+ success: false,
71
+ },
72
+ ],
73
+ },
74
+ },
75
+ {
76
+ id: 3,
77
+ name: "collection with no results",
78
+ protocol: "protocol",
79
+ },
80
+ ];
81
+
82
+ const renderCategory = (items: any[] = collections) =>
83
+ renderWithProviders(
84
+ <SelfTestsCategory
85
+ type="collection"
86
+ linkName="collections"
87
+ csrfToken="token"
88
+ items={items}
89
+ store={buildStore()}
90
+ />
91
+ );
92
+
93
+ describe("SelfTestsCategory", () => {
94
+ it("renders a list of items", () => {
95
+ const { container } = renderCategory();
96
+
97
+ const root = container.querySelector(".self-tests-category");
98
+ expect(root).toBeInTheDocument();
99
+ expect(root).toHaveClass("has-additional-content");
100
+
101
+ const titles = Array.from(
102
+ container.querySelectorAll("ul .panel-title")
103
+ ).map((el) => el.textContent);
104
+ expect(titles).toStrictEqual([
105
+ "collection with success",
106
+ "collection with failure",
107
+ "collection with no results",
108
+ ]);
109
+
110
+ // One SelfTests per item, each carrying the category's type and its item.
111
+ const selfTests = screen.getAllByTestId("self-tests");
112
+ expect(selfTests).toHaveLength(3);
113
+ selfTests.forEach((el, idx) => {
114
+ expect(el).toHaveAttribute("data-type", "collection");
115
+ expect(el).toHaveAttribute("data-item-id", String(collections[idx].id));
116
+ });
117
+ });
118
+
119
+ it("color-codes", () => {
120
+ const { container } = renderCategory();
121
+ const panels = container.querySelectorAll(".panel");
122
+ expect(panels[0]).toHaveClass("panel-success");
123
+ expect(panels[1]).toHaveClass("panel-danger");
124
+ expect(panels[2]).toHaveClass("panel-default");
125
+ });
126
+
127
+ it("opens the panel by default if there's only one", () => {
128
+ // With multiple items, none are open by default.
129
+ const multi = renderCategory();
130
+ expect(
131
+ Array.from(multi.container.querySelectorAll(".panel-heading")).some(
132
+ (h) => h.getAttribute("aria-expanded") === "true"
133
+ )
134
+ ).toBe(false);
135
+ multi.unmount();
136
+
137
+ // A fresh render with a single item opens that panel by default.
138
+ // (`openByDefault` seeds the panel's initial state, so a new mount is needed.)
139
+ const single = renderCategory([collections[0]]);
140
+ const singleHeaders = single.container.querySelectorAll(".panel-heading");
141
+ expect(singleHeaders).toHaveLength(1);
142
+ expect(singleHeaders[0]).toHaveAttribute("aria-expanded", "true");
143
+ });
144
+
145
+ it("passes the sortByCollection prop to SelfTests", () => {
146
+ const { rerender } = renderCategory();
147
+ expect(screen.getAllByTestId("self-tests")[0]).toHaveAttribute(
148
+ "data-sort-by-collection",
149
+ "false"
150
+ );
151
+
152
+ const withCollection = {
153
+ id: 99,
154
+ name: "collection with sorting",
155
+ protocol: "protocol",
156
+ self_test_results: {
157
+ duration: 1.75,
158
+ start: "2018-08-07T19:34:54Z",
159
+ end: "2018-08-07T19:34:55Z",
160
+ results: [{ ...initialSetupResult, collection: "sort!" }],
161
+ },
162
+ };
163
+
164
+ rerender(
165
+ <SelfTestsCategory
166
+ type="collection"
167
+ linkName="collections"
168
+ csrfToken="token"
169
+ items={[withCollection, ...collections]}
170
+ store={buildStore()}
171
+ />
172
+ );
173
+
174
+ expect(screen.getAllByTestId("self-tests")[0]).toHaveAttribute(
175
+ "data-sort-by-collection",
176
+ "true"
177
+ );
178
+ });
179
+
180
+ it("handles unnamed items", () => {
181
+ const nameless: any = {
182
+ id: 1,
183
+ name: null,
184
+ protocol: "protocol",
185
+ self_test_results: {
186
+ duration: 1.75,
187
+ start: "2018-08-07T19:34:54Z",
188
+ end: "2018-08-07T19:34:55Z",
189
+ results: [initialSetupResult],
190
+ },
191
+ };
192
+ const { container } = renderCategory([nameless]);
193
+
194
+ expect(screen.getByText("Unnamed collection")).toHaveClass("panel-title");
195
+ // Panel id becomes `${name-without-spaces}-${id}`; its body id appends "-panel".
196
+ expect(
197
+ container.querySelector("#Unnamedcollection-1-panel")
198
+ ).toBeInTheDocument();
199
+ });
200
+ });
@@ -0,0 +1,110 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ import { DiscoveryServicesData } from "../../../src/interfaces";
5
+ import ServiceWithRegistrationsEditForm from "../../../src/components/ServiceWithRegistrationsEditForm";
6
+
7
+ // `ServiceWithRegistrationsEditForm` is generic/abstract over its data type;
8
+ // the legacy test subclassed it to bind the discovery-services data type.
9
+ class DiscoveryServiceEditForm extends ServiceWithRegistrationsEditForm<DiscoveryServicesData> {}
10
+
11
+ const serviceData = {
12
+ id: 1,
13
+ protocol: "protocol 1",
14
+ };
15
+ const protocolsData = [
16
+ {
17
+ name: "protocol 1",
18
+ label: "protocol 1 label",
19
+ supports_registration: true,
20
+ settings: [],
21
+ library_settings: [],
22
+ },
23
+ ];
24
+ const allLibraries = [
25
+ { short_name: "nypl", name: "New York Public Library" },
26
+ { short_name: "bpl", name: "Brooklyn Public Library" },
27
+ { short_name: "qpl", name: "Queens Public Library" },
28
+ ];
29
+ const libraryRegistrationsData = [
30
+ {
31
+ id: 1,
32
+ libraries: [
33
+ { short_name: "nypl", status: "success", stage: "production" },
34
+ { short_name: "bpl", status: "failure", stage: "testing" },
35
+ ],
36
+ },
37
+ ];
38
+ const servicesData: any = {
39
+ discovery_services: [serviceData],
40
+ protocols: protocolsData,
41
+ allLibraries: allLibraries,
42
+ libraryRegistrations: libraryRegistrationsData,
43
+ };
44
+
45
+ const renderForm = (save = jest.fn()) =>
46
+ render(
47
+ <DiscoveryServiceEditForm
48
+ disabled={false}
49
+ data={servicesData}
50
+ item={serviceData as any}
51
+ save={save}
52
+ urlBase="url base"
53
+ listDataKey="discovery_services"
54
+ />
55
+ );
56
+
57
+ describe("ServiceWithRegistrationsEditForm", () => {
58
+ it("renders the underlying service edit form fields", () => {
59
+ renderForm();
60
+
61
+ // The base ServiceEditForm (via `super.render()`) contributes the Name text
62
+ // field and the Required Fields section.
63
+ expect(screen.getByRole("textbox")).toBeInTheDocument();
64
+ expect(
65
+ screen.getByRole("heading", { name: "Required Fields" })
66
+ ).toBeInTheDocument();
67
+ });
68
+
69
+ it("renders a registration section reflecting each library's status", () => {
70
+ renderForm();
71
+
72
+ // The registration UI only renders when the protocol supports registration
73
+ // and the data carries libraries — i.e. those props flowed to LibraryRegistration.
74
+ expect(
75
+ screen.getByRole("heading", { name: "Register libraries" })
76
+ ).toBeInTheDocument();
77
+
78
+ expect(
79
+ screen.getByRole("link", { name: "New York Public Library" })
80
+ ).toBeInTheDocument();
81
+ expect(
82
+ screen.getByRole("link", { name: "Brooklyn Public Library" })
83
+ ).toBeInTheDocument();
84
+ expect(
85
+ screen.getByRole("link", { name: "Queens Public Library" })
86
+ ).toBeInTheDocument();
87
+
88
+ // Statuses come from `data.libraryRegistrations`.
89
+ expect(screen.getByText("Registered")).toHaveClass("bg-success");
90
+ expect(screen.getByText("Registration failed")).toHaveClass("bg-danger");
91
+ expect(screen.getByText("Not registered")).toHaveClass("bg-warning");
92
+ });
93
+
94
+ it("enables the per-library registration actions when not disabled", () => {
95
+ renderForm();
96
+
97
+ // `disabled={false}` flows through to each registration button.
98
+ const updateButton = screen.getByRole("button", {
99
+ name: "Update registration",
100
+ });
101
+ const retryButton = screen.getByRole("button", {
102
+ name: "Retry registration",
103
+ });
104
+ const registerButton = screen.getByRole("button", { name: "Register" });
105
+
106
+ expect(updateButton).not.toBeDisabled();
107
+ expect(retryButton).not.toBeDisabled();
108
+ expect(registerButton).not.toBeDisabled();
109
+ });
110
+ });
@@ -0,0 +1,68 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+
5
+ import {
6
+ TabContainer,
7
+ TabContainerProps,
8
+ } from "../../../src/components/TabContainer";
9
+
10
+ describe("TabContainer", () => {
11
+ let handleSelectSpy: jest.Mock;
12
+
13
+ // A concrete subclass of the abstract TabContainer, mirroring the legacy test's
14
+ // MockTabContainer: it supplies two trivial tabs and records the selected tab
15
+ // key when handleSelect runs.
16
+ class MockTabContainer extends TabContainer<TabContainerProps> {
17
+ handleSelect(event) {
18
+ handleSelectSpy(event.target.dataset.tabkey);
19
+ }
20
+ tabs() {
21
+ return {
22
+ tab1: <div className="tab1" />,
23
+ tab2: <div className="tab2" />,
24
+ };
25
+ }
26
+ }
27
+
28
+ const renderContainer = (tab: string = null) =>
29
+ render(
30
+ <MockTabContainer tab={tab} csrfToken="token">
31
+ <div className="test-child">Moby Dick</div>
32
+ </MockTabContainer>
33
+ );
34
+
35
+ beforeEach(() => {
36
+ handleSelectSpy = jest.fn();
37
+ });
38
+
39
+ it("shows nav tabs", () => {
40
+ const { container } = renderContainer();
41
+ const items = container.querySelectorAll("ul.nav-tabs li");
42
+ const linkTexts = Array.from(items).map((li) => li.textContent);
43
+ expect(linkTexts).toContain("Tab1");
44
+ expect(linkTexts).toContain("Tab2");
45
+ expect(items[0]).toHaveClass("active");
46
+ expect(items[1]).not.toHaveClass("active");
47
+ });
48
+
49
+ it("shows default tab content", () => {
50
+ const { container } = renderContainer();
51
+ expect(container.querySelector("#tab1")).toHaveStyle({ display: "block" });
52
+ expect(container.querySelector("#tab2")).toHaveStyle({ display: "none" });
53
+ });
54
+
55
+ it("shows tab content from tab in props", () => {
56
+ const { container } = renderContainer("tab2");
57
+ expect(container.querySelector("#tab1")).toHaveStyle({ display: "none" });
58
+ expect(container.querySelector("#tab2")).toHaveStyle({ display: "block" });
59
+ });
60
+
61
+ it("uses handleSelect to navigate when tab is clicked", async () => {
62
+ const user = userEvent.setup();
63
+ renderContainer();
64
+ await user.click(screen.getByRole("link", { name: "Tab2" }));
65
+ expect(handleSelectSpy).toHaveBeenCalledTimes(1);
66
+ expect(handleSelectSpy).toHaveBeenCalledWith("tab2");
67
+ });
68
+ });
@@ -0,0 +1,74 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ import Timestamp from "../../../src/components/Timestamp";
5
+
6
+ describe("Timestamp", () => {
7
+ const timestamp = {
8
+ service: "test_service",
9
+ id: "1",
10
+ start: "start_time_string",
11
+ duration: "60",
12
+ collection_name: "collection1",
13
+ };
14
+
15
+ describe("rendering", () => {
16
+ it("renders a success panel that is not collapsible", () => {
17
+ const { container } = render(<Timestamp timestamp={timestamp} />);
18
+
19
+ expect(container.querySelector(".panel-success")).toBeInTheDocument();
20
+ expect(container.querySelector(".panel-danger")).not.toBeInTheDocument();
21
+ // A non-collapsible panel is a static panel with no toggle button.
22
+ expect(container.querySelector(".static-panel")).toBeInTheDocument();
23
+ expect(screen.queryByRole("button")).not.toBeInTheDocument();
24
+ });
25
+
26
+ it("renders the start time", () => {
27
+ const { container } = render(<Timestamp timestamp={timestamp} />);
28
+
29
+ expect(container.querySelector(".panel-title")).toHaveTextContent(
30
+ "start_time_string"
31
+ );
32
+ });
33
+
34
+ it("renders the duration", () => {
35
+ const { container } = render(<Timestamp timestamp={timestamp} />);
36
+
37
+ expect(container.querySelectorAll("ul li")).toHaveLength(1);
38
+ expect(screen.getByText("Duration: 60 seconds")).toBeInTheDocument();
39
+ });
40
+ });
41
+
42
+ describe("rendering with achievements", () => {
43
+ it("renders achievements", () => {
44
+ const { container } = render(
45
+ <Timestamp timestamp={{ ...timestamp, achievements: "Ran a script" }} />
46
+ );
47
+
48
+ expect(container.querySelectorAll("ul li")).toHaveLength(2);
49
+ const achievements = container.querySelector(".well pre");
50
+ expect(achievements).toBeInTheDocument();
51
+ expect(achievements).toHaveTextContent("Ran a script");
52
+ });
53
+ });
54
+
55
+ describe("rendering with exception", () => {
56
+ it("determines style based on whether there is an exception", () => {
57
+ const { container } = render(
58
+ <Timestamp timestamp={{ ...timestamp, exception: "Stack trace" }} />
59
+ );
60
+
61
+ expect(container.querySelector(".panel-danger")).toBeInTheDocument();
62
+ });
63
+
64
+ it("renders an exception", () => {
65
+ const { container } = render(
66
+ <Timestamp timestamp={{ ...timestamp, exception: "Stack trace" }} />
67
+ );
68
+
69
+ const exception = container.querySelector(".exception pre");
70
+ expect(exception).toBeInTheDocument();
71
+ expect(exception).toHaveTextContent("Stack trace");
72
+ });
73
+ });
74
+ });
@@ -0,0 +1,55 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+
5
+ import WithEditButton from "../../../src/components/WithEditButton";
6
+
7
+ describe("WithEditButton", () => {
8
+ it("shows children", () => {
9
+ render(
10
+ <WithEditButton disabled={false} onEdit={jest.fn()}>
11
+ child
12
+ </WithEditButton>
13
+ );
14
+
15
+ expect(screen.getByText("child")).toBeInTheDocument();
16
+ });
17
+
18
+ it("shows an edit button", () => {
19
+ render(
20
+ <WithEditButton disabled={false} onEdit={jest.fn()}>
21
+ child
22
+ </WithEditButton>
23
+ );
24
+
25
+ expect(screen.getByRole("button")).toHaveClass("edit-btn");
26
+ });
27
+
28
+ it("calls onEdit when clicked", async () => {
29
+ const user = userEvent.setup();
30
+ const onEdit = jest.fn();
31
+ render(
32
+ <WithEditButton disabled={false} onEdit={onEdit}>
33
+ child
34
+ </WithEditButton>
35
+ );
36
+
37
+ await user.click(screen.getByRole("button"));
38
+
39
+ expect(onEdit).toHaveBeenCalledTimes(1);
40
+ });
41
+
42
+ it("does nothing when disabled", async () => {
43
+ const user = userEvent.setup();
44
+ const onEdit = jest.fn();
45
+ render(
46
+ <WithEditButton disabled={true} onEdit={onEdit}>
47
+ child
48
+ </WithEditButton>
49
+ );
50
+
51
+ await user.click(screen.getByRole("button"));
52
+
53
+ expect(onEdit).not.toHaveBeenCalled();
54
+ });
55
+ });
@@ -0,0 +1,95 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+
5
+ import WithRemoveButton from "../../../src/components/WithRemoveButton";
6
+
7
+ describe("WithRemoveButton", () => {
8
+ it("shows children", () => {
9
+ render(
10
+ <WithRemoveButton disabled={false} onRemove={jest.fn()}>
11
+ child
12
+ </WithRemoveButton>
13
+ );
14
+
15
+ expect(screen.getByText("child")).toBeInTheDocument();
16
+ });
17
+
18
+ it("shows a remove button", () => {
19
+ render(
20
+ <WithRemoveButton disabled={false} onRemove={jest.fn()}>
21
+ child
22
+ </WithRemoveButton>
23
+ );
24
+
25
+ expect(screen.getByRole("button")).toHaveClass("remove-btn");
26
+ });
27
+
28
+ it("calls onRemove when clicked", async () => {
29
+ const user = userEvent.setup();
30
+ const onRemove = jest.fn();
31
+ render(
32
+ <WithRemoveButton disabled={false} onRemove={onRemove}>
33
+ child
34
+ </WithRemoveButton>
35
+ );
36
+
37
+ await user.click(screen.getByRole("button"));
38
+
39
+ expect(onRemove).toHaveBeenCalledTimes(1);
40
+ });
41
+
42
+ it("does nothing when disabled", async () => {
43
+ const user = userEvent.setup();
44
+ const onRemove = jest.fn();
45
+ render(
46
+ <WithRemoveButton disabled={true} onRemove={onRemove}>
47
+ child
48
+ </WithRemoveButton>
49
+ );
50
+
51
+ await user.click(screen.getByRole("button"));
52
+
53
+ expect(onRemove).not.toHaveBeenCalled();
54
+ });
55
+
56
+ it("calls onRemove when confirmation returns true", async () => {
57
+ const user = userEvent.setup();
58
+ const onRemove = jest.fn();
59
+ const confirmRemoval = jest.fn().mockReturnValue(true);
60
+ render(
61
+ <WithRemoveButton
62
+ disabled={false}
63
+ onRemove={onRemove}
64
+ confirmRemoval={confirmRemoval}
65
+ >
66
+ child
67
+ </WithRemoveButton>
68
+ );
69
+
70
+ await user.click(screen.getByRole("button"));
71
+
72
+ expect(confirmRemoval).toHaveBeenCalledTimes(1);
73
+ expect(onRemove).toHaveBeenCalledTimes(1);
74
+ });
75
+
76
+ it("does not call onRemove when confirmation returns false", async () => {
77
+ const user = userEvent.setup();
78
+ const onRemove = jest.fn();
79
+ const confirmRemoval = jest.fn().mockReturnValue(false);
80
+ render(
81
+ <WithRemoveButton
82
+ disabled={false}
83
+ onRemove={onRemove}
84
+ confirmRemoval={confirmRemoval}
85
+ >
86
+ child
87
+ </WithRemoveButton>
88
+ );
89
+
90
+ await user.click(screen.getByRole("button"));
91
+
92
+ expect(confirmRemoval).toHaveBeenCalledTimes(1);
93
+ expect(onRemove).not.toHaveBeenCalled();
94
+ });
95
+ });
@@ -0,0 +1,50 @@
1
+ import * as React from "react";
2
+ import * as ReactDOM from "react-dom";
3
+ import { Router } from "react-router";
4
+
5
+ import SetupPage from "../../src/components/SetupPage";
6
+
7
+ // index.tsx does `export = CirculationAdmin`.
8
+ const CirculationAdmin = require("../../src/index");
9
+
10
+ /** Walk a React element tree for an element of the given type — the structural
11
+ * analog of enzyme's `wrapper.find(Type)`, used where the tree is the app root
12
+ * and there is no rendered DOM to query against. */
13
+ function containsType(node: any, type: React.ElementType): boolean {
14
+ if (!node || typeof node !== "object") return false;
15
+ if (Array.isArray(node)) return node.some((n) => containsType(n, type));
16
+ if (node.type === type) return true;
17
+ return containsType(node.props && node.props.children, type);
18
+ }
19
+
20
+ describe("CirculationAdmin", () => {
21
+ let renderSpy: jest.SpyInstance;
22
+
23
+ beforeEach(() => {
24
+ // Capture what the bootstrap hands to ReactDOM.render without letting it
25
+ // paint into the jsdom document. We assert on the captured element tree
26
+ // (the structural analog of the original enzyme `wrapper.find(...)`); what
27
+ // SetupPage and the routed pages render is covered by their own tests.
28
+ renderSpy = jest
29
+ .spyOn(ReactDOM, "render")
30
+ .mockImplementation(() => undefined);
31
+ });
32
+
33
+ afterEach(() => {
34
+ jest.restoreAllMocks();
35
+ });
36
+
37
+ it("renders the setup page when setting up", () => {
38
+ new CirculationAdmin({ settingUp: true, csrfToken: "token" });
39
+
40
+ expect(renderSpy).toHaveBeenCalledTimes(1);
41
+ expect(containsType(renderSpy.mock.calls[0][0], SetupPage)).toBe(true);
42
+ });
43
+
44
+ it("renders the router otherwise", () => {
45
+ new CirculationAdmin({ csrfToken: "token" });
46
+
47
+ expect(renderSpy).toHaveBeenCalledTimes(1);
48
+ expect(containsType(renderSpy.mock.calls[0][0], Router)).toBe(true);
49
+ });
50
+ });
@@ -1,38 +0,0 @@
1
- import * as React from "react";
2
- import { render, screen } from "@testing-library/react";
3
- import userEvent from "@testing-library/user-event";
4
- import AdvancedSearchBuilder from "../../../src/components/AdvancedSearchBuilder";
5
-
6
- describe("AdvancedSearchBuilder", () => {
7
- it("renders a placeholder and help text when the publication date filter is selected", async () => {
8
- userEvent.setup();
9
-
10
- const query = {
11
- id: "0",
12
- key: "genre",
13
- value: "Horror",
14
- };
15
-
16
- render(
17
- <AdvancedSearchBuilder
18
- isOwner={true}
19
- name="search"
20
- query={query}
21
- selectedQueryId="0"
22
- />
23
- );
24
-
25
- await userEvent.selectOptions(
26
- screen.getByRole("combobox", { name: "filter field key" }),
27
- screen.getByRole("option", { name: "Publication Date" })
28
- );
29
-
30
- const filterValueField = screen.getByRole("textbox", {
31
- name: "filter value",
32
- });
33
-
34
- expect(filterValueField).toHaveAttribute("placeholder", "YYYY-MM-DD");
35
- expect(filterValueField).toHaveAccessibleDescription(/publication date/i);
36
- expect(filterValueField).toHaveAccessibleDescription(/YYYY-MM-DD/i);
37
- });
38
- });