fix(ui): wait for process events before timeline tests by davdhacs · Pull Request #21422 · stackrox/stackrox · GitHub
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,57 @@ export function checkNetworkGraphEmptyState() {
);
}

export function waitForNetworkFlows(timeoutMs = 600000) {
const interval = 15000;
const maxAttempts = Math.ceil(timeoutMs / interval);

function poll(attempt) {
if (attempt >= maxAttempts) {
throw new Error(
`Timed out after ${timeoutMs / 1000}s waiting for network flow data`
);
}
const auth = { bearer: Cypress.env('ROX_AUTH_TOKEN') };
return cy
.request({ url: '/v1/clusters', auth, failOnStatusCode: false })
.then((clustersResponse) => {
const clusters = clustersResponse?.body?.clusters ?? [];
if (clusters.length === 0) {
cy.log('No clusters found, retrying...');
cy.wait(interval);
return poll(attempt + 1);
}
const clusterId = clusters[0].id;
return cy
.request({
url: `/v1/networkgraph/cluster/${clusterId}?query=Namespace:stackrox`,
auth,
failOnStatusCode: false,
})
.then((graphResponse) => {
const nodes = graphResponse?.body?.nodes ?? [];
const collectorNode = nodes.find(
(n) => n?.entity?.deployment?.name === 'collector'
);
const hasOutEdges =
collectorNode?.outEdges &&
Object.keys(collectorNode.outEdges).length > 0;
if (hasOutEdges) {
cy.log('Network flow data ready for collector');
return;
}
cy.log(
`Network flows not ready (attempt ${attempt + 1}/${maxAttempts}), retrying...`
);
cy.wait(interval);
return poll(attempt + 1);
});
});
}

return poll(0);
}

export function updateAndCloseCidrModal() {
cy.clock();
cy.get(networkGraphSelectors.updateCidrBlocksButton).click();
Expand Down
36 changes: 36 additions & 0 deletions ui/apps/platform/cypress/integration/risk/Risk.helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,42 @@ export function clickFirstDrillDownButtonInEventTimeline(fixtureForPodEventTimel
);
}

// data readiness

export function waitForProcessEvents(timeoutMs = 600000) {
const interval = 15000;
const maxAttempts = Math.ceil(timeoutMs / interval);

function poll(attempt) {
if (attempt >= maxAttempts) {
throw new Error(
`Timed out after ${timeoutMs / 1000}s waiting for process events`
);
}
const auth = { bearer: Cypress.env('ROX_AUTH_TOKEN') };
return cy
.request({
url: '/v1/processcount',
auth,
failOnStatusCode: false,
})
.then((response) => {
const count = response?.body?.count ?? 0;
if (count > 10) {
cy.log(`Process events ready: ${count} processes`);
return;
}
cy.log(
`Process count: ${count} (attempt ${attempt + 1}/${maxAttempts}), retrying...`
);
cy.wait(interval);
return poll(attempt + 1);
});
}

return poll(0);
}

// interact

export function clickTab(tabText) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
viewFirstRiskDeployment,
viewGraph,
visitRiskDeployments,
waitForProcessEvents,
} from './Risk.helpers';
import { selectors } from './Risk.selectors';

Expand All @@ -27,6 +28,10 @@ const fixtureForDeploymentEventTimeline = 'risks/eventTimeline/deploymentEventTi
describe('Risk Event Timeline for Deployment', () => {
withAuth();

before(() => {
waitForProcessEvents();
});

describe('Clustering Events', () => {
it('should show the clustered event markers', () => {
// mocking data to thoroughly test the clustering
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
viewFirstRiskDeployment,
viewGraph,
visitRiskDeployments,
waitForProcessEvents,
} from './Risk.helpers';
import { selectors } from './Risk.selectors';

Expand All @@ -30,6 +31,10 @@ const fixtureForPodEventTimeline = 'risks/eventTimeline/podEventTimeline.json';
describe('Risk Event Timeline for Pod', () => {
withAuth();

before(() => {
waitForProcessEvents();
});

describe('Clustering Events', () => {
it('should show the clustered event markers', () => {
openEventTimeline();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,3 +432,61 @@ export function hasTableColumnHeadings(tableColumnHeadings) {

cy.get('.rt-th').should('have.length', tableColumnHeadings.length);
}

// data readiness

/**
* Triggers an image scan and polls GraphQL until imageCVECount > 0.
* Used as a before() hook for vulnmanagement tests that depend on
* real image CVE data from the API.
*
* The workflow also triggers a scan before Cypress starts, so this
* is a safety net that ensures the data is actually indexed.
*
* @param {number} timeoutMs - Maximum time to wait (default 2 minutes)
*/
export function waitForImageCVEs(timeoutMs = 120000) {
const interval = 5000;
const maxAttempts = Math.ceil(timeoutMs / interval);
const auth = { bearer: Cypress.env('ROX_AUTH_TOKEN') };

// Trigger a scan in case the workflow hasn't already
cy.request({
method: 'POST',
url: '/v1/images/scan',
auth,
body: { imageName: 'docker.io/nginx:1.12', force: true },
failOnStatusCode: false,
}).then((scanResponse) => {
cy.log(`Image scan triggered: ${scanResponse.status}`);
});

function poll(attempt) {
if (attempt >= maxAttempts) {
throw new Error(
`Timed out after ${timeoutMs / 1000}s waiting for image CVE data`
);
}
return cy
.request({
method: 'POST',
url: '/api/graphql',
auth,
headers: { 'Content-Type': 'application/json' },
body: { query: '{ imageCVECount }' },
failOnStatusCode: false,
})
.then((response) => {
const count = response?.body?.data?.imageCVECount ?? 0;
if (count > 0) {
cy.log(`Image CVE data ready: imageCVECount=${count}`);
return;
}
cy.log(`Waiting for image CVEs... attempt ${attempt + 1}/${maxAttempts}`);
cy.wait(interval);
return poll(attempt + 1);
});
}

return poll(0);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ import {
verifyConditionalCVEs,
verifySecondaryEntities,
visitVulnerabilityManagementEntities,
waitForImageCVEs,
} from './VulnerabilityManagement.helpers';

const entitiesKey = 'clusters';

describe('Vulnerability Management Clusters', () => {
withAuth();

before(() => {
waitForImageCVEs();
});

it('should display all the columns', () => {
visitVulnerabilityManagementEntities(entitiesKey);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
verifyVulnerabilityManagementDashboardCVEs,
visitVulnerabilityManagementDashboard,
visitVulnerabilityManagementDashboardFromLeftNav,
waitForImageCVEs,
} from './VulnerabilityManagement.helpers';
import { selectors } from './VulnerabilityManagement.selectors';

Expand Down Expand Up @@ -35,6 +36,10 @@ function selectTopRiskyOption(optionText) {
describe('Vulnerability Management Dashboard', () => {
withAuth();

before(() => {
waitForImageCVEs();
});

it('should visit using the left nav', () => {
visitVulnerabilityManagementDashboardFromLeftNav();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import withAuth from '../../helpers/basicAuth';
import {
interactAndWaitForVulnerabilityManagementEntity,
visitVulnerabilityManagementDashboard,
waitForImageCVEs,
} from './VulnerabilityManagement.helpers';
import { selectors } from './VulnerabilityManagement.selectors';

Expand Down Expand Up @@ -51,6 +52,10 @@ function selectTopRiskiestOption(optionText) {
describe('Vulnerability Management Dashboard', () => {
withAuth();

before(() => {
waitForImageCVEs();
});

// Some tests might fail in local deployment.

it('has item link to image page from Top riskiest images', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ import {
verifyConditionalCVEs,
verifySecondaryEntities,
visitVulnerabilityManagementEntities,
waitForImageCVEs,
} from './VulnerabilityManagement.helpers';

const entitiesKey = 'deployments';

describe('Vulnerability Management Deployments', () => {
withAuth();

before(() => {
waitForImageCVEs();
});

it('should display table columns', () => {
visitVulnerabilityManagementEntities(entitiesKey);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import withAuth from '../../helpers/basicAuth';
import {
interactAndWaitForVulnerabilityManagementEntity,
visitVulnerabilityManagementEntities,
waitForImageCVEs,
} from './VulnerabilityManagement.helpers';
import { selectors } from './VulnerabilityManagement.selectors';

describe('Entities single views', () => {
withAuth();

before(() => {
waitForImageCVEs();
});

// Some tests might fail in local deployment.

it('should show a CVE description in overview when coming from cve list', () => {
Expand Down
Loading
Loading