datajunction-ui 0.0.1-rc.2 → 0.0.1-rc.21

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 (136) hide show
  1. package/.env +1 -0
  2. package/.prettierignore +3 -1
  3. package/dj-logo.svg +10 -0
  4. package/package.json +43 -13
  5. package/public/favicon.ico +0 -0
  6. package/src/__tests__/reportWebVitals.test.jsx +44 -0
  7. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +5 -39
  8. package/src/app/components/DeleteNode.jsx +79 -0
  9. package/src/app/components/ListGroupItem.jsx +8 -1
  10. package/src/app/components/NamespaceHeader.jsx +4 -13
  11. package/src/app/components/QueryInfo.jsx +77 -0
  12. package/src/app/components/Tab.jsx +3 -2
  13. package/src/app/components/ToggleSwitch.jsx +20 -0
  14. package/src/app/components/__tests__/QueryInfo.test.jsx +55 -0
  15. package/src/app/components/__tests__/Tab.test.jsx +27 -0
  16. package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
  17. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +3 -0
  18. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
  19. package/src/app/components/djgraph/Collapse.jsx +46 -0
  20. package/src/app/components/djgraph/DJNode.jsx +60 -82
  21. package/src/app/components/djgraph/DJNodeColumns.jsx +71 -0
  22. package/src/app/components/djgraph/DJNodeDimensions.jsx +75 -0
  23. package/src/app/components/djgraph/LayoutFlow.jsx +104 -0
  24. package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
  25. package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
  26. package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
  27. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +84 -40
  28. package/src/app/constants.js +2 -0
  29. package/src/app/icons/AlertIcon.jsx +32 -0
  30. package/src/app/icons/CollapsedIcon.jsx +15 -0
  31. package/src/app/icons/DJLogo.jsx +36 -0
  32. package/src/app/icons/DeleteIcon.jsx +21 -0
  33. package/src/app/icons/EditIcon.jsx +18 -0
  34. package/src/app/icons/ExpandedIcon.jsx +15 -0
  35. package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
  36. package/src/app/icons/InvalidIcon.jsx +14 -0
  37. package/src/app/icons/PythonIcon.jsx +52 -0
  38. package/src/app/icons/TableIcon.jsx +14 -0
  39. package/src/app/icons/ValidIcon.jsx +14 -0
  40. package/src/app/index.tsx +79 -26
  41. package/src/app/pages/AddEditNodePage/FormikSelect.jsx +46 -0
  42. package/src/app/pages/AddEditNodePage/FullNameField.jsx +37 -0
  43. package/src/app/pages/AddEditNodePage/Loadable.jsx +16 -0
  44. package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +89 -0
  45. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +77 -0
  46. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +93 -0
  47. package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +75 -0
  48. package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +31 -0
  49. package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
  50. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +53 -0
  51. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +53 -0
  52. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +3 -0
  53. package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +178 -0
  54. package/src/app/pages/AddEditNodePage/index.jsx +357 -0
  55. package/src/app/pages/LoginPage/__tests__/index.test.jsx +70 -0
  56. package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
  57. package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
  58. package/src/app/pages/LoginPage/index.jsx +90 -0
  59. package/src/app/pages/NamespacePage/AddNamespacePopover.jsx +86 -0
  60. package/src/app/pages/NamespacePage/Explorer.jsx +57 -0
  61. package/src/app/pages/NamespacePage/Loadable.jsx +9 -7
  62. package/src/app/pages/NamespacePage/__tests__/index.test.jsx +95 -0
  63. package/src/app/pages/NamespacePage/index.jsx +132 -31
  64. package/src/app/pages/NodePage/ClientCodePopover.jsx +32 -0
  65. package/src/app/pages/NodePage/EditColumnPopover.jsx +102 -0
  66. package/src/app/pages/NodePage/LinkDimensionPopover.jsx +135 -0
  67. package/src/app/pages/NodePage/Loadable.jsx +9 -7
  68. package/src/app/pages/NodePage/NodeColumnTab.jsx +106 -27
  69. package/src/app/pages/NodePage/NodeGraphTab.jsx +94 -148
  70. package/src/app/pages/NodePage/NodeHistory.jsx +212 -0
  71. package/src/app/pages/NodePage/NodeInfoTab.jsx +166 -51
  72. package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
  73. package/src/app/pages/NodePage/NodeMaterializationTab.jsx +174 -0
  74. package/src/app/pages/NodePage/NodeSQLTab.jsx +82 -0
  75. package/src/app/pages/NodePage/NodeStatus.jsx +14 -20
  76. package/src/app/pages/NodePage/NodesWithDimension.jsx +42 -0
  77. package/src/app/pages/NodePage/__tests__/ClientCodePopover.test.jsx +49 -0
  78. package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +148 -0
  79. package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +165 -0
  80. package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +591 -0
  81. package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +57 -0
  82. package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +725 -0
  83. package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
  84. package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +402 -0
  85. package/src/app/pages/NodePage/index.jsx +151 -41
  86. package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
  87. package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
  88. package/src/app/pages/RegisterTablePage/index.jsx +163 -0
  89. package/src/app/pages/Root/__tests__/index.test.jsx +77 -0
  90. package/src/app/pages/Root/index.tsx +32 -4
  91. package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
  92. package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
  93. package/src/app/pages/SQLBuilderPage/index.jsx +390 -0
  94. package/src/app/providers/djclient.jsx +5 -0
  95. package/src/app/services/DJService.js +398 -22
  96. package/src/app/services/__tests__/DJService.test.jsx +609 -0
  97. package/src/mocks/mockNodes.jsx +1397 -0
  98. package/src/setupTests.ts +31 -1
  99. package/src/styles/dag.css +111 -5
  100. package/src/styles/index.css +467 -31
  101. package/src/styles/login.css +67 -0
  102. package/src/styles/node-creation.scss +197 -0
  103. package/src/styles/styles.scss +44 -0
  104. package/src/styles/styles.scss.d.ts +9 -0
  105. package/src/utils/form.jsx +23 -0
  106. package/tsconfig.json +1 -5
  107. package/webpack.config.js +29 -6
  108. package/.babelrc +0 -4
  109. package/.env.local +0 -4
  110. package/.env.production +0 -1
  111. package/.github/pull_request_template.md +0 -11
  112. package/.github/workflows/ci.yml +0 -33
  113. package/.vscode/extensions.json +0 -7
  114. package/.vscode/launch.json +0 -15
  115. package/.vscode/settings.json +0 -25
  116. package/Dockerfile +0 -7
  117. package/dist/5fa71a03d45dc2e3d61447f3013a303d.png +0 -0
  118. package/dist/index.html +0 -1
  119. package/dist/main.js +0 -23303
  120. package/dist/static/main.05a86d446163fd5f17d3.js +0 -2
  121. package/dist/static/main.05a86d446163fd5f17d3.js.LICENSE.txt +0 -98
  122. package/dist/static/main.9e53bed734dae98e5b10.js +0 -2
  123. package/dist/static/main.9e53bed734dae98e5b10.js.LICENSE.txt +0 -98
  124. package/dist/static/main.js +0 -2
  125. package/dist/static/main.js.LICENSE.txt +0 -98
  126. package/dist/static/vendor.05a86d446163fd5f17d3.js +0 -2
  127. package/dist/static/vendor.05a86d446163fd5f17d3.js.LICENSE.txt +0 -29
  128. package/dist/static/vendor.9e53bed734dae98e5b10.js +0 -2
  129. package/dist/static/vendor.9e53bed734dae98e5b10.js.LICENSE.txt +0 -29
  130. package/dist/static/vendor.js +0 -2
  131. package/dist/static/vendor.js.LICENSE.txt +0 -29
  132. package/dist/vendor.js +0 -281
  133. package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
  134. package/src/app/pages/ListNamespacesPage/index.jsx +0 -52
  135. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -45
  136. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
@@ -0,0 +1,725 @@
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
+ node_dag: jest.fn().mockReturnValue(mocks.mockNodeDAG),
27
+ clientCode: jest.fn().mockReturnValue('dj_client = DJClient()'),
28
+ columns: jest.fn(),
29
+ history: jest.fn(),
30
+ revisions: jest.fn(),
31
+ materializations: jest.fn(),
32
+ sql: jest.fn(),
33
+ cube: jest.fn(),
34
+ compiledSql: jest.fn(),
35
+ node_lineage: jest.fn(),
36
+ nodesWithDimension: jest.fn(),
37
+ attributes: jest.fn(),
38
+ dimensions: jest.fn(),
39
+ },
40
+ };
41
+ };
42
+
43
+ const defaultProps = {
44
+ name: 'default.avg_repair_price',
45
+ djNode: {
46
+ namespace: 'default',
47
+ node_revision_id: 24,
48
+ node_id: 24,
49
+ type: 'metric',
50
+ name: 'default.avg_repair_price',
51
+ display_name: 'Default: Avg Repair Price',
52
+ version: 'v1.0',
53
+ status: 'valid',
54
+ mode: 'published',
55
+ catalog: {
56
+ id: 1,
57
+ uuid: '0fc18295-e1a2-4c3c-b72a-894725c12488',
58
+ created_at: '2023-08-21T16:48:51.146121+00:00',
59
+ updated_at: '2023-08-21T16:48:51.146122+00:00',
60
+ extra_params: {},
61
+ name: 'warehouse',
62
+ },
63
+ schema_: null,
64
+ table: null,
65
+ description: 'Average repair price',
66
+ query:
67
+ 'SELECT avg(price) default_DOT_avg_repair_price \n FROM default.repair_order_details\n\n',
68
+ availability: null,
69
+ columns: [
70
+ {
71
+ name: 'default_DOT_avg_repair_price',
72
+ type: 'double',
73
+ attributes: [],
74
+ dimension: null,
75
+ },
76
+ ],
77
+ updated_at: '2023-08-21T16:48:56.932231+00:00',
78
+ materializations: [],
79
+ parents: [
80
+ {
81
+ name: 'default.repair_order_details',
82
+ },
83
+ ],
84
+ created_at: '2023-08-21T16:48:56.932162+00:00',
85
+ tags: [],
86
+ primary_key: [],
87
+ createNodeClientCode:
88
+ '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)',
89
+ dimensions: [
90
+ {
91
+ name: 'default.date_dim.dateint',
92
+ type: 'timestamp',
93
+ path: [
94
+ 'default.repair_order_details.repair_order_id',
95
+ 'default.repair_order.hard_hat_id',
96
+ 'default.hard_hat.birth_date',
97
+ ],
98
+ },
99
+ {
100
+ name: 'default.date_dim.dateint',
101
+ type: 'timestamp',
102
+ path: [
103
+ 'default.repair_order_details.repair_order_id',
104
+ 'default.repair_order.hard_hat_id',
105
+ 'default.hard_hat.hire_date',
106
+ ],
107
+ },
108
+ {
109
+ name: 'default.date_dim.day',
110
+ type: 'int',
111
+ path: [
112
+ 'default.repair_order_details.repair_order_id',
113
+ 'default.repair_order.hard_hat_id',
114
+ 'default.hard_hat.birth_date',
115
+ ],
116
+ },
117
+ {
118
+ name: 'default.date_dim.day',
119
+ type: 'int',
120
+ path: [
121
+ 'default.repair_order_details.repair_order_id',
122
+ 'default.repair_order.hard_hat_id',
123
+ 'default.hard_hat.hire_date',
124
+ ],
125
+ },
126
+ {
127
+ name: 'default.date_dim.month',
128
+ type: 'int',
129
+ path: [
130
+ 'default.repair_order_details.repair_order_id',
131
+ 'default.repair_order.hard_hat_id',
132
+ 'default.hard_hat.birth_date',
133
+ ],
134
+ },
135
+ {
136
+ name: 'default.date_dim.month',
137
+ type: 'int',
138
+ path: [
139
+ 'default.repair_order_details.repair_order_id',
140
+ 'default.repair_order.hard_hat_id',
141
+ 'default.hard_hat.hire_date',
142
+ ],
143
+ },
144
+ {
145
+ name: 'default.date_dim.year',
146
+ type: 'int',
147
+ path: [
148
+ 'default.repair_order_details.repair_order_id',
149
+ 'default.repair_order.hard_hat_id',
150
+ 'default.hard_hat.birth_date',
151
+ ],
152
+ },
153
+ {
154
+ name: 'default.date_dim.year',
155
+ type: 'int',
156
+ path: [
157
+ 'default.repair_order_details.repair_order_id',
158
+ 'default.repair_order.hard_hat_id',
159
+ 'default.hard_hat.hire_date',
160
+ ],
161
+ },
162
+ {
163
+ name: 'default.hard_hat.address',
164
+ type: 'string',
165
+ path: [
166
+ 'default.repair_order_details.repair_order_id',
167
+ 'default.repair_order.hard_hat_id',
168
+ ],
169
+ },
170
+ {
171
+ name: 'default.hard_hat.birth_date',
172
+ type: 'date',
173
+ path: [
174
+ 'default.repair_order_details.repair_order_id',
175
+ 'default.repair_order.hard_hat_id',
176
+ ],
177
+ },
178
+ {
179
+ name: 'default.hard_hat.city',
180
+ type: 'string',
181
+ path: [
182
+ 'default.repair_order_details.repair_order_id',
183
+ 'default.repair_order.hard_hat_id',
184
+ ],
185
+ },
186
+ {
187
+ name: 'default.hard_hat.contractor_id',
188
+ type: 'int',
189
+ path: [
190
+ 'default.repair_order_details.repair_order_id',
191
+ 'default.repair_order.hard_hat_id',
192
+ ],
193
+ },
194
+ {
195
+ name: 'default.hard_hat.country',
196
+ type: 'string',
197
+ path: [
198
+ 'default.repair_order_details.repair_order_id',
199
+ 'default.repair_order.hard_hat_id',
200
+ ],
201
+ },
202
+ {
203
+ name: 'default.hard_hat.first_name',
204
+ type: 'string',
205
+ path: [
206
+ 'default.repair_order_details.repair_order_id',
207
+ 'default.repair_order.hard_hat_id',
208
+ ],
209
+ },
210
+ {
211
+ name: 'default.hard_hat.hard_hat_id',
212
+ type: 'int',
213
+ path: [
214
+ 'default.repair_order_details.repair_order_id',
215
+ 'default.repair_order.hard_hat_id',
216
+ ],
217
+ },
218
+ {
219
+ name: 'default.hard_hat.hire_date',
220
+ type: 'date',
221
+ path: [
222
+ 'default.repair_order_details.repair_order_id',
223
+ 'default.repair_order.hard_hat_id',
224
+ ],
225
+ },
226
+ {
227
+ name: 'default.hard_hat.last_name',
228
+ type: 'string',
229
+ path: [
230
+ 'default.repair_order_details.repair_order_id',
231
+ 'default.repair_order.hard_hat_id',
232
+ ],
233
+ },
234
+ {
235
+ name: 'default.hard_hat.manager',
236
+ type: 'int',
237
+ path: [
238
+ 'default.repair_order_details.repair_order_id',
239
+ 'default.repair_order.hard_hat_id',
240
+ ],
241
+ },
242
+ {
243
+ name: 'default.hard_hat.postal_code',
244
+ type: 'string',
245
+ path: [
246
+ 'default.repair_order_details.repair_order_id',
247
+ 'default.repair_order.hard_hat_id',
248
+ ],
249
+ },
250
+ {
251
+ name: 'default.hard_hat.state',
252
+ type: 'string',
253
+ path: [
254
+ 'default.repair_order_details.repair_order_id',
255
+ 'default.repair_order.hard_hat_id',
256
+ ],
257
+ },
258
+ {
259
+ name: 'default.hard_hat.title',
260
+ type: 'string',
261
+ path: [
262
+ 'default.repair_order_details.repair_order_id',
263
+ 'default.repair_order.hard_hat_id',
264
+ ],
265
+ },
266
+ ],
267
+ },
268
+ };
269
+
270
+ it('renders the NodeInfo tab correctly', async () => {
271
+ const djClient = mockDJClient();
272
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
273
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
274
+ const element = (
275
+ <DJClientContext.Provider value={djClient}>
276
+ <NodePage {...defaultProps} />
277
+ </DJClientContext.Provider>
278
+ );
279
+ const { container } = render(
280
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
281
+ <Routes>
282
+ <Route path="nodes/:name" element={element} />
283
+ </Routes>
284
+ </MemoryRouter>,
285
+ );
286
+
287
+ await waitFor(() => {
288
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
289
+ 'default.num_repair_orders',
290
+ );
291
+ userEvent.click(screen.getByRole('button', { name: 'Info' }));
292
+
293
+ expect(
294
+ screen.getByRole('dialog', { name: 'NodeName' }),
295
+ ).toHaveTextContent('default.num_repair_orders');
296
+
297
+ expect(screen.getByRole('button', { name: 'Info' })).toBeInTheDocument();
298
+ expect(
299
+ screen.getByRole('dialog', { name: 'Description' }),
300
+ ).toHaveTextContent('Number of repair orders');
301
+
302
+ expect(screen.getByRole('dialog', { name: 'Version' })).toHaveTextContent(
303
+ 'v1.0',
304
+ );
305
+
306
+ // expect(screen.getByRole('dialog', { name: 'Table' })).not.toBeInTheDocument();
307
+ expect(
308
+ screen.getByRole('dialog', { name: 'NodeStatus' }),
309
+ ).toBeInTheDocument();
310
+
311
+ expect(screen.getByRole('dialog', { name: 'Tags' })).toHaveTextContent(
312
+ '',
313
+ );
314
+
315
+ expect(
316
+ screen.getByRole('dialog', { name: 'PrimaryKey' }),
317
+ ).toHaveTextContent('repair_order_id, country');
318
+
319
+ expect(
320
+ screen.getByRole('dialog', { name: 'DisplayName' }),
321
+ ).toHaveTextContent('Default: Num Repair Orders');
322
+
323
+ expect(
324
+ screen.getByRole('dialog', { name: 'NodeType' }),
325
+ ).toHaveTextContent('metric');
326
+
327
+ expect(
328
+ container.getElementsByClassName('language-sql'),
329
+ ).toMatchSnapshot();
330
+ });
331
+ }, 60000);
332
+
333
+ it('renders the NodeInfo tab correctly for cube nodes', async () => {
334
+ const djClient = mockDJClient();
335
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockCubeNode);
336
+ djClient.DataJunctionAPI.cube.mockReturnValue(mocks.mockCubesCube);
337
+ const element = (
338
+ <DJClientContext.Provider value={djClient}>
339
+ <NodePage {...defaultProps} />
340
+ </DJClientContext.Provider>
341
+ );
342
+ const { container } = render(
343
+ <MemoryRouter initialEntries={['/nodes/default.repair_orders_cube']}>
344
+ <Routes>
345
+ <Route path="nodes/:name" element={element} />
346
+ </Routes>
347
+ </MemoryRouter>,
348
+ );
349
+
350
+ await waitFor(() => {
351
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
352
+ 'default.repair_orders_cube',
353
+ );
354
+ userEvent.click(screen.getByRole('button', { name: 'Info' }));
355
+
356
+ expect(
357
+ screen.getByRole('dialog', { name: 'NodeName' }),
358
+ ).toHaveTextContent('default.repair_orders_cube');
359
+
360
+ expect(screen.getByRole('button', { name: 'Info' })).toBeInTheDocument();
361
+ expect(
362
+ screen.getByRole('dialog', { name: 'Description' }),
363
+ ).toHaveTextContent('Repair Orders');
364
+
365
+ expect(screen.getByRole('dialog', { name: 'Version' })).toHaveTextContent(
366
+ 'v1.0',
367
+ );
368
+
369
+ expect(
370
+ screen.getByRole('dialog', { name: 'PrimaryKey' }),
371
+ ).toHaveTextContent('');
372
+
373
+ expect(
374
+ screen.getByRole('dialog', { name: 'DisplayName' }),
375
+ ).toHaveTextContent('Default: Repair Orders Cube');
376
+
377
+ expect(
378
+ screen.getByRole('dialog', { name: 'NodeType' }),
379
+ ).toHaveTextContent('cube');
380
+
381
+ expect(
382
+ screen.getByRole('dialog', { name: 'NodeType' }),
383
+ ).toHaveTextContent('cube');
384
+
385
+ expect(screen.getByText('Cube Elements')).toBeInTheDocument();
386
+ screen
387
+ .getAllByRole('cell', { name: 'CubeElement' })
388
+ .map(cube => cube.hasAttribute('a'));
389
+ });
390
+ }, 60000);
391
+
392
+ it('renders the NodeColumns tab correctly', async () => {
393
+ const djClient = mockDJClient();
394
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
395
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
396
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
397
+ djClient.DataJunctionAPI.attributes.mockReturnValue(mocks.attributes);
398
+ djClient.DataJunctionAPI.dimensions.mockReturnValue(mocks.dimensions);
399
+ const element = (
400
+ <DJClientContext.Provider value={djClient}>
401
+ <NodePage />
402
+ </DJClientContext.Provider>
403
+ );
404
+ render(
405
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
406
+ <Routes>
407
+ <Route path="nodes/:name" element={element} />
408
+ </Routes>
409
+ </MemoryRouter>,
410
+ );
411
+ await waitFor(() => {
412
+ fireEvent.click(screen.getByRole('button', { name: 'Columns' }));
413
+ expect(djClient.DataJunctionAPI.columns).toHaveBeenCalledWith(
414
+ mocks.mockMetricNode,
415
+ );
416
+ expect(
417
+ screen.getByRole('columnheader', { name: 'ColumnName' }),
418
+ ).toHaveTextContent('default_DOT_avg_repair_price');
419
+ expect(
420
+ screen.getByRole('columnheader', { name: 'ColumnType' }),
421
+ ).toHaveTextContent('double');
422
+
423
+ // check that the edit column popover can be clicked
424
+ const editColumnPopover = screen.getByRole('button', {
425
+ name: 'EditColumn',
426
+ });
427
+ expect(editColumnPopover).toBeInTheDocument();
428
+ fireEvent.click(editColumnPopover);
429
+ expect(
430
+ screen.getByRole('button', { name: 'SaveEditColumn' }),
431
+ ).toBeInTheDocument();
432
+
433
+ // check that the link dimension popover can be clicked
434
+ const linkDimensionPopover = screen.getByRole('button', {
435
+ name: 'LinkDimension',
436
+ });
437
+ expect(linkDimensionPopover).toBeInTheDocument();
438
+ fireEvent.click(linkDimensionPopover);
439
+ expect(
440
+ screen.getByRole('button', { name: 'SaveLinkDimension' }),
441
+ ).toBeInTheDocument();
442
+ });
443
+ });
444
+ // check compiled SQL on nodeInfo page
445
+
446
+ it('renders the NodeHistory tab correctly', async () => {
447
+ const djClient = mockDJClient();
448
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
449
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
450
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
451
+ djClient.DataJunctionAPI.history.mockReturnValue(mocks.metricNodeHistory);
452
+ djClient.DataJunctionAPI.revisions.mockReturnValue(
453
+ mocks.metricNodeRevisions,
454
+ );
455
+
456
+ const element = (
457
+ <DJClientContext.Provider value={djClient}>
458
+ <NodePage />
459
+ </DJClientContext.Provider>
460
+ );
461
+ const { container } = render(
462
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
463
+ <Routes>
464
+ <Route path="nodes/:name" element={element} />
465
+ </Routes>
466
+ </MemoryRouter>,
467
+ );
468
+ await waitFor(async () => {
469
+ fireEvent.click(screen.getByRole('button', { name: 'History' }));
470
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
471
+ mocks.mockMetricNode.name,
472
+ );
473
+ expect(djClient.DataJunctionAPI.history).toHaveBeenCalledWith(
474
+ 'node',
475
+ mocks.mockMetricNode.name,
476
+ );
477
+ expect(djClient.DataJunctionAPI.revisions).toHaveBeenCalledWith(
478
+ mocks.mockMetricNode.name,
479
+ );
480
+ expect(
481
+ screen.getByRole('table', { name: 'Revisions' }),
482
+ ).toMatchSnapshot();
483
+ expect(screen.getByRole('table', { name: 'Activity' })).toHaveTextContent(
484
+ 'ActivityTypeNameUserTimestampDetailscreatenodedefault.avg_repair_priceunknown2023-08-21T16:48:56.950482+00:00',
485
+ );
486
+ screen
487
+ .queryAllByRole('cell', {
488
+ name: 'HistoryAttribute',
489
+ })
490
+ .forEach(cell => expect(cell).toHaveTextContent(/Set col1 as /));
491
+
492
+ screen
493
+ .queryAllByRole('cell', {
494
+ name: 'HistoryCreateLink',
495
+ })
496
+ .forEach(cell =>
497
+ expect(cell).toHaveTextContent(
498
+ 'Linked col1 todefault.hard_hat viahard_hat_id',
499
+ ),
500
+ );
501
+
502
+ screen
503
+ .queryAllByRole('cell', {
504
+ name: 'HistoryCreateMaterialization',
505
+ })
506
+ .forEach(cell =>
507
+ expect(cell).toHaveTextContent(
508
+ 'Initialized materialization some_random_materialization',
509
+ ),
510
+ );
511
+
512
+ screen
513
+ .queryAllByRole('cell', {
514
+ name: 'HistoryNodeStatusChange',
515
+ })
516
+ .forEach(cell =>
517
+ expect(cell).toHaveTextContent(
518
+ 'Status changed from valid to invalid Caused by a change in upstream default.repair_order_details',
519
+ ),
520
+ );
521
+ screen.debug();
522
+ });
523
+ });
524
+
525
+ it('renders compiled sql correctly', async () => {
526
+ const djClient = mockDJClient();
527
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
528
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
529
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
530
+ djClient.DataJunctionAPI.compiledSql.mockReturnValue('select 1');
531
+
532
+ const element = (
533
+ <DJClientContext.Provider value={djClient}>
534
+ <NodePage />
535
+ </DJClientContext.Provider>
536
+ );
537
+ render(
538
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
539
+ <Routes>
540
+ <Route path="nodes/:name" element={element} />
541
+ </Routes>
542
+ </MemoryRouter>,
543
+ );
544
+ await waitFor(() => {
545
+ fireEvent.click(screen.getByRole('checkbox', { name: 'ToggleSwitch' }));
546
+ expect(djClient.DataJunctionAPI.compiledSql).toHaveBeenCalledWith(
547
+ mocks.mockMetricNode.name,
548
+ );
549
+ });
550
+ });
551
+
552
+ it('renders an empty NodeMaterialization tab correctly', async () => {
553
+ const djClient = mockDJClient();
554
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
555
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
556
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
557
+ djClient.DataJunctionAPI.materializations.mockReturnValue([]);
558
+
559
+ const element = (
560
+ <DJClientContext.Provider value={djClient}>
561
+ <NodePage />
562
+ </DJClientContext.Provider>
563
+ );
564
+ render(
565
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
566
+ <Routes>
567
+ <Route path="nodes/:name" element={element} />
568
+ </Routes>
569
+ </MemoryRouter>,
570
+ );
571
+ await waitFor(() => {
572
+ fireEvent.click(screen.getByRole('button', { name: 'Materializations' }));
573
+ expect(djClient.DataJunctionAPI.materializations).toHaveBeenCalledWith(
574
+ mocks.mockMetricNode.name,
575
+ );
576
+ screen.getByText('No materializations available for this node');
577
+ });
578
+ });
579
+
580
+ it('renders the NodeMaterialization tab with materializations correctly', async () => {
581
+ const djClient = mockDJClient();
582
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
583
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
584
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
585
+ djClient.DataJunctionAPI.materializations.mockReturnValue(
586
+ mocks.nodeMaterializations,
587
+ );
588
+
589
+ const element = (
590
+ <DJClientContext.Provider value={djClient}>
591
+ <NodePage />
592
+ </DJClientContext.Provider>
593
+ );
594
+ render(
595
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
596
+ <Routes>
597
+ <Route path="nodes/:name" element={element} />
598
+ </Routes>
599
+ </MemoryRouter>,
600
+ );
601
+ await waitFor(() => {
602
+ fireEvent.click(screen.getByRole('button', { name: 'Materializations' }));
603
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
604
+ mocks.mockMetricNode.name,
605
+ );
606
+ expect(djClient.DataJunctionAPI.materializations).toHaveBeenCalledWith(
607
+ mocks.mockMetricNode.name,
608
+ );
609
+
610
+ expect(
611
+ screen.getByRole('table', { name: 'Materializations' }),
612
+ ).toMatchSnapshot();
613
+ });
614
+ });
615
+
616
+ it('renders the NodeSQL tab', async () => {
617
+ const djClient = mockDJClient();
618
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
619
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
620
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
621
+ djClient.DataJunctionAPI.sql.mockReturnValue(mocks.metricNodeColumns);
622
+ const element = (
623
+ <DJClientContext.Provider value={djClient}>
624
+ <NodePage />
625
+ </DJClientContext.Provider>
626
+ );
627
+ render(
628
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
629
+ <Routes>
630
+ <Route path="nodes/:name" element={element} />
631
+ </Routes>
632
+ </MemoryRouter>,
633
+ );
634
+ await waitFor(() => {
635
+ const sqlButton = screen.getByRole('button', { name: 'SQL' });
636
+ sqlButton.click();
637
+ expect(djClient.DataJunctionAPI.sql).toHaveBeenCalledWith(
638
+ 'default.num_repair_orders',
639
+ { dimensions: [], filters: [] },
640
+ );
641
+ });
642
+ });
643
+
644
+ it('renders a NodeColumnLineage tab correctly', async () => {
645
+ const djClient = mockDJClient();
646
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
647
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNodeJson);
648
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
649
+ djClient.DataJunctionAPI.node_lineage.mockReturnValue(
650
+ mocks.mockNodeLineage,
651
+ );
652
+
653
+ const element = (
654
+ <DJClientContext.Provider value={djClient}>
655
+ <NodePage />
656
+ </DJClientContext.Provider>
657
+ );
658
+ render(
659
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
660
+ <Routes>
661
+ <Route path="nodes/:name" element={element} />
662
+ </Routes>
663
+ </MemoryRouter>,
664
+ );
665
+ await waitFor(() => {
666
+ fireEvent.click(screen.getByRole('button', { name: 'Lineage' }));
667
+ expect(djClient.DataJunctionAPI.node_lineage).toHaveBeenCalledWith(
668
+ mocks.mockMetricNode.name,
669
+ );
670
+ });
671
+ });
672
+
673
+ it('renders a NodeGraph tab correctly', async () => {
674
+ const djClient = mockDJClient();
675
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
676
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNodeJson);
677
+ djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
678
+
679
+ const element = (
680
+ <DJClientContext.Provider value={djClient}>
681
+ <NodePage />
682
+ </DJClientContext.Provider>
683
+ );
684
+ render(
685
+ <MemoryRouter initialEntries={['/nodes/default.num_repair_orders']}>
686
+ <Routes>
687
+ <Route path="nodes/:name" element={element} />
688
+ </Routes>
689
+ </MemoryRouter>,
690
+ );
691
+ await waitFor(() => {
692
+ fireEvent.click(screen.getByRole('button', { name: 'Graph' }));
693
+ expect(djClient.DataJunctionAPI.node_dag).toHaveBeenCalledWith(
694
+ mocks.mockMetricNode.name,
695
+ );
696
+ });
697
+ });
698
+
699
+ it('renders Linked Nodes tab correctly', async () => {
700
+ const djClient = mockDJClient();
701
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockDimensionNode);
702
+ djClient.DataJunctionAPI.nodesWithDimension.mockReturnValue(
703
+ mocks.mockLinkedNodes,
704
+ );
705
+
706
+ const element = (
707
+ <DJClientContext.Provider value={djClient}>
708
+ <NodePage />
709
+ </DJClientContext.Provider>
710
+ );
711
+ render(
712
+ <MemoryRouter initialEntries={['/nodes/default.dispatcher']}>
713
+ <Routes>
714
+ <Route path="nodes/:name" element={element} />
715
+ </Routes>
716
+ </MemoryRouter>,
717
+ );
718
+ await waitFor(() => {
719
+ fireEvent.click(screen.getByRole('button', { name: 'Linked Nodes' }));
720
+ expect(djClient.DataJunctionAPI.nodesWithDimension).toHaveBeenCalledWith(
721
+ mocks.mockDimensionNode.name,
722
+ );
723
+ });
724
+ });
725
+ });