diff --git a/cypress.config.ts b/cypress.config.ts index 1ea9873d..120aa4b3 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,8 +1,79 @@ import { defineConfig } from 'cypress'; import { plugin as cypressGrepPlugin } from '@cypress/grep/plugin'; +import { execFileSync } from 'child_process'; import * as fs from 'fs'; +import * as path from 'path'; import * as console from 'console'; +const ARTIFACTS_DIR = './gui_test_screenshots/artifacts'; +const OLS_NAMESPACE = 'openshift-lightspeed'; +const OC_TIMEOUT = 30000; + +const CLUSTER_RESOURCES = [ + 'pods', + 'services', + 'deployments', + 'replicasets', + 'routes', + 'rolebindings', + 'serviceaccounts', + 'olsconfig', + 'clusterserviceversion', + 'installplan', + 'configmap', +]; + +function runOC(args: string[], kubeconfigPath: string): string | null { + const argv = kubeconfigPath ? [...args, '--kubeconfig', kubeconfigPath] : args; + try { + return execFileSync('oc', argv, { + encoding: 'utf-8', + timeout: OC_TIMEOUT, + }); + } catch (e: unknown) { + console.error(`oc ${args.slice(0, 3).join(' ')} failed: ${e}`); + return null; + } +} + +function gatherClusterArtifacts(kubeconfigPath: string) { + const clusterDir = path.join(ARTIFACTS_DIR, 'cluster'); + const podLogsDir = path.join(clusterDir, 'podlogs'); + fs.mkdirSync(podLogsDir, { recursive: true }); + + for (const resource of CLUSTER_RESOURCES) { + const output = runOC(['get', resource, '-n', OLS_NAMESPACE, '-o', 'yaml'], kubeconfigPath); + if (output) { + fs.writeFileSync(path.join(clusterDir, `${resource}.yaml`), output); + } + } + + // Pod logs + const podsJson = runOC(['get', 'pods', '-n', OLS_NAMESPACE, '-o', 'json'], kubeconfigPath); + if (podsJson) { + try { + const pods = JSON.parse(podsJson); + for (const pod of pods.items || []) { + const podName = pod.metadata?.name; + const containers = (pod.spec?.containers || []).map((c: { name: string }) => c.name); + for (const container of containers) { + const logs = runOC( + ['logs', `pod/${podName}`, '-c', container, '-n', OLS_NAMESPACE], + kubeconfigPath, + ); + if (logs) { + fs.writeFileSync(path.join(podLogsDir, `${podName}-${container}.log`), logs); + } + } + } + } catch (e: unknown) { + console.error(`Failed to parse pod JSON: ${e}`); + } + } + + console.log(`Cluster artifacts gathered in ${clusterDir}`); +} + export default defineConfig({ screenshotsFolder: './gui_test_screenshots/cypress/screenshots', screenshotOnRunFailure: true, @@ -72,13 +143,17 @@ export default defineConfig({ console.table(data); return null; }, + gatherClusterArtifacts() { + gatherClusterArtifacts(config.env.KUBECONFIG_PATH || ''); + return null; + }, }); cypressGrepPlugin(config); - on('after:spec', (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => { + on('after:spec', (_spec: Cypress.Spec, results: CypressCommandLine.RunResult) => { if (results && results.video) { // Do we have failures for any retry attempts? - const failures = results.tests.some((test) => - test.attempts.some((attempt) => attempt.state === 'failed'), + const failures = results.tests?.some((test) => + test.attempts?.some((attempt) => attempt.state === 'failed'), ); if (!failures && fs.existsSync(results.video)) { // Delete the video if the spec passed and no tests retried diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index dfff9974..39382626 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -4,6 +4,16 @@ import { register } from '@cypress/grep'; register(); +Cypress.on('uncaught:exception', (err) => { + // The console defines __load_plugin_entry__ via webpack module federation + // before loading plugin bundles, but race conditions during page load can + // cause the plugin script to execute before the global is set. + // This is a console-internal timing issue, not a test failure. + if (err.message?.includes('__load_plugin_entry__')) { + return false; + } +}); + // Collect browser console errors and warnings for output after each test const browserLogs = []; diff --git a/tests/tests/lightspeed-install.cy.ts b/tests/tests/lightspeed-install.cy.ts index 55c622b2..b22313b4 100644 --- a/tests/tests/lightspeed-install.cy.ts +++ b/tests/tests/lightspeed-install.cy.ts @@ -355,6 +355,9 @@ describe('OLS UI', () => { }); after(() => { + // Gather cluster artifacts before cleanup so resources still exist + cy.task('gatherClusterArtifacts'); + if (Cypress.env('SKIP_OLS_SETUP')) { cy.task('log', 'Skip OLS uninstall because CYPRESS_SKIP_OLS_SETUP is true'); } else {