@@ -15,10 +15,49 @@ test.describe(
1515 ( ) => {
1616 test . setTimeout ( 60000 ) ;
1717
18+ const createdResources : Array < { cardName ?: string ; folderName ?: string } > =
19+ [ ] ;
20+
1821 test . beforeEach ( async ( { page, request } ) => {
1922 await loginAsAdmin ( page , request ) ;
2023 } ) ;
2124
25+ test . afterEach ( async ( { page } ) => {
26+ const adminModelCardPage = new AdminModelCardPage ( page ) ;
27+ for ( const r of [ ...createdResources ] . reverse ( ) ) {
28+ if ( r . cardName ) {
29+ try {
30+ await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
31+ await adminModelCardPage . waitForTableLoad ( ) ;
32+ await adminModelCardPage . applyNameFilter ( r . cardName ) ;
33+ const row = adminModelCardPage . getRowByName ( r . cardName ) ;
34+ if ( await row . isVisible ( ) ) {
35+ await adminModelCardPage . deleteModelCardByName ( r . cardName ) ;
36+ }
37+ } catch {
38+ // ignore — card may have already been deleted by the test
39+ }
40+ }
41+ if ( r . folderName ) {
42+ try {
43+ await moveToTrashAndVerify ( page , r . folderName , 'admin-data' ) ;
44+ } catch {
45+ // ignore — folder may already be in trash or deleted
46+ }
47+ try {
48+ await deleteForeverAndVerifyFromTrash (
49+ page ,
50+ r . folderName ,
51+ 'admin-data' ,
52+ ) ;
53+ } catch {
54+ // ignore — folder may already be permanently deleted
55+ }
56+ }
57+ }
58+ createdResources . length = 0 ;
59+ } ) ;
60+
2261 // 5.1 Superadmin can delete a model card via the trash icon with confirmation
2362 test ( 'Superadmin can delete a model card via the trash icon with confirmation' , async ( {
2463 page,
@@ -29,6 +68,9 @@ test.describe(
2968 const folderName = `e2e-test-delete-single-folder-${ timestamp } ` ;
3069 const cardName = `e2e-test-delete-single-${ timestamp } ` ;
3170
71+ // Track folder for cleanup (card will be deleted by test, folder needs cleanup)
72+ createdResources . push ( { folderName } ) ;
73+
3274 // Setup: create a dedicated folder and model card
3375 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
3476 await adminModelCardPage . waitForTableLoad ( ) ;
@@ -47,13 +89,12 @@ test.describe(
4789
4890 const confirmDialog = adminModelCardPage . getDeleteConfirmDialog ( ) ;
4991
50- // Verify the confirmation dialog shows description and item name
92+ // Verify the confirmation dialog shows description text
93+ // (For single item + requireConfirmInput, the item list box is removed;
94+ // the card name appears in the description text instead)
5195 await expect (
5296 confirmDialog . getByText ( / A r e y o u s u r e y o u w a n t t o d e l e t e / ) ,
5397 ) . toBeVisible ( ) ;
54- await expect (
55- confirmDialog . getByText ( cardName , { exact : true } ) . first ( ) ,
56- ) . toBeVisible ( ) ;
5798 await expect (
5899 confirmDialog . getByText ( 'This action cannot be undone.' ) ,
59100 ) . toBeVisible ( ) ;
@@ -83,9 +124,7 @@ test.describe(
83124 '0 items' ,
84125 ) ;
85126
86- // Cleanup: move folder to trash then permanently delete
87- await moveToTrashAndVerify ( page , folderName , 'admin-data' ) ;
88- await deleteForeverAndVerifyFromTrash ( page , folderName , 'admin-data' ) ;
127+ // Card was successfully deleted — afterEach only needs to clean up the folder
89128 } ) ;
90129
91130 // 5.2 Superadmin can cancel a single-delete confirmation without deleting
@@ -98,6 +137,9 @@ test.describe(
98137 const folderName = `e2e-test-no-delete-folder-${ timestamp } ` ;
99138 const cardName = `e2e-test-no-delete-${ timestamp } ` ;
100139
140+ // Track both for cleanup (test does not delete them)
141+ createdResources . push ( { cardName, folderName } ) ;
142+
101143 // Setup: create a dedicated folder and model card
102144 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
103145 await adminModelCardPage . waitForTableLoad ( ) ;
@@ -122,11 +164,6 @@ test.describe(
122164
123165 // Verify the model card is still in the table
124166 await expect ( adminModelCardPage . getRowByName ( cardName ) ) . toBeVisible ( ) ;
125-
126- // Cleanup: delete card only, then move folder to trash and permanently delete
127- await adminModelCardPage . deleteModelCardByName ( cardName ) ;
128- await moveToTrashAndVerify ( page , folderName , 'admin-data' ) ;
129- await deleteForeverAndVerifyFromTrash ( page , folderName , 'admin-data' ) ;
130167 } ) ;
131168
132169 // 5.3 Superadmin can select multiple model cards and delete them in bulk
@@ -144,6 +181,9 @@ test.describe(
144181 `${ filterPrefix } -3` ,
145182 ] ;
146183
184+ // Track folder for cleanup (cards will be deleted by test)
185+ createdResources . push ( { folderName } ) ;
186+
147187 // Setup: create a shared folder via the "+" button for the first card,
148188 // then reuse it for the remaining cards
149189 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
@@ -171,7 +211,9 @@ test.describe(
171211 } ) ;
172212
173213 // Check the header checkbox to select all visible rows
174- await adminModelCardPage . getHeaderCheckbox ( ) . check ( ) ;
214+ // Use .click() instead of .check() — Ant Design's "select all" checkbox can be in
215+ // indeterminate state when some rows are pre-selected, and .check() fails in that case.
216+ await adminModelCardPage . getHeaderCheckbox ( ) . click ( ) ;
175217
176218 // Verify the BAISelectionLabel appears showing selected count
177219 await expect ( adminModelCardPage . getSelectionLabel ( ) ) . toBeVisible ( ) ;
@@ -194,7 +236,9 @@ test.describe(
194236 }
195237
196238 // Type "Delete" in the confirmation input (required for bulk delete)
197- await bulkDialog . getByRole ( 'textbox' ) . fill ( 'Delete' ) ;
239+ // Use getByLabel to find the input via its Form.Item label text, which is more
240+ // robust than getByRole('textbox') when allowClear is present on the Input.
241+ await bulkDialog . getByLabel ( / T y p e .* t o c o n f i r m / i) . fill ( 'Delete' ) ;
198242
199243 // Click Delete to confirm
200244 await bulkDialog . getByRole ( 'button' , { name : 'Delete' } ) . click ( ) ;
@@ -205,10 +249,7 @@ test.describe(
205249 // Verify the selection label disappears
206250 await expect ( adminModelCardPage . getSelectionLabel ( ) ) . toBeHidden ( ) ;
207251
208- // Cleanup: model cards were deleted but the shared folder remains;
209- // move it to trash and permanently delete
210- await moveToTrashAndVerify ( page , folderName , 'admin-data' ) ;
211- await deleteForeverAndVerifyFromTrash ( page , folderName , 'admin-data' ) ;
252+ // Cards were deleted by test — afterEach only needs to clean up the shared folder
212253 } ) ;
213254
214255 // 5.4 Superadmin can cancel bulk deletion
@@ -220,6 +261,10 @@ test.describe(
220261 const filterPrefix = `e2e-test-bulk-cancel-${ timestamp } ` ;
221262 const cardNames = [ `${ filterPrefix } -1` , `${ filterPrefix } -2` ] ;
222263
264+ // Track both cards and folder for cleanup (test does not delete them)
265+ createdResources . push ( { cardName : cardNames [ 0 ] , folderName } ) ;
266+ createdResources . push ( { cardName : cardNames [ 1 ] } ) ;
267+
223268 // Setup: create a shared folder via the "+" button for the first card,
224269 // then reuse it for the second card
225270 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
@@ -267,13 +312,6 @@ test.describe(
267312 for ( const name of cardNames ) {
268313 await expect ( adminModelCardPage . getRowByName ( name ) ) . toBeVisible ( ) ;
269314 }
270-
271- // Cleanup: delete each model card (card only), then clean up the shared folder
272- for ( const name of cardNames ) {
273- await adminModelCardPage . deleteModelCardByName ( name ) ;
274- }
275- await moveToTrashAndVerify ( page , folderName , 'admin-data' ) ;
276- await deleteForeverAndVerifyFromTrash ( page , folderName , 'admin-data' ) ;
277315 } ) ;
278316
279317 // 5.5 Superadmin can clear selection using the BAISelectionLabel clear button
@@ -286,6 +324,9 @@ test.describe(
286324 const folderName = `e2e-test-clear-sel-folder-${ timestamp } ` ;
287325 const cardName = `e2e-test-clear-sel-${ timestamp } ` ;
288326
327+ // Track both for cleanup (test does not delete them)
328+ createdResources . push ( { cardName, folderName } ) ;
329+
289330 // Setup: create a model card so the table has at least one row with a checkbox
290331 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
291332 await adminModelCardPage . waitForTableLoad ( ) ;
@@ -320,11 +361,6 @@ test.describe(
320361
321362 // Verify the BAISelectionLabel disappears
322363 await expect ( adminModelCardPage . getSelectionLabel ( ) ) . toBeHidden ( ) ;
323-
324- // Cleanup: delete the model card then clean up the folder
325- await adminModelCardPage . deleteModelCardByName ( cardName ) ;
326- await moveToTrashAndVerify ( page , folderName , 'admin-data' ) ;
327- await deleteForeverAndVerifyFromTrash ( page , folderName , 'admin-data' ) ;
328364 } ) ;
329365
330366 // 5.6 Superadmin can select all model cards using the header checkbox
@@ -337,6 +373,9 @@ test.describe(
337373 const folderName = `e2e-test-select-all-folder-${ timestamp } ` ;
338374 const cardName = `e2e-test-select-all-${ timestamp } ` ;
339375
376+ // Track both for cleanup
377+ createdResources . push ( { cardName, folderName } ) ;
378+
340379 // Setup: create a model card so the table has at least one row
341380 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
342381 await adminModelCardPage . waitForTableLoad ( ) ;
@@ -370,17 +409,6 @@ test.describe(
370409 selectionText ?. match ( / ( \d + ) s e l e c t e d / ) ?. [ 1 ] ?? '0' ,
371410 ) ;
372411 expect ( selectedCount ) . toBeGreaterThan ( 0 ) ;
373-
374- // Cleanup: navigate fresh to reset selection state, then delete the model card
375- await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
376- await adminModelCardPage . waitForTableLoad ( ) ;
377- await adminModelCardPage . applyNameFilter ( cardName ) ;
378- await expect ( adminModelCardPage . getRowByName ( cardName ) ) . toBeVisible ( {
379- timeout : 15000 ,
380- } ) ;
381- await adminModelCardPage . deleteModelCardByName ( cardName ) ;
382- await moveToTrashAndVerify ( page , folderName , 'admin-data' ) ;
383- await deleteForeverAndVerifyFromTrash ( page , folderName , 'admin-data' ) ;
384412 } ) ;
385413
386414 // 5.7 Superadmin can delete a model card and its associated folder together
@@ -393,6 +421,11 @@ test.describe(
393421 const folderName = `e2e-test-delete-folder-${ timestamp } ` ;
394422 const cardName = `e2e-test-delete-with-folder-${ timestamp } ` ;
395423
424+ // No createdResources.push here — this test fully handles its own cleanup:
425+ // the modal deletes the card, and deleteForeverAndVerifyFromTrash permanently
426+ // removes the folder at the end of the test body. afterEach must not attempt
427+ // to re-clean a folder that no longer exists (it would hang until timeout).
428+
396429 // Create a model card with a new dedicated folder via the "+" button
397430 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
398431 await adminModelCardPage . waitForTableLoad ( ) ;
@@ -445,20 +478,27 @@ test.describe(
445478 await expect ( goToTrashLink ) . toBeVisible ( ) ;
446479
447480 // Click "Go to Data > Trash" and verify URL includes folder filter
481+ // The link navigates to /admin-data (not /data) with statusCategory=deleted and folder filter.
482+ // The filter param value may appear URL-encoded in various ways depending on the library
483+ // (e.g. `name+%3D%3D+%22folderName%22` or `name%20%3D%3D%20%22folderName%22`).
484+ // Use decodeURIComponent after re-encoding to normalize before comparison.
448485 await goToTrashLink . click ( ) ;
449486 await page . waitForURL (
450- ( url ) =>
451- url . pathname === '/data' &&
452- url . searchParams . get ( 'statusCategory' ) === 'deleted' &&
453- url . searchParams . get ( 'filter' ) === `name == "${ folderName } "` ,
487+ ( url ) => {
488+ if ( url . pathname !== '/admin-data' ) return false ;
489+ if ( url . searchParams . get ( 'statusCategory' ) !== 'deleted' )
490+ return false ;
491+ const rawFilter = url . searchParams . get ( 'filter' ) ?? '' ;
492+ return rawFilter === `name == "${ folderName } "` ;
493+ } ,
454494 { timeout : 10000 } ,
455495 ) ;
456496
457497 // Verify the folder row is visible in the trash list and permanently delete it
458498 await deleteForeverAndVerifyFromTrash ( page , folderName , 'admin-data' ) ;
459499 } ) ;
460500
461- // 5.8 Superadmin deletes card only: notification shows correct message and Go to Trash navigates correctly
501+ // 5.8 Superadmin deletes card only: notification shows correct message (no folder link)
462502 test ( 'Superadmin can delete a model card only and navigate to trash without folder filter' , async ( {
463503 page,
464504 } ) => {
@@ -468,6 +508,9 @@ test.describe(
468508 const folderName = `e2e-test-keep-folder-${ timestamp } ` ;
469509 const cardName = `e2e-test-delete-card-only-${ timestamp } ` ;
470510
511+ // Track folder for cleanup (card is deleted by test, folder is kept)
512+ createdResources . push ( { folderName } ) ;
513+
471514 // Create a model card with a new dedicated folder via the "+" button
472515 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
473516 await adminModelCardPage . waitForTableLoad ( ) ;
@@ -495,30 +538,15 @@ test.describe(
495538 // Confirm deletion
496539 await adminModelCardPage . getDeleteConfirmButton ( ) . click ( ) ;
497540
498- // Verify the notification message for card-only deletion
541+ // When folder is NOT deleted, a message.success() toast is shown (no "Go to Data > Trash" link)
542+ // Verify the success toast for card-only deletion
499543 await expect (
500544 page . getByText (
501545 'Model card has been deleted. The model folder was not deleted.' ,
502546 ) ,
503547 ) . toBeVisible ( { timeout : 15000 } ) ;
504548
505- // Verify "Go to Data > Trash" link is visible
506- const goToTrashLink = page . getByText ( 'Go to Data > Trash' ) ;
507- await expect ( goToTrashLink ) . toBeVisible ( ) ;
508-
509- // Click "Go to Data > Trash" and verify URL (no folder filter)
510- await goToTrashLink . click ( ) ;
511- await page . waitForURL (
512- ( url ) =>
513- url . pathname === '/data' &&
514- url . searchParams . get ( 'statusCategory' ) === 'deleted' &&
515- ! url . searchParams . has ( 'filter' ) ,
516- { timeout : 10000 } ,
517- ) ;
518-
519- // Cleanup: move the kept test folder to trash then permanently delete it
520- await moveToTrashAndVerify ( page , folderName , 'admin-data' ) ;
521- await deleteForeverAndVerifyFromTrash ( page , folderName , 'admin-data' ) ;
549+ // Card was deleted; afterEach will clean up the folder
522550 } ) ;
523551 } ,
524552) ;
0 commit comments