datajunction-ui 0.0.1-rc.9 → 0.0.2

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 (205) hide show
  1. package/.env +2 -0
  2. package/.prettierignore +3 -1
  3. package/Makefile +9 -0
  4. package/dj-logo.svg +10 -0
  5. package/package.json +53 -14
  6. package/public/favicon.ico +0 -0
  7. package/public/index.html +1 -1
  8. package/src/__tests__/reportWebVitals.test.jsx +44 -0
  9. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +5 -109
  10. package/src/app/components/AddNodeDropdown.jsx +44 -0
  11. package/src/app/components/ListGroupItem.jsx +9 -1
  12. package/src/app/components/NamespaceHeader.jsx +4 -13
  13. package/src/app/components/NodeListActions.jsx +69 -0
  14. package/src/app/components/NodeMaterializationDelete.jsx +90 -0
  15. package/src/app/components/QueryInfo.jsx +172 -0
  16. package/src/app/components/Search.jsx +94 -0
  17. package/src/app/components/Tab.jsx +8 -1
  18. package/src/app/components/ToggleSwitch.jsx +20 -0
  19. package/src/app/components/__tests__/NodeListActions.test.jsx +94 -0
  20. package/src/app/components/__tests__/QueryInfo.test.jsx +55 -0
  21. package/src/app/components/__tests__/Search.test.jsx +63 -0
  22. package/src/app/components/__tests__/Tab.test.jsx +27 -0
  23. package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
  24. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +8 -3
  25. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
  26. package/src/app/components/djgraph/Collapse.jsx +47 -0
  27. package/src/app/components/djgraph/DJNode.jsx +61 -83
  28. package/src/app/components/djgraph/DJNodeColumns.jsx +75 -0
  29. package/src/app/components/djgraph/DJNodeDimensions.jsx +75 -0
  30. package/src/app/components/djgraph/LayoutFlow.jsx +106 -0
  31. package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
  32. package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
  33. package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
  34. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +84 -40
  35. package/src/app/components/forms/Action.jsx +8 -0
  36. package/src/app/components/forms/NodeNameField.jsx +64 -0
  37. package/src/app/components/search.css +17 -0
  38. package/src/app/constants.js +2 -0
  39. package/src/app/icons/AddItemIcon.jsx +16 -0
  40. package/src/app/icons/AlertIcon.jsx +33 -0
  41. package/src/app/icons/CollapsedIcon.jsx +15 -0
  42. package/src/app/icons/CommitIcon.jsx +45 -0
  43. package/src/app/icons/DJLogo.jsx +36 -0
  44. package/src/app/icons/DeleteIcon.jsx +21 -0
  45. package/src/app/icons/DiffIcon.jsx +63 -0
  46. package/src/app/icons/EditIcon.jsx +18 -0
  47. package/src/app/icons/ExpandedIcon.jsx +15 -0
  48. package/src/app/icons/EyeIcon.jsx +20 -0
  49. package/src/app/icons/FilterIcon.jsx +7 -0
  50. package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
  51. package/src/app/icons/InvalidIcon.jsx +16 -0
  52. package/src/app/icons/JupyterExportIcon.jsx +25 -0
  53. package/src/app/icons/LoadingIcon.jsx +14 -0
  54. package/src/app/icons/NodeIcon.jsx +49 -0
  55. package/src/app/icons/PythonIcon.jsx +14 -0
  56. package/src/app/icons/TableIcon.jsx +14 -0
  57. package/src/app/icons/ValidIcon.jsx +16 -0
  58. package/src/app/index.tsx +118 -37
  59. package/src/app/pages/AddEditNodePage/AlertMessage.jsx +10 -0
  60. package/src/app/pages/AddEditNodePage/ColumnsSelect.jsx +84 -0
  61. package/src/app/pages/AddEditNodePage/DescriptionField.jsx +17 -0
  62. package/src/app/pages/AddEditNodePage/DisplayNameField.jsx +16 -0
  63. package/src/app/pages/AddEditNodePage/FormikSelect.jsx +51 -0
  64. package/src/app/pages/AddEditNodePage/FullNameField.jsx +38 -0
  65. package/src/app/pages/AddEditNodePage/Loadable.jsx +20 -0
  66. package/src/app/pages/AddEditNodePage/MetricMetadataFields.jsx +75 -0
  67. package/src/app/pages/AddEditNodePage/MetricQueryField.jsx +71 -0
  68. package/src/app/pages/AddEditNodePage/NamespaceField.jsx +40 -0
  69. package/src/app/pages/AddEditNodePage/NodeModeField.jsx +14 -0
  70. package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +94 -0
  71. package/src/app/pages/AddEditNodePage/OwnersField.jsx +54 -0
  72. package/src/app/pages/AddEditNodePage/RequiredDimensionsSelect.jsx +54 -0
  73. package/src/app/pages/AddEditNodePage/TagsField.jsx +47 -0
  74. package/src/app/pages/AddEditNodePage/UpstreamNodeField.jsx +49 -0
  75. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +109 -0
  76. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +287 -0
  77. package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +75 -0
  78. package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +31 -0
  79. package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
  80. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +54 -0
  81. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +3 -0
  82. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +3 -0
  83. package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +224 -0
  84. package/src/app/pages/AddEditNodePage/index.jsx +506 -0
  85. package/src/app/pages/AddEditTagPage/Loadable.jsx +16 -0
  86. package/src/app/pages/AddEditTagPage/__tests__/AddEditTagPage.test.jsx +107 -0
  87. package/src/app/pages/AddEditTagPage/index.jsx +132 -0
  88. package/src/app/pages/CubeBuilderPage/DimensionsSelect.jsx +152 -0
  89. package/src/app/pages/CubeBuilderPage/Loadable.jsx +16 -0
  90. package/src/app/pages/CubeBuilderPage/MetricsSelect.jsx +75 -0
  91. package/src/app/pages/CubeBuilderPage/__tests__/index.test.jsx +373 -0
  92. package/src/app/pages/CubeBuilderPage/index.jsx +291 -0
  93. package/src/app/pages/LoginPage/LoginForm.jsx +124 -0
  94. package/src/app/pages/LoginPage/SignupForm.jsx +156 -0
  95. package/src/app/pages/LoginPage/__tests__/index.test.jsx +97 -0
  96. package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
  97. package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
  98. package/src/app/pages/LoginPage/index.jsx +17 -0
  99. package/src/app/pages/NamespacePage/AddNamespacePopover.jsx +85 -0
  100. package/src/app/pages/NamespacePage/Explorer.jsx +61 -0
  101. package/src/app/pages/NamespacePage/FieldControl.jsx +21 -0
  102. package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +30 -0
  103. package/src/app/pages/NamespacePage/TagSelect.jsx +44 -0
  104. package/src/app/pages/NamespacePage/UserSelect.jsx +47 -0
  105. package/src/app/pages/NamespacePage/__tests__/index.test.jsx +297 -0
  106. package/src/app/pages/NamespacePage/index.jsx +319 -42
  107. package/src/app/pages/NodePage/AddBackfillPopover.jsx +165 -0
  108. package/src/app/pages/NodePage/AddMaterializationPopover.jsx +222 -0
  109. package/src/app/pages/NodePage/AvailabilityStateBlock.jsx +67 -0
  110. package/src/app/pages/NodePage/ClientCodePopover.jsx +94 -0
  111. package/src/app/pages/NodePage/DimensionFilter.jsx +86 -0
  112. package/src/app/pages/NodePage/EditColumnDescriptionPopover.jsx +116 -0
  113. package/src/app/pages/NodePage/EditColumnPopover.jsx +116 -0
  114. package/src/app/pages/NodePage/LinkDimensionPopover.jsx +164 -0
  115. package/src/app/pages/NodePage/MaterializationConfigField.jsx +60 -0
  116. package/src/app/pages/NodePage/NodeColumnTab.jsx +256 -30
  117. package/src/app/pages/NodePage/NodeDependenciesTab.jsx +153 -0
  118. package/src/app/pages/NodePage/NodeGraphTab.jsx +119 -148
  119. package/src/app/pages/NodePage/NodeHistory.jsx +236 -0
  120. package/src/app/pages/NodePage/NodeInfoTab.jsx +325 -49
  121. package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
  122. package/src/app/pages/NodePage/NodeMaterializationTab.jsx +585 -0
  123. package/src/app/pages/NodePage/NodeRevisionMaterializationTab.jsx +58 -0
  124. package/src/app/pages/NodePage/NodeStatus.jsx +100 -31
  125. package/src/app/pages/NodePage/NodeValidateTab.jsx +367 -0
  126. package/src/app/pages/NodePage/NodesWithDimension.jsx +42 -0
  127. package/src/app/pages/NodePage/NotebookDownload.jsx +36 -0
  128. package/src/app/pages/NodePage/PartitionColumnPopover.jsx +151 -0
  129. package/src/app/pages/NodePage/PartitionValueForm.jsx +60 -0
  130. package/src/app/pages/NodePage/RevisionDiff.jsx +209 -0
  131. package/src/app/pages/NodePage/WatchNodeButton.jsx +226 -0
  132. package/src/app/pages/NodePage/__tests__/AddBackfillPopover.test.jsx +56 -0
  133. package/src/app/pages/NodePage/__tests__/AddMaterializationPopover.test.jsx +87 -0
  134. package/src/app/pages/NodePage/__tests__/DimensionFilter.test.jsx +74 -0
  135. package/src/app/pages/NodePage/__tests__/EditColumnDescriptionPopover.test.jsx +149 -0
  136. package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +148 -0
  137. package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +161 -0
  138. package/src/app/pages/NodePage/__tests__/NodeColumnTab.test.jsx +166 -0
  139. package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +151 -0
  140. package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +595 -0
  141. package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +58 -0
  142. package/src/app/pages/NodePage/__tests__/NodeMaterializationTab.test.jsx +190 -0
  143. package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +872 -0
  144. package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
  145. package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +164 -0
  146. package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +19 -0
  147. package/src/app/pages/NodePage/index.jsx +190 -44
  148. package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
  149. package/src/app/pages/OverviewPage/ByStatusPanel.jsx +69 -0
  150. package/src/app/pages/OverviewPage/DimensionNodeUsagePanel.jsx +48 -0
  151. package/src/app/pages/OverviewPage/GovernanceWarningsPanel.jsx +107 -0
  152. package/src/app/pages/OverviewPage/Loadable.jsx +16 -0
  153. package/src/app/pages/OverviewPage/NodesByTypePanel.jsx +63 -0
  154. package/src/app/pages/OverviewPage/OverviewPanel.jsx +94 -0
  155. package/src/app/pages/OverviewPage/TrendsPanel.jsx +66 -0
  156. package/src/app/pages/OverviewPage/__tests__/ByStatusPanel.test.jsx +36 -0
  157. package/src/app/pages/OverviewPage/__tests__/DimensionNodeUsagePanel.test.jsx +76 -0
  158. package/src/app/pages/OverviewPage/__tests__/GovernanceWarningsPanel.test.jsx +77 -0
  159. package/src/app/pages/OverviewPage/__tests__/NodesByTypePanel.test.jsx +86 -0
  160. package/src/app/pages/OverviewPage/__tests__/OverviewPanel.test.jsx +78 -0
  161. package/src/app/pages/OverviewPage/__tests__/TrendsPanel.test.jsx +120 -0
  162. package/src/app/pages/OverviewPage/__tests__/index.test.jsx +54 -0
  163. package/src/app/pages/OverviewPage/index.jsx +22 -0
  164. package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
  165. package/src/app/pages/RegisterTablePage/__tests__/RegisterTablePage.test.jsx +110 -0
  166. package/src/app/pages/RegisterTablePage/__tests__/__snapshots__/RegisterTablePage.test.jsx.snap +38 -0
  167. package/src/app/pages/RegisterTablePage/index.jsx +142 -0
  168. package/src/app/pages/Root/__tests__/index.test.jsx +79 -0
  169. package/src/app/pages/Root/index.tsx +84 -6
  170. package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
  171. package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
  172. package/src/app/pages/SQLBuilderPage/index.jsx +390 -0
  173. package/src/app/pages/TagPage/Loadable.jsx +16 -0
  174. package/src/app/pages/TagPage/__tests__/TagPage.test.jsx +70 -0
  175. package/src/app/pages/TagPage/index.jsx +79 -0
  176. package/src/app/services/DJService.js +1314 -21
  177. package/src/app/services/__tests__/DJService.test.jsx +1559 -0
  178. package/src/index.tsx +1 -0
  179. package/src/mocks/mockNodes.jsx +1474 -0
  180. package/src/setupTests.ts +31 -1
  181. package/src/styles/dag.css +117 -5
  182. package/src/styles/index.css +1027 -30
  183. package/src/styles/loading.css +34 -0
  184. package/src/styles/login.css +81 -0
  185. package/src/styles/node-creation.scss +276 -0
  186. package/src/styles/node-list.css +4 -0
  187. package/src/styles/overview.css +72 -0
  188. package/src/styles/sorted-table.css +15 -0
  189. package/src/styles/styles.scss +44 -0
  190. package/src/styles/styles.scss.d.ts +9 -0
  191. package/src/utils/form.jsx +23 -0
  192. package/webpack.config.js +16 -6
  193. package/.babelrc +0 -4
  194. package/.env.local +0 -4
  195. package/.env.production +0 -1
  196. package/.github/pull_request_template.md +0 -11
  197. package/.github/workflows/ci.yml +0 -33
  198. package/.vscode/extensions.json +0 -7
  199. package/.vscode/launch.json +0 -15
  200. package/.vscode/settings.json +0 -25
  201. package/Dockerfile +0 -7
  202. package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
  203. package/src/app/pages/ListNamespacesPage/index.jsx +0 -62
  204. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -45
  205. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
@@ -0,0 +1,87 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, waitFor, screen } from '@testing-library/react';
3
+ import DJClientContext from '../../../providers/djclient';
4
+ import AddMaterializationPopover from '../AddMaterializationPopover';
5
+ import { mocks } from '../../../../mocks/mockNodes';
6
+
7
+ const mockDjClient = {
8
+ DataJunctionAPI: {
9
+ materialize: jest.fn(),
10
+ materializationInfo: jest.fn(),
11
+ },
12
+ };
13
+
14
+ describe('<AddMaterializationPopover />', () => {
15
+ it('renders correctly and handles form submission', async () => {
16
+ // Mock onSubmit function
17
+ const onSubmitMock = jest.fn();
18
+ mockDjClient.DataJunctionAPI.materialize.mockReturnValue({
19
+ status: 201,
20
+ json: {
21
+ message: 'Saved!',
22
+ },
23
+ });
24
+ mockDjClient.DataJunctionAPI.materializationInfo.mockReturnValue({
25
+ status: 200,
26
+ json: {
27
+ job_types: [
28
+ {
29
+ name: 'spark_sql',
30
+ label: 'Spark SQL',
31
+ description: 'Spark SQL materialization job',
32
+ allowed_node_types: ['transform', 'dimension', 'cube'],
33
+ job_class: 'SparkSqlMaterializationJob',
34
+ },
35
+ {
36
+ name: 'druid_metrics_cube',
37
+ label: 'Druid Cube',
38
+ description:
39
+ 'Used to materialize a cube to Druid for low-latency access to a set of metrics and dimensions. While the logical cube definition is at the level of metrics and dimensions, a materialized Druid cube will reference measures and dimensions, with rollup configured on the measures where appropriate.',
40
+ allowed_node_types: ['cube'],
41
+ job_class: 'DruidCubeMaterializationJob',
42
+ },
43
+ ],
44
+ strategies: [
45
+ {
46
+ name: 'full',
47
+ label: 'Full',
48
+ },
49
+ {
50
+ name: 'snapshot',
51
+ label: 'Snapshot',
52
+ },
53
+ {
54
+ name: 'incremental_time',
55
+ label: 'Incremental Time',
56
+ },
57
+ {
58
+ name: 'view',
59
+ label: 'View',
60
+ },
61
+ ],
62
+ },
63
+ });
64
+
65
+ // Render the component
66
+ const { getByText } = render(
67
+ <DJClientContext.Provider value={mockDjClient}>
68
+ <AddMaterializationPopover
69
+ node={mocks.mockMetricNode}
70
+ onSubmit={onSubmitMock}
71
+ />
72
+ </DJClientContext.Provider>,
73
+ );
74
+
75
+ // Open the popover
76
+ fireEvent.click(getByText('+ Add Materialization'));
77
+
78
+ // Save the materialization
79
+ fireEvent.click(getByText('Save'));
80
+
81
+ // Expect setAttributes to be called
82
+ await waitFor(() => {
83
+ expect(mockDjClient.DataJunctionAPI.materialize).toHaveBeenCalled();
84
+ expect(getByText('Saved!')).toBeInTheDocument();
85
+ });
86
+ });
87
+ });
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import DJClientContext from '../../../providers/djclient';
5
+ import DimensionFilter from '../DimensionFilter';
6
+
7
+ // Mock DJClientContext
8
+ const mockDJClient = {
9
+ DataJunctionAPI: {
10
+ node: jest.fn(),
11
+ nodeData: jest.fn(),
12
+ },
13
+ };
14
+
15
+ const mockDimension = {
16
+ label: 'Dimension Label [Test]',
17
+ value: 'dimension_value',
18
+ metadata: {
19
+ node_name: 'test_node',
20
+ node_display_name: 'Test Node',
21
+ },
22
+ };
23
+
24
+ const getByTextStartsWith = (container, text) => {
25
+ return Array.from(container.querySelectorAll('*')).find(element => {
26
+ return element.textContent.trim().startsWith(text);
27
+ });
28
+ };
29
+
30
+ describe('DimensionFilter', () => {
31
+ it('fetches dimension data and renders correctly', async () => {
32
+ // Mock node response
33
+ const mockNodeResponse = {
34
+ type: 'dimension',
35
+ name: 'test_node',
36
+ columns: [
37
+ {
38
+ name: 'id',
39
+ attributes: [{ attribute_type: { name: 'primary_key' } }],
40
+ },
41
+ { name: 'name', attributes: [{ attribute_type: { name: 'label' } }] },
42
+ ],
43
+ };
44
+ mockDJClient.DataJunctionAPI.node.mockResolvedValue(mockNodeResponse);
45
+
46
+ // Mock node data response
47
+ const mockNodeDataResponse = {
48
+ results: [
49
+ {
50
+ columns: [{ semantic_entity: 'id' }, { semantic_entity: 'name' }],
51
+ rows: [
52
+ [1, 'Value 1'],
53
+ [2, 'Value 2'],
54
+ ],
55
+ },
56
+ ],
57
+ };
58
+ mockDJClient.DataJunctionAPI.nodeData.mockResolvedValue(
59
+ mockNodeDataResponse,
60
+ );
61
+
62
+ const { container } = render(
63
+ <DJClientContext.Provider value={mockDJClient}>
64
+ <DimensionFilter dimension={mockDimension} onChange={jest.fn()} />
65
+ </DJClientContext.Provider>,
66
+ );
67
+
68
+ // Check if the dimension label and node display name are rendered
69
+ expect(
70
+ getByTextStartsWith(container, 'Dimension Label'),
71
+ ).toBeInTheDocument();
72
+ expect(screen.getByText('Test Node')).toBeInTheDocument();
73
+ });
74
+ });
@@ -0,0 +1,149 @@
1
+ import React from 'react';
2
+ import {
3
+ render,
4
+ fireEvent,
5
+ waitFor,
6
+ screen,
7
+ within,
8
+ } from '@testing-library/react';
9
+ import EditColumnDescriptionPopover from '../EditColumnDescriptionPopover';
10
+ import DJClientContext from '../../../providers/djclient';
11
+
12
+ const mockDjClient = {
13
+ DataJunctionAPI: {
14
+ setColumnDescription: jest.fn(),
15
+ },
16
+ };
17
+
18
+ describe('<EditColumnDescriptionPopover />', () => {
19
+ it('renders correctly and handles successful form submission', async () => {
20
+ // Mock necessary data
21
+ const column = {
22
+ name: 'column1',
23
+ description: 'Initial description',
24
+ };
25
+ const node = { name: 'default.node1' };
26
+
27
+ // Mock onSubmit function
28
+ const onSubmitMock = jest.fn();
29
+
30
+ mockDjClient.DataJunctionAPI.setColumnDescription.mockReturnValue({
31
+ status: 200,
32
+ json: { message: 'Description updated successfully' },
33
+ });
34
+
35
+ // Render the component
36
+ render(
37
+ <DJClientContext.Provider value={mockDjClient}>
38
+ <EditColumnDescriptionPopover
39
+ column={column}
40
+ node={node}
41
+ onSubmit={onSubmitMock}
42
+ />
43
+ </DJClientContext.Provider>,
44
+ );
45
+
46
+ // Open the popover
47
+ fireEvent.click(screen.getByLabelText('EditColumnDescription'));
48
+
49
+ // Update the description
50
+ const dialog = screen.getByRole('dialog', { name: /edit-description/i });
51
+ const descriptionTextarea = within(dialog).getByRole('textbox');
52
+ fireEvent.change(descriptionTextarea, {
53
+ target: { value: 'Updated description' },
54
+ });
55
+
56
+ // Submit the form
57
+ fireEvent.click(screen.getByText('Save'));
58
+
59
+ // Expect setColumnDescription to be called
60
+ await waitFor(() => {
61
+ expect(
62
+ mockDjClient.DataJunctionAPI.setColumnDescription,
63
+ ).toHaveBeenCalledWith('default.node1', 'column1', 'Updated description');
64
+ expect(screen.getByText('Saved!')).toBeInTheDocument();
65
+ expect(onSubmitMock).toHaveBeenCalled();
66
+ });
67
+ });
68
+
69
+ it('handles failed form submission', async () => {
70
+ // Mock necessary data
71
+ const column = {
72
+ name: 'column1',
73
+ description: 'Initial description',
74
+ };
75
+ const node = { name: 'default.node1' };
76
+
77
+ // Mock onSubmit function
78
+ const onSubmitMock = jest.fn();
79
+
80
+ mockDjClient.DataJunctionAPI.setColumnDescription.mockReturnValue({
81
+ status: 500,
82
+ json: { message: 'Server error' },
83
+ });
84
+
85
+ // Render the component
86
+ render(
87
+ <DJClientContext.Provider value={mockDjClient}>
88
+ <EditColumnDescriptionPopover
89
+ column={column}
90
+ node={node}
91
+ onSubmit={onSubmitMock}
92
+ />
93
+ </DJClientContext.Provider>,
94
+ );
95
+
96
+ // Open the popover
97
+ fireEvent.click(screen.getByLabelText('EditColumnDescription'));
98
+
99
+ // Update the description
100
+ const dialog = screen.getByRole('dialog', { name: /edit-description/i });
101
+ const descriptionTextarea = within(dialog).getByRole('textbox');
102
+ fireEvent.change(descriptionTextarea, {
103
+ target: { value: 'Updated description' },
104
+ });
105
+
106
+ // Submit the form
107
+ fireEvent.click(screen.getByText('Save'));
108
+
109
+ // Expect setColumnDescription to be called and the failure message to show up
110
+ await waitFor(() => {
111
+ expect(
112
+ mockDjClient.DataJunctionAPI.setColumnDescription,
113
+ ).toHaveBeenCalledWith('default.node1', 'column1', 'Updated description');
114
+ expect(screen.getByText('Server error')).toBeInTheDocument();
115
+ expect(onSubmitMock).toHaveBeenCalled();
116
+ });
117
+ });
118
+
119
+ it('renders with empty initial description', async () => {
120
+ // Mock necessary data with no description
121
+ const column = {
122
+ name: 'column1',
123
+ description: null,
124
+ };
125
+ const node = { name: 'default.node1' };
126
+
127
+ // Mock onSubmit function
128
+ const onSubmitMock = jest.fn();
129
+
130
+ // Render the component
131
+ render(
132
+ <DJClientContext.Provider value={mockDjClient}>
133
+ <EditColumnDescriptionPopover
134
+ column={column}
135
+ node={node}
136
+ onSubmit={onSubmitMock}
137
+ />
138
+ </DJClientContext.Provider>,
139
+ );
140
+
141
+ // Open the popover
142
+ fireEvent.click(screen.getByLabelText('EditColumnDescription'));
143
+
144
+ // Check that the textarea is empty
145
+ const dialog = screen.getByRole('dialog', { name: /edit-description/i });
146
+ const descriptionTextarea = within(dialog).getByRole('textbox');
147
+ expect(descriptionTextarea.value).toBe('');
148
+ });
149
+ });
@@ -0,0 +1,148 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, waitFor, screen } from '@testing-library/react';
3
+ import EditColumnPopover from '../EditColumnPopover';
4
+ import DJClientContext from '../../../providers/djclient';
5
+
6
+ const mockDjClient = {
7
+ DataJunctionAPI: {
8
+ setAttributes: jest.fn(),
9
+ },
10
+ };
11
+
12
+ describe('<EditColumnPopover />', () => {
13
+ it('renders correctly and handles form submission', async () => {
14
+ // Mock necessary data
15
+ const column = {
16
+ name: 'column1',
17
+ dimension: { name: 'dimension1' },
18
+ attributes: [
19
+ { attribute_type: { name: 'primary_key', namespace: 'system' } },
20
+ ],
21
+ };
22
+ const node = { name: 'default.node1' };
23
+ const options = [
24
+ { value: 'dimension', label: 'Dimension' },
25
+ { value: 'primary_key', label: 'Primary Key' },
26
+ ];
27
+
28
+ // Mock onSubmit function
29
+ const onSubmitMock = jest.fn();
30
+
31
+ mockDjClient.DataJunctionAPI.setAttributes.mockReturnValue({
32
+ status: 201,
33
+ json: [
34
+ {
35
+ name: 'id',
36
+ type: 'int',
37
+ attributes: [
38
+ { attribute_type: { name: 'primary_key', namespace: 'system' } },
39
+ ],
40
+ dimension: null,
41
+ },
42
+ ],
43
+ });
44
+
45
+ // Render the component
46
+ const { getByLabelText, getByText, getByTestId } = render(
47
+ <DJClientContext.Provider value={mockDjClient}>
48
+ <EditColumnPopover
49
+ column={column}
50
+ node={node}
51
+ options={options}
52
+ onSubmit={onSubmitMock}
53
+ />
54
+ </DJClientContext.Provider>,
55
+ );
56
+
57
+ // Open the popover
58
+ fireEvent.click(getByLabelText('EditColumn'));
59
+
60
+ // Click on one attribute in the select
61
+ const editAttributes = getByTestId('edit-attributes');
62
+ fireEvent.keyDown(editAttributes.firstChild, { key: 'ArrowDown' });
63
+ fireEvent.click(screen.getByText('Dimension'));
64
+ fireEvent.click(getByText('Save'));
65
+ getByText('Save').click();
66
+
67
+ // Expect setAttributes to be called
68
+ await waitFor(() => {
69
+ expect(mockDjClient.DataJunctionAPI.setAttributes).toHaveBeenCalled();
70
+ expect(getByText('Saved!')).toBeInTheDocument();
71
+ });
72
+
73
+ // Click on two attributes in the select
74
+ fireEvent.keyDown(editAttributes.firstChild, { key: 'ArrowDown' });
75
+ fireEvent.click(screen.getByText('Dimension'));
76
+ fireEvent.click(screen.getByText('Primary Key'));
77
+ fireEvent.click(getByText('Save'));
78
+ getByText('Save').click();
79
+
80
+ // Expect setAttributes to be called
81
+ await waitFor(() => {
82
+ expect(mockDjClient.DataJunctionAPI.setAttributes).toHaveBeenCalledWith(
83
+ 'default.node1',
84
+ 'column1',
85
+ ['primary_key', 'dimension'],
86
+ );
87
+ expect(getByText('Saved!')).toBeInTheDocument();
88
+ });
89
+
90
+ // Close the popover
91
+ fireEvent.click(getByLabelText('EditColumn'));
92
+ });
93
+
94
+ it('handles failed form submission', async () => {
95
+ // Mock necessary data
96
+ const column = {
97
+ name: 'column1',
98
+ dimension: { name: 'dimension1' },
99
+ attributes: [
100
+ { attribute_type: { name: 'primary_key', namespace: 'system' } },
101
+ ],
102
+ };
103
+ const node = { name: 'default.node1' };
104
+ const options = [
105
+ { value: 'dimension', label: 'Dimension' },
106
+ { value: 'primary_key', label: 'Primary Key' },
107
+ ];
108
+
109
+ // Mock onSubmit function
110
+ const onSubmitMock = jest.fn();
111
+
112
+ mockDjClient.DataJunctionAPI.setAttributes.mockReturnValue({
113
+ status: 500,
114
+ json: { message: 'bad request' },
115
+ });
116
+
117
+ // Render the component
118
+ const { getByLabelText, getByText, getByTestId } = render(
119
+ <DJClientContext.Provider value={mockDjClient}>
120
+ <EditColumnPopover
121
+ column={column}
122
+ node={node}
123
+ options={options}
124
+ onSubmit={onSubmitMock}
125
+ />
126
+ </DJClientContext.Provider>,
127
+ );
128
+
129
+ // Open the popover
130
+ fireEvent.click(getByLabelText('EditColumn'));
131
+
132
+ // Click on one attribute in the select
133
+ const editAttributes = getByTestId('edit-attributes');
134
+ fireEvent.keyDown(editAttributes.firstChild, { key: 'ArrowDown' });
135
+ fireEvent.click(screen.getByText('Dimension'));
136
+ fireEvent.click(getByText('Save'));
137
+ getByText('Save').click();
138
+
139
+ // Expect setAttributes to be called and the failure message to show up
140
+ await waitFor(() => {
141
+ expect(mockDjClient.DataJunctionAPI.setAttributes).toHaveBeenCalled();
142
+ expect(getByText('bad request')).toBeInTheDocument();
143
+ });
144
+
145
+ // Close the popover
146
+ fireEvent.click(getByLabelText('EditColumn'));
147
+ });
148
+ });
@@ -0,0 +1,161 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, waitFor, screen } from '@testing-library/react';
3
+ import LinkDimensionPopover from '../LinkDimensionPopover';
4
+ import DJClientContext from '../../../providers/djclient';
5
+
6
+ const mockDjClient = {
7
+ DataJunctionAPI: {
8
+ linkDimension: jest.fn(),
9
+ unlinkDimension: jest.fn(),
10
+ },
11
+ };
12
+
13
+ describe('<LinkDimensionPopover />', () => {
14
+ it('renders correctly and handles form submission', async () => {
15
+ // Mock necessary data
16
+ const column = {
17
+ name: 'column1',
18
+ dimension: { name: 'default.dimension1' },
19
+ };
20
+ const node = { name: 'default.node1' };
21
+ const options = [
22
+ { value: 'default.dimension1', label: 'Dimension 1' },
23
+ { value: 'default.dimension2', label: 'Dimension 2' },
24
+ ];
25
+
26
+ // Mock onSubmit function
27
+ const onSubmitMock = jest.fn();
28
+
29
+ mockDjClient.DataJunctionAPI.linkDimension.mockReturnValue({
30
+ status: 200,
31
+ json: { message: 'Success' },
32
+ });
33
+
34
+ mockDjClient.DataJunctionAPI.unlinkDimension.mockReturnValue({
35
+ status: 200,
36
+ json: { message: 'Success' },
37
+ });
38
+
39
+ // Render the component
40
+ const { getByLabelText, getByText, getByTestId } = render(
41
+ <DJClientContext.Provider value={mockDjClient}>
42
+ <LinkDimensionPopover
43
+ column={column}
44
+ dimensionNodes={['default.dimension1']}
45
+ node={node}
46
+ options={options}
47
+ onSubmit={onSubmitMock}
48
+ />
49
+ </DJClientContext.Provider>,
50
+ );
51
+
52
+ // Open the popover
53
+ fireEvent.click(getByLabelText('LinkDimension'));
54
+
55
+ // Click on a dimension and save
56
+ const linkDimension = getByTestId('link-dimension');
57
+ fireEvent.keyDown(linkDimension.firstChild, { key: 'ArrowDown' });
58
+ fireEvent.click(screen.getByText('Dimension 2'));
59
+ fireEvent.click(getByText('Save'));
60
+
61
+ // Expect linkDimension to be called
62
+ await waitFor(() => {
63
+ expect(mockDjClient.DataJunctionAPI.linkDimension).toHaveBeenCalledWith(
64
+ 'default.node1',
65
+ 'column1',
66
+ 'default.dimension2',
67
+ );
68
+ expect(getByText('Saved!')).toBeInTheDocument();
69
+ });
70
+
71
+ // Click on the 'Remove' option and save
72
+ const removeButton = screen.getByLabelText('Remove default.dimension1');
73
+ fireEvent.click(removeButton);
74
+ fireEvent.click(getByText('Save'));
75
+
76
+ // Expect unlinkDimension to be called
77
+ await waitFor(() => {
78
+ expect(mockDjClient.DataJunctionAPI.unlinkDimension).toHaveBeenCalledWith(
79
+ 'default.node1',
80
+ 'column1',
81
+ 'default.dimension1',
82
+ );
83
+ expect(getByText('Removed dimension link!')).toBeInTheDocument();
84
+ });
85
+ });
86
+
87
+ it('handles failed form submission', async () => {
88
+ // Mock necessary data
89
+ const column = {
90
+ name: 'column1',
91
+ dimension: { name: 'default.dimension1' },
92
+ };
93
+ const node = { name: 'default.node1' };
94
+ const options = [
95
+ { value: 'default.dimension1', label: 'Dimension 1' },
96
+ { value: 'default.dimension2', label: 'Dimension 2' },
97
+ ];
98
+
99
+ // Mock onSubmit function
100
+ const onSubmitMock = jest.fn();
101
+
102
+ mockDjClient.DataJunctionAPI.linkDimension.mockReturnValue({
103
+ status: 500,
104
+ json: { message: 'Failed due to nonexistent dimension' },
105
+ });
106
+
107
+ mockDjClient.DataJunctionAPI.unlinkDimension.mockReturnValue({
108
+ status: 500,
109
+ json: { message: 'Failed due to no dimension link' },
110
+ });
111
+
112
+ // Render the component
113
+ const { getByLabelText, getByText, getByTestId } = render(
114
+ <DJClientContext.Provider value={mockDjClient}>
115
+ <LinkDimensionPopover
116
+ column={column}
117
+ dimensionNodes={['default.dimension1']}
118
+ node={node}
119
+ options={options}
120
+ onSubmit={onSubmitMock}
121
+ />
122
+ </DJClientContext.Provider>,
123
+ );
124
+
125
+ // Open the popover
126
+ fireEvent.click(getByLabelText('LinkDimension'));
127
+
128
+ // Click on a dimension and save
129
+ const linkDimension = getByTestId('link-dimension');
130
+ fireEvent.keyDown(linkDimension.firstChild, { key: 'ArrowDown' });
131
+ fireEvent.click(screen.getByText('Dimension 2'));
132
+ fireEvent.click(getByText('Save'));
133
+
134
+ // Expect linkDimension to be called
135
+ await waitFor(() => {
136
+ expect(mockDjClient.DataJunctionAPI.linkDimension).toHaveBeenCalledWith(
137
+ 'default.node1',
138
+ 'column1',
139
+ 'default.dimension2',
140
+ );
141
+ expect(
142
+ getByText('Failed due to nonexistent dimension'),
143
+ ).toBeInTheDocument();
144
+ });
145
+
146
+ // Click on the 'Remove' option and save
147
+ const removeButton = screen.getByLabelText('Remove default.dimension1');
148
+ fireEvent.click(removeButton);
149
+ fireEvent.click(getByText('Save'));
150
+
151
+ // Expect unlinkDimension to be called
152
+ await waitFor(() => {
153
+ expect(mockDjClient.DataJunctionAPI.unlinkDimension).toHaveBeenCalledWith(
154
+ 'default.node1',
155
+ 'column1',
156
+ 'default.dimension1',
157
+ );
158
+ expect(getByText('Failed due to no dimension link')).toBeInTheDocument();
159
+ });
160
+ });
161
+ });