@truedat/qx 5.12.2 → 5.12.7

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.
Files changed (52) hide show
  1. package/package.json +8 -3
  2. package/src/api.js +3 -1
  3. package/src/components/QxContext.js +3 -0
  4. package/src/components/QxRoutes.js +6 -1
  5. package/src/components/common/DescriptionInput.js +47 -0
  6. package/src/components/common/TestFormWrapper.js +33 -0
  7. package/src/components/common/TypeSelector.js +33 -0
  8. package/src/components/common/index.js +4 -0
  9. package/src/components/functions/FunctionEditor.js +200 -0
  10. package/src/components/functions/FunctionParams.js +122 -0
  11. package/src/components/functions/Functions.js +152 -0
  12. package/src/components/functions/__tests__/FunctionEditor.spec.js +195 -0
  13. package/src/components/functions/__tests__/FunctionParams.spec.js +108 -0
  14. package/src/components/functions/__tests__/Functions.spec.js +95 -0
  15. package/src/components/functions/__tests__/__snapshots__/FunctionEditor.spec.js.snap +1563 -0
  16. package/src/components/functions/__tests__/__snapshots__/FunctionParams.spec.js.snap +228 -0
  17. package/src/components/functions/__tests__/__snapshots__/Functions.spec.js.snap +86 -0
  18. package/src/components/functions/__tests__/useWatchParams.spec.js +23 -0
  19. package/src/components/functions/expressions/ConstantSelector.js +26 -0
  20. package/src/components/functions/expressions/Expression.js +40 -0
  21. package/src/components/functions/expressions/FieldSelector.js +56 -0
  22. package/src/components/functions/expressions/FunctionArgs.js +49 -0
  23. package/src/components/functions/expressions/FunctionSelector.js +95 -0
  24. package/src/components/functions/expressions/ParamSelector.js +51 -0
  25. package/src/components/functions/expressions/ShapeSelector.js +74 -0
  26. package/src/components/functions/expressions/__tests__/ConstantSelector.spec.js +64 -0
  27. package/src/components/functions/expressions/__tests__/Expression.spec.js +131 -0
  28. package/src/components/functions/expressions/__tests__/FunctionArgs.spec.js +86 -0
  29. package/src/components/functions/expressions/__tests__/FunctionSelector.spec.js +69 -0
  30. package/src/components/functions/expressions/__tests__/ParamSelector.spec.js +115 -0
  31. package/src/components/functions/expressions/__tests__/ShapeSelector.spec.js +107 -0
  32. package/src/components/functions/expressions/__tests__/__snapshots__/ConstantSelector.spec.js.snap +149 -0
  33. package/src/components/functions/expressions/__tests__/__snapshots__/Expression.spec.js.snap +904 -0
  34. package/src/components/functions/expressions/__tests__/__snapshots__/FunctionArgs.spec.js.snap +392 -0
  35. package/src/components/functions/expressions/__tests__/__snapshots__/FunctionSelector.spec.js.snap +377 -0
  36. package/src/components/functions/expressions/__tests__/__snapshots__/ParamSelector.spec.js.snap +95 -0
  37. package/src/components/functions/expressions/__tests__/__snapshots__/ShapeSelector.spec.js.snap +290 -0
  38. package/src/components/functions/expressions/constantInputs/AnySelector.js +29 -0
  39. package/src/components/functions/expressions/constantInputs/BooleanSelector.js +37 -0
  40. package/src/components/functions/expressions/constantInputs/DefaultSelector.js +34 -0
  41. package/src/components/functions/expressions/constantInputs/__tests__/AnySelector.spec.js +63 -0
  42. package/src/components/functions/expressions/constantInputs/__tests__/BooleanSelector.spec.js +51 -0
  43. package/src/components/functions/expressions/constantInputs/__tests__/DefaultSelector.spec.js +56 -0
  44. package/src/components/functions/expressions/constantInputs/__tests__/__snapshots__/AnySelector.spec.js.snap +236 -0
  45. package/src/components/functions/expressions/constantInputs/__tests__/__snapshots__/BooleanSelector.spec.js.snap +101 -0
  46. package/src/components/functions/expressions/constantInputs/__tests__/__snapshots__/DefaultSelector.spec.js.snap +39 -0
  47. package/src/components/functions/expressions/constantInputs/index.js +5 -0
  48. package/src/components/functions/useWatchParams.js +13 -0
  49. package/src/hooks/__tests__/useFunctions.spec.js +101 -0
  50. package/src/hooks/useFunctions.js +33 -0
  51. package/src/styles/Expression.less +102 -0
  52. package/src/types.js +38 -0
@@ -0,0 +1,195 @@
1
+ import React from "react";
2
+ import { waitFor } from "@testing-library/react";
3
+ import { render } from "@truedat/test/render";
4
+ import userEvent from "@testing-library/user-event";
5
+ import FunctionEditor from "../FunctionEditor";
6
+
7
+ const renderOpts = {
8
+ messages: {
9
+ en: {
10
+ "functions.expression.shape.function": "function",
11
+ "functions.expression.shape.constant": "constant",
12
+ "functions.expression.shape.param": "param",
13
+ "functions.form.expression.function.placeholder": "function.placeholder",
14
+ "group.props.name": "name",
15
+ "functions.form.name": "name",
16
+ "functions.form.add_description": "add_description",
17
+ "functions.form.name": "name",
18
+ "functions.form.add_param": "add_param",
19
+ "actions.save": "save",
20
+ "actions.cancel": "cancel",
21
+ "actions.delete": "delete",
22
+ "functions.action.delete.header": "delete_header",
23
+ "functions.action.delete.content": "delete_content",
24
+ "functions.form.output": "output",
25
+ "functions.form.params": "params",
26
+ "form.validation.required": "required",
27
+ "confirmation.yes": "confirm_yes",
28
+ "confirmation.no": "confirm_no",
29
+ "actions.discard.confirmation.header": "confirmation_header",
30
+ "actions.discard.confirmation.content": "confirmation_content",
31
+ "functions.expression.constant.false": "false",
32
+ "functions.expression.constant.true": "true",
33
+ "functions.form.description": "description",
34
+ },
35
+ },
36
+ fallback: "lazy",
37
+ };
38
+
39
+ const props = {
40
+ selectedFunction: {
41
+ name: "",
42
+ description: "",
43
+ type: "boolean",
44
+ params: [],
45
+ expression: { shape: "function", value: null },
46
+ },
47
+ functions: [
48
+ {
49
+ name: "eq",
50
+ type: "boolean",
51
+ params: [
52
+ { name: "arg1", type: "any" },
53
+ { name: "arg2", type: "any" },
54
+ ],
55
+ },
56
+ ],
57
+ onSubmit: jest.fn(),
58
+ onCancel: jest.fn(),
59
+ onDelete: jest.fn(),
60
+ isSubmitting: false,
61
+ setDirty: jest.fn(),
62
+ };
63
+
64
+ describe("<FunctionEditor />", () => {
65
+ it("matches the latest snapshot", () => {
66
+ const { container } = render(<FunctionEditor {...props} />, renderOpts);
67
+ expect(container).toMatchSnapshot();
68
+ });
69
+
70
+ it("matches snapshot without selected function", async () => {
71
+ const props = { setDirty: jest.fn() };
72
+ const { container, queryByText } = render(
73
+ <FunctionEditor {...props} />,
74
+ renderOpts
75
+ );
76
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
77
+ expect(container).toMatchSnapshot();
78
+ });
79
+
80
+ it("matches snapshot without onDelete", () => {
81
+ const thisProps = {
82
+ ...props,
83
+ onDelete: null,
84
+ };
85
+ const { container } = render(<FunctionEditor {...thisProps} />, renderOpts);
86
+ expect(container).toMatchSnapshot();
87
+ });
88
+
89
+ it("test cancel button", () => {
90
+ const onCancel = jest.fn();
91
+ const thisProps = { ...props, onCancel };
92
+ const { getByRole } = render(<FunctionEditor {...thisProps} />, renderOpts);
93
+
94
+ userEvent.click(getByRole("button", { name: /cancel/i }));
95
+ expect(onCancel).toHaveBeenCalled();
96
+ });
97
+
98
+ it("test cancel button with confirm", async () => {
99
+ const onCancel = jest.fn();
100
+ const thisProps = { ...props, onCancel };
101
+ const { container, getByRole, getAllByRole } = render(
102
+ <FunctionEditor {...thisProps} />,
103
+ renderOpts
104
+ );
105
+
106
+ userEvent.type(getAllByRole("textbox")[0], "name");
107
+ userEvent.click(getByRole("option", { name: /string/ }));
108
+ userEvent.click(getAllByRole("listitem")[0]);
109
+ userEvent.type(getAllByRole("textbox")[1], "description");
110
+
111
+ userEvent.click(getByRole("option", { name: /boolean/ }));
112
+ userEvent.click(getByRole("option", { name: /constant/i }));
113
+ userEvent.click(getByRole("option", { name: /true/i }));
114
+
115
+ await waitFor(() =>
116
+ expect(getByRole("button", { name: /save/i })).toBeEnabled()
117
+ );
118
+
119
+ userEvent.click(getByRole("button", { name: /cancel/i }));
120
+ expect(onCancel).toHaveBeenCalledTimes(0);
121
+
122
+ userEvent.click(getByRole("button", { name: /modal-negative-action/i }));
123
+ expect(onCancel).toHaveBeenCalledTimes(0);
124
+
125
+ userEvent.click(getByRole("button", { name: /cancel/i }));
126
+ expect(onCancel).toHaveBeenCalledTimes(0);
127
+
128
+ expect(container).toMatchSnapshot();
129
+
130
+ userEvent.click(getByRole("button", { name: /modal-affirmative-action/i }));
131
+ expect(onCancel).toHaveBeenCalledTimes(1);
132
+ });
133
+
134
+ it("test delete button", () => {
135
+ const onDelete = jest.fn();
136
+ const thisProps = { ...props, onDelete };
137
+ const { container, getByRole } = render(
138
+ <FunctionEditor {...thisProps} />,
139
+ renderOpts
140
+ );
141
+
142
+ userEvent.click(getByRole("button", { name: /delete/i }));
143
+ expect(onDelete).toHaveBeenCalledTimes(0);
144
+
145
+ userEvent.click(getByRole("button", { name: /modal-negative-action/i }));
146
+ expect(onDelete).toHaveBeenCalledTimes(0);
147
+
148
+ userEvent.click(getByRole("button", { name: /delete/i }));
149
+ expect(onDelete).toHaveBeenCalledTimes(0);
150
+
151
+ expect(container).toMatchSnapshot();
152
+
153
+ userEvent.click(getByRole("button", { name: /modal-affirmative-action/i }));
154
+ expect(onDelete).toHaveBeenCalledTimes(1);
155
+ });
156
+
157
+ it("test submit", async () => {
158
+ const onSubmit = jest.fn();
159
+ const thisProps = { ...props, onSubmit };
160
+ const { getByRole, getAllByRole } = render(
161
+ <FunctionEditor {...thisProps} />,
162
+ renderOpts
163
+ );
164
+
165
+ expect(getByRole("button", { name: /save/i })).toBeDisabled();
166
+
167
+ userEvent.type(getAllByRole("textbox")[0], "name");
168
+ userEvent.click(getByRole("option", { name: /constant/i }));
169
+ userEvent.click(getByRole("option", { name: /true/i }));
170
+
171
+ await waitFor(() =>
172
+ expect(getByRole("button", { name: /save/i })).toBeEnabled()
173
+ );
174
+
175
+ userEvent.click(getByRole("button", { name: /save/i }));
176
+
177
+ await waitFor(() =>
178
+ expect(getByRole("button", { name: /save/i })).toBeEnabled()
179
+ );
180
+
181
+ expect(onSubmit.mock.calls[0][0]).toEqual({
182
+ description: "",
183
+ expression: {
184
+ shape: "constant",
185
+ value: {
186
+ type: "boolean",
187
+ value: "true",
188
+ },
189
+ },
190
+ name: "name",
191
+ params: [],
192
+ type: "boolean",
193
+ });
194
+ });
195
+ });
@@ -0,0 +1,108 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { waitFor } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import TestFormWrapper from "@truedat/qx/components/common/TestFormWrapper";
6
+ import FunctionParams from "../FunctionParams";
7
+
8
+ const renderOpts = {
9
+ messages: {
10
+ en: {
11
+ "functions.form.required": "required",
12
+ "functions.form.add_description": "add_description",
13
+ "functions.form.name": "name",
14
+ "functions.form.add_param": "add_param",
15
+ "functions.form.description": "description",
16
+ },
17
+ },
18
+ fallback: "lazy",
19
+ };
20
+
21
+ describe("<FunctionParams />", () => {
22
+ it("matches the latest snapshot without param", async () => {
23
+ const { container, queryByText } = render(
24
+ <TestFormWrapper>
25
+ <FunctionParams />
26
+ </TestFormWrapper>,
27
+ renderOpts
28
+ );
29
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
30
+ expect(container).toMatchSnapshot();
31
+ });
32
+
33
+ it("matches the latest snapshot with param", async () => {
34
+ const defaultValues = {
35
+ params: [{ name: "param1", type: "boolean", id: 1 }],
36
+ };
37
+ const { container, queryByText } = render(
38
+ <TestFormWrapper defaultValues={defaultValues}>
39
+ <FunctionParams />
40
+ </TestFormWrapper>,
41
+ renderOpts
42
+ );
43
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
44
+ expect(container).toMatchSnapshot();
45
+ });
46
+
47
+ it("user interaction", async () => {
48
+ const watcher = jest.fn();
49
+
50
+ const testArgName = "test_arg_name";
51
+ const testArgDescription = "test_arg_description";
52
+
53
+ const { queryByText, getByRole, getByPlaceholderText, getByText } = render(
54
+ <TestFormWrapper watcher={watcher}>
55
+ <FunctionParams />
56
+ </TestFormWrapper>,
57
+ renderOpts
58
+ );
59
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
60
+ userEvent.click(getByRole("button", { name: /add_param/i }));
61
+
62
+ const argInput = getByPlaceholderText(/name/i);
63
+ const typeSelector = getByRole("listbox");
64
+ const stringOption = getByRole("option", { name: /string/i });
65
+ const descriptionInputLabel = getByText(/add_description/i);
66
+
67
+ await waitFor(() => expect(argInput).toBeInTheDocument());
68
+ await waitFor(() => expect(typeSelector).toBeInTheDocument());
69
+ await waitFor(() => expect(descriptionInputLabel).toBeInTheDocument());
70
+
71
+ userEvent.click(typeSelector);
72
+ await waitFor(() => expect(descriptionInputLabel).toBeInTheDocument());
73
+
74
+ userEvent.click(stringOption);
75
+
76
+ userEvent.type(argInput, testArgName);
77
+
78
+ userEvent.click(descriptionInputLabel);
79
+ const descriptionInput = getByPlaceholderText(/description/i);
80
+ await waitFor(() => expect(descriptionInput).toBeInTheDocument());
81
+
82
+ userEvent.type(descriptionInput, testArgDescription);
83
+
84
+ expect(watcher).lastCalledWith({
85
+ params: [
86
+ {
87
+ description: "test_arg_description",
88
+ id: 1,
89
+ name: "test_arg_name",
90
+ type: "string",
91
+ },
92
+ ],
93
+ });
94
+
95
+ userEvent.hover(argInput);
96
+ const deleteButton = getByRole("button", { name: /delete/i });
97
+ await waitFor(() => expect(deleteButton).toBeInTheDocument());
98
+ userEvent.unhover(argInput);
99
+ await waitFor(() => expect(deleteButton).not.toBeInTheDocument());
100
+
101
+ userEvent.hover(argInput);
102
+ const newDeleteButton = getByRole("button", { name: /delete/i });
103
+ userEvent.click(newDeleteButton);
104
+ await waitFor(() => expect(argInput).not.toBeInTheDocument());
105
+
106
+ expect(watcher).lastCalledWith({ params: [] });
107
+ });
108
+ });
@@ -0,0 +1,95 @@
1
+ import React from "react";
2
+ import { waitFor } from "@testing-library/react";
3
+ import { render } from "@truedat/test/render";
4
+ import Functions from "../Functions";
5
+
6
+ jest.mock("@truedat/qx/hooks/useFunctions", () => {
7
+ const originalModule = jest.requireActual("@truedat/qx/hooks/useFunctions");
8
+
9
+ return {
10
+ __esModule: true,
11
+ ...originalModule,
12
+ useFunctions: jest.fn(() => ({
13
+ data: {
14
+ data: [
15
+ {
16
+ name: "eq",
17
+ type: "boolean",
18
+ params: [
19
+ { name: "arg1", type: "any" },
20
+ { name: "arg2", type: "any" },
21
+ ],
22
+ },
23
+ {
24
+ name: "user_false",
25
+ type: "boolean",
26
+ params: [],
27
+ expression: {
28
+ shape: "constant",
29
+ value: { type: "boolean", value: "false" },
30
+ },
31
+ },
32
+ ],
33
+ },
34
+ loading: false,
35
+ })),
36
+ useFunctionCreate: jest.fn(() => ({
37
+ trigger: jest.fn(() => new Promise(() => {})),
38
+ isMutating: false,
39
+ })),
40
+ useFunctionDelete: jest.fn(() => ({
41
+ trigger: jest.fn(() => new Promise(() => {})),
42
+ isMutating: false,
43
+ })),
44
+ useFunctionUpdate: jest.fn(() => ({
45
+ trigger: jest.fn(() => new Promise(() => {})),
46
+ isMutating: false,
47
+ })),
48
+ };
49
+ });
50
+
51
+ describe("<Functions />", () => {
52
+ const renderOpts = {
53
+ messages: {
54
+ en: {
55
+ "functions.header": "functions.header",
56
+ "functions.subheader": "functions.subheader",
57
+ "functions.no_selection": "functions.no_selection",
58
+ "functions.empty_list": "functions.empty_list",
59
+ "functions.action.new": "functions.action.new",
60
+ "functions.expression.shape.function": "function",
61
+ "functions.expression.shape.constant": "constant",
62
+ "functions.expression.shape.param": "param",
63
+ "functions.form.expression.function.placeholder":
64
+ "function.placeholder",
65
+ "group.props.name": "name",
66
+ "functions.form.name": "name",
67
+ "functions.form.add_description": "add_description",
68
+ "functions.form.name": "name",
69
+ "functions.form.add_param": "add_param",
70
+ "actions.save": "save",
71
+ "actions.cancel": "cancel",
72
+ "actions.delete": "delete",
73
+ "functions.action.delete.header": "delete_header",
74
+ "functions.action.delete.content": "delete_content",
75
+ "functions.form.output": "output",
76
+ "functions.form.params": "params",
77
+ "form.validation.required": "required",
78
+ "confirmation.yes": "confirm_yes",
79
+ "confirmation.no": "confirm_no",
80
+ "actions.discard.confirmation.header": "confirmation_header",
81
+ "actions.discard.confirmation.content": "confirmation_content",
82
+ "functions.expression.constant.false": "false",
83
+ "functions.expression.constant.true": "true",
84
+ "functions.form.description": "description",
85
+ },
86
+ },
87
+ fallback: "lazy",
88
+ };
89
+
90
+ it("matches the latest snapshot", async () => {
91
+ const { container, queryByText } = render(<Functions />, renderOpts);
92
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
93
+ expect(container).toMatchSnapshot();
94
+ });
95
+ });