From 8de5448de847a619d81bd3935261c6629c997cff Mon Sep 17 00:00:00 2001 From: Joao Fula Date: Thu, 14 May 2026 09:07:28 +0200 Subject: [PATCH 1/3] adding artifact exporting. Should help investigations reverting changes around tour --- cypress.config.ts | 73 ++++++++++++++++++++++++++++ tests/tests/lightspeed-install.cy.ts | 17 +------ 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/cypress.config.ts b/cypress.config.ts index 1ea9873d..b8e8eea9 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, @@ -85,6 +156,8 @@ export default defineConfig({ fs.unlinkSync(results.video); } } + console.log('Gathering cluster artifacts...'); + gatherClusterArtifacts(config.env.KUBECONFIG_PATH || ''); }); return config; }, diff --git a/tests/tests/lightspeed-install.cy.ts b/tests/tests/lightspeed-install.cy.ts index 55c622b2..6215ee08 100644 --- a/tests/tests/lightspeed-install.cy.ts +++ b/tests/tests/lightspeed-install.cy.ts @@ -331,23 +331,8 @@ describe('OLS UI', () => { }); cy.visit('/'); - // Dismiss tour if it appears. The tour renders asynchronously after the - // page loads, so a single synchronous jQuery check can miss it. Instead, - // retry the check multiple times with a delay to give the tour time to - // render before concluding it won't appear. + cy.byTestID('tour-step-footer-secondary', { timeout: MINUTE }).click(); cy.get(mainButton, { timeout: 5 * MINUTE }).should('exist'); - const tourSelector = '[data-test="tour-step-footer-secondary"]'; - const dismissTour = (retriesLeft: number): void => { - cy.get('body').then(($body) => { - if ($body.find(tourSelector).length > 0) { - cy.wrap($body).find(tourSelector).click(); - } else if (retriesLeft > 0) { - cy.wait(1000); - dismissTour(retriesLeft - 1); - } - }); - }; - dismissTour(30); // Wait 2 minutes for the page to reload so it doesn't happen during tests cy.wait(120000); From f26cc3e196454792719c5a30cd8fdc5c38736786 Mon Sep 17 00:00:00 2001 From: Joao Fula Date: Thu, 14 May 2026 12:25:23 +0200 Subject: [PATCH 2/3] trying to handle uncaught exception adding ls --- cypress.config.ts | 4 ++-- cypress/support/e2e.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cypress.config.ts b/cypress.config.ts index b8e8eea9..bd4ed9b1 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -148,8 +148,8 @@ export default defineConfig({ 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 = []; From ecd03a48e2ec66182b772300c6b4143e5866811b Mon Sep 17 00:00:00 2001 From: Joao Fula Date: Fri, 15 May 2026 09:58:13 +0200 Subject: [PATCH 3/3] Correcting commit sha in 4.19 pipeline changing the artifact gathering to reverting tour changes reverting tour changes reverting tour changes --- cypress.config.ts | 8 +++++--- tests/tests/lightspeed-install.cy.ts | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/cypress.config.ts b/cypress.config.ts index bd4ed9b1..120aa4b3 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -143,9 +143,13 @@ 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) => @@ -156,8 +160,6 @@ export default defineConfig({ fs.unlinkSync(results.video); } } - console.log('Gathering cluster artifacts...'); - gatherClusterArtifacts(config.env.KUBECONFIG_PATH || ''); }); return config; }, diff --git a/tests/tests/lightspeed-install.cy.ts b/tests/tests/lightspeed-install.cy.ts index 6215ee08..b22313b4 100644 --- a/tests/tests/lightspeed-install.cy.ts +++ b/tests/tests/lightspeed-install.cy.ts @@ -331,8 +331,23 @@ describe('OLS UI', () => { }); cy.visit('/'); - cy.byTestID('tour-step-footer-secondary', { timeout: MINUTE }).click(); + // Dismiss tour if it appears. The tour renders asynchronously after the + // page loads, so a single synchronous jQuery check can miss it. Instead, + // retry the check multiple times with a delay to give the tour time to + // render before concluding it won't appear. cy.get(mainButton, { timeout: 5 * MINUTE }).should('exist'); + const tourSelector = '[data-test="tour-step-footer-secondary"]'; + const dismissTour = (retriesLeft: number): void => { + cy.get('body').then(($body) => { + if ($body.find(tourSelector).length > 0) { + cy.wrap($body).find(tourSelector).click(); + } else if (retriesLeft > 0) { + cy.wait(1000); + dismissTour(retriesLeft - 1); + } + }); + }; + dismissTour(30); // Wait 2 minutes for the page to reload so it doesn't happen during tests cy.wait(120000); @@ -340,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 {