@thepalaceproject/circulation-admin 1.22.0-post.1 → 1.22.0-post.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -149,5 +149,5 @@
149
149
  "*.{js,jsx,ts,tsx,css,md}": "prettier --write",
150
150
  "*.{js,css,md}": "prettier --write"
151
151
  },
152
- "version": "1.22.0-post.1"
152
+ "version": "1.22.0-post.3"
153
153
  }
@@ -0,0 +1,158 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+ import { componentWithProviders } from "../testUtils/withProviders";
3
+ import { ContextProviderProps } from "../../../src/components/ContextProvider";
4
+ import { ConfigurationSettings, FeatureFlags } from "../../../src/interfaces";
5
+ import {
6
+ useMayRequestInventoryReports,
7
+ useMayViewCollectionBarChart,
8
+ } from "../../../src/businessRules/roleBasedAccess";
9
+
10
+ const setupWrapper = ({
11
+ roles,
12
+ featureFlags,
13
+ }: Partial<ConfigurationSettings>) => {
14
+ const contextProviderProps: ContextProviderProps = {
15
+ featureFlags,
16
+ roles,
17
+ email: "email",
18
+ csrfToken: "token",
19
+ };
20
+ return componentWithProviders({ contextProviderProps });
21
+ };
22
+
23
+ describe("Business rules for role-based access", () => {
24
+ const libraryMatch = "match";
25
+ const libraryMismatch = "mismatch";
26
+
27
+ describe("controls access to inventory reports", () => {
28
+ const testAccess = (
29
+ expectedResult: boolean,
30
+ config: Partial<ConfigurationSettings>
31
+ ) => {
32
+ const wrapper = setupWrapper(config);
33
+ const { result } = renderHook(
34
+ () => useMayRequestInventoryReports({ library: libraryMatch }),
35
+ { wrapper }
36
+ );
37
+ expect(result.current).toBe(expectedResult);
38
+ };
39
+
40
+ it("restricts access to only sysadmins, if the restriction feature flag is true", () => {
41
+ const featureFlags: FeatureFlags = { reportsOnlyForSysadmins: true };
42
+
43
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
44
+
45
+ testAccess(false, { roles: [{ role: "manager-all" }], featureFlags });
46
+ testAccess(false, { roles: [{ role: "librarian-all" }], featureFlags });
47
+
48
+ testAccess(false, {
49
+ roles: [{ role: "manager", library: libraryMatch }],
50
+ featureFlags,
51
+ });
52
+ testAccess(false, {
53
+ roles: [{ role: "manager", library: libraryMismatch }],
54
+ featureFlags,
55
+ });
56
+ testAccess(false, {
57
+ roles: [{ role: "librarian", library: libraryMatch }],
58
+ featureFlags,
59
+ });
60
+ testAccess(false, {
61
+ roles: [{ role: "librarian", library: libraryMismatch }],
62
+ featureFlags,
63
+ });
64
+ });
65
+
66
+ it("allows all users, if the restriction feature flag is is false", () => {
67
+ const featureFlags: FeatureFlags = { reportsOnlyForSysadmins: false };
68
+
69
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
70
+
71
+ testAccess(true, { roles: [{ role: "manager-all" }], featureFlags });
72
+ testAccess(true, { roles: [{ role: "librarian-all" }], featureFlags });
73
+
74
+ testAccess(true, {
75
+ roles: [{ role: "manager", library: libraryMatch }],
76
+ featureFlags,
77
+ });
78
+ testAccess(true, {
79
+ roles: [{ role: "manager", library: libraryMismatch }],
80
+ featureFlags,
81
+ });
82
+ testAccess(true, {
83
+ roles: [{ role: "librarian", library: libraryMatch }],
84
+ featureFlags,
85
+ });
86
+ testAccess(true, {
87
+ roles: [{ role: "librarian", library: libraryMismatch }],
88
+ featureFlags,
89
+ });
90
+ });
91
+
92
+ it("allows all users, if the restriction feature flag is not set", () => {
93
+ const featureFlags: FeatureFlags = {};
94
+
95
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
96
+
97
+ testAccess(true, { roles: [{ role: "manager-all" }], featureFlags });
98
+ testAccess(true, { roles: [{ role: "librarian-all" }], featureFlags });
99
+
100
+ testAccess(true, {
101
+ roles: [{ role: "manager", library: libraryMatch }],
102
+ featureFlags,
103
+ });
104
+ testAccess(true, {
105
+ roles: [{ role: "manager", library: libraryMismatch }],
106
+ featureFlags,
107
+ });
108
+ testAccess(true, {
109
+ roles: [{ role: "librarian", library: libraryMatch }],
110
+ featureFlags,
111
+ });
112
+ testAccess(true, {
113
+ roles: [{ role: "librarian", library: libraryMismatch }],
114
+ featureFlags,
115
+ });
116
+ });
117
+ });
118
+
119
+ describe("controls access to the collection statistics barchart", () => {
120
+ const testAccess = (
121
+ expectedResult: boolean,
122
+ config: Partial<ConfigurationSettings>
123
+ ) => {
124
+ const wrapper = setupWrapper(config);
125
+ const { result } = renderHook(
126
+ () => useMayViewCollectionBarChart({ library: libraryMatch }),
127
+ { wrapper }
128
+ );
129
+ expect(result.current).toBe(expectedResult);
130
+ };
131
+
132
+ it("restricts access to sysadmins", () => {
133
+ const featureFlags: FeatureFlags = {};
134
+
135
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
136
+
137
+ testAccess(false, { roles: [{ role: "manager-all" }], featureFlags });
138
+ testAccess(false, { roles: [{ role: "librarian-all" }], featureFlags });
139
+
140
+ testAccess(false, {
141
+ roles: [{ role: "manager", library: libraryMatch }],
142
+ featureFlags,
143
+ });
144
+ testAccess(false, {
145
+ roles: [{ role: "manager", library: libraryMismatch }],
146
+ featureFlags,
147
+ });
148
+ testAccess(false, {
149
+ roles: [{ role: "librarian", library: libraryMatch }],
150
+ featureFlags,
151
+ });
152
+ testAccess(false, {
153
+ roles: [{ role: "librarian", library: libraryMismatch }],
154
+ featureFlags,
155
+ });
156
+ });
157
+ });
158
+ });
@@ -1,8 +1,9 @@
1
1
  import * as React from "react";
2
2
  import { render } from "@testing-library/react";
3
3
  import LibraryStats, {
4
- CustomTooltip,
4
+ ALL_LIBRARIES_HEADING,
5
5
  } from "../../../src/components/LibraryStats";
6
+ import { CustomTooltip } from "../../../src/components/StatsCollectionsBarChart";
6
7
  import {
7
8
  componentWithProviders,
8
9
  renderWithProviders,
@@ -190,7 +191,7 @@ describe("Dashboard Statistics", () => {
190
191
 
191
192
  // We should show our content without the loading state.
192
193
  assertNotLoadingState({ queryByRole });
193
- getByRole("heading", { level: 2, name: "Statistics for All Libraries" });
194
+ getByRole("heading", { level: 2, name: ALL_LIBRARIES_HEADING });
194
195
 
195
196
  // We haven't made another call, since the response is cached.
196
197
  expect(fetchMock.calls()).toHaveLength(1);
@@ -201,7 +202,7 @@ describe("Dashboard Statistics", () => {
201
202
 
202
203
  // We should show our content immediately, without entering the loading state.
203
204
  assertNotLoadingState({ queryByRole });
204
- getByRole("heading", { level: 2, name: "Statistics for All Libraries" });
205
+ getByRole("heading", { level: 2, name: ALL_LIBRARIES_HEADING });
205
206
 
206
207
  // We never tried to fetch anything because the result is cached.
207
208
  expect(fetchMock.calls()).toHaveLength(0);
@@ -216,10 +217,10 @@ describe("Dashboard Statistics", () => {
216
217
  assertNotLoadingState({ queryByRole });
217
218
  getByRole("heading", {
218
219
  level: 2,
219
- name: `${sampleLibraryName} Statistics`,
220
+ name: `${sampleLibraryName} Dashboard`,
220
221
  });
221
- getByRole("heading", { level: 3, name: "Patrons" });
222
- getByText("132");
222
+ getByRole("heading", { level: 3, name: "Current Circulation Activity" });
223
+ getByText("623");
223
224
 
224
225
  // We never tried to fetch anything because the result is cached.
225
226
  expect(fetchMock.calls()).toHaveLength(0);
@@ -233,9 +234,9 @@ describe("Dashboard Statistics", () => {
233
234
  // We should show our content immediately, without entering the loading state.
234
235
  assertNotLoadingState({ queryByRole });
235
236
 
236
- getByRole("heading", { level: 2, name: "Statistics for All Libraries" });
237
- getByRole("heading", { level: 3, name: "Patrons" });
238
- getByText("145");
237
+ getByRole("heading", { level: 2, name: ALL_LIBRARIES_HEADING });
238
+ getByRole("heading", { level: 3, name: "Current Circulation Activity" });
239
+ getByText("1.6k");
239
240
 
240
241
  // We never tried to fetch anything because the result is cached.
241
242
  expect(fetchMock.calls()).toHaveLength(0);
@@ -0,0 +1,72 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+ import {
3
+ useAppAdmin,
4
+ useAppContext,
5
+ useAppEmail,
6
+ useAppFeatureFlags,
7
+ useCsrfToken,
8
+ } from "../../../src/context/appContext";
9
+ import { componentWithProviders } from "../testUtils/withProviders";
10
+ import { ContextProviderProps } from "../../../src/components/ContextProvider";
11
+ import { FeatureFlags } from "../../../src/interfaces";
12
+
13
+ // TODO: These tests may need to be adjusted in the future.
14
+ // Currently, an AppContext.Provider is injected into the component tree
15
+ // by the ContextProvider, which itself uses a legacy context API. (See
16
+ // https://legacy.reactjs.org/docs/legacy-context.html)
17
+ // but that will change once uses of that API have been removed.
18
+
19
+ describe("AppContext", () => {
20
+ const expectedCsrfToken = "token";
21
+ const expectedEmail = "email";
22
+ const expectedFeatureFlags: FeatureFlags = {
23
+ // @ts-expect-error - "testTrue" & "testFalse" aren't valid feature flags
24
+ testTrue: true,
25
+ testFalse: false,
26
+ };
27
+ const expectedRoles = [{ role: "system" }];
28
+
29
+ const contextProviderProps: ContextProviderProps = {
30
+ csrfToken: expectedCsrfToken,
31
+ featureFlags: expectedFeatureFlags,
32
+ roles: expectedRoles,
33
+ email: expectedEmail,
34
+ };
35
+ const wrapper = componentWithProviders({ contextProviderProps });
36
+
37
+ it("provides useAppContext context hook", () => {
38
+ const { result } = renderHook(() => useAppContext(), { wrapper });
39
+ const value = result.current;
40
+ expect(value.csrfToken).toEqual(expectedCsrfToken);
41
+ expect(value.admin.email).toEqual(expectedEmail);
42
+ expect(value.admin.roles).toEqual(expectedRoles);
43
+ expect(value.featureFlags).toEqual(expectedFeatureFlags);
44
+ });
45
+
46
+ it("provides useAppAdmin context hook", () => {
47
+ const { result } = renderHook(() => useAppAdmin(), { wrapper });
48
+ const admin = result.current;
49
+ expect(admin.email).toEqual(expectedEmail);
50
+ expect(admin.roles).toEqual(expectedRoles);
51
+ });
52
+
53
+ it("provides useAppEmail context hook", () => {
54
+ const { result } = renderHook(() => useAppEmail(), { wrapper });
55
+ const email = result.current;
56
+ expect(email).toEqual(expectedEmail);
57
+ });
58
+
59
+ it("provides useCsrfToken context hook", () => {
60
+ const { result } = renderHook(() => useCsrfToken(), { wrapper });
61
+ const token = result.current;
62
+ expect(token).toEqual(expectedCsrfToken);
63
+ });
64
+
65
+ it("provides useAppFeatureFlags context hook", () => {
66
+ const { result } = renderHook(() => useAppFeatureFlags(), {
67
+ wrapper,
68
+ });
69
+ const flags = result.current;
70
+ expect(flags).toEqual(expectedFeatureFlags);
71
+ });
72
+ });