datajunction-ui 0.0.92 → 0.0.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/app/components/NodeComponents.jsx +4 -0
- package/src/app/components/Tab.jsx +11 -16
- package/src/app/components/__tests__/Tab.test.jsx +4 -2
- package/src/app/hooks/useWorkspaceData.js +226 -0
- package/src/app/index.tsx +17 -1
- package/src/app/pages/MyWorkspacePage/ActiveBranchesSection.jsx +38 -107
- package/src/app/pages/MyWorkspacePage/MyNodesSection.jsx +31 -6
- package/src/app/pages/MyWorkspacePage/MyWorkspacePage.css +5 -0
- package/src/app/pages/MyWorkspacePage/NeedsAttentionSection.jsx +86 -100
- package/src/app/pages/MyWorkspacePage/TypeGroupGrid.jsx +7 -11
- package/src/app/pages/MyWorkspacePage/__tests__/ActiveBranchesSection.test.jsx +79 -11
- package/src/app/pages/MyWorkspacePage/__tests__/CollectionsSection.test.jsx +22 -0
- package/src/app/pages/MyWorkspacePage/__tests__/MaterializationsSection.test.jsx +57 -0
- package/src/app/pages/MyWorkspacePage/__tests__/MyNodesSection.test.jsx +60 -18
- package/src/app/pages/MyWorkspacePage/__tests__/MyWorkspacePage.test.jsx +156 -162
- package/src/app/pages/MyWorkspacePage/__tests__/NeedsAttentionSection.test.jsx +17 -18
- package/src/app/pages/MyWorkspacePage/__tests__/NotificationsSection.test.jsx +179 -0
- package/src/app/pages/MyWorkspacePage/__tests__/TypeGroupGrid.test.jsx +169 -49
- package/src/app/pages/MyWorkspacePage/index.jsx +41 -73
- package/src/app/pages/NodePage/NodeDataFlowTab.jsx +464 -0
- package/src/app/pages/NodePage/NodeDependenciesTab.jsx +1 -1
- package/src/app/pages/NodePage/NodeDimensionsTab.jsx +362 -0
- package/src/app/pages/NodePage/NodeLineageTab.jsx +1 -0
- package/src/app/pages/NodePage/NodesWithDimension.jsx +3 -3
- package/src/app/pages/NodePage/__tests__/NodeDataFlowTab.test.jsx +428 -0
- package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +18 -1
- package/src/app/pages/NodePage/__tests__/NodeDimensionsTab.test.jsx +362 -0
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +28 -3
- package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +2 -2
- package/src/app/pages/NodePage/index.jsx +15 -8
- package/src/app/services/DJService.js +73 -6
- package/src/app/services/__tests__/DJService.test.jsx +591 -0
- package/src/styles/index.css +32 -0
|
@@ -175,6 +175,185 @@ describe('<NotificationsSection />', () => {
|
|
|
175
175
|
expect(link).toHaveAttribute('href', '/nodes/default.test_metric/history');
|
|
176
176
|
});
|
|
177
177
|
|
|
178
|
+
it('should show username part for single notification from another user', () => {
|
|
179
|
+
const otherUserNotification = [
|
|
180
|
+
{
|
|
181
|
+
entity_name: 'default.other_metric',
|
|
182
|
+
activity_type: 'update',
|
|
183
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
184
|
+
user: 'other.person@example.com',
|
|
185
|
+
node_type: 'metric',
|
|
186
|
+
display_name: 'Other Metric',
|
|
187
|
+
},
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
render(
|
|
191
|
+
<MemoryRouter>
|
|
192
|
+
<NotificationsSection
|
|
193
|
+
notifications={otherUserNotification}
|
|
194
|
+
username="test.user@example.com"
|
|
195
|
+
loading={false}
|
|
196
|
+
/>
|
|
197
|
+
</MemoryRouter>,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// Should show "other.person" (split on @)
|
|
201
|
+
expect(screen.getByText(/by other\.person/)).toBeInTheDocument();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should show "you + N others" when current user is among multiple updaters', () => {
|
|
205
|
+
const multiUserNotifications = [
|
|
206
|
+
{
|
|
207
|
+
entity_name: 'default.shared_metric',
|
|
208
|
+
activity_type: 'update',
|
|
209
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
210
|
+
user: 'test.user@example.com',
|
|
211
|
+
node_type: 'metric',
|
|
212
|
+
display_name: 'Shared Metric',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
entity_name: 'default.shared_metric',
|
|
216
|
+
activity_type: 'update',
|
|
217
|
+
created_at: '2024-01-02T00:00:00Z',
|
|
218
|
+
user: 'alice@example.com',
|
|
219
|
+
node_type: 'metric',
|
|
220
|
+
display_name: 'Shared Metric',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
entity_name: 'default.shared_metric',
|
|
224
|
+
activity_type: 'update',
|
|
225
|
+
created_at: '2024-01-03T00:00:00Z',
|
|
226
|
+
user: 'bob@example.com',
|
|
227
|
+
node_type: 'metric',
|
|
228
|
+
display_name: 'Shared Metric',
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
render(
|
|
233
|
+
<MemoryRouter>
|
|
234
|
+
<NotificationsSection
|
|
235
|
+
notifications={multiUserNotifications}
|
|
236
|
+
username="test.user@example.com"
|
|
237
|
+
loading={false}
|
|
238
|
+
/>
|
|
239
|
+
</MemoryRouter>,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// 3 unique users including current user → "you + 2 others"
|
|
243
|
+
expect(screen.getByText(/you \+ 2 others/)).toBeInTheDocument();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should show "N users" when multiple users not including current user', () => {
|
|
247
|
+
const otherUsersNotifications = [
|
|
248
|
+
{
|
|
249
|
+
entity_name: 'default.their_metric',
|
|
250
|
+
activity_type: 'update',
|
|
251
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
252
|
+
user: 'alice@example.com',
|
|
253
|
+
node_type: 'metric',
|
|
254
|
+
display_name: 'Their Metric',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
entity_name: 'default.their_metric',
|
|
258
|
+
activity_type: 'update',
|
|
259
|
+
created_at: '2024-01-02T00:00:00Z',
|
|
260
|
+
user: 'bob@example.com',
|
|
261
|
+
node_type: 'metric',
|
|
262
|
+
display_name: 'Their Metric',
|
|
263
|
+
},
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
render(
|
|
267
|
+
<MemoryRouter>
|
|
268
|
+
<NotificationsSection
|
|
269
|
+
notifications={otherUsersNotifications}
|
|
270
|
+
username="test.user@example.com"
|
|
271
|
+
loading={false}
|
|
272
|
+
/>
|
|
273
|
+
</MemoryRouter>,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
expect(screen.getByText(/2 users/)).toBeInTheDocument();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should show "you" when users array is empty and mostRecent.user is current user', () => {
|
|
280
|
+
const nullUserNotification = [
|
|
281
|
+
{
|
|
282
|
+
entity_name: 'default.my_metric',
|
|
283
|
+
activity_type: 'update',
|
|
284
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
285
|
+
user: 'test.user@example.com',
|
|
286
|
+
// user field is present but filter(u => u != null && u !== '') keeps it
|
|
287
|
+
// To hit the users.length === 0 branch we need user=null or user=''
|
|
288
|
+
},
|
|
289
|
+
];
|
|
290
|
+
|
|
291
|
+
// Use a notification where user is null/empty so allUsers filter removes it
|
|
292
|
+
const emptyUserNotification = [
|
|
293
|
+
{
|
|
294
|
+
entity_name: 'default.my_metric',
|
|
295
|
+
activity_type: 'update',
|
|
296
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
297
|
+
user: null, // filtered out → users.length === 0
|
|
298
|
+
node_type: 'metric',
|
|
299
|
+
display_name: 'My Metric',
|
|
300
|
+
details: { version: 'v1' },
|
|
301
|
+
},
|
|
302
|
+
];
|
|
303
|
+
// mostRecent.user is null → falls to 'unknown'
|
|
304
|
+
render(
|
|
305
|
+
<MemoryRouter>
|
|
306
|
+
<NotificationsSection
|
|
307
|
+
notifications={emptyUserNotification}
|
|
308
|
+
username="test.user@example.com"
|
|
309
|
+
loading={false}
|
|
310
|
+
/>
|
|
311
|
+
</MemoryRouter>,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
expect(screen.getByText(/by unknown/)).toBeInTheDocument();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should show "you" in zero-users fallback when mostRecent.user matches username', () => {
|
|
318
|
+
const emptyStringUserNotification = [
|
|
319
|
+
{
|
|
320
|
+
entity_name: 'default.my_metric',
|
|
321
|
+
activity_type: 'update',
|
|
322
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
323
|
+
user: '', // filtered out → users.length === 0
|
|
324
|
+
node_type: 'metric',
|
|
325
|
+
display_name: 'My Metric',
|
|
326
|
+
},
|
|
327
|
+
];
|
|
328
|
+
// mostRecent.user is '' which !== username → goes to split('@')[0] || 'unknown' → 'unknown'
|
|
329
|
+
// To get "you" in zero-users path: mostRecent.user must equal username
|
|
330
|
+
// We need user field stored (not filtered) to equal username but filtered...
|
|
331
|
+
// Actually user='' is filtered. Let's set user=username so it passes filter and users=[username]
|
|
332
|
+
// → users.length === 1 and users[0] === username → "you"
|
|
333
|
+
const selfNotification = [
|
|
334
|
+
{
|
|
335
|
+
entity_name: 'default.my_metric',
|
|
336
|
+
activity_type: 'update',
|
|
337
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
338
|
+
user: 'test.user@example.com',
|
|
339
|
+
node_type: 'metric',
|
|
340
|
+
display_name: 'My Metric',
|
|
341
|
+
},
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
render(
|
|
345
|
+
<MemoryRouter>
|
|
346
|
+
<NotificationsSection
|
|
347
|
+
notifications={selfNotification}
|
|
348
|
+
username="test.user@example.com"
|
|
349
|
+
loading={false}
|
|
350
|
+
/>
|
|
351
|
+
</MemoryRouter>,
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
expect(screen.getByText(/by you/)).toBeInTheDocument();
|
|
355
|
+
});
|
|
356
|
+
|
|
178
357
|
it('should limit notifications to 15', () => {
|
|
179
358
|
const manyNotifications = Array.from({ length: 20 }, (_, i) => ({
|
|
180
359
|
entity_name: `default.metric_${i}`,
|
|
@@ -23,7 +23,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
23
23
|
const mockGroupedData = [
|
|
24
24
|
{
|
|
25
25
|
type: 'metric',
|
|
26
|
-
|
|
26
|
+
hasMore: false,
|
|
27
27
|
nodes: [
|
|
28
28
|
{
|
|
29
29
|
name: 'default.revenue',
|
|
@@ -63,7 +63,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
type: 'dimension',
|
|
66
|
-
|
|
66
|
+
hasMore: false,
|
|
67
67
|
nodes: [
|
|
68
68
|
{
|
|
69
69
|
name: 'default.dim_users',
|
|
@@ -96,7 +96,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
96
96
|
expect(screen.getByText('No nodes to display')).toBeInTheDocument();
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
it('should render type cards
|
|
99
|
+
it('should render type cards', () => {
|
|
100
100
|
render(
|
|
101
101
|
<MemoryRouter>
|
|
102
102
|
<TypeGroupGrid
|
|
@@ -107,8 +107,8 @@ describe('<TypeGroupGrid />', () => {
|
|
|
107
107
|
</MemoryRouter>,
|
|
108
108
|
);
|
|
109
109
|
|
|
110
|
-
expect(screen.getByText('Metrics
|
|
111
|
-
expect(screen.getByText('Dimensions
|
|
110
|
+
expect(screen.getByText('Metrics')).toBeInTheDocument();
|
|
111
|
+
expect(screen.getByText('Dimensions')).toBeInTheDocument();
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
it('should display up to 10 nodes per type', () => {
|
|
@@ -130,12 +130,12 @@ describe('<TypeGroupGrid />', () => {
|
|
|
130
130
|
expect(screen.getByText('default.bounce_rate')).toBeInTheDocument();
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
it('should show "
|
|
134
|
-
const
|
|
133
|
+
it('should show "More →" link when hasMore is true', () => {
|
|
134
|
+
const hasMoreData = [
|
|
135
135
|
{
|
|
136
136
|
type: 'metric',
|
|
137
|
-
|
|
138
|
-
nodes: Array.from({ length:
|
|
137
|
+
hasMore: true,
|
|
138
|
+
nodes: Array.from({ length: 11 }, (_, i) => ({
|
|
139
139
|
name: `default.metric_${i}`,
|
|
140
140
|
type: 'metric',
|
|
141
141
|
current: {
|
|
@@ -148,18 +148,17 @@ describe('<TypeGroupGrid />', () => {
|
|
|
148
148
|
render(
|
|
149
149
|
<MemoryRouter>
|
|
150
150
|
<TypeGroupGrid
|
|
151
|
-
groupedData={
|
|
151
|
+
groupedData={hasMoreData}
|
|
152
152
|
username="test.user@example.com"
|
|
153
153
|
activeTab="owned"
|
|
154
154
|
/>
|
|
155
155
|
</MemoryRouter>,
|
|
156
156
|
);
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
expect(screen.getByText('+5 more →')).toBeInTheDocument();
|
|
158
|
+
expect(screen.getByText('More →')).toBeInTheDocument();
|
|
160
159
|
});
|
|
161
160
|
|
|
162
|
-
it('should not show "
|
|
161
|
+
it('should not show "More →" link when hasMore is false', () => {
|
|
163
162
|
render(
|
|
164
163
|
<MemoryRouter>
|
|
165
164
|
<TypeGroupGrid
|
|
@@ -170,17 +169,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
170
169
|
</MemoryRouter>,
|
|
171
170
|
);
|
|
172
171
|
|
|
173
|
-
|
|
174
|
-
const metricCard = screen
|
|
175
|
-
.getByText('Metrics (5)')
|
|
176
|
-
.closest('.type-group-card');
|
|
177
|
-
expect(metricCard).not.toHaveTextContent('more →');
|
|
178
|
-
|
|
179
|
-
// Dimensions: 2 nodes, no "+X more" needed
|
|
180
|
-
const dimensionCard = screen
|
|
181
|
-
.getByText('Dimensions (2)')
|
|
182
|
-
.closest('.type-group-card');
|
|
183
|
-
expect(dimensionCard).not.toHaveTextContent('more →');
|
|
172
|
+
expect(screen.queryByText('More →')).not.toBeInTheDocument();
|
|
184
173
|
});
|
|
185
174
|
|
|
186
175
|
it('should render node badges and links', () => {
|
|
@@ -227,7 +216,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
227
216
|
const recentNodes = [
|
|
228
217
|
{
|
|
229
218
|
type: 'metric',
|
|
230
|
-
|
|
219
|
+
hasMore: false,
|
|
231
220
|
nodes: [
|
|
232
221
|
{
|
|
233
222
|
name: 'default.recent',
|
|
@@ -260,16 +249,15 @@ describe('<TypeGroupGrid />', () => {
|
|
|
260
249
|
);
|
|
261
250
|
|
|
262
251
|
// Should show time in hours or days format
|
|
263
|
-
// Note: exact values depend on when test runs, so we just check they exist
|
|
264
252
|
expect(screen.getAllByText(/\d+[mhd]$/)).toHaveLength(2);
|
|
265
253
|
});
|
|
266
254
|
|
|
267
255
|
it('should generate correct filter URLs for owned tab', () => {
|
|
268
|
-
const
|
|
256
|
+
const hasMoreData = [
|
|
269
257
|
{
|
|
270
258
|
type: 'metric',
|
|
271
|
-
|
|
272
|
-
nodes: Array.from({ length:
|
|
259
|
+
hasMore: true,
|
|
260
|
+
nodes: Array.from({ length: 11 }, (_, i) => ({
|
|
273
261
|
name: `default.metric_${i}`,
|
|
274
262
|
type: 'metric',
|
|
275
263
|
current: {
|
|
@@ -282,14 +270,14 @@ describe('<TypeGroupGrid />', () => {
|
|
|
282
270
|
render(
|
|
283
271
|
<MemoryRouter>
|
|
284
272
|
<TypeGroupGrid
|
|
285
|
-
groupedData={
|
|
273
|
+
groupedData={hasMoreData}
|
|
286
274
|
username="test.user@example.com"
|
|
287
275
|
activeTab="owned"
|
|
288
276
|
/>
|
|
289
277
|
</MemoryRouter>,
|
|
290
278
|
);
|
|
291
279
|
|
|
292
|
-
const moreLink = screen.getByText('
|
|
280
|
+
const moreLink = screen.getByText('More →');
|
|
293
281
|
expect(moreLink).toHaveAttribute(
|
|
294
282
|
'href',
|
|
295
283
|
'/?ownedBy=test.user%40example.com&type=metric',
|
|
@@ -297,11 +285,11 @@ describe('<TypeGroupGrid />', () => {
|
|
|
297
285
|
});
|
|
298
286
|
|
|
299
287
|
it('should generate correct filter URLs for edited tab', () => {
|
|
300
|
-
const
|
|
288
|
+
const hasMoreData = [
|
|
301
289
|
{
|
|
302
290
|
type: 'metric',
|
|
303
|
-
|
|
304
|
-
nodes: Array.from({ length:
|
|
291
|
+
hasMore: true,
|
|
292
|
+
nodes: Array.from({ length: 11 }, (_, i) => ({
|
|
305
293
|
name: `default.metric_${i}`,
|
|
306
294
|
type: 'metric',
|
|
307
295
|
current: {
|
|
@@ -314,14 +302,14 @@ describe('<TypeGroupGrid />', () => {
|
|
|
314
302
|
render(
|
|
315
303
|
<MemoryRouter>
|
|
316
304
|
<TypeGroupGrid
|
|
317
|
-
groupedData={
|
|
305
|
+
groupedData={hasMoreData}
|
|
318
306
|
username="test.user@example.com"
|
|
319
307
|
activeTab="edited"
|
|
320
308
|
/>
|
|
321
309
|
</MemoryRouter>,
|
|
322
310
|
);
|
|
323
311
|
|
|
324
|
-
const moreLink = screen.getByText('
|
|
312
|
+
const moreLink = screen.getByText('More →');
|
|
325
313
|
expect(moreLink).toHaveAttribute(
|
|
326
314
|
'href',
|
|
327
315
|
'/?updatedBy=test.user%40example.com&type=metric',
|
|
@@ -340,16 +328,16 @@ describe('<TypeGroupGrid />', () => {
|
|
|
340
328
|
);
|
|
341
329
|
|
|
342
330
|
// "metric" should be displayed as "Metrics"
|
|
343
|
-
expect(screen.getByText('Metrics
|
|
331
|
+
expect(screen.getByText('Metrics')).toBeInTheDocument();
|
|
344
332
|
// "dimension" should be displayed as "Dimensions"
|
|
345
|
-
expect(screen.getByText('Dimensions
|
|
333
|
+
expect(screen.getByText('Dimensions')).toBeInTheDocument();
|
|
346
334
|
});
|
|
347
335
|
|
|
348
336
|
it('should handle nodes with only repo (no branch)', () => {
|
|
349
337
|
const repoOnlyData = [
|
|
350
338
|
{
|
|
351
339
|
type: 'metric',
|
|
352
|
-
|
|
340
|
+
hasMore: false,
|
|
353
341
|
nodes: [
|
|
354
342
|
{
|
|
355
343
|
name: 'default.test',
|
|
@@ -380,7 +368,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
380
368
|
const branchOnlyData = [
|
|
381
369
|
{
|
|
382
370
|
type: 'metric',
|
|
383
|
-
|
|
371
|
+
hasMore: false,
|
|
384
372
|
nodes: [
|
|
385
373
|
{
|
|
386
374
|
name: 'default.test',
|
|
@@ -411,7 +399,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
411
399
|
const noGitData = [
|
|
412
400
|
{
|
|
413
401
|
type: 'metric',
|
|
414
|
-
|
|
402
|
+
hasMore: false,
|
|
415
403
|
nodes: [
|
|
416
404
|
{
|
|
417
405
|
name: 'default.test',
|
|
@@ -440,7 +428,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
440
428
|
const invalidNodeData = [
|
|
441
429
|
{
|
|
442
430
|
type: 'metric',
|
|
443
|
-
|
|
431
|
+
hasMore: false,
|
|
444
432
|
nodes: [
|
|
445
433
|
{
|
|
446
434
|
name: 'default.invalid_metric',
|
|
@@ -469,7 +457,7 @@ describe('<TypeGroupGrid />', () => {
|
|
|
469
457
|
const draftNodeData = [
|
|
470
458
|
{
|
|
471
459
|
type: 'metric',
|
|
472
|
-
|
|
460
|
+
hasMore: false,
|
|
473
461
|
nodes: [
|
|
474
462
|
{
|
|
475
463
|
name: 'default.draft_metric',
|
|
@@ -494,11 +482,143 @@ describe('<TypeGroupGrid />', () => {
|
|
|
494
482
|
expect(screen.getByTitle('Draft mode')).toBeInTheDocument();
|
|
495
483
|
});
|
|
496
484
|
|
|
485
|
+
it('should show star icon for default branch nodes', () => {
|
|
486
|
+
const defaultBranchData = [
|
|
487
|
+
{
|
|
488
|
+
type: 'metric',
|
|
489
|
+
hasMore: false,
|
|
490
|
+
nodes: [
|
|
491
|
+
{
|
|
492
|
+
name: 'default.test',
|
|
493
|
+
type: 'metric',
|
|
494
|
+
gitInfo: {
|
|
495
|
+
repo: 'myorg/myrepo',
|
|
496
|
+
branch: 'main',
|
|
497
|
+
defaultBranch: 'main',
|
|
498
|
+
},
|
|
499
|
+
current: { updatedAt: '2024-01-01T10:00:00Z' },
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
},
|
|
503
|
+
];
|
|
504
|
+
|
|
505
|
+
render(
|
|
506
|
+
<MemoryRouter>
|
|
507
|
+
<TypeGroupGrid
|
|
508
|
+
groupedData={defaultBranchData}
|
|
509
|
+
username="test.user@example.com"
|
|
510
|
+
activeTab="owned"
|
|
511
|
+
/>
|
|
512
|
+
</MemoryRouter>,
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
// Star icon should appear for default branch
|
|
516
|
+
expect(screen.getByText('⭐')).toBeInTheDocument();
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should not show star icon for non-default branch nodes', () => {
|
|
520
|
+
const featureBranchData = [
|
|
521
|
+
{
|
|
522
|
+
type: 'metric',
|
|
523
|
+
hasMore: false,
|
|
524
|
+
nodes: [
|
|
525
|
+
{
|
|
526
|
+
name: 'default.test',
|
|
527
|
+
type: 'metric',
|
|
528
|
+
gitInfo: {
|
|
529
|
+
repo: 'myorg/myrepo',
|
|
530
|
+
branch: 'feature-branch',
|
|
531
|
+
defaultBranch: 'main',
|
|
532
|
+
},
|
|
533
|
+
current: { updatedAt: '2024-01-01T10:00:00Z' },
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
},
|
|
537
|
+
];
|
|
538
|
+
|
|
539
|
+
render(
|
|
540
|
+
<MemoryRouter>
|
|
541
|
+
<TypeGroupGrid
|
|
542
|
+
groupedData={featureBranchData}
|
|
543
|
+
username="test.user@example.com"
|
|
544
|
+
activeTab="owned"
|
|
545
|
+
/>
|
|
546
|
+
</MemoryRouter>,
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
expect(screen.queryByText('⭐')).not.toBeInTheDocument();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('should show SVG git-branch separator when both repo and branch are present', () => {
|
|
553
|
+
const repoPlusBranchData = [
|
|
554
|
+
{
|
|
555
|
+
type: 'metric',
|
|
556
|
+
hasMore: false,
|
|
557
|
+
nodes: [
|
|
558
|
+
{
|
|
559
|
+
name: 'default.test',
|
|
560
|
+
type: 'metric',
|
|
561
|
+
gitInfo: {
|
|
562
|
+
repo: 'myorg/myrepo',
|
|
563
|
+
branch: 'main',
|
|
564
|
+
defaultBranch: 'main',
|
|
565
|
+
},
|
|
566
|
+
current: { updatedAt: '2024-01-01T10:00:00Z' },
|
|
567
|
+
},
|
|
568
|
+
],
|
|
569
|
+
},
|
|
570
|
+
];
|
|
571
|
+
|
|
572
|
+
const { container } = render(
|
|
573
|
+
<MemoryRouter>
|
|
574
|
+
<TypeGroupGrid
|
|
575
|
+
groupedData={repoPlusBranchData}
|
|
576
|
+
username="test.user@example.com"
|
|
577
|
+
activeTab="owned"
|
|
578
|
+
/>
|
|
579
|
+
</MemoryRouter>,
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
// SVG separator should be present when both repo and branch exist
|
|
583
|
+
expect(container.querySelector('svg')).toBeInTheDocument();
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it('should not show SVG separator when only repo (no branch)', () => {
|
|
587
|
+
const repoOnlyData = [
|
|
588
|
+
{
|
|
589
|
+
type: 'metric',
|
|
590
|
+
hasMore: false,
|
|
591
|
+
nodes: [
|
|
592
|
+
{
|
|
593
|
+
name: 'default.test',
|
|
594
|
+
type: 'metric',
|
|
595
|
+
gitInfo: {
|
|
596
|
+
repo: 'myorg/myrepo',
|
|
597
|
+
},
|
|
598
|
+
current: { updatedAt: '2024-01-01T10:00:00Z' },
|
|
599
|
+
},
|
|
600
|
+
],
|
|
601
|
+
},
|
|
602
|
+
];
|
|
603
|
+
|
|
604
|
+
const { container } = render(
|
|
605
|
+
<MemoryRouter>
|
|
606
|
+
<TypeGroupGrid
|
|
607
|
+
groupedData={repoOnlyData}
|
|
608
|
+
username="test.user@example.com"
|
|
609
|
+
activeTab="owned"
|
|
610
|
+
/>
|
|
611
|
+
</MemoryRouter>,
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
expect(container.querySelector('svg')).not.toBeInTheDocument();
|
|
615
|
+
});
|
|
616
|
+
|
|
497
617
|
it('should handle nodes without updatedAt', () => {
|
|
498
618
|
const noTimeData = [
|
|
499
619
|
{
|
|
500
620
|
type: 'metric',
|
|
501
|
-
|
|
621
|
+
hasMore: false,
|
|
502
622
|
nodes: [
|
|
503
623
|
{
|
|
504
624
|
name: 'default.test',
|
|
@@ -524,11 +644,11 @@ describe('<TypeGroupGrid />', () => {
|
|
|
524
644
|
});
|
|
525
645
|
|
|
526
646
|
it('should handle watched tab filter URLs', () => {
|
|
527
|
-
const
|
|
647
|
+
const hasMoreData = [
|
|
528
648
|
{
|
|
529
649
|
type: 'metric',
|
|
530
|
-
|
|
531
|
-
nodes: Array.from({ length:
|
|
650
|
+
hasMore: true,
|
|
651
|
+
nodes: Array.from({ length: 11 }, (_, i) => ({
|
|
532
652
|
name: `default.metric_${i}`,
|
|
533
653
|
type: 'metric',
|
|
534
654
|
current: {
|
|
@@ -542,14 +662,14 @@ describe('<TypeGroupGrid />', () => {
|
|
|
542
662
|
render(
|
|
543
663
|
<MemoryRouter>
|
|
544
664
|
<TypeGroupGrid
|
|
545
|
-
groupedData={
|
|
665
|
+
groupedData={hasMoreData}
|
|
546
666
|
username="test.user@example.com"
|
|
547
667
|
activeTab="watched"
|
|
548
668
|
/>
|
|
549
669
|
</MemoryRouter>,
|
|
550
670
|
);
|
|
551
671
|
|
|
552
|
-
const moreLink = screen.getByText('
|
|
672
|
+
const moreLink = screen.getByText('More →');
|
|
553
673
|
// For watched tab, should filter by type only
|
|
554
674
|
expect(moreLink).toHaveAttribute('href', '/?type=metric');
|
|
555
675
|
});
|