@truedat/audit 8.5.4 → 8.5.6

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/audit",
3
- "version": "8.5.4",
3
+ "version": "8.5.6",
4
4
  "description": "Truedat Web Audit Module",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -51,7 +51,7 @@
51
51
  "@testing-library/jest-dom": "^6.6.3",
52
52
  "@testing-library/react": "^16.3.0",
53
53
  "@testing-library/user-event": "^14.6.1",
54
- "@truedat/test": "8.5.4",
54
+ "@truedat/test": "8.5.6",
55
55
  "identity-obj-proxy": "^3.0.0",
56
56
  "jest": "^29.7.0",
57
57
  "redux-saga-test-plan": "^4.0.6"
@@ -83,5 +83,5 @@
83
83
  "semantic-ui-react": "^3.0.0-beta.2",
84
84
  "swr": "^2.3.3"
85
85
  },
86
- "gitHead": "75272567eb3ec948a5cdeb8346ef9cacac58267f"
86
+ "gitHead": "41e5e6138f5622558bae4151e720c040c4581162"
87
87
  }
@@ -11,6 +11,7 @@ import {
11
11
  } from "semantic-ui-react";
12
12
  import { FormattedMessage, useIntl } from "react-intl";
13
13
  import { connect } from "react-redux";
14
+ import { visibleEvents } from "../subscriptionConstants";
14
15
  import SubscriptionCrumbs from "./SubscriptionCrumbs";
15
16
  import SubscriptionActions from "./SubscriptionActions";
16
17
 
@@ -75,7 +76,7 @@ export const Subscription = ({
75
76
  : identifier;
76
77
  const resourceName = subscription.resource?.name;
77
78
  const periodicity = subscription.periodicity;
78
- const events = subscription.scope?.events;
79
+ const events = visibleEvents(subscription.scope?.events);
79
80
  const status = subscription.scope?.status;
80
81
  const filters = subscription.scope?.filters;
81
82
  const template = _.find({ id: filters?.template?.id })(templates);
@@ -9,11 +9,14 @@ import {
9
9
  CONCEPT_EVENTS,
10
10
  DEFAULT_ICON,
11
11
  EVENTS_BY_TYPE,
12
+ expandPairedEvents,
13
+ isHiddenPairedEvent,
12
14
  isQualityControlSubscriptionsEnabled,
13
15
  RESOURCE_TYPES,
14
16
  RESOURCE_TYPE_ICONS,
15
17
  STATUSES_BY_EVENT,
16
18
  SUBSCRIBER_TYPE_ICONS,
19
+ visibleEvents,
17
20
  isSubset,
18
21
  } from "../subscriptionConstants";
19
22
  import ContentFilters from "./ContentFilters";
@@ -483,18 +486,20 @@ export const SubscriptionForm = ({
483
486
  disabled={_.isEmpty(eventsForType)}
484
487
  label={formatMessage({ id: "subscriptions.events" })}
485
488
  onBlur={onBlur}
486
- onChange={(_e, { value }) => onChange(value)}
489
+ onChange={(_e, { value }) => onChange(expandPairedEvents(value))}
487
490
  placeholder={formatMessage({ id: "subscriptions.events" })}
488
- value={value}
491
+ value={visibleEvents(value)}
489
492
  required
490
- options={eventsForType.map((event, key) => ({
491
- key,
492
- value: event,
493
- text: formatMessage({
494
- id: `subscriptions.events.${event}`,
495
- defaultMessage: event,
496
- }),
497
- }))}
493
+ options={eventsForType
494
+ .filter((event) => !isHiddenPairedEvent(event))
495
+ .map((event, key) => ({
496
+ key,
497
+ value: event,
498
+ text: formatMessage({
499
+ id: `subscriptions.events.${event}`,
500
+ defaultMessage: event,
501
+ }),
502
+ }))}
498
503
  />
499
504
  )}
500
505
  />
@@ -0,0 +1,303 @@
1
+ import userEvent from "@testing-library/user-event";
2
+ import { waitFor, within } from "@testing-library/react";
3
+ import { render, waitForLoad } from "@truedat/test/render";
4
+ import { domainsMock } from "@truedat/test/mocks";
5
+ import SubscriptionForm from "../SubscriptionForm";
6
+
7
+ jest.mock("@truedat/bg/concepts/relations/components/ConceptSelector", () => ({
8
+ __esModule: true,
9
+ default: ({ handleConceptSelected }) => (
10
+ <button
11
+ data-testid="mock-concept-selector"
12
+ onClick={() =>
13
+ handleConceptSelected({
14
+ business_concept_id: "42",
15
+ type: "foo",
16
+ })
17
+ }
18
+ type="button"
19
+ >
20
+ MockConceptSelector
21
+ </button>
22
+ ),
23
+ }));
24
+
25
+ jest.mock("@truedat/dd/components/StructureSelector", () => ({
26
+ __esModule: true,
27
+ default: ({ onSelect }) => (
28
+ <button
29
+ data-testid="mock-structure-selector"
30
+ onClick={() =>
31
+ onSelect({ id: "7", name: "MyStructure", domain_id: "99" })
32
+ }
33
+ type="button"
34
+ >
35
+ MockStructureSelector
36
+ </button>
37
+ ),
38
+ }));
39
+
40
+ jest.mock("@truedat/cx/sources/components/SourceSelector", () => ({
41
+ __esModule: true,
42
+ default: ({ onChange }) => (
43
+ <button
44
+ data-testid="mock-source-selector"
45
+ onClick={() =>
46
+ onChange(null, {
47
+ value: "5",
48
+ options: [{ key: "5", text: "MySource" }],
49
+ })
50
+ }
51
+ type="button"
52
+ >
53
+ MockSourceSelector
54
+ </button>
55
+ ),
56
+ }));
57
+
58
+ jest.mock(
59
+ "@truedat/qx/components/qualityControls/QualityControlSelector",
60
+ () => ({
61
+ __esModule: true,
62
+ default: ({ onSelect }) => (
63
+ <button
64
+ data-testid="mock-qc-selector"
65
+ onClick={() => onSelect({ quality_control_id: "11", name: "MyQC" })}
66
+ type="button"
67
+ >
68
+ MockQCSelector
69
+ </button>
70
+ ),
71
+ })
72
+ );
73
+
74
+ jest.mock("@truedat/auth/users/components/UserSelector", () => ({
75
+ __esModule: true,
76
+ default: ({ onChange, value }) => (
77
+ <button
78
+ data-testid="mock-user-selector"
79
+ onClick={() => onChange(null, { value: "2" })}
80
+ type="button"
81
+ >
82
+ MockUserSelector:{value}
83
+ </button>
84
+ ),
85
+ }));
86
+
87
+ jest.mock("@truedat/core/hooks/useUserFilters");
88
+
89
+ const variables = { action: "manageSubscriptions" };
90
+ const mocks = [domainsMock(variables)];
91
+ const state = {
92
+ roles: [{ name: "role1" }, { name: "role2" }],
93
+ templates: [],
94
+ users: [
95
+ { full_name: "user1", id: 1 },
96
+ { full_name: "user2", id: 2 },
97
+ ],
98
+ };
99
+ const renderOpts = { state, mocks };
100
+
101
+ describe("<SubscriptionForm /> lazy selectors and email", () => {
102
+ it("submits with concept resource selected via ConceptSelector", async () => {
103
+ const onSubmit = jest.fn();
104
+ const user = userEvent.setup({ delay: null });
105
+
106
+ const rendered = render(
107
+ <SubscriptionForm onSubmit={onSubmit} />,
108
+ renderOpts
109
+ );
110
+ await waitForLoad(rendered);
111
+
112
+ await user.click(rendered.getByText(/^user$/i));
113
+ await user.click(await rendered.findByTestId("mock-user-selector"));
114
+
115
+ await user.click(rendered.getByText("concept"));
116
+ await user.click(await rendered.findByTestId("mock-concept-selector"));
117
+
118
+ await user.click(rendered.getByText(/daily/i));
119
+ await user.click(await rendered.findByText(/concept_published/i));
120
+
121
+ const saveButton = rendered.getByRole("button", { name: "actions.save" });
122
+ await waitFor(() => expect(saveButton).toBeEnabled());
123
+
124
+ await user.click(saveButton);
125
+
126
+ await waitFor(() => {
127
+ expect(onSubmit).toHaveBeenCalledWith(
128
+ expect.objectContaining({
129
+ subscriber: { type: "user", identifier: "2" },
130
+ scope: expect.objectContaining({
131
+ resource_type: "concept",
132
+ resource_id: "42",
133
+ }),
134
+ periodicity: "daily",
135
+ })
136
+ );
137
+ });
138
+ });
139
+
140
+ it("submits with data_structure resource selected via StructureSelector", async () => {
141
+ const onSubmit = jest.fn();
142
+ const user = userEvent.setup({ delay: null });
143
+
144
+ const rendered = render(
145
+ <SubscriptionForm onSubmit={onSubmit} />,
146
+ renderOpts
147
+ );
148
+ await waitForLoad(rendered);
149
+
150
+ await user.click(rendered.getByText(/^user$/i));
151
+ await user.click(await rendered.findByTestId("mock-user-selector"));
152
+
153
+ await user.click(rendered.getByText(/data_structure/i));
154
+ await user.click(await rendered.findByTestId("mock-structure-selector"));
155
+
156
+ await user.click(rendered.getByText(/daily/i));
157
+ await user.click(await rendered.findByText(/structure_note_published/i));
158
+
159
+ const saveButton = rendered.getByRole("button", { name: "actions.save" });
160
+ await waitFor(() => expect(saveButton).toBeEnabled());
161
+
162
+ await user.click(saveButton);
163
+
164
+ await waitFor(() => {
165
+ expect(onSubmit).toHaveBeenCalledWith(
166
+ expect.objectContaining({
167
+ subscriber: { type: "user", identifier: "2" },
168
+ scope: expect.objectContaining({
169
+ resource_type: "data_structure",
170
+ resource_id: "7",
171
+ resource_name: "MyStructure",
172
+ domain_id: "99",
173
+ }),
174
+ periodicity: "daily",
175
+ })
176
+ );
177
+ });
178
+ });
179
+
180
+ it("submits with source resource selected via SourceSelector", async () => {
181
+ const onSubmit = jest.fn();
182
+ const user = userEvent.setup({ delay: null });
183
+
184
+ const rendered = render(
185
+ <SubscriptionForm onSubmit={onSubmit} />,
186
+ renderOpts
187
+ );
188
+ await waitForLoad(rendered);
189
+
190
+ await user.click(rendered.getByText(/^user$/i));
191
+ await user.click(await rendered.findByTestId("mock-user-selector"));
192
+
193
+ await user.click(rendered.getByText(/^source$/i));
194
+ await user.click(await rendered.findByTestId("mock-source-selector"));
195
+
196
+ await user.click(rendered.getByText(/daily/i));
197
+ await user.click(await rendered.findByText(/status_changed/i));
198
+ await user.click(await rendered.findByText(/job_status_started/i));
199
+
200
+ const saveButton = rendered.getByRole("button", { name: "actions.save" });
201
+ await waitFor(() => expect(saveButton).toBeEnabled());
202
+
203
+ await user.click(saveButton);
204
+
205
+ await waitFor(() => {
206
+ expect(onSubmit).toHaveBeenCalledWith(
207
+ expect.objectContaining({
208
+ subscriber: { type: "user", identifier: "2" },
209
+ scope: expect.objectContaining({
210
+ resource_type: "source",
211
+ resource_id: "5",
212
+ resource_name: "MySource",
213
+ }),
214
+ periodicity: "daily",
215
+ })
216
+ );
217
+ });
218
+ });
219
+
220
+ it("submits with quality_control resource selected via QualityControlSelector", async () => {
221
+ const onSubmit = jest.fn();
222
+ const user = userEvent.setup({ delay: null });
223
+
224
+ const rendered = render(
225
+ <SubscriptionForm onSubmit={onSubmit} />,
226
+ renderOpts
227
+ );
228
+ await waitForLoad(rendered);
229
+
230
+ await user.click(rendered.getByText(/^user$/i));
231
+ await user.click(await rendered.findByTestId("mock-user-selector"));
232
+
233
+ await user.click(rendered.getByText(/quality_control/i));
234
+ await user.click(await rendered.findByTestId("mock-qc-selector"));
235
+
236
+ await user.click(rendered.getByText(/daily/i));
237
+ await user.click(
238
+ await rendered.findByText(/quality_control_version_draft_created/i)
239
+ );
240
+
241
+ const saveButton = rendered.getByRole("button", { name: "actions.save" });
242
+ await waitFor(() => expect(saveButton).toBeEnabled());
243
+
244
+ await user.click(saveButton);
245
+
246
+ await waitFor(() => {
247
+ expect(onSubmit).toHaveBeenCalledWith(
248
+ expect.objectContaining({
249
+ subscriber: { type: "user", identifier: "2" },
250
+ scope: expect.objectContaining({
251
+ resource_type: "quality_control",
252
+ resource_id: "11",
253
+ resource_name: "MyQC",
254
+ }),
255
+ periodicity: "daily",
256
+ })
257
+ );
258
+ });
259
+ });
260
+
261
+ it("updates email subscriber identifier when typing into email input", async () => {
262
+ const onSubmit = jest.fn();
263
+ const user = userEvent.setup({ delay: null });
264
+
265
+ const rendered = render(
266
+ <SubscriptionForm onSubmit={onSubmit} />,
267
+ renderOpts
268
+ );
269
+ await waitForLoad(rendered);
270
+
271
+ await user.click(rendered.getByText(/email/i));
272
+
273
+ await waitFor(() =>
274
+ expect(rendered.getByTestId("email-input")).toBeInTheDocument()
275
+ );
276
+
277
+ const emailInput = within(rendered.getByTestId("email-input")).getByRole(
278
+ "textbox"
279
+ );
280
+ await user.type(emailInput, "test@example.com");
281
+
282
+ expect(emailInput).toHaveValue("test@example.com");
283
+
284
+ await user.click(rendered.getByText(/data_structure/i));
285
+ await user.click(await rendered.findByTestId("mock-structure-selector"));
286
+
287
+ await user.click(rendered.getByText(/daily/i));
288
+ await user.click(await rendered.findByText(/structure_note_published/i));
289
+
290
+ const saveButton = rendered.getByRole("button", { name: "actions.save" });
291
+ await waitFor(() => expect(saveButton).toBeEnabled());
292
+
293
+ await user.click(saveButton);
294
+
295
+ await waitFor(() => {
296
+ expect(onSubmit).toHaveBeenCalledWith(
297
+ expect.objectContaining({
298
+ subscriber: { type: "email", identifier: "test@example.com" },
299
+ })
300
+ );
301
+ });
302
+ });
303
+ });
@@ -33,7 +33,9 @@ export const CONCEPT_EVENTS = [
33
33
  "concept_rejected",
34
34
  "concept_rejection_canceled",
35
35
  "new_concept_draft",
36
+ "create_concept_draft",
36
37
  "update_concept_draft",
38
+ "update_concept",
37
39
  "delete_concept_draft",
38
40
  "comment_created",
39
41
  "relation_created",
@@ -41,6 +43,26 @@ export const CONCEPT_EVENTS = [
41
43
  "relation_deleted",
42
44
  ];
43
45
 
46
+ export const PAIRED_EVENTS = {
47
+ new_concept_draft: "create_concept_draft",
48
+ update_concept_draft: "update_concept",
49
+ };
50
+
51
+ export const HIDDEN_PAIRED_EVENTS = _.values(PAIRED_EVENTS);
52
+
53
+ export const isHiddenPairedEvent = (event) =>
54
+ _.includes(event)(HIDDEN_PAIRED_EVENTS);
55
+
56
+ export const expandPairedEvents = (events = []) =>
57
+ _.uniq(
58
+ _.flatMap((event) =>
59
+ PAIRED_EVENTS[event] ? [event, PAIRED_EVENTS[event]] : [event]
60
+ )(events)
61
+ );
62
+
63
+ export const visibleEvents = (events = []) =>
64
+ _.filter((event) => !isHiddenPairedEvent(event))(events);
65
+
44
66
  export const STRUCTURE_EVENTS = [
45
67
  "structure_note_published",
46
68
  "structure_note_pending_approval",