datajunction-ui 0.0.1-rc.9 → 0.0.2-0.dev1

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 (244) hide show
  1. package/.env +2 -0
  2. package/.prettierignore +3 -1
  3. package/Makefile +9 -0
  4. package/cleanup-deps.sh +70 -0
  5. package/dj-logo.svg +10 -0
  6. package/package.json +53 -14
  7. package/public/favicon.ico +0 -0
  8. package/public/index.html +1 -1
  9. package/runit.sh +30 -0
  10. package/runit2.sh +30 -0
  11. package/src/__tests__/reportWebVitals.test.jsx +44 -0
  12. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +5 -109
  13. package/src/app/components/AddNodeDropdown.jsx +44 -0
  14. package/src/app/components/ListGroupItem.jsx +9 -1
  15. package/src/app/components/NamespaceHeader.jsx +4 -13
  16. package/src/app/components/NodeListActions.jsx +69 -0
  17. package/src/app/components/NodeMaterializationDelete.jsx +90 -0
  18. package/src/app/components/NotificationBell.tsx +223 -0
  19. package/src/app/components/QueryInfo.jsx +172 -0
  20. package/src/app/components/Search.jsx +94 -0
  21. package/src/app/components/Tab.jsx +8 -1
  22. package/src/app/components/ToggleSwitch.jsx +20 -0
  23. package/src/app/components/UserMenu.tsx +100 -0
  24. package/src/app/components/__tests__/NodeListActions.test.jsx +94 -0
  25. package/src/app/components/__tests__/NodeMaterializationDelete.test.jsx +263 -0
  26. package/src/app/components/__tests__/NotificationBell.test.tsx +302 -0
  27. package/src/app/components/__tests__/QueryInfo.test.jsx +183 -0
  28. package/src/app/components/__tests__/Search.test.jsx +307 -0
  29. package/src/app/components/__tests__/Tab.test.jsx +27 -0
  30. package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
  31. package/src/app/components/__tests__/UserMenu.test.tsx +241 -0
  32. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +8 -3
  33. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
  34. package/src/app/components/djgraph/Collapse.jsx +47 -0
  35. package/src/app/components/djgraph/DJNode.jsx +61 -83
  36. package/src/app/components/djgraph/DJNodeColumns.jsx +75 -0
  37. package/src/app/components/djgraph/DJNodeDimensions.jsx +75 -0
  38. package/src/app/components/djgraph/LayoutFlow.jsx +106 -0
  39. package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
  40. package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
  41. package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
  42. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +84 -40
  43. package/src/app/components/forms/Action.jsx +8 -0
  44. package/src/app/components/forms/NodeNameField.jsx +64 -0
  45. package/src/app/components/search.css +17 -0
  46. package/src/app/constants.js +2 -0
  47. package/src/app/icons/AddItemIcon.jsx +16 -0
  48. package/src/app/icons/AlertIcon.jsx +33 -0
  49. package/src/app/icons/CollapsedIcon.jsx +15 -0
  50. package/src/app/icons/CommitIcon.jsx +45 -0
  51. package/src/app/icons/DJLogo.jsx +36 -0
  52. package/src/app/icons/DeleteIcon.jsx +21 -0
  53. package/src/app/icons/DiffIcon.jsx +63 -0
  54. package/src/app/icons/EditIcon.jsx +18 -0
  55. package/src/app/icons/ExpandedIcon.jsx +15 -0
  56. package/src/app/icons/EyeIcon.jsx +20 -0
  57. package/src/app/icons/FilterIcon.jsx +7 -0
  58. package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
  59. package/src/app/icons/InvalidIcon.jsx +16 -0
  60. package/src/app/icons/JupyterExportIcon.jsx +25 -0
  61. package/src/app/icons/LoadingIcon.jsx +14 -0
  62. package/src/app/icons/NodeIcon.jsx +49 -0
  63. package/src/app/icons/NotificationIcon.jsx +27 -0
  64. package/src/app/icons/PythonIcon.jsx +14 -0
  65. package/src/app/icons/SettingsIcon.jsx +28 -0
  66. package/src/app/icons/TableIcon.jsx +14 -0
  67. package/src/app/icons/ValidIcon.jsx +16 -0
  68. package/src/app/icons/WrenchIcon.jsx +36 -0
  69. package/src/app/index.tsx +130 -37
  70. package/src/app/pages/AddEditNodePage/AlertMessage.jsx +10 -0
  71. package/src/app/pages/AddEditNodePage/ColumnMetadata.jsx +61 -0
  72. package/src/app/pages/AddEditNodePage/ColumnsMetadataInput.jsx +72 -0
  73. package/src/app/pages/AddEditNodePage/ColumnsSelect.jsx +84 -0
  74. package/src/app/pages/AddEditNodePage/CustomMetadataField.jsx +144 -0
  75. package/src/app/pages/AddEditNodePage/DescriptionField.jsx +17 -0
  76. package/src/app/pages/AddEditNodePage/DisplayNameField.jsx +16 -0
  77. package/src/app/pages/AddEditNodePage/ExperimentationExtension.jsx +338 -0
  78. package/src/app/pages/AddEditNodePage/FormikSelect.jsx +64 -0
  79. package/src/app/pages/AddEditNodePage/FullNameField.jsx +38 -0
  80. package/src/app/pages/AddEditNodePage/Loadable.jsx +20 -0
  81. package/src/app/pages/AddEditNodePage/MetricMetadataFields.jsx +75 -0
  82. package/src/app/pages/AddEditNodePage/MetricQueryField.jsx +71 -0
  83. package/src/app/pages/AddEditNodePage/NamespaceField.jsx +40 -0
  84. package/src/app/pages/AddEditNodePage/NodeModeField.jsx +14 -0
  85. package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +94 -0
  86. package/src/app/pages/AddEditNodePage/OwnersField.jsx +54 -0
  87. package/src/app/pages/AddEditNodePage/RequiredDimensionsSelect.jsx +54 -0
  88. package/src/app/pages/AddEditNodePage/TagsField.jsx +47 -0
  89. package/src/app/pages/AddEditNodePage/UpstreamNodeField.jsx +49 -0
  90. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +110 -0
  91. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +291 -0
  92. package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +75 -0
  93. package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +31 -0
  94. package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
  95. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +54 -0
  96. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +3 -0
  97. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +3 -0
  98. package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +224 -0
  99. package/src/app/pages/AddEditNodePage/index.jsx +545 -0
  100. package/src/app/pages/AddEditTagPage/Loadable.jsx +16 -0
  101. package/src/app/pages/AddEditTagPage/__tests__/AddEditTagPage.test.jsx +107 -0
  102. package/src/app/pages/AddEditTagPage/index.jsx +132 -0
  103. package/src/app/pages/CubeBuilderPage/DimensionsSelect.jsx +152 -0
  104. package/src/app/pages/CubeBuilderPage/Loadable.jsx +16 -0
  105. package/src/app/pages/CubeBuilderPage/MetricsSelect.jsx +75 -0
  106. package/src/app/pages/CubeBuilderPage/__tests__/index.test.jsx +373 -0
  107. package/src/app/pages/CubeBuilderPage/index.jsx +291 -0
  108. package/src/app/pages/LoginPage/LoginForm.jsx +124 -0
  109. package/src/app/pages/LoginPage/SignupForm.jsx +156 -0
  110. package/src/app/pages/LoginPage/__tests__/index.test.jsx +97 -0
  111. package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
  112. package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
  113. package/src/app/pages/LoginPage/index.jsx +17 -0
  114. package/src/app/pages/NamespacePage/AddNamespacePopover.jsx +85 -0
  115. package/src/app/pages/NamespacePage/Explorer.jsx +232 -0
  116. package/src/app/pages/NamespacePage/FieldControl.jsx +21 -0
  117. package/src/app/pages/NamespacePage/NodeModeSelect.jsx +27 -0
  118. package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +30 -0
  119. package/src/app/pages/NamespacePage/TagSelect.jsx +44 -0
  120. package/src/app/pages/NamespacePage/UserSelect.jsx +47 -0
  121. package/src/app/pages/NamespacePage/__tests__/AddNamespacePopover.test.jsx +283 -0
  122. package/src/app/pages/NamespacePage/__tests__/index.test.jsx +331 -0
  123. package/src/app/pages/NamespacePage/index.jsx +356 -42
  124. package/src/app/pages/NodePage/AddBackfillPopover.jsx +165 -0
  125. package/src/app/pages/NodePage/AddComplexDimensionLinkPopover.jsx +367 -0
  126. package/src/app/pages/NodePage/AddMaterializationPopover.jsx +222 -0
  127. package/src/app/pages/NodePage/AvailabilityStateBlock.jsx +67 -0
  128. package/src/app/pages/NodePage/ClientCodePopover.jsx +94 -0
  129. package/src/app/pages/NodePage/DimensionFilter.jsx +86 -0
  130. package/src/app/pages/NodePage/EditColumnDescriptionPopover.jsx +116 -0
  131. package/src/app/pages/NodePage/EditColumnPopover.jsx +116 -0
  132. package/src/app/pages/NodePage/LinkDimensionPopover.jsx +164 -0
  133. package/src/app/pages/NodePage/ManageDimensionLinksDialog.jsx +526 -0
  134. package/src/app/pages/NodePage/MaterializationConfigField.jsx +60 -0
  135. package/src/app/pages/NodePage/NodeColumnTab.jsx +421 -30
  136. package/src/app/pages/NodePage/NodeDependenciesTab.jsx +153 -0
  137. package/src/app/pages/NodePage/NodeGraphTab.jsx +119 -148
  138. package/src/app/pages/NodePage/NodeHistory.jsx +236 -0
  139. package/src/app/pages/NodePage/NodeInfoTab.jsx +346 -49
  140. package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
  141. package/src/app/pages/NodePage/NodeMaterializationTab.jsx +585 -0
  142. package/src/app/pages/NodePage/NodeRevisionMaterializationTab.jsx +58 -0
  143. package/src/app/pages/NodePage/NodeStatus.jsx +100 -31
  144. package/src/app/pages/NodePage/NodeValidateTab.jsx +367 -0
  145. package/src/app/pages/NodePage/NodesWithDimension.jsx +42 -0
  146. package/src/app/pages/NodePage/NotebookDownload.jsx +36 -0
  147. package/src/app/pages/NodePage/PartitionColumnPopover.jsx +151 -0
  148. package/src/app/pages/NodePage/PartitionValueForm.jsx +60 -0
  149. package/src/app/pages/NodePage/RevisionDiff.jsx +209 -0
  150. package/src/app/pages/NodePage/WatchNodeButton.jsx +226 -0
  151. package/src/app/pages/NodePage/__tests__/AddBackfillPopover.test.jsx +56 -0
  152. package/src/app/pages/NodePage/__tests__/AddComplexDimensionLinkPopover.test.jsx +459 -0
  153. package/src/app/pages/NodePage/__tests__/AddMaterializationPopover.test.jsx +87 -0
  154. package/src/app/pages/NodePage/__tests__/DimensionFilter.test.jsx +74 -0
  155. package/src/app/pages/NodePage/__tests__/EditColumnDescriptionPopover.test.jsx +149 -0
  156. package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +144 -0
  157. package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +132 -0
  158. package/src/app/pages/NodePage/__tests__/ManageDimensionLinksDialog.test.jsx +390 -0
  159. package/src/app/pages/NodePage/__tests__/NodeColumnTab.test.jsx +166 -0
  160. package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +151 -0
  161. package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +595 -0
  162. package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +58 -0
  163. package/src/app/pages/NodePage/__tests__/NodeMaterializationTab.test.jsx +190 -0
  164. package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +882 -0
  165. package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
  166. package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +164 -0
  167. package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +19 -0
  168. package/src/app/pages/NodePage/index.jsx +190 -44
  169. package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
  170. package/src/app/pages/NotificationsPage/Loadable.jsx +6 -0
  171. package/src/app/pages/NotificationsPage/__tests__/index.test.jsx +287 -0
  172. package/src/app/pages/NotificationsPage/index.jsx +136 -0
  173. package/src/app/pages/OverviewPage/ByStatusPanel.jsx +69 -0
  174. package/src/app/pages/OverviewPage/DimensionNodeUsagePanel.jsx +48 -0
  175. package/src/app/pages/OverviewPage/GovernanceWarningsPanel.jsx +107 -0
  176. package/src/app/pages/OverviewPage/Loadable.jsx +16 -0
  177. package/src/app/pages/OverviewPage/NodesByTypePanel.jsx +63 -0
  178. package/src/app/pages/OverviewPage/OverviewPanel.jsx +94 -0
  179. package/src/app/pages/OverviewPage/TrendsPanel.jsx +66 -0
  180. package/src/app/pages/OverviewPage/__tests__/ByStatusPanel.test.jsx +36 -0
  181. package/src/app/pages/OverviewPage/__tests__/DimensionNodeUsagePanel.test.jsx +76 -0
  182. package/src/app/pages/OverviewPage/__tests__/GovernanceWarningsPanel.test.jsx +77 -0
  183. package/src/app/pages/OverviewPage/__tests__/NodesByTypePanel.test.jsx +86 -0
  184. package/src/app/pages/OverviewPage/__tests__/OverviewPanel.test.jsx +78 -0
  185. package/src/app/pages/OverviewPage/__tests__/TrendsPanel.test.jsx +120 -0
  186. package/src/app/pages/OverviewPage/__tests__/index.test.jsx +54 -0
  187. package/src/app/pages/OverviewPage/index.jsx +22 -0
  188. package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
  189. package/src/app/pages/RegisterTablePage/__tests__/RegisterTablePage.test.jsx +112 -0
  190. package/src/app/pages/RegisterTablePage/__tests__/__snapshots__/RegisterTablePage.test.jsx.snap +38 -0
  191. package/src/app/pages/RegisterTablePage/index.jsx +142 -0
  192. package/src/app/pages/Root/__tests__/index.test.jsx +44 -0
  193. package/src/app/pages/Root/index.tsx +92 -10
  194. package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
  195. package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
  196. package/src/app/pages/SQLBuilderPage/index.jsx +390 -0
  197. package/src/app/pages/SettingsPage/CreateServiceAccountModal.jsx +152 -0
  198. package/src/app/pages/SettingsPage/Loadable.jsx +16 -0
  199. package/src/app/pages/SettingsPage/NotificationSubscriptionsSection.jsx +189 -0
  200. package/src/app/pages/SettingsPage/ProfileSection.jsx +41 -0
  201. package/src/app/pages/SettingsPage/ServiceAccountsSection.jsx +95 -0
  202. package/src/app/pages/SettingsPage/__tests__/CreateServiceAccountModal.test.jsx +318 -0
  203. package/src/app/pages/SettingsPage/__tests__/NotificationSubscriptionsSection.test.jsx +233 -0
  204. package/src/app/pages/SettingsPage/__tests__/ProfileSection.test.jsx +65 -0
  205. package/src/app/pages/SettingsPage/__tests__/ServiceAccountsSection.test.jsx +150 -0
  206. package/src/app/pages/SettingsPage/__tests__/index.test.jsx +184 -0
  207. package/src/app/pages/SettingsPage/index.jsx +148 -0
  208. package/src/app/pages/TagPage/Loadable.jsx +16 -0
  209. package/src/app/pages/TagPage/__tests__/TagPage.test.jsx +70 -0
  210. package/src/app/pages/TagPage/index.jsx +79 -0
  211. package/src/app/services/DJService.js +1444 -21
  212. package/src/app/services/__tests__/DJService.test.jsx +2118 -0
  213. package/src/app/utils/__tests__/date.test.js +198 -0
  214. package/src/app/utils/date.js +65 -0
  215. package/src/index.tsx +1 -0
  216. package/src/mocks/mockNodes.jsx +1477 -0
  217. package/src/setupTests.ts +31 -1
  218. package/src/styles/dag.css +117 -5
  219. package/src/styles/index.css +1028 -31
  220. package/src/styles/loading.css +34 -0
  221. package/src/styles/login.css +81 -0
  222. package/src/styles/nav-bar.css +274 -0
  223. package/src/styles/node-creation.scss +276 -0
  224. package/src/styles/node-list.css +4 -0
  225. package/src/styles/overview.css +72 -0
  226. package/src/styles/settings.css +787 -0
  227. package/src/styles/sorted-table.css +15 -0
  228. package/src/styles/styles.scss +44 -0
  229. package/src/styles/styles.scss.d.ts +9 -0
  230. package/src/utils/form.jsx +23 -0
  231. package/webpack.config.js +17 -6
  232. package/.babelrc +0 -4
  233. package/.env.local +0 -4
  234. package/.env.production +0 -1
  235. package/.github/pull_request_template.md +0 -11
  236. package/.github/workflows/ci.yml +0 -33
  237. package/.vscode/extensions.json +0 -7
  238. package/.vscode/launch.json +0 -15
  239. package/.vscode/settings.json +0 -25
  240. package/Dockerfile +0 -7
  241. package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
  242. package/src/app/pages/ListNamespacesPage/index.jsx +0 -62
  243. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -45
  244. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
@@ -0,0 +1,882 @@
1
+ import React from 'react';
2
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
3
+ import { mocks } from '../../../../mocks/mockNodes';
4
+ import DJClientContext from '../../../providers/djclient';
5
+ import { NodePage } from '../Loadable';
6
+ import { MemoryRouter, Route, Routes } from 'react-router-dom';
7
+ import userEvent from '@testing-library/user-event';
8
+
9
+ describe('<NodePage />', () => {
10
+ const domTestingLib = require('@testing-library/dom');
11
+ const { queryHelpers } = domTestingLib;
12
+
13
+ const queryByAttribute = attribute =>
14
+ queryHelpers.queryAllByAttribute.bind(null, attribute);
15
+
16
+ function getByAttribute(container, id, attribute, ...rest) {
17
+ const result = queryByAttribute(attribute)(container, id, ...rest);
18
+ return result[0];
19
+ }
20
+
21
+ const mockDJClient = () => {
22
+ return {
23
+ DataJunctionAPI: {
24
+ node: jest.fn(),
25
+ metric: jest.fn(),
26
+ getMetric: jest.fn(),
27
+ revalidate: jest.fn().mockReturnValue({ status: 'valid' }),
28
+ node_dag: jest.fn().mockReturnValue(mocks.mockNodeDAG),
29
+ clientCode: jest.fn().mockReturnValue('dj_client = DJClient()'),
30
+ columns: jest.fn(),
31
+ history: jest.fn(),
32
+ revisions: jest.fn(),
33
+ materializations: jest.fn(),
34
+ availabilityStates: jest.fn(),
35
+ refreshLatestMaterialization: jest.fn(),
36
+ materializationInfo: jest.fn(),
37
+ sql: jest.fn(),
38
+ cube: jest.fn(),
39
+ compiledSql: jest.fn(),
40
+ node_lineage: jest.fn(),
41
+ nodesWithDimension: jest.fn(),
42
+ attributes: jest.fn(),
43
+ dimensions: jest.fn(),
44
+ setPartition: jest.fn(),
45
+ engines: jest.fn(),
46
+ streamNodeData: jest.fn(),
47
+ nodeDimensions: jest.fn(),
48
+ getNotificationPreferences: jest.fn().mockResolvedValue([]),
49
+ subscribeToNotifications: jest.fn().mockResolvedValue({ status: 200 }),
50
+ unsubscribeFromNotifications: jest
51
+ .fn()
52
+ .mockResolvedValue({ status: 200 }),
53
+ setAttributes: jest.fn().mockResolvedValue({ status: 200 }),
54
+ linkDimension: jest.fn().mockResolvedValue({ status: 200 }),
55
+ unlinkDimension: jest.fn().mockResolvedValue({ status: 200 }),
56
+ addReferenceDimensionLink: jest.fn().mockResolvedValue({ status: 200 }),
57
+ removeReferenceDimensionLink: jest
58
+ .fn()
59
+ .mockResolvedValue({ status: 200 }),
60
+ addComplexDimensionLink: jest.fn().mockResolvedValue({ status: 200 }),
61
+ removeComplexDimensionLink: jest
62
+ .fn()
63
+ .mockResolvedValue({ status: 200 }),
64
+ },
65
+ };
66
+ };
67
+
68
+ const defaultProps = {
69
+ name: 'default.avg_repair_price',
70
+ djNode: {
71
+ namespace: 'default',
72
+ node_revision_id: 24,
73
+ node_id: 24,
74
+ type: 'metric',
75
+ name: 'default.avg_repair_price',
76
+ display_name: 'Default: Avg Repair Price',
77
+ version: 'v1.0',
78
+ status: 'valid',
79
+ mode: 'published',
80
+ catalog: {
81
+ id: 1,
82
+ uuid: '0fc18295-e1a2-4c3c-b72a-894725c12488',
83
+ created_at: '2023-08-21T16:48:51.146121+00:00',
84
+ updated_at: '2023-08-21T16:48:51.146122+00:00',
85
+ extra_params: {},
86
+ name: 'warehouse',
87
+ },
88
+ schema_: null,
89
+ table: null,
90
+ description: 'Average repair price',
91
+ query:
92
+ 'SELECT avg(price) default_DOT_avg_repair_price \n FROM default.repair_order_details\n\n',
93
+ availability: null,
94
+ dimension_links: [],
95
+ columns: [
96
+ {
97
+ name: 'default_DOT_avg_repair_price',
98
+ type: 'double',
99
+ display_name: 'Default DOT avg repair price',
100
+ attributes: [],
101
+ dimension: null,
102
+ },
103
+ ],
104
+ updated_at: '2023-08-21T16:48:56.932231+00:00',
105
+ materializations: [],
106
+ parents: [
107
+ {
108
+ name: 'default.repair_order_details',
109
+ },
110
+ ],
111
+ created_at: '2023-08-21T16:48:56.932162+00:00',
112
+ tags: [{ name: 'purpose', display_name: 'Purpose' }],
113
+ primary_key: [],
114
+ incompatible_druid_functions: ['IF'],
115
+ createNodeClientCode:
116
+ 'dj = DJBuilder(DJ_URL)\n\navg_repair_price = dj.create_metric(\n description="Average repair price",\n display_name="Default: Avg Repair Price",\n name="default.avg_repair_price",\n primary_key=[],\n query="""SELECT avg(price) default_DOT_avg_repair_price \n FROM default.repair_order_details\n\n"""\n)',
117
+ dimensions: [
118
+ {
119
+ name: 'default.date_dim.dateint',
120
+ type: 'timestamp',
121
+ path: [
122
+ 'default.repair_order_details.repair_order_id',
123
+ 'default.repair_order.hard_hat_id',
124
+ 'default.hard_hat.birth_date',
125
+ ],
126
+ },
127
+ {
128
+ name: 'default.date_dim.dateint',
129
+ type: 'timestamp',
130
+ path: [
131
+ 'default.repair_order_details.repair_order_id',
132
+ 'default.repair_order.hard_hat_id',
133
+ 'default.hard_hat.hire_date',
134
+ ],
135
+ },
136
+ {
137
+ name: 'default.date_dim.day',
138
+ type: 'int',
139
+ path: [
140
+ 'default.repair_order_details.repair_order_id',
141
+ 'default.repair_order.hard_hat_id',
142
+ 'default.hard_hat.birth_date',
143
+ ],
144
+ },
145
+ {
146
+ name: 'default.date_dim.day',
147
+ type: 'int',
148
+ path: [
149
+ 'default.repair_order_details.repair_order_id',
150
+ 'default.repair_order.hard_hat_id',
151
+ 'default.hard_hat.hire_date',
152
+ ],
153
+ },
154
+ {
155
+ name: 'default.date_dim.month',
156
+ type: 'int',
157
+ path: [
158
+ 'default.repair_order_details.repair_order_id',
159
+ 'default.repair_order.hard_hat_id',
160
+ 'default.hard_hat.birth_date',
161
+ ],
162
+ },
163
+ {
164
+ name: 'default.date_dim.month',
165
+ type: 'int',
166
+ path: [
167
+ 'default.repair_order_details.repair_order_id',
168
+ 'default.repair_order.hard_hat_id',
169
+ 'default.hard_hat.hire_date',
170
+ ],
171
+ },
172
+ {
173
+ name: 'default.date_dim.year',
174
+ type: 'int',
175
+ path: [
176
+ 'default.repair_order_details.repair_order_id',
177
+ 'default.repair_order.hard_hat_id',
178
+ 'default.hard_hat.birth_date',
179
+ ],
180
+ },
181
+ {
182
+ name: 'default.date_dim.year',
183
+ type: 'int',
184
+ path: [
185
+ 'default.repair_order_details.repair_order_id',
186
+ 'default.repair_order.hard_hat_id',
187
+ 'default.hard_hat.hire_date',
188
+ ],
189
+ },
190
+ {
191
+ name: 'default.hard_hat.address',
192
+ type: 'string',
193
+ path: [
194
+ 'default.repair_order_details.repair_order_id',
195
+ 'default.repair_order.hard_hat_id',
196
+ ],
197
+ },
198
+ {
199
+ name: 'default.hard_hat.birth_date',
200
+ type: 'date',
201
+ path: [
202
+ 'default.repair_order_details.repair_order_id',
203
+ 'default.repair_order.hard_hat_id',
204
+ ],
205
+ },
206
+ {
207
+ name: 'default.hard_hat.city',
208
+ type: 'string',
209
+ path: [
210
+ 'default.repair_order_details.repair_order_id',
211
+ 'default.repair_order.hard_hat_id',
212
+ ],
213
+ },
214
+ {
215
+ name: 'default.hard_hat.contractor_id',
216
+ type: 'int',
217
+ path: [
218
+ 'default.repair_order_details.repair_order_id',
219
+ 'default.repair_order.hard_hat_id',
220
+ ],
221
+ },
222
+ {
223
+ name: 'default.hard_hat.country',
224
+ type: 'string',
225
+ path: [
226
+ 'default.repair_order_details.repair_order_id',
227
+ 'default.repair_order.hard_hat_id',
228
+ ],
229
+ },
230
+ {
231
+ name: 'default.hard_hat.first_name',
232
+ type: 'string',
233
+ path: [
234
+ 'default.repair_order_details.repair_order_id',
235
+ 'default.repair_order.hard_hat_id',
236
+ ],
237
+ },
238
+ {
239
+ name: 'default.hard_hat.hard_hat_id',
240
+ type: 'int',
241
+ path: [
242
+ 'default.repair_order_details.repair_order_id',
243
+ 'default.repair_order.hard_hat_id',
244
+ ],
245
+ },
246
+ {
247
+ name: 'default.hard_hat.hire_date',
248
+ type: 'date',
249
+ path: [
250
+ 'default.repair_order_details.repair_order_id',
251
+ 'default.repair_order.hard_hat_id',
252
+ ],
253
+ },
254
+ {
255
+ name: 'default.hard_hat.last_name',
256
+ type: 'string',
257
+ path: [
258
+ 'default.repair_order_details.repair_order_id',
259
+ 'default.repair_order.hard_hat_id',
260
+ ],
261
+ },
262
+ {
263
+ name: 'default.hard_hat.manager',
264
+ type: 'int',
265
+ path: [
266
+ 'default.repair_order_details.repair_order_id',
267
+ 'default.repair_order.hard_hat_id',
268
+ ],
269
+ },
270
+ {
271
+ name: 'default.hard_hat.postal_code',
272
+ type: 'string',
273
+ path: [
274
+ 'default.repair_order_details.repair_order_id',
275
+ 'default.repair_order.hard_hat_id',
276
+ ],
277
+ },
278
+ {
279
+ name: 'default.hard_hat.state',
280
+ type: 'string',
281
+ path: [
282
+ 'default.repair_order_details.repair_order_id',
283
+ 'default.repair_order.hard_hat_id',
284
+ ],
285
+ },
286
+ {
287
+ name: 'default.hard_hat.title',
288
+ type: 'string',
289
+ path: [
290
+ 'default.repair_order_details.repair_order_id',
291
+ 'default.repair_order.hard_hat_id',
292
+ ],
293
+ },
294
+ ],
295
+ },
296
+ };
297
+
298
+ it('renders the NodeInfo tab correctly for a metric node', async () => {
299
+ const djClient = mockDJClient();
300
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
301
+ djClient.DataJunctionAPI.getMetric.mockReturnValue(
302
+ mocks.mockMetricNodeJson,
303
+ );
304
+ const element = (
305
+ <DJClientContext.Provider value={djClient}>
306
+ <NodePage {...defaultProps} />
307
+ </DJClientContext.Provider>
308
+ );
309
+ const { container } = render(
310
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders/info']}>
311
+ <Routes>
312
+ <Route path="nodes/:name/:tab" element={element} />
313
+ </Routes>
314
+ </MemoryRouter>,
315
+ );
316
+
317
+ await waitFor(() => {
318
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
319
+ 'default.num_repair_orders',
320
+ );
321
+
322
+ expect(
323
+ screen.getByRole('dialog', { name: 'NodeName' }),
324
+ ).toHaveTextContent('default.num_repair_orders');
325
+
326
+ expect(screen.getByRole('button', { name: 'Info' })).toBeInTheDocument();
327
+ expect(
328
+ screen.getByRole('dialog', { name: 'Description' }),
329
+ ).toHaveTextContent('Number of repair orders');
330
+
331
+ expect(screen.getByRole('dialog', { name: 'Version' })).toHaveTextContent(
332
+ 'v1.0',
333
+ );
334
+
335
+ expect(
336
+ screen.getByRole('dialog', { name: 'NodeStatus' }),
337
+ ).toBeInTheDocument();
338
+
339
+ expect(screen.getByRole('dialog', { name: 'Tags' })).toHaveTextContent(
340
+ 'Purpose',
341
+ );
342
+
343
+ expect(
344
+ screen.getByRole('dialog', { name: 'RequiredDimensions' }),
345
+ ).toHaveTextContent('');
346
+
347
+ expect(
348
+ screen.getByRole('dialog', { name: 'DisplayName' }),
349
+ ).toHaveTextContent('Default: Num Repair Orders');
350
+
351
+ expect(
352
+ screen.getByRole('dialog', { name: 'NodeType' }),
353
+ ).toHaveTextContent('metric');
354
+
355
+ expect(
356
+ container.getElementsByClassName('language-sql'),
357
+ ).toMatchSnapshot();
358
+ });
359
+ }, 60000);
360
+
361
+ it('renders the NodeInfo tab correctly for cube nodes', async () => {
362
+ const djClient = mockDJClient();
363
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockCubeNode);
364
+ djClient.DataJunctionAPI.cube.mockReturnValue(mocks.mockCubesCube);
365
+ const element = (
366
+ <DJClientContext.Provider value={djClient}>
367
+ <NodePage {...defaultProps} />
368
+ </DJClientContext.Provider>
369
+ );
370
+ const { container } = render(
371
+ <MemoryRouter initialEntries={['/nodes/default.repair_orders_cube']}>
372
+ <Routes>
373
+ <Route path="nodes/:name" element={element} />
374
+ </Routes>
375
+ </MemoryRouter>,
376
+ );
377
+
378
+ await waitFor(() => {
379
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
380
+ 'default.repair_orders_cube',
381
+ );
382
+ userEvent.click(screen.getByRole('button', { name: 'Info' }));
383
+
384
+ expect(
385
+ screen.getByRole('dialog', { name: 'NodeName' }),
386
+ ).toHaveTextContent('default.repair_orders_cube');
387
+
388
+ expect(screen.getByRole('button', { name: 'Info' })).toBeInTheDocument();
389
+ expect(
390
+ screen.getByRole('dialog', { name: 'Description' }),
391
+ ).toHaveTextContent('Repair Orders');
392
+
393
+ expect(screen.getByRole('dialog', { name: 'Version' })).toHaveTextContent(
394
+ 'v1.0',
395
+ );
396
+
397
+ expect(
398
+ screen.getByRole('dialog', { name: 'PrimaryKey' }),
399
+ ).toHaveTextContent('');
400
+
401
+ expect(
402
+ screen.getByRole('dialog', { name: 'DisplayName' }),
403
+ ).toHaveTextContent('Default: Repair Orders Cube');
404
+
405
+ expect(
406
+ screen.getByRole('dialog', { name: 'NodeType' }),
407
+ ).toHaveTextContent('cube');
408
+
409
+ expect(
410
+ screen.getByRole('dialog', { name: 'NodeType' }),
411
+ ).toHaveTextContent('cube');
412
+
413
+ expect(screen.getByText('Cube Elements')).toBeInTheDocument();
414
+ screen
415
+ .getAllByRole('cell', { name: 'CubeElement' })
416
+ .map(cube => cube.hasAttribute('a'));
417
+ });
418
+ }, 60000);
419
+
420
+ it('renders the NodeColumns tab correctly', async () => {
421
+ const djClient = mockDJClient();
422
+ djClient.DataJunctionAPI.node.mockResolvedValue(mocks.mockMetricNode);
423
+ djClient.DataJunctionAPI.getMetric.mockReturnValue(
424
+ mocks.mockMetricNodeJson,
425
+ );
426
+ djClient.DataJunctionAPI.columns.mockResolvedValue(mocks.metricNodeColumns);
427
+ djClient.DataJunctionAPI.attributes.mockResolvedValue(mocks.attributes);
428
+ djClient.DataJunctionAPI.dimensions.mockResolvedValue(mocks.dimensions);
429
+ djClient.DataJunctionAPI.engines.mockReturnValue([]);
430
+ djClient.DataJunctionAPI.setPartition.mockResolvedValue({
431
+ status: 200,
432
+ json: { message: '' },
433
+ });
434
+
435
+ const element = (
436
+ <DJClientContext.Provider value={djClient}>
437
+ <NodePage />
438
+ </DJClientContext.Provider>
439
+ );
440
+ render(
441
+ <MemoryRouter
442
+ initialEntries={['/nodes/default.num_repair_orders/columns']}
443
+ >
444
+ <Routes>
445
+ <Route path="nodes/:name/:tab" element={element} />
446
+ </Routes>
447
+ </MemoryRouter>,
448
+ );
449
+ await waitFor(() => {
450
+ expect(djClient.DataJunctionAPI.columns).toHaveBeenCalledWith(
451
+ mocks.mockMetricNode,
452
+ );
453
+ expect(
454
+ screen.getByRole('columnheader', { name: 'ColumnName' }),
455
+ ).toHaveTextContent('default_DOT_avg_repair_price');
456
+ expect(
457
+ screen.getByRole('columnheader', { name: 'ColumnDisplayName' }),
458
+ ).toHaveTextContent('Default DOT avg repair price');
459
+ expect(
460
+ screen.getByRole('columnheader', { name: 'ColumnType' }),
461
+ ).toHaveTextContent('double');
462
+
463
+ // check that the edit column popover can be clicked
464
+ const editColumnPopover = screen.getByRole('button', {
465
+ name: 'EditColumn',
466
+ });
467
+ expect(editColumnPopover).toBeInTheDocument();
468
+ fireEvent.click(editColumnPopover);
469
+ expect(
470
+ screen.getByRole('button', { name: 'SaveEditColumn' }),
471
+ ).toBeInTheDocument();
472
+
473
+ // check that the manage dimension links dialog can be opened
474
+ const manageDimensionLinksButton = screen.getByRole('button', {
475
+ name: 'ManageDimensionLinksToggle',
476
+ });
477
+ expect(manageDimensionLinksButton).toBeInTheDocument();
478
+ fireEvent.click(manageDimensionLinksButton);
479
+ expect(
480
+ screen.getByRole('dialog', { name: 'ManageDimensionLinksDialog' }),
481
+ ).toBeInTheDocument();
482
+
483
+ // check that the set column partition popover can be clicked
484
+ const partitionColumnPopover = screen.getByRole('button', {
485
+ name: 'PartitionColumn',
486
+ });
487
+ expect(partitionColumnPopover).toBeInTheDocument();
488
+ fireEvent.click(partitionColumnPopover);
489
+ const savePartition = screen.getByRole('button', {
490
+ name: 'SaveEditColumn',
491
+ });
492
+ expect(savePartition).toBeInTheDocument();
493
+ fireEvent.click(savePartition);
494
+ expect(screen.getByText('Saved!'));
495
+ });
496
+ }, 60000);
497
+
498
+ it('renders the NodeHistory tab correctly', async () => {
499
+ const djClient = mockDJClient();
500
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
501
+ djClient.DataJunctionAPI.getMetric.mockReturnValue(
502
+ mocks.mockMetricNodeJson,
503
+ );
504
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
505
+ djClient.DataJunctionAPI.history.mockReturnValue(mocks.metricNodeHistory);
506
+ djClient.DataJunctionAPI.revisions.mockReturnValue(
507
+ mocks.metricNodeRevisions,
508
+ );
509
+
510
+ const element = (
511
+ <DJClientContext.Provider value={djClient}>
512
+ <NodePage />
513
+ </DJClientContext.Provider>
514
+ );
515
+ const { container } = render(
516
+ <MemoryRouter
517
+ initialEntries={['/nodes/default.num_repair_orders/history']}
518
+ >
519
+ <Routes>
520
+ <Route path="nodes/:name/:tab" element={element} />
521
+ </Routes>
522
+ </MemoryRouter>,
523
+ );
524
+ await waitFor(async () => {
525
+ fireEvent.click(screen.getByRole('button', { name: 'History' }));
526
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
527
+ mocks.mockMetricNode.name,
528
+ );
529
+ expect(djClient.DataJunctionAPI.history).toHaveBeenCalledWith(
530
+ 'node',
531
+ mocks.mockMetricNode.name,
532
+ );
533
+ expect(
534
+ screen.getByRole('list', { name: 'Activity' }),
535
+ ).toBeInTheDocument();
536
+ screen
537
+ .queryAllByRole('cell', {
538
+ name: 'HistoryAttribute',
539
+ })
540
+ .forEach(cell => expect(cell).toHaveTextContent(/Set col1 as /));
541
+
542
+ screen
543
+ .queryAllByRole('cell', {
544
+ name: 'HistoryCreateLink',
545
+ })
546
+ .forEach(cell =>
547
+ expect(cell).toHaveTextContent(
548
+ 'Linked col1 todefault.hard_hat viahard_hat_id',
549
+ ),
550
+ );
551
+
552
+ screen
553
+ .queryAllByRole('cell', {
554
+ name: 'HistoryCreateMaterialization',
555
+ })
556
+ .forEach(cell =>
557
+ expect(cell).toHaveTextContent(
558
+ 'Initialized materialization some_random_materialization',
559
+ ),
560
+ );
561
+
562
+ screen
563
+ .queryAllByRole('cell', {
564
+ name: 'HistoryNodeStatusChange',
565
+ })
566
+ .forEach(cell =>
567
+ expect(cell).toHaveTextContent(
568
+ 'Status changed from valid to invalid Caused by a change in upstream default.repair_order_details',
569
+ ),
570
+ );
571
+ });
572
+ });
573
+
574
+ it('renders compiled sql correctly', async () => {
575
+ const djClient = mockDJClient();
576
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockTransformNode);
577
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
578
+ djClient.DataJunctionAPI.compiledSql.mockReturnValue('select 1');
579
+
580
+ const element = (
581
+ <DJClientContext.Provider value={djClient}>
582
+ <NodePage />
583
+ </DJClientContext.Provider>
584
+ );
585
+ render(
586
+ <MemoryRouter initialEntries={[`/nodes/${mocks.mockTransformNode.name}`]}>
587
+ <Routes>
588
+ <Route path="nodes/:name" element={element} />
589
+ </Routes>
590
+ </MemoryRouter>,
591
+ );
592
+ await waitFor(() => {
593
+ fireEvent.click(screen.getByRole('checkbox', { name: 'ToggleSwitch' }));
594
+ expect(djClient.DataJunctionAPI.compiledSql).toHaveBeenCalledWith(
595
+ mocks.mockTransformNode.name,
596
+ );
597
+ });
598
+ });
599
+
600
+ it('renders an empty NodeMaterialization tab correctly', async () => {
601
+ const djClient = mockDJClient();
602
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
603
+ djClient.DataJunctionAPI.getMetric.mockReturnValue(
604
+ mocks.mockMetricNodeJson,
605
+ );
606
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
607
+ djClient.DataJunctionAPI.materializations.mockReturnValue([]);
608
+ djClient.DataJunctionAPI.availabilityStates.mockReturnValue([]);
609
+
610
+ const element = (
611
+ <DJClientContext.Provider value={djClient}>
612
+ <NodePage />
613
+ </DJClientContext.Provider>
614
+ );
615
+ render(
616
+ <MemoryRouter
617
+ initialEntries={['/nodes/default.num_repair_orders/materializations']}
618
+ >
619
+ <Routes>
620
+ <Route path="nodes/:name/:tab" element={element} />
621
+ </Routes>
622
+ </MemoryRouter>,
623
+ );
624
+ await waitFor(
625
+ () => {
626
+ fireEvent.click(
627
+ screen.getByRole('button', { name: 'Materializations' }),
628
+ );
629
+ expect(djClient.DataJunctionAPI.materializations).toHaveBeenCalledWith(
630
+ mocks.mockMetricNode.name,
631
+ );
632
+ screen.getByText(
633
+ 'No materialization workflows configured for this revision.',
634
+ );
635
+ },
636
+ { timeout: 5000 },
637
+ );
638
+ });
639
+
640
+ it('renders the NodeMaterialization tab with materializations correctly', async () => {
641
+ const djClient = mockDJClient();
642
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockTransformNode);
643
+ djClient.DataJunctionAPI.getMetric.mockReturnValue(
644
+ mocks.mockMetricNodeJson,
645
+ );
646
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
647
+ djClient.DataJunctionAPI.materializations.mockReturnValue(
648
+ mocks.nodeMaterializations,
649
+ );
650
+ djClient.DataJunctionAPI.availabilityStates.mockReturnValue([]);
651
+
652
+ djClient.DataJunctionAPI.materializationInfo.mockReturnValue(
653
+ mocks.materializationInfo,
654
+ );
655
+
656
+ const element = (
657
+ <DJClientContext.Provider value={djClient}>
658
+ <NodePage />
659
+ </DJClientContext.Provider>
660
+ );
661
+ render(
662
+ <MemoryRouter
663
+ initialEntries={[
664
+ '/nodes/default.repair_order_transform/materializations',
665
+ ]}
666
+ >
667
+ <Routes>
668
+ <Route path="nodes/:name/:tab" element={element} />
669
+ </Routes>
670
+ </MemoryRouter>,
671
+ );
672
+ await waitFor(
673
+ () => {
674
+ fireEvent.click(
675
+ screen.getByRole('button', { name: 'Materializations' }),
676
+ );
677
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
678
+ mocks.mockTransformNode.name,
679
+ );
680
+ expect(djClient.DataJunctionAPI.materializations).toHaveBeenCalledWith(
681
+ mocks.mockTransformNode.name,
682
+ );
683
+ },
684
+ { timeout: 3000 },
685
+ );
686
+ }, 60000);
687
+
688
+ it('renders the NodeValidate tab', async () => {
689
+ const djClient = mockDJClient();
690
+ window.scrollTo = jest.fn();
691
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
692
+ djClient.DataJunctionAPI.nodeDimensions.mockReturnValue([]);
693
+ djClient.DataJunctionAPI.getMetric.mockReturnValue(
694
+ mocks.mockMetricNodeJson,
695
+ );
696
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
697
+ djClient.DataJunctionAPI.sql.mockReturnValue({
698
+ sql: 'SELECT * FROM testNode',
699
+ });
700
+ const streamNodeData = {
701
+ onmessage: jest.fn(),
702
+ onerror: jest.fn(),
703
+ close: jest.fn(),
704
+ };
705
+ djClient.DataJunctionAPI.streamNodeData.mockResolvedValue(streamNodeData);
706
+ djClient.DataJunctionAPI.streamNodeData.mockResolvedValueOnce({
707
+ state: 'FINISHED',
708
+ results: [
709
+ {
710
+ columns: [{ name: 'column1' }, { name: 'column2' }],
711
+ rows: [
712
+ [1, 'value1'],
713
+ [2, 'value2'],
714
+ ],
715
+ },
716
+ ],
717
+ });
718
+
719
+ const element = (
720
+ <DJClientContext.Provider value={djClient}>
721
+ <NodePage />
722
+ </DJClientContext.Provider>
723
+ );
724
+ render(
725
+ <MemoryRouter
726
+ initialEntries={['/nodes/default.num_repair_orders/validate']}
727
+ >
728
+ <Routes>
729
+ <Route path="nodes/:name/:tab" element={element} />
730
+ </Routes>
731
+ </MemoryRouter>,
732
+ );
733
+
734
+ await waitFor(() => {
735
+ expect(screen.getByText('Group By')).toBeInTheDocument();
736
+ expect(screen.getByText('Add Filters')).toBeInTheDocument();
737
+ expect(screen.getByText('Generated Query')).toBeInTheDocument();
738
+ expect(screen.getByText('Results')).toBeInTheDocument();
739
+ });
740
+ // Click on the 'Validate' tab
741
+ fireEvent.click(screen.getByRole('button', { name: '► Validate' }));
742
+
743
+ await waitFor(() => {
744
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
745
+ mocks.mockMetricNode.name,
746
+ );
747
+ expect(djClient.DataJunctionAPI.sql).toHaveBeenCalledWith(
748
+ mocks.mockMetricNode.name,
749
+ { dimensions: [], filters: [] },
750
+ );
751
+ expect(djClient.DataJunctionAPI.nodeDimensions).toHaveBeenCalledWith(
752
+ mocks.mockMetricNode.name,
753
+ );
754
+ });
755
+
756
+ // Click on 'Run' to run the node query
757
+ const runButton = screen.getByText('► Run');
758
+ fireEvent.click(runButton);
759
+
760
+ await waitFor(() => {
761
+ expect(djClient.DataJunctionAPI.streamNodeData).toHaveBeenCalledWith(
762
+ mocks.mockMetricNode.name,
763
+ { dimensions: [], filters: [] },
764
+ );
765
+ expect(streamNodeData.onmessage).toBeDefined();
766
+ expect(streamNodeData.onerror).toBeDefined();
767
+ });
768
+
769
+ const infoTab = screen.getByRole('button', { name: 'QueryInfo' });
770
+ const resultsTab = screen.getByText('Results');
771
+
772
+ // Initially, the Results tab should be active
773
+ expect(resultsTab).toHaveClass('active');
774
+ expect(infoTab).not.toHaveClass('active');
775
+
776
+ // Click on the Info tab first
777
+ fireEvent.click(infoTab);
778
+
779
+ await waitFor(() => {
780
+ // Now, the Info tab should be active
781
+ expect(infoTab).toHaveClass('active');
782
+ expect(resultsTab).not.toHaveClass('active');
783
+ });
784
+
785
+ // Click on the Results tab
786
+ fireEvent.click(resultsTab);
787
+
788
+ await waitFor(() => {
789
+ // Now, the Results tab should be active again
790
+ expect(resultsTab).toHaveClass('active');
791
+ expect(infoTab).not.toHaveClass('active');
792
+ });
793
+ });
794
+
795
+ it('renders a NodeColumnLineage tab correctly', async () => {
796
+ const djClient = mockDJClient();
797
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
798
+ djClient.DataJunctionAPI.getMetric.mockReturnValue(
799
+ mocks.mockMetricNodeJson,
800
+ );
801
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
802
+ djClient.DataJunctionAPI.node_lineage.mockReturnValue(
803
+ mocks.mockNodeLineage,
804
+ );
805
+
806
+ const element = (
807
+ <DJClientContext.Provider value={djClient}>
808
+ <NodePage />
809
+ </DJClientContext.Provider>
810
+ );
811
+ render(
812
+ <MemoryRouter
813
+ initialEntries={['/nodes/default.num_repair_orders/lineage']}
814
+ >
815
+ <Routes>
816
+ <Route path="nodes/:name/:tab" element={element} />
817
+ </Routes>
818
+ </MemoryRouter>,
819
+ );
820
+ await waitFor(() => {
821
+ fireEvent.click(screen.getByRole('button', { name: 'Lineage' }));
822
+ expect(djClient.DataJunctionAPI.node_lineage).toHaveBeenCalledWith(
823
+ mocks.mockMetricNode.name,
824
+ );
825
+ });
826
+ });
827
+
828
+ it('renders a NodeGraph tab correctly', async () => {
829
+ const djClient = mockDJClient();
830
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
831
+ djClient.DataJunctionAPI.getMetric.mockReturnValue(
832
+ mocks.mockMetricNodeJson,
833
+ );
834
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
835
+
836
+ const element = (
837
+ <DJClientContext.Provider value={djClient}>
838
+ <NodePage />
839
+ </DJClientContext.Provider>
840
+ );
841
+ render(
842
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders/graph']}>
843
+ <Routes>
844
+ <Route path="nodes/:name/:tab" element={element} />
845
+ </Routes>
846
+ </MemoryRouter>,
847
+ );
848
+ await waitFor(() => {
849
+ fireEvent.click(screen.getByRole('button', { name: 'Graph' }));
850
+ expect(djClient.DataJunctionAPI.node_dag).toHaveBeenCalledWith(
851
+ mocks.mockMetricNode.name,
852
+ );
853
+ });
854
+ });
855
+
856
+ it('renders Linked Nodes tab correctly', async () => {
857
+ const djClient = mockDJClient();
858
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockDimensionNode);
859
+ djClient.DataJunctionAPI.nodesWithDimension.mockReturnValue(
860
+ mocks.mockLinkedNodes,
861
+ );
862
+
863
+ const element = (
864
+ <DJClientContext.Provider value={djClient}>
865
+ <NodePage />
866
+ </DJClientContext.Provider>
867
+ );
868
+ render(
869
+ <MemoryRouter initialEntries={['/nodes/default.dispatcher/linked']}>
870
+ <Routes>
871
+ <Route path="nodes/:name/:tab" element={element} />
872
+ </Routes>
873
+ </MemoryRouter>,
874
+ );
875
+ await waitFor(() => {
876
+ fireEvent.click(screen.getByRole('button', { name: 'Linked Nodes' }));
877
+ expect(djClient.DataJunctionAPI.nodesWithDimension).toHaveBeenCalledWith(
878
+ mocks.mockDimensionNode.name,
879
+ );
880
+ });
881
+ });
882
+ });