Skip to content

Commit 930f6af

Browse files
authored
Merge pull request #15859 from yonasberhe23/fix_cronjob_test
Automation: fix cronjob test
2 parents 0666c95 + 8de1b37 commit 930f6af

File tree

3 files changed

+130
-46
lines changed

3 files changed

+130
-46
lines changed

cypress/e2e/po/components/resource-detail-masthead.po.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import ComponentPo from '@/cypress/e2e/po/components/component.po';
1+
import ComponentPo, { GetOptions } from '@/cypress/e2e/po/components/component.po';
22

33
export default class ResourceDetailMastheadPo extends ComponentPo {
44
/**
55
* Get the resource status badge in the masthead
66
*/
7-
resourceStatus() {
8-
return this.self().find('h1.title .badge-state .msg');
7+
resourceStatus(options?: GetOptions) {
8+
return this.self().find('h1.title .badge-state .msg', options);
99
}
1010

1111
/**

cypress/e2e/po/edit/resource-detail.po.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import CruResourcePo from '@/cypress/e2e/po/components/cru-resource.po';
44
import ResourceYamlPo from '@/cypress/e2e/po/components/resource-yaml.po';
55
import ResourceDetailMastheadPo from '@/cypress/e2e/po/components/resource-detail-masthead.po';
66
import TabbedPo from '@/cypress/e2e/po/components/tabbed.po';
7+
import ResourceTablePo from '@/cypress/e2e/po/components/resource-table.po';
78

89
export default class ResourceDetailPo extends ComponentPo {
910
/**
@@ -42,6 +43,18 @@ export default class ResourceDetailPo extends ComponentPo {
4243
return new TabbedPo('[data-testid="tabbed"]');
4344
}
4445

46+
/**
47+
* @param tabId - the id of the tab
48+
* @param index - the index of the list (only used for 'related' tab)
49+
* @returns the list of the tab
50+
*/
51+
tabbedList(tabId: string, index?: number) {
52+
const baseSelector = `#${ tabId } [data-testid="sortable-table-list-container"]`;
53+
const selector = tabId === 'related' ? `${ baseSelector }:nth-of-type(${ index })` : baseSelector;
54+
55+
return new ResourceTablePo(selector);
56+
}
57+
4558
title(): Cypress.Chainable<string> {
4659
return this.self().find('.title-bar h1.title, .primaryheader h1').invoke('text');
4760
}
@@ -53,4 +66,8 @@ export default class ResourceDetailPo extends ComponentPo {
5366
masthead() {
5467
return new ResourceDetailMastheadPo(this.self());
5568
}
69+
70+
resourceGauges() {
71+
return this.self().find('.gauges .count-gauge');
72+
}
5673
}

cypress/e2e/tests/pages/explorer2/workloads/cronjobs.spec.ts

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import SortableTablePo from '@/cypress/e2e/po/components/sortable-table.po';
55
import ClusterDashboardPagePo from '@/cypress/e2e/po/pages/explorer/cluster-dashboard.po';
66
import { generateCronJobsDataSmall } from '@/cypress/e2e/blueprints/explorer/workloads/cronjobs/cronjobs-get';
77
import { 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

1010
describe('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

Comments
 (0)