@thepalaceproject/circulation-admin 1.43.0-post.18 → 1.43.0-post.20

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/jest.config.js CHANGED
@@ -1,15 +1,17 @@
1
1
  /** @type {import('ts-jest').JestConfigWithTsJest} */
2
-
3
- // Options shared by both projects.
4
- const sharedProjectConfig = {
2
+ module.exports = {
5
3
  preset: "ts-jest",
4
+ // `jest-fixed-jsdom` restores Node's fetch/Response globals so MSW can
5
+ // intercept them.
6
+ testEnvironment: "jest-fixed-jsdom",
6
7
  testEnvironmentOptions: {
7
- // Without this, jsdom's default `["browser"]` export condition resolves sinon
8
- // to its ESM bundle, which ts-jest does not transform. Every legacy suite then
9
- // dies with `SyntaxError: Unexpected token 'export'`.
8
+ // MSW / jest-fixed-jsdom rely on the default resolution conditions; the empty
9
+ // string keeps jsdom's "browser" condition from pulling in ESM-only builds
10
+ // that ts-jest would not transform.
10
11
  customExportConditions: [""],
11
12
  },
12
13
  setupFiles: ["<rootDir>/jest.polyfills.js"],
14
+ setupFilesAfterEnv: ["<rootDir>/tests/jest/jest-setup.ts"],
13
15
  moduleNameMapper: {
14
16
  "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
15
17
  "<rootDir>/tests/__mocks__/fileMock.js",
@@ -20,44 +22,9 @@ const sharedProjectConfig = {
20
22
  // files that no test imports — otherwise they are silently omitted from the
21
23
  // report rather than counted as uncovered, and the coverage percentage rises
22
24
  // while real coverage is unchanged.
23
- roots: ["<rootDir>/src"],
24
- };
25
-
26
- module.exports = {
25
+ roots: ["<rootDir>/src", "<rootDir>/tests/jest"],
26
+ testMatch: ["<rootDir>/tests/jest/**/*.test.{js,jsx,ts,tsx}"],
27
27
  coverageDirectory: "coverage/jest",
28
28
  coverageReporters: ["lcov", "text-summary"],
29
- // Must live at the top level, not inside `projects`: jest reads
30
- // `collectCoverageFrom` off the *global* config. Set it only per-project and it
31
- // is silently undefined globally, so every executed file gets instrumented —
32
- // including test helpers and everything under `tests/`.
33
- collectCoverageFrom: [
34
- "src/**/*.{ts,tsx}",
35
- "!src/**/__tests__/**",
36
- "!src/**/*.d.ts",
37
- ],
38
- projects: [
39
- {
40
- ...sharedProjectConfig,
41
- displayName: "unit",
42
- roots: [...sharedProjectConfig.roots, "<rootDir>/tests/jest"],
43
- testMatch: ["<rootDir>/tests/jest/**/*.test.{js,jsx,ts,tsx}"],
44
- // `jest-fixed-jsdom` restores Node's fetch/Response globals so MSW can
45
- // intercept them.
46
- testEnvironment: "jest-fixed-jsdom",
47
- setupFilesAfterEnv: ["<rootDir>/tests/jest/jest-setup.ts"],
48
- },
49
- {
50
- // Legacy enzyme + chai + sinon tests, still in `src/**/__tests__/`. They are
51
- // being rewritten as React Testing Library tests under `tests/jest/`; when the
52
- // last one is gone, delete this project, its environment, and its setup file.
53
- //
54
- // These cannot use `jest-fixed-jsdom`: it swaps in undici's `FormData`, which
55
- // rejects `new FormData(formElement)` and crashes any test that submits a form.
56
- ...sharedProjectConfig,
57
- displayName: "legacy",
58
- testMatch: ["<rootDir>/src/**/__tests__/*-test.{ts,tsx}"],
59
- testEnvironment: "<rootDir>/tests/jest/legacyJsdomEnvironment.js",
60
- setupFilesAfterEnv: ["<rootDir>/tests/jest/legacy-setup.ts"],
61
- },
62
- ],
29
+ collectCoverageFrom: ["src/**/*.{ts,tsx}", "!src/**/*.d.ts"],
63
30
  };
package/package.json CHANGED
@@ -84,13 +84,10 @@
84
84
  "@types/react-router": "^3.0.28",
85
85
  "@types/recharts": "^1.8.28",
86
86
  "@types/redux-mock-store": "^1.0.3",
87
- "chai": "4.2.0",
88
87
  "clean-webpack-plugin": "^2.0.1",
89
88
  "copy-webpack-plugin": "14.0.0",
90
89
  "css-loader": "^7.1.4",
91
90
  "dotenv-cli": "^11.0.0",
92
- "enzyme": "^3.11.0",
93
- "enzyme-adapter-react-16": "^1.15.6",
94
91
  "eslint": "^9.39.4",
95
92
  "eslint-config-prettier": "^10.1.8",
96
93
  "eslint-plugin-jsx-a11y": "^6.10.2",
@@ -119,7 +116,6 @@
119
116
  "sass-lint": "^1.13.1",
120
117
  "sass-loader": "^17.0.0",
121
118
  "selenium-standalone": "^10.0.2",
122
- "sinon": "22.0.0",
123
119
  "style-loader": "^4.0.0",
124
120
  "terser-webpack-plugin": "^5.3.6",
125
121
  "ts-jest": "^29.2.6",
@@ -143,5 +139,5 @@
143
139
  "*.{js,jsx,ts,tsx,css,md}": "prettier --write",
144
140
  "*.{js,css,md}": "prettier --write"
145
141
  },
146
- "version": "1.43.0-post.18"
142
+ "version": "1.43.0-post.20"
147
143
  }
@@ -7,7 +7,6 @@ import {
7
7
  PER_LIBRARY_UNSUPPRESS_REL,
8
8
  } from "../../../src/features/book/bookEditorSlice";
9
9
  import { BookDetailsEditor } from "../../../src/components/BookDetailsEditor";
10
- import { expect } from "chai";
11
10
  import { store } from "../../../src/store";
12
11
  import * as fetchMock from "fetch-mock-jest";
13
12
 
@@ -93,8 +92,8 @@ describe("BookDetails", () => {
93
92
  const hideButton = queryByRole("button", { name: "Hide" });
94
93
  const restoreButton = queryByRole("button", { name: "Restore" });
95
94
 
96
- expect(hideButton).to.be.null;
97
- expect(restoreButton).to.be.null;
95
+ expect(hideButton).toBeNull();
96
+ expect(restoreButton).toBeNull();
98
97
  });
99
98
 
100
99
  it("don't show restore button if not a library's admin", () => {
@@ -110,8 +109,8 @@ describe("BookDetails", () => {
110
109
  const hideButton = queryByRole("button", { name: "Hide" });
111
110
  const restoreButton = queryByRole("button", { name: "Restore" });
112
111
 
113
- expect(hideButton).to.be.null;
114
- expect(restoreButton).to.be.null;
112
+ expect(hideButton).toBeNull();
113
+ expect(restoreButton).toBeNull();
115
114
  });
116
115
 
117
116
  it("uses modal for suppress book confirmation", async () => {
@@ -149,8 +148,8 @@ describe("BookDetails", () => {
149
148
  await user.click(cancelButton);
150
149
  confirmButton = queryByRole("button", { name: "Suppress Availability" });
151
150
  cancelButton = queryByRole("button", { name: "Cancel" });
152
- expect(confirmButton).to.be.null;
153
- expect(cancelButton).to.be.null;
151
+ expect(confirmButton).toBeNull();
152
+ expect(cancelButton).toBeNull();
154
153
 
155
154
  // Clicking `Hide` again should show the modal again.
156
155
  await user.click(hideButton);
@@ -163,15 +162,15 @@ describe("BookDetails", () => {
163
162
  getByRole("button", { name: "Dismiss" });
164
163
 
165
164
  // Check that the API was invoked.
166
- expect(suppressBook.mock.calls.length).to.equal(1);
167
- expect(suppressBook.mock.calls[0][0]).to.equal("/suppress/href");
165
+ expect(suppressBook.mock.calls.length).toBe(1);
166
+ expect(suppressBook.mock.calls[0][0]).toBe("/suppress/href");
168
167
  const fetchCalls = fetchMock.calls();
169
- expect(fetchCalls.length).to.equal(1);
168
+ expect(fetchCalls.length).toBe(1);
170
169
  const fetchCall = fetchCalls[0];
171
170
  const fetchOptions = fetchCalls[0][1];
172
- expect(fetchCall[0]).to.equal("/suppress/href");
173
- expect(fetchOptions["headers"]["X-CSRF-Token"]).to.contain("token");
174
- expect(fetchOptions["method"]).to.equal("POST");
171
+ expect(fetchCall[0]).toBe("/suppress/href");
172
+ expect(fetchOptions["headers"]["X-CSRF-Token"]).toContain("token");
173
+ expect(fetchOptions["method"]).toBe("POST");
175
174
  });
176
175
  it("uses modal for unsuppress book confirmation", async () => {
177
176
  // Configure standard constructors so that RTK Query works in tests with FetchMockJest
@@ -213,8 +212,8 @@ describe("BookDetails", () => {
213
212
  await user.click(cancelButton);
214
213
  confirmButton = queryByRole("button", { name: "Restore Availability" });
215
214
  cancelButton = queryByRole("button", { name: "Cancel" });
216
- expect(confirmButton).to.be.null;
217
- expect(cancelButton).to.be.null;
215
+ expect(confirmButton).toBeNull();
216
+ expect(cancelButton).toBeNull();
218
217
 
219
218
  // Clicking `Restore` again should show the modal again.
220
219
  await user.click(restoreButton);
@@ -227,14 +226,14 @@ describe("BookDetails", () => {
227
226
  getByRole("button", { name: "Dismiss" });
228
227
 
229
228
  // Check that the API was invoked.
230
- expect(unsuppressBook.mock.calls.length).to.equal(1);
231
- expect(unsuppressBook.mock.calls[0][0]).to.equal("/unsuppress/href");
229
+ expect(unsuppressBook.mock.calls.length).toBe(1);
230
+ expect(unsuppressBook.mock.calls[0][0]).toBe("/unsuppress/href");
232
231
  const fetchCalls = fetchMock.calls();
233
- expect(fetchCalls.length).to.equal(1);
232
+ expect(fetchCalls.length).toBe(1);
234
233
  const fetchCall = fetchCalls[0];
235
234
  const fetchOptions = fetchCalls[0][1];
236
- expect(fetchCall[0]).to.equal("/unsuppress/href");
237
- expect(fetchOptions["headers"]["X-CSRF-Token"]).to.contain("token");
238
- expect(fetchOptions["method"]).to.equal("DELETE");
235
+ expect(fetchCall[0]).toBe("/unsuppress/href");
236
+ expect(fetchOptions["headers"]["X-CSRF-Token"]).toContain("token");
237
+ expect(fetchOptions["method"]).toBe("DELETE");
239
238
  });
240
239
  });
@@ -4,8 +4,8 @@ import userEvent from "@testing-library/user-event";
4
4
 
5
5
  import { renderWithProviders } from "../testUtils/withProviders";
6
6
  import buildStore from "../../../src/store";
7
- import genreData from "../../../src/components/__tests__/genreData";
8
- import classificationsData from "../../../src/components/__tests__/classificationsData";
7
+ import genreData from "../fixtures/genreData";
8
+ import classificationsData from "../fixtures/classificationsData";
9
9
  // Render the CONNECTED default export so that mapStateToProps / mapDispatchToProps
10
10
  // are exercised. The genre tree and classifications are fetched on mount and fed
11
11
  // back in through the Redux store; `book` and `refreshCatalog` are own props.
@@ -4,7 +4,7 @@ import userEvent from "@testing-library/user-event";
4
4
 
5
5
  import ClassificationsForm from "../../../src/components/ClassificationsForm";
6
6
  import { BookData } from "../../../src/interfaces";
7
- import genreData from "../../../src/components/__tests__/genreData";
7
+ import genreData from "../fixtures/genreData";
8
8
 
9
9
  // ClassificationsForm is a plain (unconnected) class component, so a bare
10
10
  // `render` is enough — no store/context/router needed.
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import { render, screen } from "@testing-library/react";
3
3
 
4
4
  import ClassificationsTable from "../../../src/components/ClassificationsTable";
5
- import classificationsData from "../../../src/components/__tests__/classificationsData";
5
+ import classificationsData from "../fixtures/classificationsData";
6
6
 
7
7
  // Mirrors ClassificationsTable's own `readableType`: it strips the genres URL
8
8
  // prefix so the displayed type is human-readable.
@@ -313,7 +313,7 @@ describe("CustomListsForBook", () => {
313
313
  expect(screen.getByRole("button", { name: "Add" })).toBeDisabled();
314
314
  });
315
315
 
316
- it("resyncs its lists when the book url changes", () => {
316
+ it("shows the new book's lists when it is rerendered for another book", () => {
317
317
  const commonProps = {
318
318
  csrfToken: "token",
319
319
  library: "library",
@@ -331,10 +331,12 @@ describe("CustomListsForBook", () => {
331
331
  customListsForBook={listsForBook as any}
332
332
  />
333
333
  );
334
- // list 2 (the book's only list) is in the current column.
335
- expect(screen.getByText("list 2")).toBeInTheDocument();
334
+ // list 2 (the book's only list) is in the current column, which renders
335
+ // each list as a link; the add-menu renders options instead.
336
+ expect(screen.getByRole("link", { name: "list 2" })).toBeInTheDocument();
336
337
 
337
- // A new book url re-syncs the current lists from the new customListsForBook.
338
+ // The current column renders straight from customListsForBook, so a new
339
+ // book's lists replace the old ones on rerender.
338
340
  rerender(
339
341
  <UnconnectedCustomListsForBook
340
342
  {...commonProps}
@@ -343,6 +345,9 @@ describe("CustomListsForBook", () => {
343
345
  customListsForBook={[allCustomLists[0]] as any}
344
346
  />
345
347
  );
346
- expect(screen.getByText("list 1")).toBeInTheDocument();
348
+ expect(screen.getByRole("link", { name: "list 1" })).toBeInTheDocument();
349
+ expect(
350
+ screen.queryByRole("link", { name: "list 2" })
351
+ ).not.toBeInTheDocument();
347
352
  });
348
353
  });
@@ -3,7 +3,7 @@ import { render, screen, fireEvent } from "@testing-library/react";
3
3
  import userEvent from "@testing-library/user-event";
4
4
 
5
5
  import GenreForm from "../../../src/components/GenreForm";
6
- import genreData from "../../../src/components/__tests__/genreData";
6
+ import genreData from "../fixtures/genreData";
7
7
 
8
8
  const genreOptions = Object.keys(genreData["Fiction"]).map(
9
9
  (name) => genreData["Fiction"][name]
@@ -1,7 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { render, screen } from "@testing-library/react";
3
3
  import userEvent from "@testing-library/user-event";
4
- import { stub } from "sinon";
5
4
  import { LaneData } from "../../../src/interfaces";
6
5
  import Lane from "../../../src/components/Lane";
7
6
 
@@ -20,8 +19,8 @@ jest.mock("react-router", () => ({
20
19
  ),
21
20
  }));
22
21
 
23
- const renderLanes = stub();
24
- const toggleLaneVisibility = stub();
22
+ const renderLanes = jest.fn();
23
+ const toggleLaneVisibility = jest.fn();
25
24
 
26
25
  function createLaneData(displayName: string, isAutomated: boolean): LaneData {
27
26
  return {
@@ -1,7 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { render, screen, waitFor, within } from "@testing-library/react";
3
3
  import userEvent from "@testing-library/user-event";
4
- import { stub } from "sinon";
5
4
  import { LaneData } from "../../../src/interfaces";
6
5
  import LaneEditor from "../../../src/components/LaneEditor";
7
6
  import * as navigate from "../../../src/utils/navigate";
@@ -62,11 +61,15 @@ const customListsData = [
62
61
  { id: 1, name: "list 1", entries: [], is_owner: true, is_shared: false },
63
62
  ];
64
63
 
65
- const editLane = stub().returns(new Promise<void>((resolve) => resolve()));
64
+ const editLane = jest
65
+ .fn()
66
+ .mockReturnValue(new Promise<void>((resolve) => resolve()));
66
67
 
67
- const deleteLane = stub().returns(new Promise<void>((resolve) => resolve()));
68
+ const deleteLane = jest
69
+ .fn()
70
+ .mockReturnValue(new Promise<void>((resolve) => resolve()));
68
71
 
69
- const toggleLaneVisibility = stub();
72
+ const toggleLaneVisibility = jest.fn();
70
73
 
71
74
  function createLaneData(displayName: string, isAutomated: boolean): LaneData {
72
75
  return {
@@ -104,7 +107,7 @@ describe("LaneEditor", () => {
104
107
  editOrCreate="edit"
105
108
  editLane={editLane}
106
109
  deleteLane={deleteLane}
107
- findParentOfLane={stub().returns(laneData)}
110
+ findParentOfLane={jest.fn().mockReturnValue(laneData)}
108
111
  toggleLaneVisibility={toggleLaneVisibility}
109
112
  />
110
113
  );
@@ -139,7 +142,7 @@ describe("LaneEditor", () => {
139
142
  editOrCreate="edit"
140
143
  editLane={editLane}
141
144
  deleteLane={deleteLane}
142
- findParentOfLane={stub().returns(laneData)}
145
+ findParentOfLane={jest.fn().mockReturnValue(laneData)}
143
146
  toggleLaneVisibility={toggleLaneVisibility}
144
147
  />
145
148
  );
@@ -177,7 +180,7 @@ describe("LaneEditor", () => {
177
180
  editOrCreate="edit"
178
181
  editLane={editLane}
179
182
  deleteLane={deleteLane}
180
- findParentOfLane={stub().returns(laneData)}
183
+ findParentOfLane={jest.fn().mockReturnValue(laneData)}
181
184
  toggleLaneVisibility={toggleLaneVisibility}
182
185
  />
183
186
  );
@@ -28,13 +28,6 @@ const normalizedData = normalizeStatistics(statisticsApiResponseData);
28
28
  global.ResizeObserver = require("resize-observer-polyfill");
29
29
 
30
30
  describe("Dashboard Statistics", () => {
31
- // NB: This adds test to the already existing tests in:
32
- // - `src/components/__tests__/LibraryStats-test.tsx`.
33
- // - `src/components/__tests__/SingleStatListItem-test.tsx`.
34
- //
35
- // Those tests should eventually be migrated here and
36
- // adapted to the Jest/React Testing Library paradigm.
37
-
38
31
  // Configure standard constructors so that RTK Query works in tests with FetchMockJest
39
32
  Object.assign(fetchMock.config, {
40
33
  fetch,
@@ -3,7 +3,6 @@ import reducer, {
3
3
  getBookData,
4
4
  submitBookData,
5
5
  } from "../../../src/features/book/bookEditorSlice";
6
- import { expect } from "chai";
7
6
  import * as fetchMock from "fetch-mock-jest";
8
7
  import { BookData } from "@thepalaceproject/web-opds-client/lib/interfaces";
9
8
  import { RequestError } from "@thepalaceproject/web-opds-client/lib/DataFetcher";
@@ -57,12 +56,12 @@ describe("Redux bookEditorSlice...", () => {
57
56
 
58
57
  describe("reducers...", () => {
59
58
  it("should return the initial state from undefined, if no action is passed", () => {
60
- expect(reducer(undefined, { type: "unknown" })).to.deep.equal(
59
+ expect(reducer(undefined, { type: "unknown" })).toStrictEqual(
61
60
  initialState
62
61
  );
63
62
  });
64
63
  it("should return the initial state from initialState, if no action is passed", () => {
65
- expect(reducer(initialState, { type: "unknown" })).to.deep.equal(
64
+ expect(reducer(initialState, { type: "unknown" })).toStrictEqual(
66
65
  initialState
67
66
  );
68
67
  });
@@ -70,7 +69,7 @@ describe("Redux bookEditorSlice...", () => {
70
69
  // This is dispatched by `web-opds`client`, but we need to handle it, too.
71
70
  const action = { type: ActionCreator.BOOK_CLEAR };
72
71
 
73
- expect(reducer(fetchedState, action)).to.deep.equal(initialState);
72
+ expect(reducer(fetchedState, action)).toStrictEqual(initialState);
74
73
  });
75
74
 
76
75
  it("should handle getBookData.pending", () => {
@@ -81,11 +80,11 @@ describe("Redux bookEditorSlice...", () => {
81
80
  const previousState = { ...initialState, url: null, isFetching: false };
82
81
  const state = reducer(previousState, action);
83
82
 
84
- expect(state.url).to.equal("https://example.com/book");
85
- expect(state.data).to.be.null;
86
- expect(state.isFetching).to.equal(true);
87
- expect(state.fetchError).to.be.null;
88
- expect(state.editError).to.be.null;
83
+ expect(state.url).toBe("https://example.com/book");
84
+ expect(state.data).toBeNull();
85
+ expect(state.isFetching).toBe(true);
86
+ expect(state.fetchError).toBeNull();
87
+ expect(state.editError).toBeNull();
89
88
  });
90
89
  it("should handle getBookData.fulfilled", () => {
91
90
  const action = {
@@ -101,11 +100,11 @@ describe("Redux bookEditorSlice...", () => {
101
100
  };
102
101
  const state = reducer(previousState, action);
103
102
 
104
- expect(state.url).to.equal("https://example.com/book");
105
- expect(state.data).to.deep.equal(bookData);
106
- expect(state.isFetching).to.equal(false);
107
- expect(state.fetchError).to.be.null;
108
- expect(state.editError).to.be.null;
103
+ expect(state.url).toBe("https://example.com/book");
104
+ expect(state.data).toStrictEqual(bookData);
105
+ expect(state.isFetching).toBe(false);
106
+ expect(state.fetchError).toBeNull();
107
+ expect(state.editError).toBeNull();
109
108
  });
110
109
  it("should handle getBookData.rejected", () => {
111
110
  const errorObject = { error: "some error object" };
@@ -122,11 +121,11 @@ describe("Redux bookEditorSlice...", () => {
122
121
  };
123
122
  const state = reducer(previousState, action);
124
123
 
125
- expect(state.url).to.equal("https://example.com/book");
126
- expect(state.data).to.be.null;
127
- expect(state.isFetching).to.equal(false);
128
- expect(state.fetchError).to.deep.equal(errorObject);
129
- expect(state.editError).to.be.null;
124
+ expect(state.url).toBe("https://example.com/book");
125
+ expect(state.data).toBeNull();
126
+ expect(state.isFetching).toBe(false);
127
+ expect(state.fetchError).toStrictEqual(errorObject);
128
+ expect(state.editError).toBeNull();
130
129
  });
131
130
 
132
131
  it("should handle submitBookData.pending", () => {
@@ -134,7 +133,7 @@ describe("Redux bookEditorSlice...", () => {
134
133
  const previousState = { ...fetchedState, isFetching: false };
135
134
  const state = reducer(previousState, action);
136
135
 
137
- expect(state).to.deep.equal({ ...fetchedState, isFetching: true });
136
+ expect(state).toStrictEqual({ ...fetchedState, isFetching: true });
138
137
  });
139
138
  it("should handle submitBookData.fulfilled", () => {
140
139
  const action = {
@@ -144,7 +143,7 @@ describe("Redux bookEditorSlice...", () => {
144
143
  const previousState = { ...fetchedState, isFetching: true };
145
144
  const state = reducer(previousState, action);
146
145
 
147
- expect(state).to.deep.equal({
146
+ expect(state).toStrictEqual({
148
147
  ...fetchedState,
149
148
  isFetching: false,
150
149
  editError: null,
@@ -158,7 +157,7 @@ describe("Redux bookEditorSlice...", () => {
158
157
  const previousState = { ...fetchedState, isFetching: true };
159
158
  const state = reducer(previousState, action);
160
159
 
161
- expect(state).to.deep.equal({
160
+ expect(state).toStrictEqual({
162
161
  ...fetchedState,
163
162
  isFetching: false,
164
163
  editError: "some value",
@@ -200,22 +199,22 @@ describe("Redux bookEditorSlice...", () => {
200
199
  const dispatchCalls = dispatch.mock.calls;
201
200
 
202
201
  const payload = result.payload as BookData;
203
- expect(payload.id).to.equal(
202
+ expect(payload.id).toBe(
204
203
  "urn:uuid:1cca9468-c447-4303-bc5a-c57470b85cb1"
205
204
  );
206
- expect(payload.title).to.equal(
205
+ expect(payload.title).toBe(
207
206
  "Tea-Cup Reading and Fortune-Telling by Tea Leaves"
208
207
  );
209
208
 
210
- expect(dispatchCalls.length).to.equal(2);
211
- expect(dispatchCalls[0][0].type).to.equal(getBookData.pending.type);
212
- expect(dispatchCalls[0][0].payload).to.equal(undefined);
213
- expect(dispatchCalls[0][0].meta.arg).to.deep.equal({
209
+ expect(dispatchCalls.length).toBe(2);
210
+ expect(dispatchCalls[0][0].type).toBe(getBookData.pending.type);
211
+ expect(dispatchCalls[0][0].payload).toBe(undefined);
212
+ expect(dispatchCalls[0][0].meta.arg).toStrictEqual({
214
213
  url: goodBookUrl,
215
214
  });
216
- expect(dispatchCalls[1][0].type).to.equal(getBookData.fulfilled.type);
217
- expect(dispatchCalls[1][0].payload).to.deep.equal(payload);
218
- expect(dispatchCalls[1][0].meta.arg).to.deep.equal({
215
+ expect(dispatchCalls[1][0].type).toBe(getBookData.fulfilled.type);
216
+ expect(dispatchCalls[1][0].payload).toStrictEqual(payload);
217
+ expect(dispatchCalls[1][0].meta.arg).toStrictEqual({
219
218
  url: goodBookUrl,
220
219
  });
221
220
  });
@@ -226,18 +225,18 @@ describe("Redux bookEditorSlice...", () => {
226
225
  const dispatchCalls = dispatch.mock.calls;
227
226
 
228
227
  const payload = result.payload as RequestError;
229
- expect(payload.response).to.equal(FETCH_OPDS_PARSE_ERROR_MESSAGE);
230
- expect(payload.url).to.equal(brokenBookUrl);
228
+ expect(payload.response).toBe(FETCH_OPDS_PARSE_ERROR_MESSAGE);
229
+ expect(payload.url).toBe(brokenBookUrl);
231
230
 
232
- expect(dispatchCalls.length).to.equal(2);
233
- expect(dispatchCalls[0][0].type).to.equal(getBookData.pending.type);
234
- expect(dispatchCalls[0][0].payload).to.equal(undefined);
235
- expect(dispatchCalls[0][0].meta.arg).to.deep.equal({
231
+ expect(dispatchCalls.length).toBe(2);
232
+ expect(dispatchCalls[0][0].type).toBe(getBookData.pending.type);
233
+ expect(dispatchCalls[0][0].payload).toBe(undefined);
234
+ expect(dispatchCalls[0][0].meta.arg).toStrictEqual({
236
235
  url: brokenBookUrl,
237
236
  });
238
- expect(dispatchCalls[1][0].type).to.equal(getBookData.rejected.type);
239
- expect(dispatchCalls[1][0].payload).to.deep.equal(payload);
240
- expect(dispatchCalls[1][0].meta.arg).to.deep.equal({
237
+ expect(dispatchCalls[1][0].type).toBe(getBookData.rejected.type);
238
+ expect(dispatchCalls[1][0].payload).toStrictEqual(payload);
239
+ expect(dispatchCalls[1][0].meta.arg).toStrictEqual({
241
240
  url: brokenBookUrl,
242
241
  });
243
242
  });
@@ -249,20 +248,20 @@ describe("Redux bookEditorSlice...", () => {
249
248
 
250
249
  const payload = result.payload as RequestError;
251
250
 
252
- expect(result.type).to.equal(getBookData.rejected.type);
253
- expect(result.meta.arg).to.deep.equal({ url: errorBookUrl });
254
- expect(payload.response).to.equal("Internal server error");
255
- expect(payload.url).to.equal(errorBookUrl);
251
+ expect(result.type).toBe(getBookData.rejected.type);
252
+ expect(result.meta.arg).toStrictEqual({ url: errorBookUrl });
253
+ expect(payload.response).toBe("Internal server error");
254
+ expect(payload.url).toBe(errorBookUrl);
256
255
 
257
- expect(dispatchCalls.length).to.equal(2);
258
- expect(dispatchCalls[0][0].type).to.equal(getBookData.pending.type);
259
- expect(dispatchCalls[0][0].payload).to.equal(undefined);
260
- expect(dispatchCalls[0][0].meta.arg).to.deep.equal({
256
+ expect(dispatchCalls.length).toBe(2);
257
+ expect(dispatchCalls[0][0].type).toBe(getBookData.pending.type);
258
+ expect(dispatchCalls[0][0].payload).toBe(undefined);
259
+ expect(dispatchCalls[0][0].meta.arg).toStrictEqual({
261
260
  url: errorBookUrl,
262
261
  });
263
- expect(dispatchCalls[1][0].type).to.equal(getBookData.rejected.type);
264
- expect(dispatchCalls[1][0].payload).to.deep.equal(payload);
265
- expect(dispatchCalls[1][0].meta.arg).to.deep.equal({
262
+ expect(dispatchCalls[1][0].type).toBe(getBookData.rejected.type);
263
+ expect(dispatchCalls[1][0].payload).toStrictEqual(payload);
264
+ expect(dispatchCalls[1][0].meta.arg).toStrictEqual({
266
265
  url: errorBookUrl,
267
266
  });
268
267
  });
@@ -327,27 +326,23 @@ describe("Redux bookEditorSlice...", () => {
327
326
  const dispatchCalls = dispatch.mock.calls;
328
327
  const fetchCalls = fetchMock.calls();
329
328
 
330
- expect(fetchCalls.length).to.equal(1);
331
- expect(fetchCalls[0].identifier).to.equal("valid-csrf-token-post");
332
- expect(
333
- (fetchCalls[0][1].headers as Headers).get(csrfTokenHeader)
334
- ).to.equal(validCsrfToken);
329
+ expect(fetchCalls.length).toBe(1);
330
+ expect(fetchCalls[0].identifier).toBe("valid-csrf-token-post");
331
+ expect((fetchCalls[0][1].headers as Headers).get(csrfTokenHeader)).toBe(
332
+ validCsrfToken
333
+ );
335
334
 
336
- expect(fetchCalls[0][0]).to.equal(editBookUrl);
337
- expect(fetchCalls[0][1].method).to.equal("POST");
338
- expect(fetchCalls[0][1].body).to.equal(formData);
335
+ expect(fetchCalls[0][0]).toBe(editBookUrl);
336
+ expect(fetchCalls[0][1].method).toBe("POST");
337
+ expect(fetchCalls[0][1].body).toBe(formData);
339
338
 
340
- expect(dispatchCalls.length).to.equal(3);
341
- expect(dispatchCalls[0][0].type).to.equal(submitBookData.pending.type);
342
- expect(dispatchCalls[0][0].payload).to.equal(undefined);
339
+ expect(dispatchCalls.length).toBe(3);
340
+ expect(dispatchCalls[0][0].type).toBe(submitBookData.pending.type);
341
+ expect(dispatchCalls[0][0].payload).toBe(undefined);
343
342
  // On a successful update, the second dispatch is to re-fetch the updated book data.
344
343
  // The third dispatch is for the fulfilled action.
345
- expect(dispatchCalls[2][0].type).to.equal(
346
- submitBookData.fulfilled.type
347
- );
348
- expect(dispatchCalls[2][0].payload.body.toString()).to.equal(
349
- "Success!"
350
- );
344
+ expect(dispatchCalls[2][0].type).toBe(submitBookData.fulfilled.type);
345
+ expect(dispatchCalls[2][0].payload.body.toString()).toBe("Success!");
351
346
  });
352
347
  it("should fail, if the user is unauthorized", async () => {
353
348
  const csrfToken = "invalid-token";
@@ -365,24 +360,24 @@ describe("Redux bookEditorSlice...", () => {
365
360
  const dispatchCalls = dispatch.mock.calls;
366
361
  const fetchCalls = fetchMock.calls();
367
362
 
368
- expect(fetchCalls.length).to.equal(1);
369
- expect(fetchCalls[0].identifier).to.equal("invalid-csrf-token-post");
363
+ expect(fetchCalls.length).toBe(1);
364
+ expect(fetchCalls[0].identifier).toBe("invalid-csrf-token-post");
370
365
  expect(
371
366
  (fetchCalls[0][1].headers as Headers).get(csrfTokenHeader)
372
- ).not.to.equal(validCsrfToken);
367
+ ).not.toBe(validCsrfToken);
373
368
 
374
- expect(fetchCalls[0][0]).to.equal(editBookUrl);
375
- expect(fetchCalls[0][1].method).to.equal("POST");
376
- expect(fetchCalls[0][1].body).to.equal(formData);
369
+ expect(fetchCalls[0][0]).toBe(editBookUrl);
370
+ expect(fetchCalls[0][1].method).toBe("POST");
371
+ expect(fetchCalls[0][1].body).toBe(formData);
377
372
 
378
- expect(dispatchCalls.length).to.equal(2);
379
- expect(dispatchCalls[0][0].type).to.equal(submitBookData.pending.type);
380
- expect(dispatchCalls[0][0].payload).to.equal(undefined);
373
+ expect(dispatchCalls.length).toBe(2);
374
+ expect(dispatchCalls[0][0].type).toBe(submitBookData.pending.type);
375
+ expect(dispatchCalls[0][0].payload).toBe(undefined);
381
376
  // There is no re-fetch on a failed request, ...
382
377
  // ...so the second dispatch is for the rejected action.
383
- expect(dispatchCalls[1][0].type).to.equal(submitBookData.rejected.type);
384
- expect(dispatchCalls[1][0].payload.status).to.equal(400);
385
- expect(dispatchCalls[1][0].payload.response).to.equal(
378
+ expect(dispatchCalls[1][0].type).toBe(submitBookData.rejected.type);
379
+ expect(dispatchCalls[1][0].payload.status).toBe(400);
380
+ expect(dispatchCalls[1][0].payload.response).toBe(
386
381
  "There was an error saving your changes."
387
382
  );
388
383
  });