@thepalaceproject/circulation-admin 1.42.2 → 1.43.0-post.10
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/.node-version +1 -1
- package/.nvmrc +1 -1
- package/CLAUDE.md +17 -8
- package/README.md +7 -7
- package/dist/circulation-admin.js +1 -1
- package/eslint.config.mjs +9 -1
- package/jest.config.js +56 -9
- package/package.json +10 -20
- package/tests/jest/actions.test.ts +962 -0
- 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/JsonField.test.tsx +20 -0
- 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/ProtocolFormField.test.tsx +36 -0
- package/tests/jest/components/SaveButton.test.tsx +47 -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/UpdatingLoader.test.tsx +36 -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/editorAdapter.test.ts +245 -0
- package/tests/jest/index.test.tsx +50 -0
- package/tests/jest/legacy-setup.ts +13 -0
- package/tests/jest/legacyJsdomEnvironment.js +49 -0
- package/tests/jest/models/Admin.test.ts +206 -0
- package/tests/jest/reducers/bookCoverPreview.test.ts +81 -0
- package/tests/jest/reducers/classifications.test.ts +161 -0
- package/tests/jest/reducers/collections.test.ts +96 -0
- package/tests/jest/reducers/complaints.test.ts +166 -0
- package/tests/jest/reducers/createFetchEditReducer.test.ts +220 -0
- package/tests/jest/reducers/createRegisterLibraryReducer.test.ts +51 -0
- package/tests/jest/reducers/customListDetails.test.ts +120 -0
- package/tests/jest/reducers/customListEditor.test.ts +2823 -0
- package/tests/jest/reducers/managePatrons.test.ts +71 -0
- package/tests/jest/testUtils/formDataShim.test.ts +135 -0
- package/tests/jest/testUtils/formDataShim.ts +112 -0
- package/tests/jest/utils/sharedFunctions.test.ts +121 -0
- package/tsconfig.json +3 -4
- package/testReporter.js +0 -29
- package/tests/jest/components/AdvancedSearchBuilder.test.tsx +0 -38
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as PropTypes from "prop-types";
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
|
|
6
|
+
import buildStore from "../../../src/store";
|
|
7
|
+
import Admin from "../../../src/models/Admin";
|
|
8
|
+
|
|
9
|
+
// Both tab panels fetch on mount (ResetAdobeId is a connected component; Debug
|
|
10
|
+
// Authentication uses react-query). They're incidental to a tab-navigation test,
|
|
11
|
+
// so mock them to markers. ResetAdobeId echoes the csrfToken asserted below.
|
|
12
|
+
jest.mock("../../../src/components/ResetAdobeId", () => ({
|
|
13
|
+
__esModule: true,
|
|
14
|
+
default: (props: any) => (
|
|
15
|
+
<div data-testid="reset-adobe-id" data-csrf-token={props.csrfToken} />
|
|
16
|
+
),
|
|
17
|
+
}));
|
|
18
|
+
jest.mock("../../../src/components/DebugAuthentication", () => ({
|
|
19
|
+
__esModule: true,
|
|
20
|
+
default: () => <div data-testid="debug-authentication" />,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
import ManagePatronsTabContainer from "../../../src/components/ManagePatronsTabContainer";
|
|
24
|
+
|
|
25
|
+
// ManagePatronsTabContainer reads `admin`, `router`, and `pathFor` from the legacy
|
|
26
|
+
// React context the app supplies in production. Wrap it in a small provider that
|
|
27
|
+
// hands it those.
|
|
28
|
+
class LegacyContextProvider extends React.Component<{ context: any }> {
|
|
29
|
+
static childContextTypes = {
|
|
30
|
+
router: PropTypes.object,
|
|
31
|
+
pathFor: PropTypes.func,
|
|
32
|
+
admin: PropTypes.object,
|
|
33
|
+
};
|
|
34
|
+
getChildContext() {
|
|
35
|
+
return this.props.context;
|
|
36
|
+
}
|
|
37
|
+
render() {
|
|
38
|
+
return this.props.children;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("ManagePatronsTabContainer", () => {
|
|
43
|
+
const systemAdmin = new Admin([{ role: "system" }]);
|
|
44
|
+
const libraryManagerNYPL = new Admin([{ role: "manager", library: "NYPL" }]);
|
|
45
|
+
const libraryManagerA = new Admin([{ role: "manager", library: "a" }]);
|
|
46
|
+
const librarian = new Admin([{ role: "librarian", library: "NYPL" }]);
|
|
47
|
+
|
|
48
|
+
const renderWithAdmin = (admin: Admin, push: jest.Mock = jest.fn()) => {
|
|
49
|
+
const context = { router: { push }, pathFor: jest.fn(), admin };
|
|
50
|
+
const result = render(
|
|
51
|
+
<LegacyContextProvider context={context}>
|
|
52
|
+
<ManagePatronsTabContainer
|
|
53
|
+
tab={null}
|
|
54
|
+
csrfToken="token"
|
|
55
|
+
store={buildStore()}
|
|
56
|
+
library="NYPL"
|
|
57
|
+
/>
|
|
58
|
+
</LegacyContextProvider>
|
|
59
|
+
);
|
|
60
|
+
return { ...result, push };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
describe("for system admin", () => {
|
|
64
|
+
it("shows tabs", () => {
|
|
65
|
+
renderWithAdmin(systemAdmin);
|
|
66
|
+
expect(
|
|
67
|
+
screen.getByRole("link", { name: "Reset Adobe ID" })
|
|
68
|
+
).toBeInTheDocument();
|
|
69
|
+
expect(
|
|
70
|
+
screen.getByRole("link", { name: "Debug Authentication" })
|
|
71
|
+
).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("shows components", () => {
|
|
75
|
+
renderWithAdmin(systemAdmin);
|
|
76
|
+
expect(screen.getByTestId("reset-adobe-id")).toHaveAttribute(
|
|
77
|
+
"data-csrf-token",
|
|
78
|
+
"token"
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("uses router to navigate when tab is clicked", async () => {
|
|
83
|
+
const user = userEvent.setup();
|
|
84
|
+
const { push } = renderWithAdmin(systemAdmin);
|
|
85
|
+
await user.click(screen.getByRole("link", { name: "Reset Adobe ID" }));
|
|
86
|
+
expect(push).toHaveBeenCalledTimes(1);
|
|
87
|
+
expect(push).toHaveBeenCalledWith("/admin/web/patrons/NYPL/resetAdobeId");
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("for right library manager", () => {
|
|
92
|
+
it("shows tabs", () => {
|
|
93
|
+
renderWithAdmin(libraryManagerNYPL);
|
|
94
|
+
expect(
|
|
95
|
+
screen.getByRole("link", { name: "Reset Adobe ID" })
|
|
96
|
+
).toBeInTheDocument();
|
|
97
|
+
expect(
|
|
98
|
+
screen.getByRole("link", { name: "Debug Authentication" })
|
|
99
|
+
).toBeInTheDocument();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("marks the current tab as active", () => {
|
|
103
|
+
const { container } = renderWithAdmin(libraryManagerNYPL);
|
|
104
|
+
const tabItems = container.querySelectorAll("ul.nav-tabs li");
|
|
105
|
+
expect(tabItems).toHaveLength(2);
|
|
106
|
+
expect(tabItems[0]).toHaveClass("active");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("shows components", () => {
|
|
110
|
+
renderWithAdmin(libraryManagerNYPL);
|
|
111
|
+
expect(screen.getByTestId("reset-adobe-id")).toHaveAttribute(
|
|
112
|
+
"data-csrf-token",
|
|
113
|
+
"token"
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("uses router to navigate when tab is clicked", async () => {
|
|
118
|
+
const user = userEvent.setup();
|
|
119
|
+
const { push } = renderWithAdmin(libraryManagerNYPL);
|
|
120
|
+
await user.click(screen.getByRole("link", { name: "Reset Adobe ID" }));
|
|
121
|
+
expect(push).toHaveBeenCalledTimes(1);
|
|
122
|
+
expect(push).toHaveBeenCalledWith("/admin/web/patrons/NYPL/resetAdobeId");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe("for wrong library manager", () => {
|
|
127
|
+
it("doesn't show tabs", () => {
|
|
128
|
+
renderWithAdmin(libraryManagerA);
|
|
129
|
+
expect(
|
|
130
|
+
screen.queryByRole("link", { name: "Reset Adobe ID" })
|
|
131
|
+
).not.toBeInTheDocument();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("for librarian", () => {
|
|
136
|
+
it("doesn't show tabs", () => {
|
|
137
|
+
renderWithAdmin(librarian);
|
|
138
|
+
expect(
|
|
139
|
+
screen.queryByRole("link", { name: "Reset Adobe ID" })
|
|
140
|
+
).not.toBeInTheDocument();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { screen, within } from "@testing-library/react";
|
|
3
|
+
import { MetadataServices } from "../../../src/components/MetadataServices";
|
|
4
|
+
import renderWithContext from "../testUtils/renderWithContext";
|
|
5
|
+
import { defaultFeatureFlags } from "../../../src/utils/featureFlags";
|
|
6
|
+
|
|
7
|
+
describe("MetadataServices", () => {
|
|
8
|
+
const data = {
|
|
9
|
+
metadata_services: [
|
|
10
|
+
{
|
|
11
|
+
id: 2,
|
|
12
|
+
protocol: "test protocol",
|
|
13
|
+
name: "sample name",
|
|
14
|
+
settings: {
|
|
15
|
+
test_setting: "test setting",
|
|
16
|
+
},
|
|
17
|
+
libraries: [{ short_name: "nypl" }],
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
protocols: [
|
|
21
|
+
{
|
|
22
|
+
name: "test protocol",
|
|
23
|
+
label: "test protocol label",
|
|
24
|
+
sitewide: false,
|
|
25
|
+
settings: [],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
allLibraries: [
|
|
29
|
+
{
|
|
30
|
+
short_name: "nypl",
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const config = { csrfToken: "token", featureFlags: defaultFeatureFlags };
|
|
36
|
+
|
|
37
|
+
it("shows metadata service list", () => {
|
|
38
|
+
renderWithContext(
|
|
39
|
+
<MetadataServices
|
|
40
|
+
data={data}
|
|
41
|
+
fetchData={jest.fn()}
|
|
42
|
+
editItem={jest.fn().mockResolvedValue(undefined)}
|
|
43
|
+
csrfToken="token"
|
|
44
|
+
isFetching={false}
|
|
45
|
+
/>,
|
|
46
|
+
config
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const items = screen.getAllByRole("listitem");
|
|
50
|
+
expect(items).toHaveLength(1);
|
|
51
|
+
expect(items[0]).toHaveTextContent("sample name: test protocol");
|
|
52
|
+
|
|
53
|
+
const editLink = within(items[0]).getByRole("link");
|
|
54
|
+
expect(editLink).toHaveAttribute(
|
|
55
|
+
"href",
|
|
56
|
+
"/admin/web/config/metadata/edit/2"
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -612,7 +612,9 @@ describe("PatronBlockingRulesEditor", () => {
|
|
|
612
612
|
});
|
|
613
613
|
|
|
614
614
|
it("disables all editing inputs and buttons when disabled prop is true", () => {
|
|
615
|
-
renderEditor(
|
|
615
|
+
renderEditor(
|
|
616
|
+
<PatronBlockingRulesEditor value={existingRules} disabled={true} />
|
|
617
|
+
);
|
|
616
618
|
|
|
617
619
|
// The Help button stays enabled even in disabled mode (read-only affordance).
|
|
618
620
|
const editingButtons = screen
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { render } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import PatronInfo from "../../../src/components/PatronInfo";
|
|
5
|
+
import { PatronData } from "../../../src/interfaces";
|
|
6
|
+
|
|
7
|
+
const patrons: PatronData[] = [
|
|
8
|
+
{
|
|
9
|
+
authorization_expires: "",
|
|
10
|
+
username: "User Name",
|
|
11
|
+
personal_name: "Personal Name",
|
|
12
|
+
email_address: "user@email.com",
|
|
13
|
+
authorization_identifier: "1234",
|
|
14
|
+
authorization_identifiers: [""],
|
|
15
|
+
block_reason: "",
|
|
16
|
+
external_type: "",
|
|
17
|
+
fines: "",
|
|
18
|
+
permanent_id: "",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
authorization_expires: "",
|
|
22
|
+
personal_name: "Personal Name2",
|
|
23
|
+
authorization_identifier: "5678",
|
|
24
|
+
authorization_identifiers: [""],
|
|
25
|
+
block_reason: "",
|
|
26
|
+
external_type: "",
|
|
27
|
+
fines: "",
|
|
28
|
+
permanent_id: "",
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
describe("PatronInfo", () => {
|
|
33
|
+
it("should display the patron's information", () => {
|
|
34
|
+
const { container } = render(<PatronInfo patron={patrons[0]} />);
|
|
35
|
+
|
|
36
|
+
const items = container.querySelectorAll("ul.patron-data-list > li");
|
|
37
|
+
expect(items).toHaveLength(4);
|
|
38
|
+
expect(items[0]).toHaveTextContent("Username:User Name");
|
|
39
|
+
expect(items[1]).toHaveTextContent("Personal Name:Personal Name");
|
|
40
|
+
expect(items[2]).toHaveTextContent("Email Address:user@email.com");
|
|
41
|
+
expect(items[3]).toHaveTextContent("Identifier:1234");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should update the patron's information if a new patron was searched and found", () => {
|
|
45
|
+
const { container, rerender } = render(<PatronInfo patron={patrons[0]} />);
|
|
46
|
+
|
|
47
|
+
rerender(<PatronInfo patron={patrons[1]} />);
|
|
48
|
+
|
|
49
|
+
const items = container.querySelectorAll("ul.patron-data-list > li");
|
|
50
|
+
expect(items).toHaveLength(2);
|
|
51
|
+
expect(items[0]).toHaveTextContent("Personal Name:Personal Name2");
|
|
52
|
+
expect(items[1]).toHaveTextContent("Identifier:5678");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -2,6 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { render, screen, fireEvent, act } from "@testing-library/react";
|
|
3
3
|
import userEvent from "@testing-library/user-event";
|
|
4
4
|
import ProtocolFormField from "../../../src/components/ProtocolFormField";
|
|
5
|
+
import { MONOSPACE_FONT_STACK } from "../../../src/fonts";
|
|
5
6
|
|
|
6
7
|
// NB: This file adds / duplicates existing tests from:
|
|
7
8
|
// - `src/components/__tests__/ProtocolFormField-test.tsx`.
|
|
@@ -64,6 +65,41 @@ describe("ProtocolFormField — json type", () => {
|
|
|
64
65
|
});
|
|
65
66
|
});
|
|
66
67
|
|
|
68
|
+
describe("ProtocolFormField — use_monospace_font", () => {
|
|
69
|
+
(
|
|
70
|
+
[
|
|
71
|
+
{ id: "text-input", use_monospace_font: true, hasStyle: true },
|
|
72
|
+
{
|
|
73
|
+
id: "textarea",
|
|
74
|
+
type: "textarea",
|
|
75
|
+
use_monospace_font: true,
|
|
76
|
+
hasStyle: true,
|
|
77
|
+
},
|
|
78
|
+
{ id: "absent", hasStyle: false },
|
|
79
|
+
] as {
|
|
80
|
+
id: string;
|
|
81
|
+
type?: string;
|
|
82
|
+
use_monospace_font?: boolean;
|
|
83
|
+
hasStyle: boolean;
|
|
84
|
+
}[]
|
|
85
|
+
).forEach(({ id, type, use_monospace_font, hasStyle }) => {
|
|
86
|
+
it(id, () => {
|
|
87
|
+
render(
|
|
88
|
+
<ProtocolFormField
|
|
89
|
+
setting={{ key: "f", label: "My Field", type, use_monospace_font }}
|
|
90
|
+
disabled={false}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
const el = screen.getByLabelText("My Field");
|
|
94
|
+
if (hasStyle) {
|
|
95
|
+
expect(el).toHaveStyle({ fontFamily: MONOSPACE_FONT_STACK });
|
|
96
|
+
} else {
|
|
97
|
+
expect(el).not.toHaveStyle({ fontFamily: MONOSPACE_FONT_STACK });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
67
103
|
describe("ProtocolFormField", () => {
|
|
68
104
|
it("renders date-picker setting", async () => {
|
|
69
105
|
const user = userEvent.setup();
|
|
@@ -0,0 +1,47 @@
|
|
|
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 SaveButton from "../../../src/components/SaveButton";
|
|
6
|
+
|
|
7
|
+
describe("SaveButton", () => {
|
|
8
|
+
it("says 'Save' when given no text", () => {
|
|
9
|
+
render(<SaveButton disabled={false} submit={jest.fn()} />);
|
|
10
|
+
|
|
11
|
+
expect(screen.getByRole("button", { name: "Save" })).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("displays the text it is given", () => {
|
|
15
|
+
render(
|
|
16
|
+
<SaveButton
|
|
17
|
+
disabled={false}
|
|
18
|
+
submit={jest.fn()}
|
|
19
|
+
text="some other string"
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
expect(
|
|
24
|
+
screen.getByRole("button", { name: "some other string" })
|
|
25
|
+
).toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("submits when clicked", async () => {
|
|
29
|
+
const user = userEvent.setup();
|
|
30
|
+
const submit = jest.fn();
|
|
31
|
+
render(<SaveButton disabled={false} submit={submit} />);
|
|
32
|
+
|
|
33
|
+
await user.click(screen.getByRole("button"));
|
|
34
|
+
|
|
35
|
+
expect(submit).toHaveBeenCalledTimes(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("does nothing when disabled", async () => {
|
|
39
|
+
const user = userEvent.setup();
|
|
40
|
+
const submit = jest.fn();
|
|
41
|
+
render(<SaveButton disabled={true} submit={submit} />);
|
|
42
|
+
|
|
43
|
+
await user.click(screen.getByRole("button"));
|
|
44
|
+
|
|
45
|
+
expect(submit).not.toHaveBeenCalled();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import SelfTestResult from "../../../src/components/SelfTestResult";
|
|
5
|
+
import { SelfTestsResult } from "../../../src/interfaces";
|
|
6
|
+
|
|
7
|
+
const stringResult: SelfTestsResult = {
|
|
8
|
+
duration: 0,
|
|
9
|
+
end: "",
|
|
10
|
+
name: "Test Result",
|
|
11
|
+
result: "abc",
|
|
12
|
+
start: "",
|
|
13
|
+
success: true,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
describe("SelfTestResult", () => {
|
|
17
|
+
it("displays a result with a string", () => {
|
|
18
|
+
const { container } = render(
|
|
19
|
+
<SelfTestResult result={stringResult} isFetching={false} />
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
expect(container.querySelector("li")).toHaveClass("success");
|
|
23
|
+
|
|
24
|
+
expect(screen.getByRole("heading", { level: 4 })).toHaveTextContent(
|
|
25
|
+
"Test Result"
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const testResult = container.querySelector("pre");
|
|
29
|
+
expect(testResult).toHaveClass("result-description");
|
|
30
|
+
expect(testResult).toHaveTextContent("abc");
|
|
31
|
+
|
|
32
|
+
expect(screen.getByText("success: true")).toHaveClass(
|
|
33
|
+
"success-description"
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// A single-line string result is not shown in a collapsible panel.
|
|
37
|
+
expect(container.querySelector(".panel")).not.toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("displays a result with a multi-line string", () => {
|
|
41
|
+
const longStringResult: SelfTestsResult = {
|
|
42
|
+
...stringResult,
|
|
43
|
+
result: "abc \n def",
|
|
44
|
+
};
|
|
45
|
+
const { container } = render(
|
|
46
|
+
<SelfTestResult result={longStringResult} isFetching={false} />
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const panel = container.querySelector(".panel");
|
|
50
|
+
expect(panel).toBeInTheDocument();
|
|
51
|
+
expect(panel).toHaveClass("panel-success");
|
|
52
|
+
expect(screen.getByText("Results")).toBeInTheDocument();
|
|
53
|
+
|
|
54
|
+
expect(container.querySelector("pre")?.textContent).toBe("abc \n def");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("displays a result with an array", () => {
|
|
58
|
+
const arrayResult: SelfTestsResult = {
|
|
59
|
+
...stringResult,
|
|
60
|
+
result: ["Thing 1", "Thing 2"],
|
|
61
|
+
};
|
|
62
|
+
const { container } = render(
|
|
63
|
+
<SelfTestResult result={arrayResult} isFetching={false} />
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const panel = container.querySelector(".panel");
|
|
67
|
+
expect(panel).toBeInTheDocument();
|
|
68
|
+
expect(panel).toHaveClass("panel-success");
|
|
69
|
+
expect(screen.getByText("Results (2)")).toBeInTheDocument();
|
|
70
|
+
|
|
71
|
+
const items = container.querySelectorAll("ol li");
|
|
72
|
+
expect(items).toHaveLength(2);
|
|
73
|
+
expect(items[0]).toHaveTextContent("Thing 1");
|
|
74
|
+
expect(items[1]).toHaveTextContent("Thing 2");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("displays a result with an empty array", () => {
|
|
78
|
+
const emptyArrayResult: SelfTestsResult = { ...stringResult, result: [] };
|
|
79
|
+
const { container } = render(
|
|
80
|
+
<SelfTestResult result={emptyArrayResult} isFetching={false} />
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(container.querySelector(".panel")).not.toBeInTheDocument();
|
|
84
|
+
expect(
|
|
85
|
+
screen.getByText("The test ran successfully, but no results were found.")
|
|
86
|
+
).toHaveClass("warning");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("displays a result with an exception", () => {
|
|
90
|
+
const exceptionResult: SelfTestsResult = {
|
|
91
|
+
...stringResult,
|
|
92
|
+
success: false,
|
|
93
|
+
exception: { class: "IntegrationException", message: "Problem!" },
|
|
94
|
+
};
|
|
95
|
+
const { container } = render(
|
|
96
|
+
<SelfTestResult result={exceptionResult} isFetching={false} />
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(container.querySelector("li")).toHaveClass("failure");
|
|
100
|
+
expect(screen.getByText("success: false")).toHaveClass(
|
|
101
|
+
"success-description"
|
|
102
|
+
);
|
|
103
|
+
expect(screen.getByText("exception: Problem!")).toHaveClass(
|
|
104
|
+
"exception-description"
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("displays debug_message when present", () => {
|
|
109
|
+
const exceptionResult: SelfTestsResult = {
|
|
110
|
+
...stringResult,
|
|
111
|
+
success: false,
|
|
112
|
+
exception: {
|
|
113
|
+
class: "IntegrationException",
|
|
114
|
+
debug_message: "debug message...",
|
|
115
|
+
message: "Problem!",
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
render(<SelfTestResult result={exceptionResult} isFetching={false} />);
|
|
119
|
+
|
|
120
|
+
expect(screen.getByText("debug: debug message...")).toHaveClass(
|
|
121
|
+
"debug-description"
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("does not display debug_message when it is empty", () => {
|
|
126
|
+
const exceptionResult: SelfTestsResult = {
|
|
127
|
+
...stringResult,
|
|
128
|
+
success: false,
|
|
129
|
+
exception: {
|
|
130
|
+
class: "IntegrationException",
|
|
131
|
+
debug_message: "",
|
|
132
|
+
message: "Problem!",
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
const { container } = render(
|
|
136
|
+
<SelfTestResult result={exceptionResult} isFetching={false} />
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(
|
|
140
|
+
container.querySelector(".exception-description")
|
|
141
|
+
).toBeInTheDocument();
|
|
142
|
+
expect(
|
|
143
|
+
container.querySelector(".debug-description")
|
|
144
|
+
).not.toBeInTheDocument();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("does not display debug_message when it is absent", () => {
|
|
148
|
+
const exceptionResult: SelfTestsResult = {
|
|
149
|
+
...stringResult,
|
|
150
|
+
success: false,
|
|
151
|
+
exception: { class: "IntegrationException", message: "Problem!" },
|
|
152
|
+
};
|
|
153
|
+
const { container } = render(
|
|
154
|
+
<SelfTestResult result={exceptionResult} isFetching={false} />
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
expect(
|
|
158
|
+
container.querySelector(".exception-description")
|
|
159
|
+
).toBeInTheDocument();
|
|
160
|
+
expect(
|
|
161
|
+
container.querySelector(".debug-description")
|
|
162
|
+
).not.toBeInTheDocument();
|
|
163
|
+
});
|
|
164
|
+
});
|