Skip to content

Commit 4a275cf

Browse files
authored
feat: Model edit and delete e2e cypress tests (#127)
* added e2e test for model delete Signed-off-by: LogicalGuy77 <[email protected]> * added model edit e2e test Signed-off-by: LogicalGuy77 <[email protected]> --------- Signed-off-by: LogicalGuy77 <[email protected]>
1 parent b47d074 commit 4a275cf

File tree

2 files changed

+686
-0
lines changed

2 files changed

+686
-0
lines changed
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
describe('Models Web App - Model Deletion Tests', () => {
2+
beforeEach(() => {
3+
// Set up default intercepts for all tests
4+
cy.intercept('GET', '/api/namespaces', {
5+
statusCode: 200,
6+
body: {
7+
namespaces: ['kubeflow-user']
8+
}
9+
}).as('getNamespaces')
10+
11+
// Mock inference services with sample data for deletion testing
12+
cy.intercept('GET', '/api/namespaces/*/inferenceservices', {
13+
statusCode: 200,
14+
body: {
15+
inferenceServices: [
16+
{
17+
metadata: {
18+
name: 'test-sklearn-model',
19+
namespace: 'kubeflow-user',
20+
creationTimestamp: '2024-01-15T10:30:00Z'
21+
},
22+
spec: {
23+
predictor: {
24+
sklearn: {
25+
storageUri: 'gs://test-bucket/sklearn-model',
26+
runtimeVersion: '0.24.1',
27+
protocolVersion: 'v1'
28+
}
29+
}
30+
},
31+
status: {
32+
conditions: [
33+
{
34+
type: 'Ready',
35+
status: 'True',
36+
lastTransitionTime: '2024-01-15T10:35:00Z'
37+
}
38+
],
39+
url: 'http://test-sklearn-model.kubeflow-user.example.com'
40+
}
41+
},
42+
{
43+
metadata: {
44+
name: 'test-tensorflow-model',
45+
namespace: 'kubeflow-user',
46+
creationTimestamp: '2024-01-15T11:00:00Z'
47+
},
48+
spec: {
49+
predictor: {
50+
tensorflow: {
51+
storageUri: 'gs://test-bucket/tensorflow-model',
52+
runtimeVersion: '2.8.0',
53+
protocolVersion: 'v1'
54+
}
55+
}
56+
},
57+
status: {
58+
conditions: [
59+
{
60+
type: 'Ready',
61+
status: 'True',
62+
lastTransitionTime: '2024-01-15T11:05:00Z'
63+
}
64+
],
65+
url: 'http://test-tensorflow-model.kubeflow-user.example.com'
66+
}
67+
}
68+
]
69+
}
70+
}).as('getInferenceServicesWithData')
71+
72+
cy.visit('/')
73+
})
74+
75+
it('should display delete buttons for inference services', () => {
76+
// Wait for data to load
77+
cy.wait('@getNamespaces')
78+
cy.wait('@getInferenceServicesWithData')
79+
80+
// Verify table shows the models
81+
cy.get('lib-table', { timeout: 3000 }).should('exist')
82+
cy.get('lib-table').within(() => {
83+
cy.contains('test-sklearn-model').should('be.visible')
84+
cy.contains('test-tensorflow-model').should('be.visible')
85+
})
86+
87+
// Verify delete buttons are present and enabled
88+
cy.get('lib-table').within(() => {
89+
cy.get('button[mat-icon-button]').then($buttons => {
90+
// Filter for delete buttons (should have delete icon)
91+
const deleteButtons = Array.from($buttons).filter(btn => {
92+
const icon = btn.querySelector('mat-icon');
93+
return icon && icon.textContent?.trim() === 'delete';
94+
});
95+
expect(deleteButtons).to.have.length.at.least(2);
96+
});
97+
})
98+
})
99+
100+
it('should successfully delete a model with confirmation', () => {
101+
// Mock successful deletion
102+
cy.intercept('DELETE', '/api/namespaces/kubeflow-user/inferenceservices/test-sklearn-model', {
103+
statusCode: 200,
104+
body: { message: 'InferenceService deleted successfully' }
105+
}).as('deleteInferenceService')
106+
107+
// Wait for initial data to load
108+
cy.wait('@getNamespaces')
109+
cy.wait('@getInferenceServicesWithData')
110+
111+
// Find and click delete button for test-sklearn-model
112+
cy.get('lib-table').within(() => {
113+
// Find the row containing test-sklearn-model and click its delete button
114+
cy.contains('tr', 'test-sklearn-model').within(() => {
115+
cy.get('button[mat-icon-button]').contains('mat-icon', 'delete').click()
116+
})
117+
})
118+
119+
// Verify confirmation dialog appears
120+
cy.get('mat-dialog-container', { timeout: 5000 }).should('be.visible')
121+
cy.get('mat-dialog-container').within(() => {
122+
cy.contains('Delete Endpoint test-sklearn-model?').should('be.visible')
123+
cy.contains('You cannot undo this action').should('be.visible')
124+
cy.get('button').contains('DELETE').should('be.visible')
125+
cy.get('button').contains('CANCEL').should('be.visible')
126+
})
127+
128+
// Click DELETE button to confirm
129+
cy.get('mat-dialog-container').within(() => {
130+
cy.get('button').contains('DELETE').click()
131+
})
132+
133+
// Verify API call was made
134+
cy.wait('@deleteInferenceService')
135+
136+
// Verify dialog closes
137+
cy.get('mat-dialog-container').should('not.exist')
138+
139+
// Verify the model row still exists after deletion is initiated
140+
cy.get('lib-table').within(() => {
141+
cy.contains('tr', 'test-sklearn-model').within(() => {
142+
// Verify status icon exists (deletion process may have changed its state)
143+
cy.get('lib-status-icon').should('exist')
144+
})
145+
})
146+
})
147+
148+
it('should cancel deletion when CANCEL is clicked', () => {
149+
// Wait for initial data to load
150+
cy.wait('@getNamespaces')
151+
cy.wait('@getInferenceServicesWithData')
152+
153+
// Find and click delete button for test-tensorflow-model
154+
cy.get('lib-table').within(() => {
155+
cy.contains('tr', 'test-tensorflow-model').within(() => {
156+
cy.get('button[mat-icon-button]').contains('mat-icon', 'delete').click()
157+
})
158+
})
159+
160+
// Verify confirmation dialog appears
161+
cy.get('mat-dialog-container', { timeout: 5000 }).should('be.visible')
162+
cy.get('mat-dialog-container').within(() => {
163+
cy.contains('Delete Endpoint test-tensorflow-model?').should('be.visible')
164+
})
165+
166+
// Click CANCEL button
167+
cy.get('mat-dialog-container').within(() => {
168+
cy.get('button').contains('CANCEL').click()
169+
})
170+
171+
// Verify dialog closes
172+
cy.get('mat-dialog-container').should('not.exist')
173+
174+
// Verify the model remains in ready state (no terminating status)
175+
cy.get('lib-table').within(() => {
176+
cy.contains('tr', 'test-tensorflow-model').within(() => {
177+
cy.contains('Preparing to delete').should('not.exist')
178+
// Should still be in ready/running state
179+
cy.get('lib-status-icon').should('exist')
180+
})
181+
})
182+
})
183+
184+
it('should handle deletion errors gracefully', () => {
185+
// Mock deletion failure
186+
cy.intercept('DELETE', '/api/namespaces/kubeflow-user/inferenceservices/test-sklearn-model', {
187+
statusCode: 500,
188+
body: { error: 'Internal server error during deletion' }
189+
}).as('deleteInferenceServiceError')
190+
191+
// Wait for initial data to load
192+
cy.wait('@getNamespaces')
193+
cy.wait('@getInferenceServicesWithData')
194+
195+
// Find and click delete button
196+
cy.get('lib-table').within(() => {
197+
cy.contains('tr', 'test-sklearn-model').within(() => {
198+
cy.get('button[mat-icon-button]').contains('mat-icon', 'delete').click()
199+
})
200+
})
201+
202+
// Confirm deletion
203+
cy.get('mat-dialog-container', { timeout: 5000 }).should('be.visible')
204+
cy.get('mat-dialog-container').within(() => {
205+
cy.get('button').contains('DELETE').click()
206+
})
207+
208+
// Wait for failed API call
209+
cy.wait('@deleteInferenceServiceError')
210+
211+
// Verify error is handled (dialog should remain open or show error)
212+
cy.get('mat-dialog-container').should('be.visible')
213+
214+
// Cancel the dialog
215+
cy.get('mat-dialog-container').within(() => {
216+
cy.get('button').contains('CANCEL').click()
217+
})
218+
219+
// Verify dialog closes and model remains in original state
220+
cy.get('mat-dialog-container').should('not.exist')
221+
cy.get('lib-table').within(() => {
222+
cy.contains('tr', 'test-sklearn-model').should('exist')
223+
})
224+
})
225+
226+
it('should show confirmation dialog and handle API call', () => {
227+
// Mock deletion response
228+
cy.intercept('DELETE', '/api/namespaces/kubeflow-user/inferenceservices/test-sklearn-model', {
229+
statusCode: 200,
230+
body: { message: 'InferenceService deleted successfully' }
231+
}).as('deleteInferenceService')
232+
233+
// Wait for initial data to load
234+
cy.wait('@getNamespaces')
235+
cy.wait('@getInferenceServicesWithData')
236+
237+
// Initiate deletion
238+
cy.get('lib-table').within(() => {
239+
cy.contains('tr', 'test-sklearn-model').within(() => {
240+
cy.get('button[mat-icon-button]').contains('mat-icon', 'delete').click()
241+
})
242+
})
243+
244+
// Confirm deletion
245+
cy.get('mat-dialog-container').within(() => {
246+
cy.get('button').contains('DELETE').click()
247+
})
248+
249+
// Verify API call was made
250+
cy.wait('@deleteInferenceService')
251+
252+
// Verify dialog closes
253+
cy.get('mat-dialog-container').should('not.exist')
254+
255+
// Verify the model row still exists (it will show terminating state)
256+
cy.get('lib-table').within(() => {
257+
cy.contains('tr', 'test-sklearn-model').should('exist')
258+
})
259+
})
260+
261+
it('should display terminating endpoints correctly', () => {
262+
// Mock data with terminating endpoint
263+
cy.intercept('GET', '/api/namespaces/*/inferenceservices', {
264+
statusCode: 200,
265+
body: {
266+
inferenceServices: [
267+
{
268+
metadata: {
269+
name: 'terminating-model',
270+
namespace: 'kubeflow-user',
271+
creationTimestamp: '2024-01-15T10:30:00Z',
272+
deletionTimestamp: '2024-01-15T12:00:00Z'
273+
},
274+
spec: {
275+
predictor: {
276+
sklearn: {
277+
storageUri: 'gs://test-bucket/model',
278+
runtimeVersion: '0.24.1'
279+
}
280+
}
281+
},
282+
status: {
283+
conditions: [
284+
{
285+
type: 'Ready',
286+
status: 'False',
287+
reason: 'Terminating',
288+
lastTransitionTime: '2024-01-15T12:00:00Z'
289+
}
290+
]
291+
}
292+
}
293+
]
294+
}
295+
}).as('getTerminatingService')
296+
297+
// Reload to get new data
298+
cy.reload()
299+
cy.wait('@getNamespaces')
300+
cy.wait('@getTerminatingService')
301+
302+
// Verify the terminating model is displayed
303+
cy.get('lib-table').within(() => {
304+
cy.contains('tr', 'terminating-model').should('exist')
305+
// Verify it has some kind of status indicator
306+
cy.contains('tr', 'terminating-model').within(() => {
307+
cy.get('lib-status-icon').should('exist')
308+
})
309+
})
310+
})
311+
312+
it('should show delete button tooltip', () => {
313+
// Wait for data to load
314+
cy.wait('@getNamespaces')
315+
cy.wait('@getInferenceServicesWithData')
316+
317+
// Hover over delete button to show tooltip
318+
cy.get('lib-table').within(() => {
319+
cy.contains('tr', 'test-sklearn-model').within(() => {
320+
cy.get('button[mat-icon-button]').contains('mat-icon', 'delete')
321+
.trigger('mouseenter')
322+
})
323+
})
324+
325+
// Verify tooltip appears
326+
cy.get('.mat-tooltip', { timeout: 3000 })
327+
.should('be.visible')
328+
.and('contain', 'Delete endpoint')
329+
})
330+
})

0 commit comments

Comments
 (0)