@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.
- package/package.json +1 -1
- package/tests/jest/components/AccountPage.test.tsx +41 -0
- package/tests/jest/components/AdvancedSearchBooleanFilter.test.tsx +231 -0
- package/tests/jest/components/AdvancedSearchFilter.test.tsx +142 -0
- package/tests/jest/components/AdvancedSearchFilterInput.test.tsx +212 -0
- package/tests/jest/components/AdvancedSearchFilterViewer.test.tsx +52 -0
- package/tests/jest/components/AdvancedSearchValueFilter.test.tsx +123 -0
- package/tests/jest/components/BookDetails.test.tsx +133 -0
- package/tests/jest/components/BookDetailsContainer.test.tsx +116 -0
- package/tests/jest/components/CatalogServices.test.tsx +63 -0
- package/tests/jest/components/CirculationEventsDownloadForm.test.tsx +41 -0
- package/tests/jest/components/ConfigPage.test.tsx +58 -0
- package/tests/jest/components/CustomListPage.test.tsx +57 -0
- package/tests/jest/components/CustomListSearch.test.tsx +275 -0
- package/tests/jest/components/CustomListsSidebar.test.tsx +251 -0
- package/tests/jest/components/DashboardPage.test.tsx +76 -0
- package/tests/jest/components/DebugAuthentication.test.tsx +1 -5
- package/tests/jest/components/DiagnosticsTabContainer.test.tsx +147 -0
- package/tests/jest/components/EditableConfigList.test.tsx +8 -2
- package/tests/jest/components/EntryPointsContainer.test.tsx +97 -0
- package/tests/jest/components/EntryPointsTabs.test.tsx +88 -0
- package/tests/jest/components/ErrorMessage.test.tsx +97 -0
- package/tests/jest/components/Footer.test.tsx +141 -0
- package/tests/jest/components/InventoryReportRequestModal.test.tsx +16 -42
- package/tests/jest/components/LaneEditor.test.tsx +2 -6
- package/tests/jest/components/LanePage.test.tsx +68 -0
- package/tests/jest/components/LanguageField.test.tsx +40 -0
- package/tests/jest/components/LoadButton.test.tsx +49 -0
- package/tests/jest/components/ManagePatrons.test.tsx +73 -0
- package/tests/jest/components/ManagePatronsForm.test.tsx +180 -0
- package/tests/jest/components/ManagePatronsTabContainer.test.tsx +143 -0
- package/tests/jest/components/MetadataServices.test.tsx +59 -0
- package/tests/jest/components/PatronBlockingRulesEditor.test.tsx +3 -1
- package/tests/jest/components/PatronInfo.test.tsx +54 -0
- package/tests/jest/components/SelfTestResult.test.tsx +164 -0
- package/tests/jest/components/SelfTestsCategory.test.tsx +200 -0
- package/tests/jest/components/ServiceWithRegistrationsEditForm.test.tsx +110 -0
- package/tests/jest/components/SetupPage.test.tsx +41 -0
- package/tests/jest/components/SingleStatListItem.test.tsx +62 -0
- package/tests/jest/components/Stats.test.tsx +4 -8
- package/tests/jest/components/TabContainer.test.tsx +68 -0
- package/tests/jest/components/Timestamp.test.tsx +74 -0
- package/tests/jest/components/TroubleshootingTabContainer.test.tsx +68 -0
- package/tests/jest/components/WelcomePage.test.tsx +43 -0
- package/tests/jest/components/WithEditButton.test.tsx +55 -0
- package/tests/jest/components/WithRemoveButton.test.tsx +95 -0
- package/tests/jest/index.test.tsx +50 -0
- package/tests/jest/components/AdvancedSearchBuilder.test.tsx +0 -38
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import SingleStatListItem, {
|
|
5
|
+
SingleStatListItemProps,
|
|
6
|
+
} from "../../../src/components/SingleStatListItem";
|
|
7
|
+
|
|
8
|
+
describe("SingleStatListItem", () => {
|
|
9
|
+
const statWithTooltip: SingleStatListItemProps = {
|
|
10
|
+
label: "Number of Widgets",
|
|
11
|
+
value: 357892,
|
|
12
|
+
tooltip: "Total Widget Count",
|
|
13
|
+
};
|
|
14
|
+
const humanReadableValue = "357.9k";
|
|
15
|
+
const formattedValue = "357,892";
|
|
16
|
+
|
|
17
|
+
it("renders a top-level <li>, with or without a tooltip", () => {
|
|
18
|
+
const { container, rerender } = render(
|
|
19
|
+
<SingleStatListItem {...statWithTooltip} />
|
|
20
|
+
);
|
|
21
|
+
expect(container.firstElementChild?.tagName).toBe("LI");
|
|
22
|
+
expect(container.firstElementChild).toHaveClass("single-stat");
|
|
23
|
+
|
|
24
|
+
// Without a tooltip the <li> is still the top-level element.
|
|
25
|
+
rerender(
|
|
26
|
+
<SingleStatListItem
|
|
27
|
+
label={statWithTooltip.label}
|
|
28
|
+
value={statWithTooltip.value}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
expect(container.firstElementChild?.tagName).toBe("LI");
|
|
32
|
+
expect(container.firstElementChild).toHaveClass("single-stat");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("shows a tooltip wrapping the value and label when one is provided", () => {
|
|
36
|
+
const { container } = render(<SingleStatListItem {...statWithTooltip} />);
|
|
37
|
+
|
|
38
|
+
const tooltip = container.querySelector('[data-toggle="tooltip"]');
|
|
39
|
+
expect(tooltip).not.toBeNull();
|
|
40
|
+
|
|
41
|
+
const title = tooltip?.getAttribute("title") ?? "";
|
|
42
|
+
expect(title).toContain(statWithTooltip.tooltip);
|
|
43
|
+
expect(title).toContain(formattedValue);
|
|
44
|
+
|
|
45
|
+
// When there's a tooltip, it wraps the (rounded) value and the label.
|
|
46
|
+
expect(tooltip).toHaveTextContent(humanReadableValue);
|
|
47
|
+
expect(tooltip).toHaveTextContent(statWithTooltip.label);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("omits the tooltip when none is provided, showing value and label directly", () => {
|
|
51
|
+
const { container } = render(
|
|
52
|
+
<SingleStatListItem
|
|
53
|
+
label={statWithTooltip.label}
|
|
54
|
+
value={statWithTooltip.value}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(container.querySelector('[data-toggle="tooltip"]')).toBeNull();
|
|
59
|
+
expect(screen.getByText(humanReadableValue)).toBeInTheDocument();
|
|
60
|
+
expect(screen.getByText(statWithTooltip.label)).toBeInTheDocument();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -382,14 +382,10 @@ describe("Dashboard Statistics", () => {
|
|
|
382
382
|
roles,
|
|
383
383
|
quicksightPagePath: fakeQuickSightHref,
|
|
384
384
|
};
|
|
385
|
-
const {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
queryByText,
|
|
390
|
-
} = renderWithProviders(<Stats library={sampleLibraryKey} />, {
|
|
391
|
-
appConfigSettings,
|
|
392
|
-
});
|
|
385
|
+
const { container, getByRole, queryByRole, queryByText } =
|
|
386
|
+
renderWithProviders(<Stats library={sampleLibraryKey} />, {
|
|
387
|
+
appConfigSettings,
|
|
388
|
+
});
|
|
393
389
|
|
|
394
390
|
// We should always render a Usage reports group when a library is specified.
|
|
395
391
|
getByRole("heading", {
|
|
@@ -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,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
|
+
// The tab content (TroubleshootingCategoryPage) renders the connected
|
|
6
|
+
// Diagnostics/Self-tests containers, which need Redux store + csrf context this
|
|
7
|
+
// test does not supply. That content is incidental here — the test is about the
|
|
8
|
+
// tab navigation — so mock it to a marker.
|
|
9
|
+
jest.mock("../../../src/components/TroubleshootingCategoryPage", () => ({
|
|
10
|
+
__esModule: true,
|
|
11
|
+
default: () => <div data-testid="category-page" />,
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
import TroubleshootingTabContainer from "../../../src/components/TroubleshootingTabContainer";
|
|
15
|
+
|
|
16
|
+
describe("TroubleshootingTabContainer", () => {
|
|
17
|
+
let goToTab: jest.Mock;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
goToTab = jest.fn();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("renders tabs and defaults to showing the Diagnostics tab", () => {
|
|
24
|
+
const { container } = render(
|
|
25
|
+
<TroubleshootingTabContainer goToTab={goToTab} tab="diagnostics" />
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const nav = container.querySelector("ul.nav-tabs");
|
|
29
|
+
expect(nav).not.toBeNull();
|
|
30
|
+
|
|
31
|
+
const tabs = nav.querySelectorAll("li");
|
|
32
|
+
expect(tabs).toHaveLength(2);
|
|
33
|
+
|
|
34
|
+
expect(tabs[0]).toHaveTextContent("Diagnostics");
|
|
35
|
+
expect(tabs[0]).toHaveClass("active");
|
|
36
|
+
|
|
37
|
+
expect(tabs[1]).toHaveTextContent("Self-tests");
|
|
38
|
+
expect(tabs[1]).not.toHaveClass("active");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("calls goToTab when another tab is clicked", async () => {
|
|
42
|
+
const user = userEvent.setup();
|
|
43
|
+
render(<TroubleshootingTabContainer goToTab={goToTab} tab="diagnostics" />);
|
|
44
|
+
|
|
45
|
+
await user.click(screen.getByRole("link", { name: "Self-tests" }));
|
|
46
|
+
|
|
47
|
+
expect(goToTab).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(goToTab).toHaveBeenCalledWith("self-tests");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("switches the active tab when the tab prop changes", () => {
|
|
52
|
+
const { container, rerender } = render(
|
|
53
|
+
<TroubleshootingTabContainer goToTab={goToTab} tab="diagnostics" />
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
let tabs = container.querySelectorAll("ul.nav-tabs li");
|
|
57
|
+
expect(tabs[0]).toHaveClass("active");
|
|
58
|
+
expect(tabs[1]).not.toHaveClass("active");
|
|
59
|
+
|
|
60
|
+
rerender(
|
|
61
|
+
<TroubleshootingTabContainer goToTab={goToTab} tab="self-tests" />
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
tabs = container.querySelectorAll("ul.nav-tabs li");
|
|
65
|
+
expect(tabs[0]).not.toHaveClass("active");
|
|
66
|
+
expect(tabs[1]).toHaveClass("active");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
// Header pulls in the react-router v3 context a plain render does not supply, and
|
|
5
|
+
// both Header and Footer are page chrome with no behavior worth exercising here.
|
|
6
|
+
// Mock them to markers so the test stays about what WelcomePage composes.
|
|
7
|
+
jest.mock("../../../src/components/Header", () => ({
|
|
8
|
+
__esModule: true,
|
|
9
|
+
default: () => <div data-testid="header" />,
|
|
10
|
+
}));
|
|
11
|
+
jest.mock("../../../src/components/Footer", () => ({
|
|
12
|
+
__esModule: true,
|
|
13
|
+
default: () => <div data-testid="footer" />,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
import WelcomePage from "../../../src/components/WelcomePage";
|
|
17
|
+
|
|
18
|
+
describe("WelcomePage", () => {
|
|
19
|
+
it("shows the header", () => {
|
|
20
|
+
render(<WelcomePage />);
|
|
21
|
+
expect(screen.getByTestId("header")).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("shows the welcome message", () => {
|
|
25
|
+
render(<WelcomePage />);
|
|
26
|
+
expect(
|
|
27
|
+
screen.getByRole("heading", {
|
|
28
|
+
name: "Welcome to the Circulation Admin Interface!",
|
|
29
|
+
})
|
|
30
|
+
).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("shows a link to the library create form", () => {
|
|
34
|
+
render(<WelcomePage />);
|
|
35
|
+
const link = screen.getByRole("link", { name: /create a new library/i });
|
|
36
|
+
expect(link).toHaveAttribute("href", "/admin/web/config/libraries/create");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("shows the footer", () => {
|
|
40
|
+
render(<WelcomePage />);
|
|
41
|
+
expect(screen.getByTestId("footer")).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -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
|
-
});
|