@@ -5,7 +5,7 @@ import SortableTablePo from '@/cypress/e2e/po/components/sortable-table.po';
55import ClusterDashboardPagePo from '@/cypress/e2e/po/pages/explorer/cluster-dashboard.po' ;
66import { generateCronJobsDataSmall } from '@/cypress/e2e/blueprints/explorer/workloads/cronjobs/cronjobs-get' ;
77import { SMALL_CONTAINER } from '@/cypress/e2e/tests/pages/explorer2/workloads/workload.utils' ;
8- import { MEDIUM_TIMEOUT_OPT , LONG_TIMEOUT_OPT } from '@/cypress/support/utils/timeouts' ;
8+ import { MEDIUM_TIMEOUT_OPT } from '@/cypress/support/utils/timeouts' ;
99
1010describe ( 'CronJobs' , { testIsolation : 'off' , tags : [ '@explorer2' , '@adminUser' ] } , ( ) => {
1111 const localCluster = 'local' ;
@@ -15,6 +15,115 @@ describe('CronJobs', { testIsolation: 'off', tags: ['@explorer2', '@adminUser']
1515 cy . login ( ) ;
1616 } ) ;
1717
18+ describe ( 'Details' , ( ) => {
19+ let cronJobName : string ;
20+ let jobName : string ;
21+ let podName : string ;
22+ const defaultNamespace = 'default' ;
23+
24+ before ( 'set up' , ( ) => {
25+ // Create a cronjob for the test
26+ cy . getRootE2EResourceName ( ) . then ( ( root ) => {
27+ cronJobName = root ;
28+
29+ return cy . createRancherResource ( 'v1' , 'batch.cronjob' , JSON . stringify ( {
30+ apiVersion : 'batch/v1' ,
31+ kind : 'CronJob' ,
32+ metadata : {
33+ name : cronJobName ,
34+ namespace : defaultNamespace
35+ } ,
36+ spec : {
37+ schedule : '1 1 1 1 1' , // basically never
38+ concurrencyPolicy : 'Allow' ,
39+ failedJobsHistoryLimit : 1 ,
40+ successfulJobsHistoryLimit : 3 ,
41+ suspend : false ,
42+ jobTemplate : {
43+ spec : {
44+ template : {
45+ spec : {
46+ containers : [ SMALL_CONTAINER ] ,
47+ restartPolicy : 'Never'
48+ }
49+ }
50+ }
51+ }
52+ }
53+ } ) ) ;
54+ } ) ;
55+ } ) ;
56+
57+ it ( 'Jobs list updates automatically in CronJob details page' , ( ) => {
58+ // Set namespace filter to include the test cronjob namespace
59+ cy . tableRowsPerPageAndNamespaceFilter ( 10 , localCluster , 'none' , `{\"local\":[\"ns://${ defaultNamespace } \"]}` ) ;
60+
61+ WorkloadsCronJobsListPagePo . navTo ( ) ;
62+ cronJobListPage . waitForPage ( ) ;
63+
64+ // Trigger "Run Now" action which will create a new job and pod from the CronJob
65+ cy . intercept ( 'POST' , `v1/batch.jobs/${ defaultNamespace } ` ) . as ( 'runNow' ) ;
66+ cronJobListPage . runNow ( cronJobName ) ;
67+ cy . wait ( '@runNow' ) . its ( 'response.statusCode' ) . should ( 'eq' , 201 ) ;
68+
69+ // Retrieve the job and pod names created by the CronJob
70+ cy . getRancherResource ( 'v1' , 'batch.job' , `${ defaultNamespace } ` ) . then ( ( resp ) => {
71+ const job = resp . body . data . find ( ( job : any ) => job . metadata . name . startsWith ( cronJobName ) ) ;
72+
73+ jobName = job . metadata . name ;
74+ cy . getRancherResource ( 'v1' , 'pods' , `${ defaultNamespace } ` ) . then ( ( resp ) => {
75+ const pod = resp . body . data . find ( ( pod : any ) => pod . metadata . name . startsWith ( cronJobName ) ) ;
76+
77+ podName = pod . metadata . name ;
78+
79+ // User is redirected to the job's details page after "Run Now"
80+ const jobDetailsPage = new WorkLoadsJobDetailsPagePo ( jobName , undefined , 'local' , defaultNamespace ) ;
81+
82+ jobDetailsPage . waitForPage ( undefined , 'pods' ) ;
83+
84+ // Verify job details page displays correct status
85+ // Job status should be Active
86+ jobDetailsPage . resourceDetail ( ) . masthead ( ) . resourceStatus ( MEDIUM_TIMEOUT_OPT )
87+ . should ( 'contain' , 'Active' ) ;
88+
89+ // Pod status should be Running
90+ jobDetailsPage . resourceDetail ( ) . resourceGauges ( ) . should ( 'contain' , 'Running' ) ;
91+ jobDetailsPage . resourceDetail ( ) . tabbedList ( 'pods' ) . resourceTableDetails ( podName , 1 ) . contains ( 'Running' , MEDIUM_TIMEOUT_OPT ) ;
92+ } ) ;
93+
94+ // Navigate back to CronJobs list page
95+ WorkloadsCronJobsListPagePo . navTo ( ) ;
96+ cronJobListPage . waitForPage ( ) ;
97+
98+ // Verify CronJob status is Active in the list
99+ cronJobListPage . resourceTableDetails ( cronJobName , 1 ) . contains ( 'Active' ) ;
100+
101+ // Navigate to CronJob details page
102+ cronJobListPage . goToDetailsPage ( cronJobName ) ;
103+
104+ const cronJobDetailsPage = new WorkloadsCronJobDetailPagePo ( cronJobName , 'local' , defaultNamespace ) ;
105+
106+ cronJobDetailsPage . waitForPage ( undefined , 'jobs' ) ;
107+
108+ // Verify CronJob status is Active in details page
109+ cronJobDetailsPage . resourceDetail ( ) . masthead ( ) . resourceStatus ( )
110+ . should ( 'contain' , 'Active' ) ;
111+
112+ // Verify the job in the jobs tab shows correct status without manual page refresh
113+ // Testing https://github.com/rancher/dashboard/issues/14981:
114+ // The job list should update automatically and not show stale "In Progress" status
115+ cronJobDetailsPage . resourceDetail ( ) . tabbedList ( 'jobs' ) . resourceTableDetails ( jobName , 1 ) . contains ( 'Active' ) ;
116+ } ) ;
117+ } ) ;
118+
119+ after ( 'clean up' , ( ) => {
120+ // Ensure the default rows per page value is set after running the tests
121+ cy . tableRowsPerPageAndNamespaceFilter ( 100 , localCluster , 'none' , '{"local":["all://user"]}' ) ;
122+ // Delete the cronjob
123+ cy . deleteRancherResource ( 'v1' , 'batch.cronjob' , `${ defaultNamespace } /${ cronJobName } ` ) ;
124+ } ) ;
125+ } ) ;
126+
18127 describe ( 'List' , { tags : [ '@noVai' , '@adminUser' ] } , ( ) => {
19128 let uniqueCronJob = SortableTablePo . firstByDefaultName ( 'cronjob' ) ;
20129 let detailsPageCronJob = SortableTablePo . firstByDefaultName ( 'detailscron' ) ;
@@ -280,48 +389,6 @@ describe('CronJobs', { testIsolation: 'off', tags: ['@explorer2', '@adminUser']
280389 . checkNotExists ( ) ;
281390 } ) ;
282391
283- it ( 'Cronjob details page refresh dashboard' , ( ) => {
284- // Set namespace filter to include the test cronjob namespace
285- cy . tableRowsPerPageAndNamespaceFilter ( 10 , localCluster , 'none' , `{\"local\":[\"ns://${ nsName3 } \"]}` ) ;
286-
287- WorkloadsCronJobsListPagePo . navTo ( ) ;
288- cronJobListPage . waitForPage ( ) ;
289-
290- cronJobListPage . runNow ( detailsPageCronJob ) ;
291-
292- cy . url ( ) . should ( 'include' , '/explorer/batch.job/' , MEDIUM_TIMEOUT_OPT ) ;
293-
294- const jobDetailsPage = new WorkLoadsJobDetailsPagePo ( 'dummy-job' ) ;
295-
296- jobDetailsPage . resourceDetail ( ) . masthead ( ) . resourceStatus ( ) . should ( 'be.visible' , LONG_TIMEOUT_OPT )
297- . and ( ( $el ) => {
298- const status = $el . text ( ) . trim ( ) ;
299-
300- expect ( [ 'Running' , 'Active' , 'Pending' , 'Creating' , 'Succeeded' ] ) . to . include ( status ) ;
301- } ) ;
302-
303- jobDetailsPage . resourceDetail ( ) . masthead ( ) . resourceStatus ( ) . should ( 'not.be.empty' ) ;
304-
305- WorkloadsCronJobsListPagePo . navTo ( ) ;
306- cronJobListPage . waitForPage ( ) ;
307-
308- cronJobListPage . goToDetailsPage ( detailsPageCronJob ) ;
309-
310- cy . url ( MEDIUM_TIMEOUT_OPT ) . should ( 'include' , '/explorer/batch.cronjob/' ) ;
311-
312- const cronJobDetailsPage = new WorkloadsCronJobDetailPagePo ( detailsPageCronJob , 'local' , nsName3 ) ;
313-
314- cronJobDetailsPage . resourceDetail ( ) . masthead ( ) . resourceStatus ( ) . should ( 'be.visible' , MEDIUM_TIMEOUT_OPT ) ;
315-
316- // CronJob should NOT show stale "In Progress" status
317- cronJobDetailsPage . resourceDetail ( ) . masthead ( ) . resourceStatus ( ) . should ( 'not.contain' , 'In Progress' ) ;
318-
319- cy . reload ( ) ;
320- cy . url ( MEDIUM_TIMEOUT_OPT ) . should ( 'include' , '/explorer/batch.cronjob/' ) ;
321-
322- cronJobDetailsPage . resourceDetail ( ) . masthead ( ) . resourceStatus ( ) . should ( 'be.visible' , MEDIUM_TIMEOUT_OPT ) ;
323- } ) ;
324-
325392 after ( 'clean up' , ( ) => {
326393 // Ensure the default rows per page value is set after running the tests
327394 cy . tableRowsPerPageAndNamespaceFilter ( 100 , localCluster , 'none' , '{"local":["all://user"]}' ) ;
0 commit comments