Skip to content

Commit db6a009

Browse files
Merge pull request #189 from anpingli/web-test
NO-JIRA: first cypress web test
2 parents 0a82fad + ecaa5a4 commit db6a009

23 files changed

Lines changed: 3497 additions & 631 deletions

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ test: test-frontend
1515
test-frontend: lint-frontend
1616
cd web && npm run test:unit
1717

18+
.PHONY: test-e2e
19+
test-e2e:
20+
cd web && npm install && npm run test:e2e
21+
1822
.PHONY: install-frontend
1923
install-frontend:
2024
cd web && npm install

web/cypress.config.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { defineConfig } from 'cypress';
2+
import path from 'path';
3+
import fs from 'fs';
4+
5+
// Plugins
6+
import registerCodeCoverage from '@cypress/code-coverage/task';
7+
import cypressGrepPlugin from '@cypress/grep/src/plugin';
8+
9+
const reportDir = process.env.ARTIFACT_DIR || '/tmp';
10+
11+
export default defineConfig({
12+
// --- General Settings ---
13+
fixturesFolder: 'fixtures',
14+
defaultCommandTimeout: 30000,
15+
viewportWidth: 1600,
16+
viewportHeight: 1200,
17+
retries: {
18+
runMode: 0,
19+
openMode: 0,
20+
},
21+
22+
// --- Artifacts & Reporting ---
23+
screenshotsFolder: path.join(reportDir, 'cypress', 'screenshots'),
24+
screenshotOnRunFailure: true,
25+
trashAssetsBeforeRuns: true,
26+
videosFolder: path.join(reportDir, 'cypress', 'videos'),
27+
video: true,
28+
videoCompression: false,
29+
30+
reporter: './node_modules/cypress-multi-reporters',
31+
reporterOptions: {
32+
reporterEnabled: 'mocha-junit-reporter, mochawesome',
33+
mochaJunitReporterReporterOptions: {
34+
mochaFile: path.join(reportDir, 'junit_cypress-[hash].xml'),
35+
toConsole: false,
36+
},
37+
mochawesomeReporterOptions: {
38+
reportDir: reportDir,
39+
reportFilename: 'cypress_report',
40+
overwrite: false,
41+
html: false,
42+
json: true,
43+
},
44+
},
45+
46+
// --- Environment Variables ---
47+
env: {
48+
grepFilterSpecs: false,
49+
KUBECONFIG_PATH: process.env.KUBECONFIG,
50+
OPENSHIFT_VERSION: process.env.CYPRESS_OPENSHIFT_VERSION,
51+
OPENSHIFT_LOGGING_ENABLED: process.env.CYPRESS_OPENSHIFT_LOGGING_ENABLED === 'true',
52+
OPENSHIFT_TRACING_ENABLED: process.env.CYPRESS_OPENSHIFT_TRACING_ENABLED === 'false',
53+
OPENSHIFT_NETOBS_ENABLED: process.env.CYPRESS_OPENSHIFT_NETOBS_ENABLED === 'false',
54+
},
55+
56+
// --- E2E Specific Configuration ---
57+
e2e: {
58+
baseUrl: process.env.CYPRESS_BASE_URL || process.env.BASE_URL || 'http://localhost:9003',
59+
pageLoadTimeout: 300000, // 5 minutes
60+
supportFile: './cypress/support/e2e.ts',
61+
specPattern: './cypress/e2e/*.cy.{js,jsx,ts,tsx}',
62+
testIsolation: false,
63+
numTestsKeptInMemory: 1,
64+
65+
// Experimental Features
66+
experimentalModifyObstructiveThirdPartyCode: true,
67+
experimentalOriginDependencies: true,
68+
experimentalMemoryManagement: true,
69+
experimentalCspAllowList: ['default-src', 'script-src'],
70+
71+
setupNodeEvents(on, config) {
72+
// --- Register Plugins ---
73+
registerCodeCoverage(on, config);
74+
cypressGrepPlugin(config);
75+
76+
// --- Browser Launch ---
77+
on('before:browser:launch', (browser, launchOptions) => {
78+
if (browser.family === 'chromium' && browser.name !== 'electron') {
79+
launchOptions.args.push('--enable-precise-memory-info');
80+
}
81+
return launchOptions;
82+
});
83+
84+
// --- Custom Tasks ---
85+
on('task', {
86+
log: (msg) => { console.log(msg); return null; },
87+
logError: (msg) => { console.error(msg); return null; },
88+
logTable: (data) => { console.table(data); return null; },
89+
readFileIfExists: (filename) => fs.existsSync(filename) ? fs.readFileSync(filename, 'utf8') : null,
90+
});
91+
92+
// --- Screenshot Rename Logic ---
93+
on('after:screenshot', async (details) => {
94+
const pathObj = path.parse(details.path);
95+
const timestamp = Date.now();
96+
const newPath = path.join(pathObj.dir, `${timestamp}_${pathObj.base}`);
97+
await fs.promises.rename(details.path, newPath);
98+
return { path: newPath };
99+
});
100+
101+
// --- Video Cleanup ---
102+
on('after:spec', (spec, results) => {
103+
if (results?.video) {
104+
const hasFailures = results.tests?.some((test) =>
105+
test.attempts.some((attempt) => attempt.state === 'failed')
106+
);
107+
if (!hasFailures && fs.existsSync(results.video)) {
108+
fs.unlinkSync(results.video);
109+
}
110+
}
111+
});
112+
113+
return config;
114+
},
115+
},
116+
});

web/cypress/e2e/acceptance.cy.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as dt from '../fixtures/data-test'
2+
3+
describe('TroubleShoot Test', { tags: ['@admin'] }, () => {
4+
before( function() {
5+
cy.uiLoginAsClusterAdminForUser("first");
6+
cy.openTroubleshootPanel();
7+
8+
});
9+
10+
after( function() {
11+
cy.closeTroubleshootPanel();
12+
cy.uiLogoutClusterAdminForUser("first");
13+
});
14+
15+
it('Essential elements validation when focus on Alerting',{tags:['@smoke']}, () => {
16+
cy.clickNavLink(['Observe', 'Alerting']);
17+
cy.openTroubleshootPanel();
18+
19+
//Elements in Title
20+
cy.get(dt.Classes.TroubleShootPanelPopoverTitleBar)
21+
.should('exist')
22+
.within(() => {
23+
cy.contains('h1', 'Troubleshooting');
24+
cy.get('h1').find('button').should('exist')
25+
cy.get('button[aria-label="Close"]').should('exist');
26+
cy.get(dt.Classes.TroubleShootPanelPopoverClose).should('exist');
27+
})
28+
29+
//Elements in Toolbar
30+
cy.get(dt.Classes.TroubleShootPanelToolBar)
31+
.should('exist')
32+
.within(() => {
33+
cy.contains('button','Focus');
34+
//timepicker button
35+
cy.contains('button', 'Last 1 hours');
36+
//Advanced button
37+
cy.get('div[aria-label="Advanced search parameters"]').should('exist');
38+
//The refresh button exists
39+
cy.get('button[aria-label="Refresh"]').should('exist');
40+
})
41+
42+
//focus on Observe->Alerting
43+
cy.focusTroubleshootPanel();
44+
45+
//Elements under in Advance Container
46+
cy.clickTroubleshootPanelAdvance();
47+
cy.get(dt.Classes.TroubleShootPanelToolBarAdvanced)
48+
.find('form')
49+
.within(() => {
50+
cy.contains('label', 'Start Query');
51+
cy.contains('div', 'Select starting data');
52+
cy.get('textarea[id="query-input"]')
53+
.should('exist')
54+
.should('have.value', 'alert:alert:{}');
55+
cy.contains('label', 'Search Type');
56+
cy.contains('div', 'Neighbours or Goal search');
57+
cy.contains('button', 'Neighbours');
58+
cy.get('button[aria-label="Minus"]').should('exist');
59+
cy.get('input[value="3"]').should('exist');
60+
cy.get('button[aria-label="Plus"]').should('exist');
61+
cy.contains('div', 'hops');
62+
cy.contains('button', 'Search');
63+
cy.contains('button', 'Cancel');
64+
});
65+
66+
//Elements under in topology-container
67+
cy.get(dt.Classes.TroubleShootPanelTopologyContainer).should('exist');
68+
cy.get('div[data-test-id="topology"]').should('exist');
69+
cy.get('g[data-id="korrel8r_graph"]')
70+
.within(() => {
71+
cy.get('ellipse').should('exist'); // node
72+
cy.get(dt.Classes.TroubleShootPanelTopologyNodeLabel).should('exist'); // text panels
73+
cy.get(dt.Classes.TroubleShootPanelTopologyNodeBackGroud).should('exist'); //backgroup panels
74+
cy.get('g[data-test-id="edge-handler"]').should('exist'); // lines
75+
})
76+
cy.get(dt.Classes.TroubleShootPanelTopologyGraphControlBar)
77+
.within(() => {
78+
cy.get('button[id="zoom-in"]').should('exist');
79+
cy.get('button[id="zoom-out"]').should('exist');
80+
cy.get('button[id="reset-view"]').should('exist');
81+
})
82+
//Two nodes in topology-container when focus on Observe->alerting
83+
cy.get('g[data-id="korrel8r_graph"]') // get the parent <g>
84+
.find('g[data-id="alert:alert"]') // find the child <g>
85+
.should('exist')
86+
.find('text')
87+
.contains('Alert')
88+
.should('exist')
89+
cy.get('g[data-id="korrel8r_graph"]') // get the parent <g>
90+
.find('g[data-id="metric:metric"]') // find the child <g>
91+
.should('exist')
92+
.find('text')
93+
.contains('Metric')
94+
.should('exist')
95+
});
96+
})

0 commit comments

Comments
 (0)