@@ -170,44 +170,38 @@ test.describe(
170170 test ( 'Superadmin can select multiple model cards and delete them in bulk' , async ( {
171171 page,
172172 } ) => {
173- test . setTimeout ( 180000 ) ;
173+ test . setTimeout ( 300000 ) ;
174174 const adminModelCardPage = new AdminModelCardPage ( page ) ;
175175 const timestamp = Date . now ( ) ;
176176 const folderName = `e2e-test-bulk-delete-folder-${ timestamp } ` ;
177177 const filterPrefix = `e2e-test-bulk-delete-${ timestamp } ` ;
178- const cardNames = [
179- `${ filterPrefix } -1` ,
180- `${ filterPrefix } -2` ,
181- `${ filterPrefix } -3` ,
182- ] ;
178+ const cardNames = [ `${ filterPrefix } -1` , `${ filterPrefix } -2` ] ;
183179
184180 // Track folder for cleanup (cards will be deleted by test)
185181 createdResources . push ( { folderName } ) ;
186182
187183 // Setup: create a shared folder via the "+" button for the first card,
188- // then reuse it for the remaining cards
184+ // then reuse it for the second card
189185 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
190186 await adminModelCardPage . waitForTableLoad ( ) ;
191187 await adminModelCardPage . createModelCard ( {
192188 name : cardNames [ 0 ] ,
193189 createNewFolderName : folderName ,
194190 } ) ;
195191
196- for ( const name of cardNames . slice ( 1 ) ) {
197- await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
198- await adminModelCardPage . waitForTableLoad ( ) ;
199- await adminModelCardPage . createModelCard ( {
200- name,
201- vfolderTitle : folderName ,
202- } ) ;
203- }
192+ await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
193+ await adminModelCardPage . waitForTableLoad ( ) ;
194+ await adminModelCardPage . createModelCard ( {
195+ name : cardNames [ 1 ] ,
196+ vfolderTitle : folderName ,
197+ } ) ;
204198
205199 // Filter to show only this run's test cards (timestamp ensures uniqueness)
206200 await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
207201 await adminModelCardPage . waitForTableLoad ( ) ;
208202 await adminModelCardPage . applyNameFilter ( filterPrefix ) ;
209203 await expect ( adminModelCardPage . getDataRows ( ) . first ( ) ) . toBeVisible ( {
210- timeout : 10000 ,
204+ timeout : 30000 ,
211205 } ) ;
212206
213207 // Check the header checkbox to select all visible rows
@@ -241,8 +235,12 @@ test.describe(
241235 // Click Delete to confirm
242236 await bulkDialog . getByRole ( 'button' , { name : 'Delete' } ) . click ( ) ;
243237
244- // Wait for bulk delete to complete — dialog closes when all mutations finish
245- await expect ( bulkDialog ) . toBeHidden ( { timeout : 90000 } ) ;
238+ // Wait for the success toast — under parallel-test load the bulk deletion
239+ // can be slow, so wait on the visible outcome rather than polling the dialog.
240+ await expect (
241+ page . getByText ( / \d + m o d e l c a r d \( s \) h a v e b e e n d e l e t e d \. / i) ,
242+ ) . toBeVisible ( { timeout : 240000 } ) ;
243+ await expect ( bulkDialog ) . toBeHidden ( ) ;
246244
247245 // Verify the selection label disappears
248246 await expect ( adminModelCardPage . getSelectionLabel ( ) ) . toBeHidden ( ) ;
@@ -284,7 +282,7 @@ test.describe(
284282 await adminModelCardPage . waitForTableLoad ( ) ;
285283 await adminModelCardPage . applyNameFilter ( filterPrefix ) ;
286284 await expect ( adminModelCardPage . getDataRows ( ) . first ( ) ) . toBeVisible ( {
287- timeout : 10000 ,
285+ timeout : 30000 ,
288286 } ) ;
289287
290288 // Select all visible rows
@@ -338,7 +336,7 @@ test.describe(
338336 await adminModelCardPage . waitForTableLoad ( ) ;
339337 await adminModelCardPage . applyNameFilter ( cardName ) ;
340338 await expect ( adminModelCardPage . getDataRows ( ) . first ( ) ) . toBeVisible ( {
341- timeout : 10000 ,
339+ timeout : 30000 ,
342340 } ) ;
343341
344342 // Check the checkbox for the first row
@@ -489,7 +487,7 @@ test.describe(
489487 const rawFilter = url . searchParams . get ( 'filter' ) ?? '' ;
490488 return rawFilter === `name == "${ folderName } "` ;
491489 } ,
492- { timeout : 10000 } ,
490+ { timeout : 30000 } ,
493491 ) ;
494492
495493 // Verify the folder row is visible in the trash list and permanently delete it
@@ -546,5 +544,179 @@ test.describe(
546544
547545 // Card was deleted; afterEach will clean up the folder
548546 } ) ;
547+
548+ // 5.9 Superadmin can bulk delete model cards and move their folders to trash
549+ test ( 'Superadmin can bulk delete model cards and move their associated folders to trash' , async ( {
550+ page,
551+ } ) => {
552+ test . setTimeout ( 300000 ) ;
553+ const adminModelCardPage = new AdminModelCardPage ( page ) ;
554+ const timestamp = Date . now ( ) ;
555+ const folderName1 = `e2e-test-bulk-trash-f1-${ timestamp } ` ;
556+ const folderName2 = `e2e-test-bulk-trash-f2-${ timestamp } ` ;
557+ const filterPrefix = `e2e-test-bulk-trash-${ timestamp } ` ;
558+ const cardNames = [ `${ filterPrefix } -1` , `${ filterPrefix } -2` ] ;
559+
560+ // Register for cleanup — afterEach handles mid-test failure leaks via try-catch.
561+ // Only push cardNames here; folders are permanently deleted at the end of the test
562+ // body via deleteForeverAndVerifyFromTrash. If afterEach also tried to
563+ // moveToTrashAndVerify them it would hang looking for already-gone folders.
564+ createdResources . push ( { cardName : cardNames [ 0 ] } ) ;
565+ createdResources . push ( { cardName : cardNames [ 1 ] } ) ;
566+
567+ // Setup: create card 1 with its own folder
568+ await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
569+ await adminModelCardPage . waitForTableLoad ( ) ;
570+ await adminModelCardPage . createModelCard ( {
571+ name : cardNames [ 0 ] ,
572+ createNewFolderName : folderName1 ,
573+ } ) ;
574+
575+ // Setup: create card 2 with its own folder
576+ await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
577+ await adminModelCardPage . waitForTableLoad ( ) ;
578+ await adminModelCardPage . createModelCard ( {
579+ name : cardNames [ 1 ] ,
580+ createNewFolderName : folderName2 ,
581+ } ) ;
582+
583+ // Filter to show only this run's test cards
584+ await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
585+ await adminModelCardPage . waitForTableLoad ( ) ;
586+ await adminModelCardPage . applyNameFilter ( filterPrefix ) ;
587+ await expect ( adminModelCardPage . getDataRows ( ) . first ( ) ) . toBeVisible ( {
588+ timeout : 30000 ,
589+ } ) ;
590+
591+ // Select all visible rows
592+ await adminModelCardPage . getHeaderCheckbox ( ) . click ( ) ;
593+ await expect ( adminModelCardPage . getSelectionLabel ( ) ) . toBeVisible ( ) ;
594+
595+ // Open bulk delete dialog
596+ await adminModelCardPage . getBulkDeleteButton ( ) . click ( ) ;
597+ const bulkDialog = adminModelCardPage . getBulkDeleteConfirmDialog ( ) ;
598+ await expect ( bulkDialog ) . toBeVisible ( ) ;
599+
600+ // Verify the "Also delete the associated model folders" checkbox is visible and unchecked by default
601+ const alsoDeleteCheckbox =
602+ adminModelCardPage . getAlsoDeleteFoldersBulkCheckbox ( ) ;
603+ await expect ( alsoDeleteCheckbox ) . toBeVisible ( ) ;
604+ await expect ( alsoDeleteCheckbox ) . not . toBeChecked ( ) ;
605+
606+ // Check the checkbox to also move folders to trash
607+ await alsoDeleteCheckbox . check ( ) ;
608+ await expect ( alsoDeleteCheckbox ) . toBeChecked ( ) ;
609+
610+ // Type "Delete" to confirm
611+ await bulkDialog . getByLabel ( / T y p e .* t o c o n f i r m / i) . fill ( 'Delete' ) ;
612+
613+ // Confirm deletion
614+ await bulkDialog . getByRole ( 'button' , { name : 'Delete' } ) . click ( ) ;
615+
616+ // Wait for the success notification — card delete + folder soft-delete run
617+ // sequentially; under parallel-test load the combined mutation can be slow,
618+ // so we wait directly on the visible outcome rather than polling the dialog.
619+ await expect (
620+ page . getByText (
621+ / m o d e l c a r d \( s \) a n d t h e i r f o l d e r s h a v e b e e n m o v e d t o t r a s h / i,
622+ ) ,
623+ ) . toBeVisible ( { timeout : 240000 } ) ;
624+
625+ // Dialog must be hidden once the notification is shown
626+ await expect ( bulkDialog ) . toBeHidden ( ) ;
627+
628+ // Verify "Go to Data > Trash" link is visible
629+ const goToTrashLink = page . getByText ( 'Go to Data > Trash' ) ;
630+ await expect ( goToTrashLink ) . toBeVisible ( ) ;
631+
632+ // Click the link and verify navigation to trash tab without folder filter
633+ await goToTrashLink . click ( ) ;
634+ await page . waitForURL (
635+ ( url ) => {
636+ if ( url . pathname !== '/admin-data' ) return false ;
637+ return url . searchParams . get ( 'statusCategory' ) === 'deleted' ;
638+ } ,
639+ { timeout : 30000 } ,
640+ ) ;
641+
642+ // Permanently delete both folders from trash for cleanup
643+ await deleteForeverAndVerifyFromTrash ( page , folderName1 , 'admin-data' ) ;
644+ await deleteForeverAndVerifyFromTrash ( page , folderName2 , 'admin-data' ) ;
645+ } ) ;
646+
647+ // 5.10 Superadmin can bulk delete model cards without moving their folders to trash
648+ test ( 'Superadmin can bulk delete model cards without moving their folders to trash' , async ( {
649+ page,
650+ } ) => {
651+ test . setTimeout ( 300000 ) ;
652+ const adminModelCardPage = new AdminModelCardPage ( page ) ;
653+ const timestamp = Date . now ( ) ;
654+ const folderName = `e2e-test-bulk-notrash-folder-${ timestamp } ` ;
655+ const filterPrefix = `e2e-test-bulk-notrash-${ timestamp } ` ;
656+ const cardNames = [ `${ filterPrefix } -1` , `${ filterPrefix } -2` ] ;
657+
658+ // Track folder for cleanup (cards deleted by test; folder stays active)
659+ createdResources . push ( { folderName } ) ;
660+
661+ // Setup: create card 1 with a new folder
662+ await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
663+ await adminModelCardPage . waitForTableLoad ( ) ;
664+ await adminModelCardPage . createModelCard ( {
665+ name : cardNames [ 0 ] ,
666+ createNewFolderName : folderName ,
667+ } ) ;
668+
669+ // Setup: create card 2 sharing the same folder
670+ await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
671+ await adminModelCardPage . waitForTableLoad ( ) ;
672+ await adminModelCardPage . createModelCard ( {
673+ name : cardNames [ 1 ] ,
674+ vfolderTitle : folderName ,
675+ } ) ;
676+
677+ // Filter to show only this run's test cards
678+ await page . goto ( `${ webuiEndpoint } /admin-serving?tab=model-store` ) ;
679+ await adminModelCardPage . waitForTableLoad ( ) ;
680+ await adminModelCardPage . applyNameFilter ( filterPrefix ) ;
681+ await expect ( adminModelCardPage . getDataRows ( ) . first ( ) ) . toBeVisible ( {
682+ timeout : 30000 ,
683+ } ) ;
684+
685+ // Select all visible rows
686+ await adminModelCardPage . getHeaderCheckbox ( ) . click ( ) ;
687+ await expect ( adminModelCardPage . getSelectionLabel ( ) ) . toBeVisible ( ) ;
688+
689+ // Open bulk delete dialog
690+ await adminModelCardPage . getBulkDeleteButton ( ) . click ( ) ;
691+ const bulkDialog = adminModelCardPage . getBulkDeleteConfirmDialog ( ) ;
692+ await expect ( bulkDialog ) . toBeVisible ( ) ;
693+
694+ // Verify the checkbox is visible but leave it unchecked (default)
695+ const alsoDeleteCheckbox =
696+ adminModelCardPage . getAlsoDeleteFoldersBulkCheckbox ( ) ;
697+ await expect ( alsoDeleteCheckbox ) . toBeVisible ( ) ;
698+ await expect ( alsoDeleteCheckbox ) . not . toBeChecked ( ) ;
699+
700+ // Type "Delete" to confirm (checkbox unchecked)
701+ await bulkDialog . getByLabel ( / T y p e .* t o c o n f i r m / i) . fill ( 'Delete' ) ;
702+
703+ // Confirm deletion
704+ await bulkDialog . getByRole ( 'button' , { name : 'Delete' } ) . click ( ) ;
705+
706+ // Wait for the success toast — under parallel-test load the bulk deletion
707+ // can be slow, so wait on the visible outcome rather than polling the dialog.
708+ await expect (
709+ page . getByText ( / \d + m o d e l c a r d \( s \) h a v e b e e n d e l e t e d \. / i) ,
710+ ) . toBeVisible ( { timeout : 240000 } ) ;
711+ await expect ( bulkDialog ) . toBeHidden ( ) ;
712+
713+ // Verify no "Go to Data > Trash" link appears
714+ await expect ( page . getByText ( 'Go to Data > Trash' ) ) . not . toBeVisible ( ) ;
715+
716+ // Verify selection label has cleared (cards were deleted)
717+ await expect ( adminModelCardPage . getSelectionLabel ( ) ) . toBeHidden ( ) ;
718+
719+ // afterEach handles folder cleanup (folder remains in active state)
720+ } ) ;
549721 } ,
550722) ;
0 commit comments