datajunction-ui 0.0.27-alpha.0 → 0.0.29
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/NamespaceHeader.jsx +96 -26
- package/src/app/components/__tests__/NamespaceHeader.test.jsx +165 -0
- package/src/app/pages/NamespacePage/Explorer.jsx +68 -10
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +21 -11
- package/src/app/pages/NamespacePage/index.jsx +316 -47
- package/src/app/pages/NotificationsPage/__tests__/index.test.jsx +28 -0
- package/src/app/pages/QueryPlannerPage/PreAggDetailsPanel.jsx +20 -20
- package/src/app/pages/QueryPlannerPage/index.jsx +1 -1
- package/src/app/pages/Root/__tests__/index.test.jsx +99 -4
- package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +177 -0
- package/src/app/pages/SettingsPage/__tests__/CreateServiceAccountModal.test.jsx +50 -0
- package/src/app/pages/SettingsPage/__tests__/NotificationSubscriptionsSection.test.jsx +95 -0
- package/src/app/pages/SettingsPage/__tests__/index.test.jsx +315 -28
- package/src/app/services/DJService.js +33 -0
- package/src/app/utils/__tests__/date.test.js +60 -140
- package/src/styles/index.css +51 -10
package/package.json
CHANGED
|
@@ -1,31 +1,101 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useContext, useEffect, useState } from 'react';
|
|
2
2
|
import HorizontalHierarchyIcon from '../icons/HorizontalHierarchyIcon';
|
|
3
|
+
import DJClientContext from '../providers/djclient';
|
|
4
|
+
|
|
5
|
+
export default function NamespaceHeader({ namespace }) {
|
|
6
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
7
|
+
const [sources, setSources] = useState(null);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const fetchSources = async () => {
|
|
11
|
+
if (namespace) {
|
|
12
|
+
try {
|
|
13
|
+
const data = await djClient.namespaceSources(namespace);
|
|
14
|
+
setSources(data);
|
|
15
|
+
} catch (e) {
|
|
16
|
+
// Silently fail - badge just won't show
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
fetchSources();
|
|
21
|
+
}, [djClient, namespace]);
|
|
22
|
+
|
|
23
|
+
const namespaceParts = namespace ? namespace.split('.') : [];
|
|
24
|
+
const namespaceList = namespaceParts.map((piece, index) => {
|
|
25
|
+
return (
|
|
26
|
+
<li className="breadcrumb-item" key={index}>
|
|
27
|
+
<a
|
|
28
|
+
className="link-body-emphasis"
|
|
29
|
+
href={'/namespaces/' + namespaceParts.slice(0, index + 1).join('.')}
|
|
30
|
+
>
|
|
31
|
+
{piece}
|
|
32
|
+
</a>
|
|
33
|
+
</li>
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Render source badge
|
|
38
|
+
const renderSourceBadge = () => {
|
|
39
|
+
if (!sources || sources.total_deployments === 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isGit = sources.primary_source?.type === 'git';
|
|
44
|
+
const hasMultiple = sources.has_multiple_sources;
|
|
3
45
|
|
|
4
|
-
export default class NamespaceHeader extends Component {
|
|
5
|
-
render() {
|
|
6
|
-
const { namespace } = this.props;
|
|
7
|
-
const namespaceParts = namespace.split('.');
|
|
8
|
-
const namespaceList = namespaceParts.map((piece, index) => {
|
|
9
|
-
return (
|
|
10
|
-
<li className="breadcrumb-item" key={index}>
|
|
11
|
-
<a
|
|
12
|
-
className="link-body-emphasis"
|
|
13
|
-
href={'/namespaces/' + namespaceParts.slice(0, index + 1).join('.')}
|
|
14
|
-
>
|
|
15
|
-
{piece}
|
|
16
|
-
</a>
|
|
17
|
-
</li>
|
|
18
|
-
);
|
|
19
|
-
});
|
|
20
46
|
return (
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
47
|
+
<li
|
|
48
|
+
className="breadcrumb-item"
|
|
49
|
+
style={{ display: 'flex', alignItems: 'center' }}
|
|
50
|
+
>
|
|
51
|
+
<span
|
|
52
|
+
title={
|
|
53
|
+
hasMultiple
|
|
54
|
+
? `Warning: ${sources.sources.length} deployment sources`
|
|
55
|
+
: isGit
|
|
56
|
+
? `CI-managed: ${sources.primary_source.repository}${
|
|
57
|
+
sources.primary_source.branch
|
|
58
|
+
? ` (${sources.primary_source.branch})`
|
|
59
|
+
: ''
|
|
60
|
+
}`
|
|
61
|
+
: 'Local/adhoc deployment'
|
|
62
|
+
}
|
|
63
|
+
style={{
|
|
64
|
+
display: 'inline-flex',
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
gap: '4px',
|
|
67
|
+
padding: '2px 8px',
|
|
68
|
+
fontSize: '11px',
|
|
69
|
+
borderRadius: '12px',
|
|
70
|
+
backgroundColor: hasMultiple
|
|
71
|
+
? '#fff3cd'
|
|
72
|
+
: isGit
|
|
73
|
+
? '#d4edda'
|
|
74
|
+
: '#e2e3e5',
|
|
75
|
+
color: hasMultiple ? '#856404' : isGit ? '#155724' : '#383d41',
|
|
76
|
+
cursor: 'help',
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
{hasMultiple ? '⚠️' : isGit ? '🔗' : '📁'}
|
|
80
|
+
{hasMultiple
|
|
81
|
+
? `${sources.sources.length} sources`
|
|
82
|
+
: isGit
|
|
83
|
+
? 'CI'
|
|
84
|
+
: 'Local'}
|
|
85
|
+
</span>
|
|
86
|
+
</li>
|
|
29
87
|
);
|
|
30
|
-
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<ol className="breadcrumb breadcrumb-chevron p-3 bg-body-tertiary rounded-3">
|
|
92
|
+
<li className="breadcrumb-item">
|
|
93
|
+
<a href="/">
|
|
94
|
+
<HorizontalHierarchyIcon />
|
|
95
|
+
</a>
|
|
96
|
+
</li>
|
|
97
|
+
{namespaceList}
|
|
98
|
+
{renderSourceBadge()}
|
|
99
|
+
</ol>
|
|
100
|
+
);
|
|
31
101
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
3
|
import { createRenderer } from 'react-test-renderer/shallow';
|
|
4
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
3
5
|
|
|
4
6
|
import NamespaceHeader from '../NamespaceHeader';
|
|
7
|
+
import DJClientContext from '../../providers/djclient';
|
|
5
8
|
|
|
6
9
|
const renderer = createRenderer();
|
|
7
10
|
|
|
@@ -11,4 +14,166 @@ describe('<NamespaceHeader />', () => {
|
|
|
11
14
|
const renderedOutput = renderer.getRenderOutput();
|
|
12
15
|
expect(renderedOutput).toMatchSnapshot();
|
|
13
16
|
});
|
|
17
|
+
|
|
18
|
+
it('should render git source badge when source type is git', async () => {
|
|
19
|
+
const mockDjClient = {
|
|
20
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
21
|
+
total_deployments: 5,
|
|
22
|
+
has_multiple_sources: false,
|
|
23
|
+
primary_source: {
|
|
24
|
+
type: 'git',
|
|
25
|
+
repository: 'github.com/test/repo',
|
|
26
|
+
branch: 'main',
|
|
27
|
+
},
|
|
28
|
+
sources: [
|
|
29
|
+
{
|
|
30
|
+
type: 'git',
|
|
31
|
+
repository: 'github.com/test/repo',
|
|
32
|
+
branch: 'main',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
render(
|
|
39
|
+
<MemoryRouter>
|
|
40
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
41
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
42
|
+
</DJClientContext.Provider>
|
|
43
|
+
</MemoryRouter>,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
await waitFor(() => {
|
|
47
|
+
expect(mockDjClient.namespaceSources).toHaveBeenCalledWith(
|
|
48
|
+
'test.namespace',
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Should render CI badge for git source
|
|
53
|
+
expect(screen.getByText(/CI/)).toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should render local source badge when source type is local', async () => {
|
|
57
|
+
const mockDjClient = {
|
|
58
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
59
|
+
total_deployments: 2,
|
|
60
|
+
has_multiple_sources: false,
|
|
61
|
+
primary_source: {
|
|
62
|
+
type: 'local',
|
|
63
|
+
hostname: 'localhost',
|
|
64
|
+
},
|
|
65
|
+
sources: [
|
|
66
|
+
{
|
|
67
|
+
type: 'local',
|
|
68
|
+
hostname: 'localhost',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
}),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
render(
|
|
75
|
+
<MemoryRouter>
|
|
76
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
77
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
78
|
+
</DJClientContext.Provider>
|
|
79
|
+
</MemoryRouter>,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
await waitFor(() => {
|
|
83
|
+
expect(mockDjClient.namespaceSources).toHaveBeenCalledWith(
|
|
84
|
+
'test.namespace',
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Should render Local badge for local source
|
|
89
|
+
expect(screen.getByText(/Local/)).toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should render warning badge when multiple sources exist', async () => {
|
|
93
|
+
const mockDjClient = {
|
|
94
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
95
|
+
total_deployments: 10,
|
|
96
|
+
has_multiple_sources: true,
|
|
97
|
+
primary_source: {
|
|
98
|
+
type: 'git',
|
|
99
|
+
repository: 'github.com/test/repo',
|
|
100
|
+
},
|
|
101
|
+
sources: [
|
|
102
|
+
{ type: 'git', repository: 'github.com/test/repo' },
|
|
103
|
+
{ type: 'local', hostname: 'localhost' },
|
|
104
|
+
],
|
|
105
|
+
}),
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
render(
|
|
109
|
+
<MemoryRouter>
|
|
110
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
111
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
112
|
+
</DJClientContext.Provider>
|
|
113
|
+
</MemoryRouter>,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(mockDjClient.namespaceSources).toHaveBeenCalledWith(
|
|
118
|
+
'test.namespace',
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Should render warning badge for multiple sources
|
|
123
|
+
expect(screen.getByText(/2 sources/)).toBeInTheDocument();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should not render badge when no deployments', async () => {
|
|
127
|
+
const mockDjClient = {
|
|
128
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
129
|
+
total_deployments: 0,
|
|
130
|
+
has_multiple_sources: false,
|
|
131
|
+
primary_source: null,
|
|
132
|
+
sources: [],
|
|
133
|
+
}),
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
render(
|
|
137
|
+
<MemoryRouter>
|
|
138
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
139
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
140
|
+
</DJClientContext.Provider>
|
|
141
|
+
</MemoryRouter>,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
await waitFor(() => {
|
|
145
|
+
expect(mockDjClient.namespaceSources).toHaveBeenCalledWith(
|
|
146
|
+
'test.namespace',
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Should not render any source badge
|
|
151
|
+
expect(screen.queryByText(/CI/)).not.toBeInTheDocument();
|
|
152
|
+
expect(screen.queryByText(/Local/)).not.toBeInTheDocument();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should handle API error gracefully', async () => {
|
|
156
|
+
const mockDjClient = {
|
|
157
|
+
namespaceSources: jest.fn().mockRejectedValue(new Error('API Error')),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
render(
|
|
161
|
+
<MemoryRouter>
|
|
162
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
163
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
164
|
+
</DJClientContext.Provider>
|
|
165
|
+
</MemoryRouter>,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
await waitFor(() => {
|
|
169
|
+
expect(mockDjClient.namespaceSources).toHaveBeenCalledWith(
|
|
170
|
+
'test.namespace',
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Should still render breadcrumb without badge
|
|
175
|
+
expect(screen.getByText('test')).toBeInTheDocument();
|
|
176
|
+
expect(screen.getByText('namespace')).toBeInTheDocument();
|
|
177
|
+
expect(screen.queryByText(/CI/)).not.toBeInTheDocument();
|
|
178
|
+
});
|
|
14
179
|
});
|
|
@@ -4,7 +4,12 @@ import ExpandedIcon from '../../icons/ExpandedIcon';
|
|
|
4
4
|
import AddItemIcon from '../../icons/AddItemIcon';
|
|
5
5
|
import DJClientContext from '../../providers/djclient';
|
|
6
6
|
|
|
7
|
-
const Explorer = ({
|
|
7
|
+
const Explorer = ({
|
|
8
|
+
item = [],
|
|
9
|
+
current,
|
|
10
|
+
isTopLevel = false,
|
|
11
|
+
namespaceSources = {},
|
|
12
|
+
}) => {
|
|
8
13
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
9
14
|
const [items, setItems] = useState([]);
|
|
10
15
|
const [expand, setExpand] = useState(false);
|
|
@@ -106,13 +111,65 @@ const Explorer = ({ item = [], current, isTopLevel = false }) => {
|
|
|
106
111
|
}}
|
|
107
112
|
>
|
|
108
113
|
{items.children && items.children.length > 0 ? (
|
|
109
|
-
<span
|
|
114
|
+
<span
|
|
115
|
+
style={{
|
|
116
|
+
fontSize: '10px',
|
|
117
|
+
color: '#94a3b8',
|
|
118
|
+
width: '12px',
|
|
119
|
+
display: 'flex',
|
|
120
|
+
alignItems: 'center',
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
110
123
|
{!expand ? <CollapsedIcon /> : <ExpandedIcon />}
|
|
111
124
|
</span>
|
|
112
125
|
) : (
|
|
113
|
-
<span style={{
|
|
126
|
+
<span style={{ width: '12px' }} />
|
|
114
127
|
)}
|
|
115
128
|
<a href={`/namespaces/${items.path}`}>{items.namespace}</a>
|
|
129
|
+
{/* Deployment source badge */}
|
|
130
|
+
{namespaceSources[items.path] &&
|
|
131
|
+
namespaceSources[items.path].total_deployments > 0 &&
|
|
132
|
+
namespaceSources[items.path].primary_source?.type === 'git' && (
|
|
133
|
+
<span
|
|
134
|
+
title={`Git: ${
|
|
135
|
+
namespaceSources[items.path].primary_source.repository ||
|
|
136
|
+
'unknown'
|
|
137
|
+
}${
|
|
138
|
+
namespaceSources[items.path].primary_source.branch
|
|
139
|
+
? ` (${namespaceSources[items.path].primary_source.branch})`
|
|
140
|
+
: ''
|
|
141
|
+
}`}
|
|
142
|
+
style={{
|
|
143
|
+
marginLeft: '6px',
|
|
144
|
+
fontSize: '9px',
|
|
145
|
+
padding: '1px 4px',
|
|
146
|
+
borderRadius: '3px',
|
|
147
|
+
backgroundColor: '#d4edda',
|
|
148
|
+
color: '#155724',
|
|
149
|
+
display: 'inline-flex',
|
|
150
|
+
alignItems: 'center',
|
|
151
|
+
gap: '2px',
|
|
152
|
+
}}
|
|
153
|
+
>
|
|
154
|
+
<svg
|
|
155
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
156
|
+
width="10"
|
|
157
|
+
height="10"
|
|
158
|
+
viewBox="0 0 24 24"
|
|
159
|
+
fill="none"
|
|
160
|
+
stroke="currentColor"
|
|
161
|
+
strokeWidth="2"
|
|
162
|
+
strokeLinecap="round"
|
|
163
|
+
strokeLinejoin="round"
|
|
164
|
+
>
|
|
165
|
+
<line x1="6" y1="3" x2="6" y2="15"></line>
|
|
166
|
+
<circle cx="18" cy="6" r="3"></circle>
|
|
167
|
+
<circle cx="6" cy="18" r="3"></circle>
|
|
168
|
+
<path d="M18 9a9 9 0 0 1-9 9"></path>
|
|
169
|
+
</svg>
|
|
170
|
+
Git
|
|
171
|
+
</span>
|
|
172
|
+
)}
|
|
116
173
|
<button
|
|
117
174
|
className="namespace-add-button"
|
|
118
175
|
onClick={e => {
|
|
@@ -144,10 +201,10 @@ const Explorer = ({ item = [], current, isTopLevel = false }) => {
|
|
|
144
201
|
{isCreatingChild && (
|
|
145
202
|
<div
|
|
146
203
|
style={{
|
|
147
|
-
paddingLeft: '
|
|
148
|
-
marginLeft: '
|
|
149
|
-
borderLeft: '1px solid
|
|
150
|
-
marginTop: '
|
|
204
|
+
paddingLeft: '0.55rem',
|
|
205
|
+
marginLeft: '0.25rem',
|
|
206
|
+
borderLeft: '1px solid #e2e8f0',
|
|
207
|
+
marginTop: '2px',
|
|
151
208
|
}}
|
|
152
209
|
>
|
|
153
210
|
<form
|
|
@@ -205,9 +262,9 @@ const Explorer = ({ item = [], current, isTopLevel = false }) => {
|
|
|
205
262
|
items.children.map((item, index) => (
|
|
206
263
|
<div
|
|
207
264
|
style={{
|
|
208
|
-
paddingLeft: '
|
|
209
|
-
marginLeft: '
|
|
210
|
-
borderLeft: '1px solid
|
|
265
|
+
paddingLeft: '0.55rem',
|
|
266
|
+
marginLeft: '0.25rem',
|
|
267
|
+
borderLeft: '1px solid #e2e8f0',
|
|
211
268
|
}}
|
|
212
269
|
key={index}
|
|
213
270
|
>
|
|
@@ -219,6 +276,7 @@ const Explorer = ({ item = [], current, isTopLevel = false }) => {
|
|
|
219
276
|
item={item}
|
|
220
277
|
current={highlight}
|
|
221
278
|
isTopLevel={false}
|
|
279
|
+
namespaceSources={namespaceSources}
|
|
222
280
|
/>
|
|
223
281
|
</div>
|
|
224
282
|
</div>
|
|
@@ -14,6 +14,8 @@ const mockDjClient = {
|
|
|
14
14
|
whoami: jest.fn(),
|
|
15
15
|
users: jest.fn(),
|
|
16
16
|
listTags: jest.fn(),
|
|
17
|
+
namespaceSources: jest.fn(),
|
|
18
|
+
namespaceSourcesBulk: jest.fn(),
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
const mockCurrentUser = { username: 'dj', email: 'dj@test.com' };
|
|
@@ -67,6 +69,10 @@ describe('NamespacePage', () => {
|
|
|
67
69
|
{ name: 'tag1' },
|
|
68
70
|
{ name: 'tag2' },
|
|
69
71
|
]);
|
|
72
|
+
mockDjClient.namespaceSources.mockResolvedValue({ sources: [] });
|
|
73
|
+
mockDjClient.namespaceSourcesBulk.mockResolvedValue({
|
|
74
|
+
namespace_sources: {},
|
|
75
|
+
});
|
|
70
76
|
mockDjClient.namespaces.mockResolvedValue([
|
|
71
77
|
{
|
|
72
78
|
namespace: 'common.one',
|
|
@@ -280,13 +286,15 @@ describe('NamespacePage', () => {
|
|
|
280
286
|
|
|
281
287
|
// Wait for namespaces to load
|
|
282
288
|
await waitFor(() => {
|
|
283
|
-
expect(screen.
|
|
289
|
+
expect(screen.getAllByText('default').length).toBeGreaterThan(0);
|
|
284
290
|
});
|
|
285
291
|
|
|
286
|
-
// Find the namespace
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
.
|
|
292
|
+
// Find the namespace in the sidebar by looking for the link with specific href
|
|
293
|
+
const allLinks = screen.getAllByRole('link');
|
|
294
|
+
const defaultNamespaceLink = allLinks.find(
|
|
295
|
+
link => link.getAttribute('href') === '/namespaces/default',
|
|
296
|
+
);
|
|
297
|
+
const defaultNamespace = defaultNamespaceLink.closest('.select-name');
|
|
290
298
|
fireEvent.mouseEnter(defaultNamespace);
|
|
291
299
|
|
|
292
300
|
// Find the add namespace button (it exists but is hidden, so use getAllByTitle)
|
|
@@ -340,13 +348,15 @@ describe('NamespacePage', () => {
|
|
|
340
348
|
|
|
341
349
|
// Wait for namespaces to load
|
|
342
350
|
await waitFor(() => {
|
|
343
|
-
expect(screen.
|
|
351
|
+
expect(screen.getAllByText('default').length).toBeGreaterThan(0);
|
|
344
352
|
});
|
|
345
353
|
|
|
346
|
-
// Find the namespace
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
.
|
|
354
|
+
// Find the namespace in the sidebar by looking for the link with specific href
|
|
355
|
+
const allLinks = screen.getAllByRole('link');
|
|
356
|
+
const defaultNamespaceLink = allLinks.find(
|
|
357
|
+
link => link.getAttribute('href') === '/namespaces/default',
|
|
358
|
+
);
|
|
359
|
+
const defaultNamespace = defaultNamespaceLink.closest('.select-name');
|
|
350
360
|
fireEvent.mouseEnter(defaultNamespace);
|
|
351
361
|
|
|
352
362
|
// Find the add namespace button (it exists but is hidden, so use getAllByTitle)
|
|
@@ -384,7 +394,7 @@ describe('NamespacePage', () => {
|
|
|
384
394
|
renderWithProviders(<NamespacePage />);
|
|
385
395
|
|
|
386
396
|
await waitFor(() => {
|
|
387
|
-
expect(screen.getByText('Quick
|
|
397
|
+
expect(screen.getByText('Quick')).toBeInTheDocument();
|
|
388
398
|
});
|
|
389
399
|
|
|
390
400
|
// Check that preset buttons are rendered
|