@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
|
@@ -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
|
-
|
|
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:
|
|
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:
|
|
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}
|
|
220
|
+
name: `${sampleLibraryName} Dashboard`,
|
|
220
221
|
});
|
|
221
|
-
getByRole("heading", { level: 3, name: "
|
|
222
|
-
getByText("
|
|
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:
|
|
237
|
-
getByRole("heading", { level: 3, name: "
|
|
238
|
-
getByText("
|
|
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
|
+
});
|