From 13c6a7f12913770cf0fbcbbae124ca4002818eff Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 8 Jul 2026 21:06:25 -0300 Subject: [PATCH 1/2] Rewrite the mount-based presentational tests with React Testing Library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 24 of the 28 mount-based enzyme suites: the AdvancedSearch filter widgets (rendered inside a real DndProvider — HTML5Backend works under jsdom), the small leaf components and buttons, the self-test and manage-patrons panels, the search/sidebar/services views, three of the tab containers, Footer, and the app bootstrap (index.tsx). The tab containers navigate via router.push rather than internal state, so "active tab" is asserted through the visible panel; where a tab panel fetches on mount and is incidental to a navigation test it is mocked to a marker. LibraryStats is deleted outright rather than ported — tests/jest/components/Stats.test.tsx already covers LibraryStats.tsx at 100% of lines and tests more (the query hook, caching, sysadmin gating). The bootstrap test keeps its structural shape: it spies on ReactDOM.render, constructs CirculationAdmin in each mode, and asserts the captured element tree contains SetupPage / a Router — the analog of the original enzyme find(), since what those roots render is covered by their own tests. Coverage rises to 94.48% (+24 lines over the baseline) with no previously- covered line lost; rendering real leaf children reaches branches the shallow and mount-with-stubbed-children tests did not. Deferred, with their enzyme originals retained: ConfigTabContainer and BookDetailsTabContainer (their full mounts are the sole cover for eleven connected panels' mapStateToProps/mapDispatchToProps wiring — that coverage is restored properly when those panels are migrated to render their connected exports in later waves) and AdvancedSearchBuilder (its one uncovered line is a react-dnd move callback reachable only through a real drop, grouped with the other drag-dependent coverage in the final wave). Mocking those panels now would have dropped 77 lines; the coverage gate caught it. 57 legacy suites remain. --- src/__tests__/index-test.ts | 33 -- .../AdvancedSearchBooleanFilter-test.tsx | 304 -------------- .../__tests__/AdvancedSearchFilter-test.tsx | 167 -------- .../AdvancedSearchFilterInput-test.tsx | 209 ---------- .../AdvancedSearchFilterViewer-test.tsx | 58 --- .../AdvancedSearchValueFilter-test.tsx | 144 ------- .../CirculationEventsDownloadForm-test.tsx | 35 -- .../__tests__/CustomListSearch-test.tsx | 393 ------------------ .../__tests__/CustomListsSidebar-test.tsx | 170 -------- .../DiagnosticsTabContainer-test.tsx | 160 ------- .../__tests__/ErrorMessage-test.tsx | 123 ------ src/components/__tests__/Footer-test.tsx | 149 ------- .../__tests__/LanguageField-test.tsx | 43 -- .../__tests__/LibraryStats-test.tsx | 281 ------------- src/components/__tests__/LoadButton-test.tsx | 46 -- .../__tests__/ManagePatronsForm-test.tsx | 151 ------- .../ManagePatronsTabContainer-test.tsx | 188 --------- .../__tests__/MetadataServices-test.tsx | 66 --- .../__tests__/SelfTestResult-test.tsx | 155 ------- .../__tests__/SelfTestsCategory-test.tsx | 186 --------- .../ServiceWithRegistrationsEditForm-test.tsx | 84 ---- .../__tests__/TabContainer-test.tsx | 79 ---- src/components/__tests__/Timestamp-test.tsx | 77 ---- .../__tests__/WithEditButton-test.tsx | 47 --- .../__tests__/WithRemoveButton-test.tsx | 69 --- .../AdvancedSearchBooleanFilter.test.tsx | 233 +++++++++++ .../components/AdvancedSearchBuilder.test.tsx | 38 -- .../components/AdvancedSearchFilter.test.tsx | 131 ++++++ .../AdvancedSearchFilterInput.test.tsx | 212 ++++++++++ .../AdvancedSearchFilterViewer.test.tsx | 52 +++ .../AdvancedSearchValueFilter.test.tsx | 123 ++++++ .../jest/components/CatalogServices.test.tsx | 45 +- .../CirculationEventsDownloadForm.test.tsx | 42 ++ .../jest/components/CustomListSearch.test.tsx | 275 ++++++++++++ .../components/CustomListsSidebar.test.tsx | 246 +++++++++++ .../DiagnosticsTabContainer.test.tsx | 147 +++++++ tests/jest/components/ErrorMessage.test.tsx | 97 +++++ tests/jest/components/Footer.test.tsx | 141 +++++++ tests/jest/components/LanguageField.test.tsx | 40 ++ tests/jest/components/LoadButton.test.tsx | 49 +++ .../components/ManagePatronsForm.test.tsx | 180 ++++++++ .../ManagePatronsTabContainer.test.tsx | 143 +++++++ .../jest/components/MetadataServices.test.tsx | 59 +++ tests/jest/components/SelfTestResult.test.tsx | 164 ++++++++ .../components/SelfTestsCategory.test.tsx | 200 +++++++++ .../ServiceWithRegistrationsEditForm.test.tsx | 110 +++++ tests/jest/components/TabContainer.test.tsx | 68 +++ tests/jest/components/Timestamp.test.tsx | 74 ++++ tests/jest/components/WithEditButton.test.tsx | 55 +++ .../jest/components/WithRemoveButton.test.tsx | 95 +++++ tests/jest/index.test.tsx | 50 +++ 51 files changed, 3004 insertions(+), 3482 deletions(-) delete mode 100644 src/__tests__/index-test.ts delete mode 100644 src/components/__tests__/AdvancedSearchBooleanFilter-test.tsx delete mode 100644 src/components/__tests__/AdvancedSearchFilter-test.tsx delete mode 100644 src/components/__tests__/AdvancedSearchFilterInput-test.tsx delete mode 100644 src/components/__tests__/AdvancedSearchFilterViewer-test.tsx delete mode 100644 src/components/__tests__/AdvancedSearchValueFilter-test.tsx delete mode 100644 src/components/__tests__/CirculationEventsDownloadForm-test.tsx delete mode 100644 src/components/__tests__/CustomListSearch-test.tsx delete mode 100644 src/components/__tests__/CustomListsSidebar-test.tsx delete mode 100644 src/components/__tests__/DiagnosticsTabContainer-test.tsx delete mode 100644 src/components/__tests__/ErrorMessage-test.tsx delete mode 100644 src/components/__tests__/Footer-test.tsx delete mode 100644 src/components/__tests__/LanguageField-test.tsx delete mode 100644 src/components/__tests__/LibraryStats-test.tsx delete mode 100644 src/components/__tests__/LoadButton-test.tsx delete mode 100644 src/components/__tests__/ManagePatronsForm-test.tsx delete mode 100644 src/components/__tests__/ManagePatronsTabContainer-test.tsx delete mode 100644 src/components/__tests__/MetadataServices-test.tsx delete mode 100644 src/components/__tests__/SelfTestResult-test.tsx delete mode 100644 src/components/__tests__/SelfTestsCategory-test.tsx delete mode 100644 src/components/__tests__/ServiceWithRegistrationsEditForm-test.tsx delete mode 100644 src/components/__tests__/TabContainer-test.tsx delete mode 100644 src/components/__tests__/Timestamp-test.tsx delete mode 100644 src/components/__tests__/WithEditButton-test.tsx delete mode 100644 src/components/__tests__/WithRemoveButton-test.tsx create mode 100644 tests/jest/components/AdvancedSearchBooleanFilter.test.tsx delete mode 100644 tests/jest/components/AdvancedSearchBuilder.test.tsx create mode 100644 tests/jest/components/AdvancedSearchFilter.test.tsx create mode 100644 tests/jest/components/AdvancedSearchFilterInput.test.tsx create mode 100644 tests/jest/components/AdvancedSearchFilterViewer.test.tsx create mode 100644 tests/jest/components/AdvancedSearchValueFilter.test.tsx rename src/components/__tests__/CatalogServices-test.tsx => tests/jest/components/CatalogServices.test.tsx (53%) create mode 100644 tests/jest/components/CirculationEventsDownloadForm.test.tsx create mode 100644 tests/jest/components/CustomListSearch.test.tsx create mode 100644 tests/jest/components/CustomListsSidebar.test.tsx create mode 100644 tests/jest/components/DiagnosticsTabContainer.test.tsx create mode 100644 tests/jest/components/ErrorMessage.test.tsx create mode 100644 tests/jest/components/Footer.test.tsx create mode 100644 tests/jest/components/LanguageField.test.tsx create mode 100644 tests/jest/components/LoadButton.test.tsx create mode 100644 tests/jest/components/ManagePatronsForm.test.tsx create mode 100644 tests/jest/components/ManagePatronsTabContainer.test.tsx create mode 100644 tests/jest/components/MetadataServices.test.tsx create mode 100644 tests/jest/components/SelfTestResult.test.tsx create mode 100644 tests/jest/components/SelfTestsCategory.test.tsx create mode 100644 tests/jest/components/ServiceWithRegistrationsEditForm.test.tsx create mode 100644 tests/jest/components/TabContainer.test.tsx create mode 100644 tests/jest/components/Timestamp.test.tsx create mode 100644 tests/jest/components/WithEditButton.test.tsx create mode 100644 tests/jest/components/WithRemoveButton.test.tsx create mode 100644 tests/jest/index.test.tsx diff --git a/src/__tests__/index-test.ts b/src/__tests__/index-test.ts deleted file mode 100644 index 648732d2ca..0000000000 --- a/src/__tests__/index-test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { expect } from "chai"; -import { spy } from "sinon"; - -import * as ReactDOM from "react-dom"; -import { mount } from "enzyme"; - -const CirculationAdmin = require("../index"); -import SetupPage from "../components/SetupPage"; -import { Router } from "react-router"; - -describe("CirculationAdmin", () => { - it("renders Setup", () => { - const renderSpy = spy(ReactDOM, "render"); - new CirculationAdmin({ settingUp: true }); - expect(renderSpy.callCount).to.equal(1); - const component = renderSpy.args[0][0]; - const wrapper = mount(component); - const setup = wrapper.find(SetupPage); - expect(setup.length).to.equal(1); - renderSpy.restore(); - }); - - it("renders Router", () => { - const renderSpy = spy(ReactDOM, "render"); - new CirculationAdmin({}); - expect(renderSpy.callCount).to.equal(1); - const component = renderSpy.args[0][0]; - const wrapper = mount(component); - const router = wrapper.find(Router); - expect(router.length).to.equal(1); - renderSpy.restore(); - }); -}); diff --git a/src/components/__tests__/AdvancedSearchBooleanFilter-test.tsx b/src/components/__tests__/AdvancedSearchBooleanFilter-test.tsx deleted file mode 100644 index 91a005b5e3..0000000000 --- a/src/components/__tests__/AdvancedSearchBooleanFilter-test.tsx +++ /dev/null @@ -1,304 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; -import * as React from "react"; -import { mount } from "enzyme"; -import { DndProvider } from "react-dnd"; -import { HTML5Backend } from "react-dnd-html5-backend"; -import AdvancedSearchFilter from "../AdvancedSearchFilter"; -import AdvancedSearchBooleanFilter from "../AdvancedSearchBooleanFilter"; - -describe("AdvancedSearchBooleanFilter", () => { - let wrapper; - let onBooleanChange; - let onMove; - let onRemove; - let onSelect; - let query; - - beforeEach(() => { - onBooleanChange = stub(); - onMove = stub(); - onRemove = stub(); - onSelect = stub(); - - query = { - id: "90", - and: [ - { - id: "91", - key: "title", - value: "foo", - }, - { - id: "92", - key: "title", - value: "bar", - }, - ], - }; - - wrapper = mount( - - - - ); - }); - - it("should render a select containing an option for each boolean operator, with the current operator selected", () => { - const operatorOptions = wrapper.find( - ".advanced-search-boolean-filter > header select > option" - ); - - expect(operatorOptions).to.have.length(2); - - expect(operatorOptions.at(0).prop("value")).to.equal("and"); - expect(operatorOptions.at(0).text()).to.equal( - "All of these filters must be matched:" - ); - expect(operatorOptions.at(0).getDOMNode().selected).to.equal(true); - - expect(operatorOptions.at(1).prop("value")).to.equal("or"); - expect(operatorOptions.at(1).text()).to.equal( - "Any of these filters may be matched:" - ); - expect(operatorOptions.at(1).getDOMNode().selected).to.equal(false); - }); - - it("should disable the boolean operator select when readOnly is true", () => { - wrapper = mount( - - - - ); - - const operatorSelect = wrapper.find( - ".advanced-search-boolean-filter > header select" - ); - - expect(operatorSelect.prop("disabled")).to.equal(true); - }); - - it("should call onBooleanChange when the boolean operator changes", () => { - const operatorSelect = wrapper.find( - ".advanced-search-boolean-filter > header select" - ); - - operatorSelect.getDOMNode().value = "or"; - operatorSelect.simulate("change"); - - expect(onBooleanChange.callCount).to.equal(1); - expect(onBooleanChange.args[0]).to.deep.equal(["90", "or"]); - }); - - it("should render a remove button", () => { - const button = wrapper.find( - ".advanced-search-boolean-filter > header button" - ); - - expect(button).to.have.length(1); - expect(button.text()).to.equal("×"); - }); - - it("should not render a remove button when readOnly is true", () => { - wrapper = mount( - - - - ); - - const button = wrapper.find( - ".advanced-search-boolean-filter > header button" - ); - - expect(button).to.have.length(0); - }); - - it("should call onRemove when the remove button is clicked", () => { - const button = wrapper.find( - ".advanced-search-boolean-filter > header button" - ); - - button.simulate("click"); - - expect(onRemove.callCount).to.equal(1); - expect(onRemove.args[0]).to.deep.equal(["90"]); - }); - - it("should call onSelect when clicked", () => { - const filter = wrapper.find(".advanced-search-boolean-filter"); - - filter.simulate("click"); - - expect(onSelect.callCount).to.equal(1); - expect(onSelect.args[0]).to.deep.equal(["90"]); - }); - - it("should call onSelect when the space bar is depressed", () => { - const filter = wrapper.find(".advanced-search-boolean-filter"); - - filter.simulate("keydown", { key: " " }); - - expect(onSelect.callCount).to.equal(1); - expect(onSelect.args[0]).to.deep.equal(["90"]); - }); - - it("should render an AdvancedSearchFilter for each child", () => { - const list = wrapper.find(".advanced-search-boolean-filter > ul"); - const filters = list.find(AdvancedSearchFilter); - - expect(filters).to.have.length(2); - - const filter1 = filters.at(0); - - expect(filter1.prop("query")).to.deep.equal(query.and[0]); - expect(filter1.prop("selectedQueryId")).to.equal("91"); - expect(filter1.prop("onBooleanChange")).to.equal(onBooleanChange); - expect(filter1.prop("onMove")).to.equal(onMove); - expect(filter1.prop("onSelect")).to.equal(onSelect); - expect(filter1.prop("onRemove")).to.equal(onRemove); - - const filter2 = filters.at(1); - - expect(filter2.prop("query")).to.deep.equal(query.and[1]); - expect(filter2.prop("selectedQueryId")).to.equal("91"); - expect(filter2.prop("onBooleanChange")).to.equal(onBooleanChange); - expect(filter2.prop("onMove")).to.equal(onMove); - expect(filter2.prop("onSelect")).to.equal(onSelect); - expect(filter2.prop("onRemove")).to.equal(onRemove); - }); - - it("should propagate the readOnly prop to AdvancedSearchFilter children", () => { - wrapper = mount( - - - - ); - - const list = wrapper.find(".advanced-search-boolean-filter > ul"); - const filters = list.find(AdvancedSearchFilter); - - expect(filters).to.have.length(2); - - expect(filters.at(0).prop("readOnly")).to.equal(true); - expect(filters.at(1).prop("readOnly")).to.equal(true); - }); - - it('should separate child filters with "and" when the boolean operator is AND', () => { - const separators = wrapper.find( - ".advanced-search-boolean-filter > ul > li > span" - ); - - expect(separators).to.have.length(1); - - expect(separators.at(0).text()).to.equal("and"); - }); - - it('should separate child filters with "or" when the boolean operator is OR', () => { - query = { - id: "90", - or: [ - { - id: "91", - key: "title", - value: "foo", - }, - { - id: "92", - key: "title", - value: "bar", - }, - ], - }; - - wrapper = mount( - - - - ); - - const separators = wrapper.find( - ".advanced-search-boolean-filter > ul > li > span" - ); - - expect(separators).to.have.length(1); - - expect(separators.at(0).text()).to.equal("or"); - }); - - it("should apply the selected class when the query id equals the selectedQueryId", () => { - wrapper = mount( - - - - ); - - expect( - wrapper.find(".advanced-search-boolean-filter").hasClass("selected") - ).to.equal(true); - }); - - it("should not apply the selected class when readOnly is true", () => { - wrapper = mount( - - - - ); - - expect( - wrapper.find(".advanced-search-boolean-filter").hasClass("selected") - ).to.equal(false); - }); -}); diff --git a/src/components/__tests__/AdvancedSearchFilter-test.tsx b/src/components/__tests__/AdvancedSearchFilter-test.tsx deleted file mode 100644 index 99870b1afc..0000000000 --- a/src/components/__tests__/AdvancedSearchFilter-test.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; -import * as React from "react"; -import { mount } from "enzyme"; -import { DndProvider } from "react-dnd"; -import { HTML5Backend } from "react-dnd-html5-backend"; -import AdvancedSearchBooleanFilter from "../AdvancedSearchBooleanFilter"; -import AdvancedSearchValueFilter from "../AdvancedSearchValueFilter"; -import AdvancedSearchFilter from "../AdvancedSearchFilter"; - -describe("AdvancedSearchFilter", () => { - let onBooleanChange; - let onMove; - let onSelect; - let onRemove; - - beforeEach(() => { - onBooleanChange = stub(); - onMove = stub(); - onSelect = stub(); - onRemove = stub(); - }); - - it("should render an AdvancedSearchBooleanFilter if the query is a boolean AND", () => { - const query = { - id: "90", - and: [ - { - id: "91", - key: "title", - value: "foo", - }, - { - id: "92", - key: "title", - value: "bar", - }, - ], - }; - - const wrapper = mount( - - - - ); - - const filter = wrapper.find(AdvancedSearchBooleanFilter); - - expect(filter.length).to.equal(1); - - expect(filter.prop("query")).to.equal(query); - expect(filter.prop("selectedQueryId")).to.equal("90"); - expect(filter.prop("onBooleanChange")).to.equal(onBooleanChange); - expect(filter.prop("onMove")).to.equal(onMove); - expect(filter.prop("onSelect")).to.equal(onSelect); - expect(filter.prop("onRemove")).to.equal(onRemove); - }); - - it("should render an AdvancedSearchBooleanFilter if the query is a boolean OR", () => { - const query = { - id: "90", - or: [ - { - id: "91", - key: "title", - value: "foo", - }, - { - id: "92", - key: "title", - value: "bar", - }, - ], - }; - - const wrapper = mount( - - - - ); - - const filter = wrapper.find(AdvancedSearchBooleanFilter); - - expect(filter.length).to.equal(1); - - expect(filter.prop("query")).to.equal(query); - expect(filter.prop("readOnly")).to.equal(true); - expect(filter.prop("selectedQueryId")).to.equal("90"); - expect(filter.prop("onBooleanChange")).to.equal(onBooleanChange); - expect(filter.prop("onMove")).to.equal(onMove); - expect(filter.prop("onSelect")).to.equal(onSelect); - expect(filter.prop("onRemove")).to.equal(onRemove); - }); - - it("should render an AdvancedSearchValueFilter if the query is a not a boolean", () => { - const query = { - id: "91", - key: "title", - value: "foo", - }; - - const wrapper = mount( - - - - ); - - const filter = wrapper.find(AdvancedSearchValueFilter); - - expect(filter.length).to.equal(1); - - expect(filter.prop("query")).to.equal(query); - expect(filter.prop("readOnly")).to.equal(true); - expect(filter.prop("selected")).to.equal(false); - expect(filter.prop("onMove")).to.equal(onMove); - expect(filter.prop("onSelect")).to.equal(onSelect); - expect(filter.prop("onRemove")).to.equal(onRemove); - }); - - it("should select the AdvancedSearchValueFilter if the query is a not a boolean and selectedQueryId is the id of the query", () => { - const query = { - id: "91", - key: "title", - value: "foo", - }; - - const wrapper = mount( - - - - ); - - const filter = wrapper.find(AdvancedSearchValueFilter); - - expect(filter.prop("selected")).to.equal(true); - }); -}); diff --git a/src/components/__tests__/AdvancedSearchFilterInput-test.tsx b/src/components/__tests__/AdvancedSearchFilterInput-test.tsx deleted file mode 100644 index 9dc82869d9..0000000000 --- a/src/components/__tests__/AdvancedSearchFilterInput-test.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; -import * as React from "react"; -import { mount } from "enzyme"; -import { fields, operators } from "../AdvancedSearchBuilder"; -import AdvancedSearchFilterInput from "../AdvancedSearchFilterInput"; - -describe("AdvancedSearchFilterInput", () => { - let wrapper; - let onAdd; - let onClearFiltersFlagChange; - - beforeEach(() => { - onAdd = stub(); - onClearFiltersFlagChange = stub(); - - wrapper = mount( - - ); - }); - - it("should render select with field names for field selection", () => { - const fieldControl = wrapper.find(".filter-key select > option"); - - expect(fieldControl).to.have.length(fields.length); - - fields.forEach((field, index) => { - expect(fieldControl.at(index).prop("value")).to.equal(field.value); - }); - }); - - it("should render a select with options for operator selection", () => { - const options = wrapper.find(".filter-operator select > option"); - - expect(options).to.have.length(operators.length); - - operators.forEach((op, index) => { - expect(options.at(index).prop("value")).to.equal(op.value); - }); - }); - - it("should restrict the operator selection, if the filter requests it", () => { - // Select the fiction field key. - const filterKeyControl = wrapper.find(".filter-key select"); - filterKeyControl.getDOMNode().value = "fiction"; - filterKeyControl.simulate("change"); - - // Look up the fiction field operator values. - const options = wrapper.find(".filter-operator select > option"); - const fictionField = fields.find((element) => { - return element.value === "fiction"; - }); - - // And make sure the number of options is correct. - expect(options).to.have.length(fictionField.operators.length); - options.forEach((option, index) => { - expect(option.prop("value")).to.equal(fictionField.operators[index]); - }); - }); - - it("should use the currently selected operator when changing filters", () => { - const filterKeyControl = wrapper.find(".filter-key select"); - const filterKeyDomNode = filterKeyControl.getDOMNode(); - - const chooseFilterKey = (value: string) => { - filterKeyDomNode.value = value; - filterKeyControl.simulate("change"); - }; - - const fictionField = fields.find((element) => { - return element.value === "fiction"; - }); - - const operatorOptions = wrapper.find(".filter-operator select"); - operatorOptions.getDOMNode().value = "regex"; - operatorOptions.simulate("change"); - - // Select data source/Distributor as the filter key. - chooseFilterKey("data_source"); - expect(operatorOptions.getDOMNode().value).to.equal("regex"); - - // But the "regex" operator is not supported by the "fiction" field`, - // so we switch to fiction's first available operator. - chooseFilterKey("fiction"); - expect(operatorOptions.getDOMNode().value).to.equal( - fictionField.operators[0] - ); - - // Since data source/distributor supports fiction's first operator, - // we'll continue to use it when changing back to that field. - chooseFilterKey("data_source"); - expect(operatorOptions.getDOMNode().value).to.equal( - fictionField.operators[0] - ); - }); - - it("should render a text input for value entry", () => { - const valueSelect = wrapper.find(".filter-value select"); - const valueInput = wrapper.find('.filter-value input[type="text"]'); - - expect(valueSelect).to.have.length(0); - expect(valueInput).to.have.length(1); - }); - - it("should render a select for value selection, if the filter requests it", () => { - const filterKeyControl = wrapper.find(".filter-key select"); - const filterKeyDomNode = filterKeyControl.getDOMNode(); - - const chooseFilterKey = (value: string) => { - filterKeyDomNode.value = value; - filterKeyControl.simulate("change"); - }; - - chooseFilterKey("fiction"); - - const valueSelect = wrapper.find(".filter-value select"); - const valueInput = wrapper.find('.filter-value input[type="text"]'); - - expect(valueSelect).to.have.length(1); - expect(valueInput).to.have.length(0); - }); - - it("should clear the current value when changing the filter", () => { - const filterKeyControl = wrapper.find(".filter-key select"); - const filterKeyDomNode = filterKeyControl.getDOMNode(); - - const chooseFilterKey = (value: string) => { - filterKeyDomNode.value = value; - filterKeyControl.simulate("change"); - }; - - const valueInput = wrapper.find(".filter-value input"); - - valueInput.getDOMNode().value = "ABC"; - valueInput.simulate("change"); - - chooseFilterKey("data_source"); - - expect(valueInput.getDOMNode().value).to.equal(""); - }); - - it("should render an Add Filter button", () => { - const button = wrapper.find("button"); - - expect(button).to.have.length(1); - expect(button.text()).to.equal("Add filter"); - }); - - it("should disable the Add Filter button when the value is blank", () => { - const valueInput = wrapper.find('input[type="text"]'); - const button = wrapper.find("button"); - - expect(button.getDOMNode().disabled).to.equal(true); - - valueInput.getDOMNode().value = " "; - valueInput.simulate("change"); - - expect(button.getDOMNode().disabled).to.equal(true); - - valueInput.getDOMNode().value = "hello"; - valueInput.simulate("change"); - - expect(button.getDOMNode().disabled).to.equal(false); - }); - - it("should call onAdd when the Add Filter button is clicked", () => { - const valueInput = wrapper.find('input[type="text"]'); - const button = wrapper.find("button"); - - valueInput.getDOMNode().value = "hello"; - valueInput.simulate("change"); - - button.simulate("click"); - - expect(onAdd.callCount).to.equal(1); - - expect(onAdd.args[0]).to.deep.equal([ - { - key: "genre", - op: "eq", - value: "hello", - }, - ]); - }); - - it("should trim whitespace from the value", () => { - const valueInput = wrapper.find('input[type="text"]'); - const button = wrapper.find("button"); - - valueInput.getDOMNode().value = "\thello "; - valueInput.simulate("change"); - - button.simulate("click"); - - expect(onAdd.callCount).to.equal(1); - - expect(onAdd.args[0]).to.deep.equal([ - { - key: "genre", - op: "eq", - value: "hello", - }, - ]); - }); -}); diff --git a/src/components/__tests__/AdvancedSearchFilterViewer-test.tsx b/src/components/__tests__/AdvancedSearchFilterViewer-test.tsx deleted file mode 100644 index eed0978a0f..0000000000 --- a/src/components/__tests__/AdvancedSearchFilterViewer-test.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; -import * as React from "react"; -import { mount } from "enzyme"; -import { DndProvider } from "react-dnd"; -import { HTML5Backend } from "react-dnd-html5-backend"; -import AdvancedSearchFilter from "../AdvancedSearchFilter"; -import AdvancedSearchFilterViewer from "../AdvancedSearchFilterViewer"; - -describe("AdvancedSearchFilterViewer", () => { - let wrapper; - let onBooleanChange; - let onMove; - let onSelect; - let onRemove; - let query; - - beforeEach(() => { - onBooleanChange = stub(); - onMove = stub(); - onSelect = stub(); - onRemove = stub(); - - query = { - id: "0", - key: "genre", - value: "Horror", - }; - - wrapper = mount( - - - - ); - }); - - it("should render an AdvancedSearchFilter", () => { - const filter = wrapper.find(AdvancedSearchFilter); - - expect(filter.length).to.equal(1); - - expect(filter.prop("query")).to.equal(query); - expect(filter.prop("readOnly")).to.equal(false); - expect(filter.prop("selectedQueryId")).to.equal("0"); - expect(filter.prop("onBooleanChange")).to.equal(onBooleanChange); - expect(filter.prop("onMove")).to.equal(onMove); - expect(filter.prop("onSelect")).to.equal(onSelect); - expect(filter.prop("onRemove")).to.equal(onRemove); - }); -}); diff --git a/src/components/__tests__/AdvancedSearchValueFilter-test.tsx b/src/components/__tests__/AdvancedSearchValueFilter-test.tsx deleted file mode 100644 index 14dbc68fd8..0000000000 --- a/src/components/__tests__/AdvancedSearchValueFilter-test.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; -import * as React from "react"; -import { mount } from "enzyme"; -import { DndProvider } from "react-dnd"; -import { HTML5Backend } from "react-dnd-html5-backend"; -import AdvancedSearchValueFilter from "../AdvancedSearchValueFilter"; -import { fields, operators } from "../AdvancedSearchBuilder"; - -describe("AdvancedSearchValueFilter", () => { - let wrapper; - let onMove; - let onRemove; - let onSelect; - let query; - - beforeEach(() => { - onMove = stub(); - onRemove = stub(); - onSelect = stub(); - - query = { - id: "91", - key: "title", - op: "eq", - value: "foo", - }; - - wrapper = mount( - - - - ); - }); - - it("should render the field label, operator symbol, and value of the query", () => { - const filterLabel = wrapper.find(".advanced-search-value-filter > span"); - - const expectedField = `${ - fields.find((field) => field.value === "title")?.label - }`; - const expectedOperator = `${ - operators.find((op) => op.value === "eq")?.symbol - }`; - const filterLabelText = `${expectedField} ${expectedOperator} foo`; - - expect(filterLabel).to.have.length(1); - expect(filterLabel.text()).to.equal(filterLabelText); - }); - - it("should apply the selected class when selected is true", () => { - wrapper = mount( - - - - ); - - expect( - wrapper.find(".advanced-search-value-filter").hasClass("selected") - ).to.equal(true); - }); - - it("should not apply the selected class when readOnly is true", () => { - wrapper = mount( - - - - ); - - expect( - wrapper.find(".advanced-search-value-filter").hasClass("selected") - ).to.equal(false); - }); - - it("should render a remove button", () => { - const button = wrapper.find(".advanced-search-value-filter > button"); - - expect(button).to.have.length(1); - expect(button.text()).to.equal("×"); - }); - - it("should not render a remove button when readOnly is true", () => { - wrapper = mount( - - - - ); - - const button = wrapper.find(".advanced-search-value-filter > button"); - - expect(button).to.have.length(0); - }); - - it("should call onRemove when the remove button is clicked", () => { - const button = wrapper.find(".advanced-search-value-filter > button"); - - button.simulate("click"); - - expect(onRemove.callCount).to.equal(1); - expect(onRemove.args[0]).to.deep.equal(["91"]); - }); - - it("should call onSelect when clicked", () => { - const filter = wrapper.find(".advanced-search-value-filter"); - - filter.simulate("click"); - - expect(onSelect.callCount).to.equal(1); - expect(onSelect.args[0]).to.deep.equal(["91"]); - }); - - it("should call onSelect when the space bar is depressed", () => { - const filter = wrapper.find(".advanced-search-value-filter"); - - filter.simulate("keydown", { key: " " }); - - expect(onSelect.callCount).to.equal(1); - expect(onSelect.args[0]).to.deep.equal(["91"]); - }); -}); diff --git a/src/components/__tests__/CirculationEventsDownloadForm-test.tsx b/src/components/__tests__/CirculationEventsDownloadForm-test.tsx deleted file mode 100644 index 7e0b9f5b60..0000000000 --- a/src/components/__tests__/CirculationEventsDownloadForm-test.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; -import { Button } from "library-simplified-reusable-components"; -import * as React from "react"; -import { mount } from "enzyme"; - -import CirculationEventsDownloadForm from "../CirculationEventsDownloadForm"; - -describe("CirculationEventsDownloadForm", () => { - let wrapper; - let hide; - - beforeEach(() => { - hide = stub(); - wrapper = mount(); - }); - - it("renders start date and end date inputs", () => { - const dates = wrapper.find("input[type='date']"); - expect(dates.length).to.equal(2); - expect(dates.at(0).props().name).to.equal("dateStart"); - expect(dates.at(1).props().name).to.equal("dateEnd"); - }); - - it("renders download and close buttons", () => { - const buttons = wrapper.find(Button); - expect(buttons.length).to.equal(2); - expect(buttons.at(0).prop("content")).to.equal("Download"); - expect(buttons.at(1).prop("content")).to.equal("Close"); - - buttons.at(1).simulate("click"); - - expect(hide.calledOnce).to.be.true; - }); -}); diff --git a/src/components/__tests__/CustomListSearch-test.tsx b/src/components/__tests__/CustomListSearch-test.tsx deleted file mode 100644 index 4eda4fc7db..0000000000 --- a/src/components/__tests__/CustomListSearch-test.tsx +++ /dev/null @@ -1,393 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; -import * as React from "react"; -import { mount } from "enzyme"; -import CustomListSearch from "../CustomListSearch"; - -describe("CustomListSearch", () => { - let wrapper; - let addAdvSearchQuery; - let search; - let updateAutoUpdate; - let updateSearchParam; - - const library = { - uuid: "uuid", - name: "name", - short_name: "short_name", - settings: { - large_collections: ["eng", "fre", "spa"], - }, - }; - - const entryPoints = ["All", "Book", "Audio"]; - - const searchParams = { - entryPoint: "all", - terms: "foo bar", - sort: "title", - language: "English", - advanced: { - include: { - query: null, - selectedQueryId: null, - clearFilters: null, - }, - exclude: { - query: null, - selectedQueryId: null, - clearFilters: null, - }, - }, - }; - - const languages = { - eng: ["English"], - spa: ["Spanish", "Castilian"], - fre: ["French"], - }; - - beforeEach(() => { - addAdvSearchQuery = stub(); - search = stub(); - updateAutoUpdate = stub(); - updateSearchParam = stub(); - - wrapper = mount( - - ); - }); - - it("renders a radio button for each entry point", () => { - const entryPointOptions = wrapper.find(".entry-points").find(".form-group"); - - expect(entryPointOptions.length).to.equal(3); - - const all = entryPointOptions.at(0); - - expect(all.text()).to.equal("All"); - - const allRadio = all.find("input"); - - expect(allRadio.props().type).to.equal("radio"); - expect(allRadio.props().name).to.equal("entry-points-selection"); - expect(allRadio.props().value).to.equal("All"); - expect(allRadio.props().disabled).to.equal(false); - expect(allRadio.props().checked).to.be.true; - - const book = entryPointOptions.at(1); - - expect(book.text()).to.equal("Book"); - - const bookRadio = book.find("input"); - - expect(bookRadio.props().type).to.equal("radio"); - expect(bookRadio.props().name).to.equal("entry-points-selection"); - expect(bookRadio.props().value).to.equal("Book"); - expect(bookRadio.props().disabled).to.equal(false); - expect(bookRadio.props().checked).to.be.false; - - const audio = entryPointOptions.at(2); - - expect(audio.text()).to.equal("Audio"); - - const audioRadio = audio.find("input"); - - expect(audioRadio.props().type).to.equal("radio"); - expect(audioRadio.props().name).to.equal("entry-points-selection"); - expect(audioRadio.props().value).to.equal("Audio"); - expect(audioRadio.props().disabled).to.equal(false); - expect(audioRadio.props().checked).to.be.false; - }); - - it("disables the entry point radio buttons when isOwner is false", () => { - wrapper.setProps({ - isOwner: false, - }); - - const entryPointOptions = wrapper.find(".entry-points").find(".form-group"); - - const all = entryPointOptions.at(0); - const allRadio = all.find("input"); - - expect(allRadio.props().disabled).to.be.true; - - const book = entryPointOptions.at(1); - const bookRadio = book.find("input"); - - expect(bookRadio.props().disabled).to.be.true; - - const audio = entryPointOptions.at(2); - const audioRadio = audio.find("input"); - - expect(audioRadio.props().disabled).to.be.true; - }); - - it("calls updateSearchParam when an entry point radio button is changed", () => { - const sortOptions = wrapper.find(".entry-points").find(".form-group"); - const audioRadio = sortOptions.at(2).find("input"); - - audioRadio.simulate("change"); - - expect(updateSearchParam.callCount).to.equal(1); - expect(updateSearchParam.args[0]).to.deep.equal(["entryPoint", "Audio"]); - }); - - it("renders a radio button for each sort option", () => { - const sortOptions = wrapper.find(".search-options").find(".form-group"); - - expect(sortOptions.length).to.equal(3); - - const relevance = sortOptions.at(0); - - expect(relevance.text()).to.equal("Relevance"); - - const relevanceRadio = relevance.find("input"); - - expect(relevanceRadio.props().type).to.equal("radio"); - expect(relevanceRadio.props().name).to.equal("sort-selection"); - expect(relevanceRadio.props().value).to.equal(""); - expect(relevanceRadio.props().disabled).to.be.false; - expect(relevanceRadio.props().checked).to.be.false; - - const title = sortOptions.at(1); - - expect(title.text()).to.equal("Title"); - - const titleRadio = title.find("input"); - - expect(titleRadio.props().type).to.equal("radio"); - expect(titleRadio.props().name).to.equal("sort-selection"); - expect(titleRadio.props().value).to.equal("title"); - expect(titleRadio.props().disabled).to.be.false; - expect(titleRadio.props().checked).to.be.true; - - const author = sortOptions.at(2); - - expect(author.text()).to.equal("Author"); - - const authorRadio = author.find("input"); - - expect(authorRadio.props().type).to.equal("radio"); - expect(authorRadio.props().name).to.equal("sort-selection"); - expect(authorRadio.props().value).to.equal("author"); - expect(authorRadio.props().disabled).to.be.false; - expect(authorRadio.props().checked).to.be.false; - }); - - it("disables the sort option radio buttons when isOwner is false", () => { - wrapper.setProps({ - isOwner: false, - }); - - const sortOptions = wrapper.find(".search-options").find(".form-group"); - - const relevance = sortOptions.at(0); - const relevanceRadio = relevance.find("input"); - - expect(relevanceRadio.props().disabled).to.be.true; - - const title = sortOptions.at(1); - const titleRadio = title.find("input"); - - expect(titleRadio.props().disabled).to.be.true; - - const author = sortOptions.at(2); - const authorRadio = author.find("input"); - - expect(authorRadio.props().disabled).to.be.true; - }); - - it("calls updateSearchParam when a sort radio button is changed", () => { - const sortOptions = wrapper.find(".search-options").find(".form-group"); - const authorRadio = sortOptions.at(2).find("input"); - - authorRadio.simulate("change"); - - expect(updateSearchParam.callCount).to.equal(1); - expect(updateSearchParam.args[0]).to.deep.equal(["sort", "author"]); - }); - - it("calls addAdvSearchQuery and search when mounted if there is a startingTitle", () => { - wrapper = mount( - - ); - - expect(addAdvSearchQuery.callCount).to.equal(1); - - expect(addAdvSearchQuery.args[0]).to.deep.equal([ - "include", - { - key: "title", - op: "eq", - value: "test", - }, - ]); - - expect(search.callCount).to.equal(1); - }); - - describe("when showAutoUpdate is false", () => { - it("does not render radio buttons for auto update on and off", () => { - const autoUpdateOptions = wrapper - .find(".auto-update") - .find(".form-group"); - - expect(autoUpdateOptions.length).to.equal(0); - }); - }); - - describe("when showAutoUpdate is true", () => { - it("renders radio buttons for auto update on and off", () => { - wrapper = mount( - - ); - - const autoUpdateOptions = wrapper - .find(".auto-update") - .find(".form-group"); - - expect(autoUpdateOptions.length).to.equal(2); - - const autoUpdateOn = autoUpdateOptions.at(0); - - expect(autoUpdateOn.text()).to.equal("Automatically update this list"); - - const autoUpdateOnRadio = autoUpdateOn.find("input"); - - expect(autoUpdateOnRadio.props().name).to.equal("auto-update"); - expect(autoUpdateOnRadio.props().disabled).to.be.false; - - const autoUpdateOff = autoUpdateOptions.at(1); - - expect(autoUpdateOff.text()).to.equal("Manually select titles"); - - const autoUpdateOffRadio = autoUpdateOff.find("input"); - - expect(autoUpdateOffRadio.props().name).to.equal("auto-update"); - expect(autoUpdateOffRadio.props().disabled).to.be.false; - }); - - it("disables the radio buttons for auto update when isOwner is false", () => { - wrapper = mount( - - ); - - const autoUpdateOptions = wrapper - .find(".auto-update") - .find(".form-group"); - - const autoUpdateOn = autoUpdateOptions.at(0); - const autoUpdateOnRadio = autoUpdateOn.find("input"); - - expect(autoUpdateOnRadio.props().disabled).to.be.true; - - const autoUpdateOff = autoUpdateOptions.at(1); - const autoUpdateOffRadio = autoUpdateOff.find("input"); - - expect(autoUpdateOffRadio.props().disabled).to.be.true; - }); - - it("disables the radio buttons for auto update when listId is not null", () => { - wrapper = mount( - - ); - - const autoUpdateOptions = wrapper - .find(".auto-update") - .find(".form-group"); - - const autoUpdateOn = autoUpdateOptions.at(0); - const autoUpdateOnRadio = autoUpdateOn.find("input"); - - expect(autoUpdateOnRadio.props().disabled).to.be.true; - - const autoUpdateOff = autoUpdateOptions.at(1); - const autoUpdateOffRadio = autoUpdateOff.find("input"); - - expect(autoUpdateOffRadio.props().disabled).to.be.true; - }); - - it("calls updateAutoUpdate when an auto update radio button is changed", () => { - wrapper = mount( - - ); - - const autoUpdateOptions = wrapper - .find(".auto-update") - .find(".form-group input"); - - const autoUpdateOnRadio = autoUpdateOptions.at(0); - const autoUpdateOffRadio = autoUpdateOptions.at(1); - - autoUpdateOffRadio.simulate("change"); - autoUpdateOnRadio.simulate("change"); - - expect(updateAutoUpdate.callCount).to.equal(2); - expect(updateAutoUpdate.args[0]).to.deep.equal([false]); - expect(updateAutoUpdate.args[1]).to.deep.equal([true]); - }); - }); -}); diff --git a/src/components/__tests__/CustomListsSidebar-test.tsx b/src/components/__tests__/CustomListsSidebar-test.tsx deleted file mode 100644 index 43adee85f1..0000000000 --- a/src/components/__tests__/CustomListsSidebar-test.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; - -import * as React from "react"; -import * as Enzyme from "enzyme"; - -import CustomListsSidebar from "../CustomListsSidebar"; -import { Link } from "react-router"; -import { Button } from "library-simplified-reusable-components"; - -describe("CustomListsSidebar", () => { - let wrapper: Enzyme.CommonWrapper; - let lists; - const deleteCustomList = stub(); - const changeSort = stub(); - - beforeEach(() => { - lists = [ - { id: 1, name: "First List", entry_count: 5, is_owner: true }, - { id: 2, name: "Second List", entry_count: 10, is_owner: true }, - ]; - wrapper = Enzyme.mount( - - ); - }); - - it("renders a sidebar with a header and a create button", () => { - expect(wrapper.render().hasClass("custom-lists-sidebar")).to.be.true; - expect(wrapper.find("h2").text()).to.equal("List Manager"); - const createButton = wrapper.find(Link).at(0); - expect(createButton.text()).to.equal("Create New List"); - expect(createButton.prop("to")).to.equal( - "/admin/web/lists/library_name/create" - ); - }); - - it("renders filter select", () => { - const select = wrapper.find('select[name="filter"]'); - - expect(select.prop("value")).to.equal("owned"); - - const options = select.find("option"); - - expect(options.length).to.equal(4); - - expect(options.at(0).prop("value")).to.equal(""); - expect(options.at(1).prop("value")).to.equal("owned"); - expect(options.at(2).prop("value")).to.equal("shared-out"); - expect(options.at(3).prop("value")).to.equal("shared-in"); - }); - - it("renders sort select", () => { - const select = wrapper.find('select[name="sort"]'); - - expect(select.prop("value")).to.equal("asc"); - - const options = select.find("option"); - - expect(options.length).to.equal(2); - - expect(options.at(0).prop("value")).to.equal("asc"); - expect(options.at(1).prop("value")).to.equal("desc"); - }); - - it("renders a list of custom list info items", () => { - const listOfLists = wrapper.find("ul"); - expect(listOfLists.length).to.equal(1); - const firstList = listOfLists.find("li").at(0); - const secondList = listOfLists.find("li").at(1); - - const firstListInfo = firstList.find(".custom-list-info > div"); - expect(firstListInfo.at(0).text()).to.equal("First List"); - expect(firstListInfo.at(1).text()).to.equal("Books in list: 5"); - expect(firstListInfo.at(2).text()).to.equal("ID-1"); - - const firstListButtons = firstList.find(".custom-list-buttons"); - const firstListEdit = firstListButtons.find(Link).at(0); - const firstListDelete = firstListButtons.find(Button).at(0); - expect(firstListEdit.text()).to.contain("Edit"); - expect(firstListEdit.prop("to")).to.equal( - "/admin/web/lists/library_name/edit/1" - ); - expect(firstListDelete.text()).to.contain("Delete"); - firstListDelete.simulate("click"); - expect(deleteCustomList.callCount).to.equal(1); - - const secondListInfo = secondList.find(".custom-list-info > div"); - expect(secondListInfo.at(0).text()).to.equal("Second List"); - expect(secondListInfo.at(1).text()).to.equal("Books in list: 10"); - expect(secondListInfo.at(2).text()).to.equal("ID-2"); - - const secondListButtons = secondList.find(".custom-list-buttons"); - const secondListEdit = secondListButtons.find(Link).at(0); - const secondListDelete = secondListButtons.find(Button).at(0); - expect(secondListEdit.text()).to.contain("Edit"); - expect(secondListEdit.prop("to")).to.equal( - "/admin/web/lists/library_name/edit/2" - ); - expect(secondListDelete.text()).to.contain("Delete"); - secondListDelete.simulate("click"); - expect(deleteCustomList.callCount).to.equal(2); - }); - - it("disables the edit button if the list is already being edited", () => { - let firstListEdit = wrapper.find(".custom-list-buttons").at(0).find(Link); - expect(firstListEdit.hasClass("disabled")).to.be.false; - expect(firstListEdit.text()).to.equal("EditPencil Icon"); - wrapper.setProps({ identifier: 1 }); - firstListEdit = wrapper - .find(".custom-list-buttons") - .at(0) - .find(Button) - .at(0); - expect(firstListEdit.text()).to.equal("Editing"); - expect(firstListEdit.prop("disabled")).to.be.true; - }); - - it("renders a view button instead of an edit button if a list is not owned", () => { - wrapper.setProps({ - lists: [{ id: 1, name: "First List", entry_count: 5, is_owner: false }], - }); - - const firstListEdit = wrapper.find(".custom-list-buttons").at(0).find(Link); - expect(firstListEdit.hasClass("disabled")).to.be.false; - expect(firstListEdit.text()).to.equal("ViewVisible Icon"); - }); - - it("displays the delete button only to library managers", () => { - let deleteButtons = wrapper.find(".custom-list-buttons button"); - expect(deleteButtons.length).to.equal(2); - wrapper.setProps({ isLibraryManager: false }); - deleteButtons = wrapper.find(".custom-list-buttons button"); - expect(deleteButtons.length).to.equal(0); - }); - - it("does not render a delete button if a list is not owned", () => { - wrapper.setProps({ - lists: [{ id: 1, name: "First List", entry_count: 5, is_owner: false }], - }); - - const buttons = wrapper.find(".custom-list-buttons button"); - expect(buttons.length).to.equal(0); - }); - - it("does not render a delete button if a list is shared", () => { - wrapper.setProps({ - lists: [ - { - id: 1, - name: "First List", - entry_count: 5, - is_owner: true, - is_shared: true, - }, - ], - }); - - const buttons = wrapper.find(".custom-list-buttons button"); - expect(buttons.length).to.equal(0); - }); -}); diff --git a/src/components/__tests__/DiagnosticsTabContainer-test.tsx b/src/components/__tests__/DiagnosticsTabContainer-test.tsx deleted file mode 100644 index 6157622e02..0000000000 --- a/src/components/__tests__/DiagnosticsTabContainer-test.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { expect } from "chai"; - -import * as React from "react"; -import { shallow, mount } from "enzyme"; -import { stub } from "sinon"; -import buildStore from "../../store"; - -import { DiagnosticsTabContainer } from "../DiagnosticsTabContainer"; -import DiagnosticsServiceType from "../DiagnosticsServiceType"; -import LoadingIndicator from "@thepalaceproject/web-opds-client/lib/components/LoadingIndicator"; -import ErrorMessage from "../ErrorMessage"; - -describe("DiagnosticsTabContainer", () => { - let wrapper; - let goToTab; - - const ts1 = { - service: "test_service", - id: "1", - start: "start_time_string", - duration: "0", - collection_name: "collection1", - }; - - const diagnostics = { - monitor: [{ test_service_1: [{ collection1: [ts1] }] }], - }; - - const fetchDiagnostics = stub(); - - beforeEach(() => { - goToTab = stub(); - wrapper = mount( - - ); - }); - - describe("rendering", () => { - it("renders a tab container", () => { - expect(wrapper.render().hasClass("tab-container")).to.be.true; - }); - - it("renders tabs and defaults to showing the Coverage Providers tab", () => { - const nav = wrapper.find(".nav-tabs").at(0); - expect(nav.length).to.equal(1); - const tabs = nav.find("li"); - expect(tabs.length).to.equal(4); - - const cpTab = tabs.at(0); - expect(cpTab.text()).to.equal("Coverage Providers"); - expect(cpTab.hasClass("active")).to.be.true; - - const monitorTab = tabs.at(1); - expect(monitorTab.text()).to.equal("Monitors"); - expect(monitorTab.hasClass("active")).to.be.false; - - const scriptTab = tabs.at(2); - expect(scriptTab.text()).to.equal("Scripts"); - expect(scriptTab.hasClass("active")).to.be.false; - - const otherTab = tabs.at(3); - expect(otherTab.text()).to.equal("Other"); - expect(otherTab.hasClass("active")).to.be.false; - }); - - it("renders tab content", () => { - wrapper.setProps({ diagnostics }); - const serviceTypes = wrapper.find(DiagnosticsServiceType); - expect(serviceTypes.length).to.equal(4); - - const cpContent = serviceTypes.at(0); - expect(cpContent.prop("type")).to.equal("coverage_provider"); - expect(cpContent.prop("services")).to.be.undefined; - - const monitorContent = serviceTypes.at(1); - expect(monitorContent.prop("type")).to.equal("monitor"); - expect(monitorContent.prop("services")).to.equal( - wrapper.prop("diagnostics")["monitor"] - ); - - const scriptContent = serviceTypes.at(2); - expect(scriptContent.prop("type")).to.equal("script"); - expect(scriptContent.prop("services")).to.be.undefined; - - const otherContent = serviceTypes.at(3); - expect(otherContent.prop("type")).to.equal("other"); - expect(otherContent.prop("services")).to.be.undefined; - }); - }); - - describe("behavior", () => { - it("calls fetchDiagnostics on mount", () => { - expect(fetchDiagnostics.called).to.be.true; - }); - - it("shows the loading indicator", () => { - expect(wrapper.find(LoadingIndicator).length).to.equal(0); - wrapper.setProps({ isLoaded: false }); - expect(wrapper.find(LoadingIndicator).length).to.equal(4); - }); - - it("shows an error message", () => { - let error = wrapper.find(ErrorMessage); - expect(error.length).to.equal(0); - - const fetchError = { - status: 401, - response: "error fetching diagnostics", - }; - wrapper.setProps({ fetchError }); - - error = wrapper.find(ErrorMessage); - expect(error.length).to.equal(4); - }); - - it("calls goToTab", () => { - wrapper = shallow( - - ); - - const tabs = wrapper.find("ul.nav-tabs").find("a"); - const monitorTab = tabs.at(1); - monitorTab.simulate("click", { - preventDefault: stub(), - currentTarget: { dataset: { tabkey: "monitor" } }, - }); - - expect(goToTab.callCount).to.equal(1); - expect(goToTab.args[0][0]).to.equal("monitor"); - }); - - it("switches tabs when the tab prop changes", () => { - let cpTab = wrapper.find("ul.nav-tabs").find("li").at(0); - let monitorTab = wrapper.find("ul.nav-tabs").find("li").at(1); - expect(cpTab.hasClass("active")).to.be.true; - expect(monitorTab.hasClass("active")).to.be.false; - - wrapper.setProps({ tab: "monitor" }); - - cpTab = wrapper.find("ul.nav-tabs").find("li").at(0); - monitorTab = wrapper.find("ul.nav-tabs").find("li").at(1); - expect(cpTab.hasClass("active")).to.be.false; - expect(monitorTab.hasClass("active")).to.be.true; - }); - }); -}); diff --git a/src/components/__tests__/ErrorMessage-test.tsx b/src/components/__tests__/ErrorMessage-test.tsx deleted file mode 100644 index 5646564832..0000000000 --- a/src/components/__tests__/ErrorMessage-test.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; - -import * as React from "react"; -import { mount } from "enzyme"; - -import ErrorMessage from "../ErrorMessage"; - -describe("ErrorMessage", () => { - it("shows logged out message for 401 error", () => { - const error = { - status: 401, - response: "", - url: "", - }; - const wrapper = mount(); - const alert = wrapper.find(".alert-danger"); - expect(alert.text()).to.contain("logged out"); - }); - - it("shows detail for problem detail", () => { - const error = { - status: 500, - response: JSON.stringify({ status: 500, detail: "detail" }), - url: "", - }; - const wrapper = mount(); - const alert = wrapper.find(".alert-danger"); - expect(alert.text()).to.contain("detail"); - }); - - it("shows response for non-json response", () => { - const error = { - status: 500, - response: "response", - url: "", - }; - const wrapper = mount(); - const alert = wrapper.find(".alert-danger"); - expect(alert.text()).to.contain("response"); - }); - - it("parses non-JSON problem detail string", () => { - // prettier-ignore - const pd = "Remote service returned a problem detail document: {\"status\": 502, \"detail\": problem text, \"title\": TITLE}"; - const error = { - status: 500, - response: pd, - url: "", - }; - const message = - "Remote service returned a problem detail document with status 502: problem text"; - const wrapper = mount(); - const alert = wrapper.find(".alert-danger"); - const title = wrapper.find("b"); - expect(alert.text()).to.contain(message); - expect(title.text()).to.equal("TITLE"); - }); - - it("can handle missing detail property in non-JSON problem detail string", () => { - // prettier-ignore - const pd = "Remote service returned a problem detail document: {\"status\": 502, \"title\": TITLE}"; - const error = { - status: 500, - response: pd, - url: "", - }; - const message = - "Remote service returned a problem detail document with status 502: "; - const wrapper = mount(); - const alert = wrapper.find(".alert-danger"); - const title = wrapper.find("b"); - expect(alert.text()).to.contain(message); - expect(title.text()).to.equal("TITLE"); - }); - - it("can handle missing status property in non-JSON problem detail string", () => { - // prettier-ignore - const pd = "Remote service returned a problem detail document: {\"detail\": problem text, \"title\": TITLE}"; - const error = { - status: 500, - response: pd, - url: "", - }; - const message = "Remote service returned a problem detail document: "; - const wrapper = mount(); - const alert = wrapper.find(".alert-danger"); - const title = wrapper.find("b"); - expect(alert.text()).to.contain(message); - expect(title.text()).to.equal("TITLE"); - }); - - it("can handle missing title property in non-JSON problem detail string", () => { - // prettier-ignore - const pd = "Remote service returned a problem detail document: {\"status\": 502, \"detail\": problem text}"; - const error = { - status: 500, - response: pd, - url: "", - }; - const message = - "Remote service returned a problem detail document with status 502: "; - const wrapper = mount(); - const alert = wrapper.find(".alert-danger"); - const title = wrapper.find("b"); - expect(alert.text()).to.contain(message); - expect(title.text()).to.equal("Error"); - }); - - it("shows try again button", () => { - const error = { - status: 500, - response: "response", - url: "", - }; - const tryAgain = stub(); - const wrapper = mount(); - const tryAgainLink = wrapper.find("button"); - expect(tryAgainLink.text()).to.contain("Try again"); - tryAgainLink.simulate("click"); - expect(tryAgain.callCount).to.equal(1); - }); -}); diff --git a/src/components/__tests__/Footer-test.tsx b/src/components/__tests__/Footer-test.tsx deleted file mode 100644 index dfd9c6f18c..0000000000 --- a/src/components/__tests__/Footer-test.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { expect } from "chai"; -import * as React from "react"; -import { mount } from "enzyme"; - -import Footer, { makeFooterSupportContactText } from "../Footer"; -import { componentWithProviders } from "../../../tests/jest/testUtils/withProviders"; -import { - DEFAULT_SUPPORT_CONTACT_TEXT, - SupportContactLink, -} from "../../context/appContext"; - -const expectedTosText = "Terms of Service Text"; -const expectedTosHref = "terms_of_service"; -const testSupportContactEmail = "helpdesk@example.com"; -const expectedSupportContact: SupportContactLink = { - href: `mailto:${testSupportContactEmail}?subject=Support+request`, - text: `Email ${testSupportContactEmail}.`, -}; -const expectedSupportContactText = makeFooterSupportContactText( - expectedSupportContact.text -); -const getFooterProviders = ({ hasSupportContactUrl = false } = {}) => { - const appConfigSettings = { - tos_link_text: expectedTosText, - tos_link_href: expectedTosHref, - // Include support contact URL only if we ask for it. - support_contact_url: hasSupportContactUrl - ? "https://example.com/link-from-deprecated-setting" - : undefined, - supportContactUrl: hasSupportContactUrl - ? expectedSupportContact.href - : undefined, - }; - return componentWithProviders({ appConfigSettings }); -}; - -describe("Footer", () => { - it("accepts an optional className prop", () => { - const wrapper = mount(