diff --git a/web/cypress/fixtures/gateway-api-template.yaml b/web/cypress/fixtures/gateway-api-template.yaml deleted file mode 100644 index 1d5b5dbfb..000000000 --- a/web/cypress/fixtures/gateway-api-template.yaml +++ /dev/null @@ -1,97 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: gateway-api-resources -objects: -- apiVersion: v1 - kind: Namespace - metadata: - name: ${NAMESPACE} - labels: - app: ${NAMESPACE_LABEL} -- apiVersion: gateway.networking.k8s.io/v1 - kind: GatewayClass - metadata: - name: openshift-default - spec: - controllerName: openshift.io/gateway-controller/v1 -- apiVersion: gateway.networking.k8s.io/v1 - kind: Gateway - metadata: - name: ${GATEWAY_NAME} - namespace: ${NAMESPACE} - spec: - gatewayClassName: openshift-default - listeners: - - name: demo - hostname: ${HOSTNAME} - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: Selector - selector: - matchLabels: - app: ${NAMESPACE_LABEL} -- apiVersion: gateway.networking.k8s.io/v1 - kind: HTTPRoute - metadata: - name: ${HTTPROUTE_NAME} - namespace: ${NAMESPACE} - spec: - parentRefs: - - name: ${GATEWAY_NAME} - namespace: ${NAMESPACE} - hostnames: ["${HOSTNAME}"] - rules: - - backendRefs: - - name: service-unsecure - port: 27017 -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: traffic-generator - namespace: ${NAMESPACE} - spec: - replicas: 1 - selector: - matchLabels: - app: traffic-generator - template: - metadata: - labels: - app: traffic-generator - spec: - containers: - - name: traffic-gen - image: registry.access.redhat.com/ubi8/ubi-minimal - command: - - /bin/sh - - -c - - | - while true; do - curl -s http://${GATEWAY_NAME}-openshift-default.${NAMESPACE}.svc.cluster.local:8080 || true - wget -q -O- http://${GATEWAY_NAME}-openshift-default.${NAMESPACE}.svc.cluster.local:8080 || true - sleep 5 - done -parameters: -- name: GATEWAY_NAME - description: Name of the Gateway resource - value: test-gateway-owner - required: true -- name: HTTPROUTE_NAME - description: Name of the HTTPRoute resource - value: test-httproute - required: true -- name: NAMESPACE - description: Namespace for Gateway and HTTPRoute resources - value: netobserv-gateway-test - required: true -- name: HOSTNAME - description: Hostname for the Gateway listener and HTTPRoute - value: gwapi.example.com - required: true -- name: NAMESPACE_LABEL - description: Label value for namespace app label - value: gwapi - required: true diff --git a/web/cypress/fixtures/gateway-api.yaml b/web/cypress/fixtures/gateway-api.yaml new file mode 100644 index 000000000..3e0bdfbdf --- /dev/null +++ b/web/cypress/fixtures/gateway-api.yaml @@ -0,0 +1,121 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: netobserv-gateway-test + labels: + app: gwapi +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: openshift-default +spec: + controllerName: openshift.io/gateway-controller/v1 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: test-gateway-owner + namespace: netobserv-gateway-test +spec: + gatewayClassName: openshift-default + listeners: + - name: demo + hostname: gwapi.example.com + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Selector + selector: + matchLabels: + app: gwapi +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: test-httproute + namespace: netobserv-gateway-test + labels: + app: gwapi +spec: + parentRefs: + - name: test-gateway-owner + namespace: netobserv-gateway-test + hostnames: ["gwapi.example.com"] + rules: + - backendRefs: + - name: echo-service + port: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: echo-service + namespace: netobserv-gateway-test + labels: + app: gwapi +spec: + selector: + app: echo-server + ports: + - protocol: TCP + port: 8080 + targetPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: echo-server + namespace: netobserv-gateway-test + labels: + app: gwapi +spec: + replicas: 1 + selector: + matchLabels: + app: echo-server + template: + metadata: + labels: + app: echo-server + spec: + containers: + - name: echo + image: hashicorp/http-echo:latest + args: + - -text=echo response + - -listen=:8080 + ports: + - containerPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: traffic-generator + namespace: netobserv-gateway-test + labels: + app: gwapi + component: traffic-generator +spec: + replicas: 1 + selector: + matchLabels: + app: traffic-generator + template: + metadata: + labels: + app: traffic-generator + spec: + containers: + - name: traffic-gen + image: curlimages/curl:latest + command: + - /bin/sh + - -c + - | + while true; do + curl -s http://test-gateway-owner-openshift-default.netobserv-gateway-test.svc.cluster.local:8080 || true + sleep 5 + done diff --git a/web/cypress/integration-tests/client_performance.cy.ts b/web/cypress/integration-tests/client_performance.cy.ts index f3124ad28..a136b18e1 100644 --- a/web/cypress/integration-tests/client_performance.cy.ts +++ b/web/cypress/integration-tests/client_performance.cy.ts @@ -1,10 +1,6 @@ -import { netflowPage, loadTimes, memoryUsage, overviewSelectors } from "@views/netflow-page" +import { netflowPage, loadTimes, memoryUsage, overviewSelectors, getTopologyScopeURL, getMemoryUsageMB } from "@views/netflow-page" import { Operator } from "@views/netobserv" -function getTopologyScopeURL(scope: string): string { - return `**/flow/metrics?filters=&limit=50&recordType=flowLog&dedup=true&packetLoss=all&timeRange=300&rateInterval=30s&step=15s&type=bytes&aggregateBy=${scope}` -} - describe("(OCP-67725, memodi) Network_Observability Client Performances", { browser: 'chrome', tags: ['Performance'] }, function () { before("tests", function () { cy.adminCLI(`oc adm policy add-cluster-role-to-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) @@ -17,10 +13,10 @@ describe("(OCP-67725, memodi) Network_Observability Client Performances", { brow beforeEach("test", function () { cy.clearLocalStorage() - cy.intercept('**/backend/api/flow/metrics*').as('call1') cy.visit('/netflow-traffic') - // wait for all calls to complete - cy.wait('@call1', { timeout: 60000 }) + // wait for page to be fully loaded + cy.get('#overview-container', { timeout: 60000 }).should('exist') + cy.byTestID('no-results-found').should('not.exist') }) @@ -37,7 +33,7 @@ describe("(OCP-67725, memodi) Network_Observability Client Performances", { brow cy.get('#overview-flex').contains(overviewSelectors.defaultPanels[0]).should('be.visible').then(() => { cy.wrap(performance.now()).then(end => { let pageload = Math.round(end - start) - let curMemoryUsage = Math.round(window.performance.memory.usedJSHeapSize / 1048576) + let curMemoryUsage = getMemoryUsageMB() cy.log(`Overview page load took ${pageload} ms.`) cy.log(`Overview page memory consumption ${curMemoryUsage} MB`) let thresPageload = loadTimes.overview + loadTimes.overview * 0.5 @@ -49,17 +45,17 @@ describe("(OCP-67725, memodi) Network_Observability Client Performances", { brow }) it("(OCP-67725, memodi, Network_Observability) should measure table page load times", function () { - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() netflowPage.clearAllFilters() const start = performance.now() - const url = '**/backend/api/flow/metrics*' + const url = '**/api/flow/metrics*' cy.intercept('GET', url, { fixture: 'perf/netflow_table_perf.json' }) cy.byTestID("table-composable").should('be.visible').then(() => { cy.wrap(performance.now()).then(end => { let pageload = Math.round(end - start) - let curMemoryUsage = Math.round(window.performance.memory.usedJSHeapSize / 1048576) + let curMemoryUsage = getMemoryUsageMB() cy.log(`Table view page load took ${pageload} ms.`) cy.log(`Table view memory consumption ${curMemoryUsage} MB`) let thresPageload = loadTimes.table + loadTimes.table * 0.5 @@ -71,7 +67,7 @@ describe("(OCP-67725, memodi) Network_Observability Client Performances", { brow }) it("(OCP-67725, memodi, Network_Observability) should measure topology page load times", function () { - cy.get('#tabs-container li:nth-child(3)').click() + cy.get('#tabs-container').contains('Topology').click() netflowPage.clearAllFilters() const start = performance.now() cy.intercept('GET', getTopologyScopeURL("namespace"), { @@ -80,7 +76,7 @@ describe("(OCP-67725, memodi) Network_Observability Client Performances", { brow cy.get('[data-surface="true"]').should('be.visible').then(() => { cy.wrap(performance.now()).then(end => { let pageload = Math.round(end - start) - let curMemoryUsage = Math.round(window.performance.memory.usedJSHeapSize / 1048576) + let curMemoryUsage = getMemoryUsageMB() cy.log(`Topology view page load took ${pageload} ms.`) cy.log(`Topology view memory consumption ${curMemoryUsage} MB`) let thresPageload = loadTimes.topology + loadTimes.topology * 0.5 @@ -94,7 +90,7 @@ describe("(OCP-67725, memodi) Network_Observability Client Performances", { brow netflowPage.resetClearFilters() }) - after("suite", function () { + after("all tests", function () { cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) }) diff --git a/web/cypress/integration-tests/dns_dashboards.cy.ts b/web/cypress/integration-tests/dns_dashboards.cy.ts index e9e9cf396..0d933daca 100644 --- a/web/cypress/integration-tests/dns_dashboards.cy.ts +++ b/web/cypress/integration-tests/dns_dashboards.cy.ts @@ -36,7 +36,7 @@ describe('(OCP-67087 Network_Observability) DNSTracking test', { tags: ['Network it("(OCP-67087, aramesha, Network_Observability) Validate DNSLatencies edge label and Query Summary stats", function () { netflowPage.visit() - cy.get('#tabs-container li:nth-child(3)').click() + cy.get('#tabs-container').contains('Topology').click() cy.get('#drawer').should('not.be.empty') cy.get(filterSelectors.filterInput).type("dns_latency>=0" + '{enter}') @@ -56,7 +56,7 @@ describe('(OCP-67087 Network_Observability) DNSTracking test', { tags: ['Network cy.byTestID("scope-dropdown").click().get("#host").click() cy.contains('Display options').should('exist').click() - cy.get('[data-test-id=edge-handler]').should('exist').each((g) => { + cy.byLegacyTestID('edge-handler').should('exist').each((g) => { expect(g.text()).to.match(/\d+\s*ms/); }); @@ -75,7 +75,7 @@ describe('(OCP-67087 Network_Observability) DNSTracking test', { tags: ['Network cy.checkDashboards(DNSPanels) }) - after("Delete flowcollector and DNS pods", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/dns_tracking.cy.ts b/web/cypress/integration-tests/dns_tracking.cy.ts index b60e99848..c60a5b591 100644 --- a/web/cypress/integration-tests/dns_tracking.cy.ts +++ b/web/cypress/integration-tests/dns_tracking.cy.ts @@ -48,7 +48,7 @@ describe('(OCP-67087 Network_Observability) DNSTracking test', { tags: ['Network }) it("(OCP-67087, aramesha) Validate DNSTracking columns and DNSName", function () { - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') netflowPage.stopAutoRefresh() @@ -91,7 +91,7 @@ describe('(OCP-67087 Network_Observability) DNSTracking test', { tags: ['Network netflowPage.resetClearFilters() }) - after("Delete flowcollector and DNS pods", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/flowRTT.cy.ts b/web/cypress/integration-tests/flowRTT.cy.ts index 66957f172..b594b0959 100644 --- a/web/cypress/integration-tests/flowRTT.cy.ts +++ b/web/cypress/integration-tests/flowRTT.cy.ts @@ -17,17 +17,6 @@ describe('(OCP-68246 Network_Observability) FlowRTT test', { tags: ['Network_Obs }) it("(OCP-68246, aramesha, Network_Observability) Verify flowRTT panels", function () { - // to reduce flakes restore default panels first time it comes to overview page - cy.openPanelsModal(); - cy.get(overviewSelectors.panelsModal).contains('Restore default panels').click(); - cy.get(overviewSelectors.panelsModal).contains('Save').click(); - netflowPage.waitForLokiQuery() - - // verify Query Summary stats for flowRTT - cy.get(querySumSelectors.avgRTT).should('exist').then(avgRTT => { - cy.checkQuerySummary(avgRTT) - }) - // verify default flowRTT panels are visible cy.checkPanel(overviewSelectors.defaultFlowRTTPanels) cy.checkPanelsNum(5); @@ -44,10 +33,23 @@ describe('(OCP-68246 Network_Observability) FlowRTT test', { tags: ['Network_Obs netflowPage.waitForLokiQuery() cy.checkPanel(overviewSelectors.allFlowRTTPanels) + + // restore default panels and verify they are visible + cy.openPanelsModal(); + cy.byTestID(overviewSelectors.resetDefault).click().byTestID(overviewSelectors.save).click() + netflowPage.waitForLokiQuery() + cy.checkPanel(overviewSelectors.defaultFlowRTTPanels) + cy.checkPanelsNum(5); + + // verify Query Summary stats for flowRTT + // Wait for flows to be collected and metrics to be non-zero (retry up to 120s) + cy.get(querySumSelectors.avgRTT, { timeout: 120000 }).should('exist').then(avgRTT => { + cy.checkQuerySummary(avgRTT) + }) }) it("(OCP-68246, aramesha, Network_Observability) Verify default flowRTT column", function () { - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') netflowPage.stopAutoRefresh() @@ -61,7 +63,7 @@ describe('(OCP-68246 Network_Observability) FlowRTT test', { tags: ['Network_Obs netflowPage.resetClearFilters() }) - after("Delete flowcollector", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/flowRTT_dashboards.cy.ts b/web/cypress/integration-tests/flowRTT_dashboards.cy.ts index dee4b4e7e..39f4f2c60 100644 --- a/web/cypress/integration-tests/flowRTT_dashboards.cy.ts +++ b/web/cypress/integration-tests/flowRTT_dashboards.cy.ts @@ -33,7 +33,7 @@ describe('(OCP-68246 Network_Observability) FlowRTT test', { tags: ['Network_Obs it("(OCP-68246, aramesha, Network_Observability) Validate flowRTT edge labels and Query Summary stats", function () { netflowPage.visit() - cy.get('#tabs-container li:nth-child(3)').click() + cy.get('#tabs-container').contains('Topology').click() cy.get('#drawer').should('not.be.empty') cy.byTestID("show-view-options-button").should('exist').click().then(views => { @@ -55,7 +55,7 @@ describe('(OCP-68246 Network_Observability) FlowRTT test', { tags: ['Network_Obs // filter on TCP protocol cy.get(filterSelectors.filterInput).type("protocol=TCP" + '{enter}').click() - cy.get('[data-test-id=edge-handler]').each((g) => { + cy.byLegacyTestID('edge-handler').each((g) => { expect(g.text()).to.match(/\d+\s*ms/); }); netflowPage.clearAllFilters() @@ -73,11 +73,11 @@ describe('(OCP-68246 Network_Observability) FlowRTT test', { tags: ['Network_Obs dashboard.visitDashboard("netobserv-main") // verify 'TCP latency,p99' panel - cy.get('[data-test="tcp-latency,-p99-chart"]').find(graphSelector.graphBody).should('not.have.class', 'graph-empty-state') + cy.checkDashboards(['tcp-latency,-p99-chart']) cy.checkDashboards(flowRTTPanels) }) - after("Delete flowcollector", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/flow_dashboards_bytes.cy.ts b/web/cypress/integration-tests/flow_dashboards_bytes.cy.ts index 5722b6d9b..c57e05e14 100644 --- a/web/cypress/integration-tests/flow_dashboards_bytes.cy.ts +++ b/web/cypress/integration-tests/flow_dashboards_bytes.cy.ts @@ -48,7 +48,7 @@ describe('Network_Observability flow dashboards tests', { tags: ['Network_Observ cy.checkDashboards(trafficRatesPanelsBottom) }) - after("delete flowcollector and NetObs Operator", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/flow_dashboards_packets.cy.ts b/web/cypress/integration-tests/flow_dashboards_packets.cy.ts index 9483672ca..9de0a6771 100644 --- a/web/cypress/integration-tests/flow_dashboards_packets.cy.ts +++ b/web/cypress/integration-tests/flow_dashboards_packets.cy.ts @@ -48,7 +48,7 @@ describe('Network_Observability flow dashboards tests', { tags: ['Network_Observ cy.checkDashboards(trafficRatesPanelsBottom) }) - after("delete flowcollector and NetObs Operator", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/gateway_topology_logo.cy.ts b/web/cypress/integration-tests/gateway_topology_logo.cy.ts index aa3a28a98..5cc0616e3 100644 --- a/web/cypress/integration-tests/gateway_topology_logo.cy.ts +++ b/web/cypress/integration-tests/gateway_topology_logo.cy.ts @@ -2,7 +2,7 @@ import { netflowPage, setupTopologyViewWithNamespaceFilter, topologyPage, topolo import { Operator } from "@views/netobserv" import { verifyResourceSVGLogo } from "@views/netobserv-logo" -describe("(OCP-87215, Network_Observability) Verify Gateway API three-level owner metadata UI Test\t", function () { +describe("(OCP-87215, Network_Observability) Verify Gateway API three-level owner metadata UI Test", { tags: ['Network_Observability'] }, function () { const gatewayNS = 'netobserv-gateway-test' const gatewayName = 'test-gateway-owner' before('any test', function () { @@ -13,9 +13,7 @@ describe("(OCP-87215, Network_Observability) Verify Gateway API three-level owne cy.checkStorageClass(this) Operator.createFlowcollector("FlowRTT") - // Deploy all Gateway API resources from combined template (includes namespace creation and traffic generator) - cy.adminCLI(`oc process -f cypress/fixtures/gateway-api-template.yaml \ - | oc apply -f -`) + cy.adminCLI('oc apply -f cypress/fixtures/gateway-api.yaml') }) beforeEach("navigate to topology view", function () { @@ -23,35 +21,23 @@ describe("(OCP-87215, Network_Observability) Verify Gateway API three-level owne }) it("(OCP-87215, kapjain, Network_Observability) should verify Gateway appears as owner-level topology node with logo", function () { - - topologyPage.selectScopeGroup("owner", null) + topologyPage.selectScopeGroup("owner") topologyPage.isViewRendered() - // Verify topology is rendered - cy.get('[data-surface="true"]').should('exist') - - // Verify nodes exist in the topology - cy.get(topologySelectors.node, { timeout: 60000 }).should('have.length.greaterThan', 0) + cy.get(topologySelectors.node, { timeout: 80000 }).should('have.length.greaterThan', 0) - // Search for Gateway in topology cy.byTestID('search-topology-element-input').should('exist').clear().type(gatewayName) - // Verify Gateway node exists and is visible - cy.get(`g[data-id*="o=Gateway.${gatewayName}"]`, { timeout: 90000 }).should('exist') - - // Validate Gateway SVG icon/logo dynamically - verifyResourceSVGLogo('Gateway', gatewayName, 60000) + verifyResourceSVGLogo('Gateway', gatewayName) }) afterEach("test", function () { netflowPage.clearAllFilters() }) - after("cleanup Gateway resources", function () { - // Delete traffic generator deployment - cy.adminCLI(`oc process -f cypress/fixtures/gateway-api-template.yaml \ - | oc delete -f -`, { failOnNonZeroExit: false }) - // Remove cluster admin role + after("all tests", function () { + cy.adminCLI('oc delete -f cypress/fixtures/gateway-api.yaml --ignore-not-found') + Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) }) diff --git a/web/cypress/integration-tests/health_dashboards.cy.ts b/web/cypress/integration-tests/health_dashboards.cy.ts index d8a304f9d..7bfae2721 100644 --- a/web/cypress/integration-tests/health_dashboards.cy.ts +++ b/web/cypress/integration-tests/health_dashboards.cy.ts @@ -84,7 +84,7 @@ describe('Network_Observability health dashboards tests', { tags: ['Network_Obse cy.checkDashboards(resourcePanels) }) - after("delete flowcollector and NetObs Operator", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/ingress_dashboard.cy.ts b/web/cypress/integration-tests/ingress_dashboard.cy.ts index ee9549a9d..7f7b1cd19 100644 --- a/web/cypress/integration-tests/ingress_dashboard.cy.ts +++ b/web/cypress/integration-tests/ingress_dashboard.cy.ts @@ -24,7 +24,7 @@ describe('Network_Observability networking dashboards tests', { tags: ['Network_ dashboard.visit() cy.visit(`/monitoring/dashboards/grafana-dashboard-ingress-operator`) - cy.get('[data-test="poll-interval-dropdown"] > .pf-v6-c-menu-toggle').should('exist').then(btn => { + cy.byTestID('poll-interval-dropdown').find('button').should('exist').then(btn => { cy.wrap(btn).click().then(drop => { cy.contains('15 seconds').should('exist').click() }) diff --git a/web/cypress/integration-tests/netflow_conversations.cy.ts b/web/cypress/integration-tests/netflow_conversations.cy.ts index d09cd067d..f37b7b587 100644 --- a/web/cypress/integration-tests/netflow_conversations.cy.ts +++ b/web/cypress/integration-tests/netflow_conversations.cy.ts @@ -14,7 +14,7 @@ describe('(OCP-71787 Network_Observability) Conversation tracking test', { tags: beforeEach('any conversation tracking test', function () { netflowPage.visit() - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') }) @@ -37,13 +37,8 @@ describe('(OCP-71787 Network_Observability) Conversation tracking test', { tags: }) cy.get(querySumSelectors.flowsCount).should('exist').then(ConversationsCnt => { - let nflows = 0 - if (warningExists) { - nflows = Number(ConversationsCnt.text().split('+ Ended conversations')[0]) - } - else { - nflows = Number(ConversationsCnt.text().split(' ')[0]) - } + // parseFloat handles formats: "123 Ended conversations", "123+ Ended conversations" + const nflows = parseFloat(ConversationsCnt.text()) cy.wait(10) expect(nflows).to.be.gte(0) }) @@ -53,7 +48,7 @@ describe('(OCP-71787 Network_Observability) Conversation tracking test', { tags: netflowPage.resetClearFilters() }) - after("delete flowcollector and NetObs Operator", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/netflow_developer_view.cy.ts b/web/cypress/integration-tests/netflow_developer_view.cy.ts index 3898fe734..4a5f32f78 100644 --- a/web/cypress/integration-tests/netflow_developer_view.cy.ts +++ b/web/cypress/integration-tests/netflow_developer_view.cy.ts @@ -40,11 +40,11 @@ describe.skip('NetObserv developer view', { tags: ['Network_Observability'] }, f // Deploy client server manifests logged in as user2 cy.cliLogin(`${user2}`, `${user2Passwd}`) - cy.exec(`oc create -f cypress/fixtures/testuser-server-client.yaml`) + cy.adminCLI(`oc create -f cypress/fixtures/testuser-server-client.yaml`) // Logout from console as user1 and login as user2 cy.uiLogout().then(() => { - cy.visit(Cypress.config('baseUrl')) + cy.visit(Cypress.config('baseUrl') || '/') }) cy.uiLogin(Cypress.env('LOGIN_IDP'), user2, user2Passwd) @@ -57,7 +57,7 @@ describe.skip('NetObserv developer view', { tags: ['Network_Observability'] }, f // Logout from console as user2 and login as user3 cy.uiLogout().then(() => { - cy.visit(Cypress.config('baseUrl')) + cy.visit(Cypress.config('baseUrl') || '/') }) cy.uiLogin(Cypress.env('LOGIN_IDP'), user3, user3Passwd) @@ -69,7 +69,7 @@ describe.skip('NetObserv developer view', { tags: ['Network_Observability'] }, f cy.adminCLI(`oc adm policy remove-cluster-role-from-user netobserv-metrics-reader ${user3}`) }) - after("after all tests are done", function () { + after("after all tests", function () { cy.adminCLI(`oc login -u system:admin`) cy.adminCLI(`oc delete flowcollector cluster`) cy.adminCLI(`oc delete project test-server --ignore-not-found`) diff --git a/web/cypress/integration-tests/netflow_export.cy.ts b/web/cypress/integration-tests/netflow_export.cy.ts index 990301fd1..40c4b8333 100644 --- a/web/cypress/integration-tests/netflow_export.cy.ts +++ b/web/cypress/integration-tests/netflow_export.cy.ts @@ -25,7 +25,7 @@ describe('(OCP-72610 Network_Observability) Export automation', { tags: ['Networ cy.readFile('cypress/downloads/overview_page.png') // Export only Top 5 average bytes rates panel - cy.get('#panel-kebab-top_avg_byte_rates-container > .pf-v6-c-menu-toggle').should('exist').click() + cy.get('#panel-kebab-top_avg_byte_rates-container button').should('exist').click() cy.contains("Export panel").should('exist').click() cy.readFile('cypress/downloads/overview_panel_top_avg_byte_rates.png') cy.exec('rm cypress/downloads/overview_page.png') @@ -39,19 +39,18 @@ describe('(OCP-72610 Network_Observability) Export automation', { tags: ['Networ cy.byTestID("table-composable").should('exist') cy.showAdvancedOptions(); cy.get('#export-button').should('exist').click() - cy.get('[data-test="export-modal-footer"] > [data-test="export-button"]').should('exist').then((exportbtn) => { - cy.wrap(exportbtn).click() - // wait for download to complete - cy.wait(3000) - // get the CSV file name - cy.exec("ls cypress/downloads").then((response) => { - // rename CSV file to export_table.csv - cy.wrap(response.stdout).should('not.be.empty') - cy.exec(`mv cypress/downloads/${response.stdout} cypress/downloads/export_table.csv`) - cy.readFile('cypress/downloads/export_table.csv') - }) - cy.exec('rm cypress/downloads/export_table.csv') + cy.byTestID('export-modal').should('be.visible') + cy.byTestID('export-modal').within(() => { + cy.byTestID('export-button').should('exist').click() }) + // get the CSV file name with retry built into exec + cy.exec("ls cypress/downloads", { timeout: 10000 }).then((response) => { + // rename CSV file to export_table.csv + cy.wrap(response.stdout).should('not.be.empty') + cy.exec(`mv cypress/downloads/${response.stdout.trim()} cypress/downloads/export_table.csv`) + cy.readFile('cypress/downloads/export_table.csv') + }) + cy.exec('rm cypress/downloads/export_table.csv') netflowPage.clearAllFilters() }) diff --git a/web/cypress/integration-tests/netflow_external_subnet.cy.ts b/web/cypress/integration-tests/netflow_external_subnet.cy.ts index 9a18d2bef..26989183b 100644 --- a/web/cypress/integration-tests/netflow_external_subnet.cy.ts +++ b/web/cypress/integration-tests/netflow_external_subnet.cy.ts @@ -17,7 +17,7 @@ describe('(OCP-67615, OCP-72874 Network_Observability) Return external traffic a it("(OCP-67615, aramesha, Network_Observability) External traffic and custom subnet label", function () { netflowPage.visit() - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') // enable SrcSubnetLabel and DstSubnetLabel columns diff --git a/web/cypress/integration-tests/netflow_table.cy.ts b/web/cypress/integration-tests/netflow_table.cy.ts index fd0d7db49..61eed239b 100644 --- a/web/cypress/integration-tests/netflow_table.cy.ts +++ b/web/cypress/integration-tests/netflow_table.cy.ts @@ -14,7 +14,7 @@ describe('(OCP-50532, OCP-50531, OCP-50530, OCP-59408 Network_Observability) Net beforeEach("test", function () { netflowPage.visit() - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') }) @@ -124,7 +124,10 @@ describe('(OCP-50532, OCP-50531, OCP-50530, OCP-59408 Network_Observability) Net netflowPage.clearAllFilters() // verify NOT filter - cy.get(filterSelectors.filterDropdown).click().get('.pf-v6-c-panel__main-body').should('be.visible') + // Clear any text in filter input to ensure FilterSearchPanel (not suggestions) is shown + cy.get(filterSelectors.filterInput).clear() + cy.get(filterSelectors.filterDropdown).click() + cy.get('#filter-popper').should('be.visible') cy.get(filterSelectors.sourceRadio).should('exist').click() cy.get(filterSelectors.columnFilter).should('exist').click().get('#namespace').should('exist').click() cy.get(filterSelectors.compareDropdown).should('exist').click().get('#not-equal').should('exist').click() @@ -136,17 +139,17 @@ describe('(OCP-50532, OCP-50531, OCP-50530, OCP-59408 Network_Observability) Net // verify src port filter and port Naming cy.get(filterSelectors.filterInput).type("src_port=3100" + '{enter}') - cy.get('#src_port-0-toggle > span.pf-v6-c-menu-toggle__text').should('contain.text', 'loki') + cy.get('#src_port-0-toggle').should('contain.text', 'loki') // disable filter cy.get('#src_port-0-toggle').click().get('#dropdown-item-disable').click() // sort by port - cy.get('[data-test=th-SrcPort] > .pf-v6-c-table__button').click() + cy.get('[data-test=th-SrcPort] button').click() // Verify SrcPort doesnt not have text loki for all rows cy.get('[data-test-td-column-id=SrcPort]').each((td) => { - cy.get('[data-test-td-column-id=SrcPort] > div > div > p').should('not.contain.text', 'loki (3100)') + cy.wrap(td).should('not.contain.text', 'loki (3100)') }) // enable filter @@ -154,7 +157,7 @@ describe('(OCP-50532, OCP-50531, OCP-50530, OCP-59408 Network_Observability) Net // Verify SrcPort has text loki for all rows cy.get('[data-test-td-column-id=SrcPort]').each((td) => { - cy.get('[data-test-td-column-id=SrcPort] > div > div > p').should('contain.text', 'loki (3100)') + cy.wrap(td).should('contain.text', 'loki (3100)') }) netflowPage.clearAllFilters() @@ -193,7 +196,7 @@ describe('(OCP-50532, OCP-50531, OCP-50530, OCP-59408 Network_Observability) Net cy.byTestID("show-histogram-button").should('exist').click() cy.get('#popover-netobserv-tour-popover-body').should('exist') // close tour - cy.get("#popover-netobserv-tour-popover-header > h6 > div > div:nth-child(2) > button").should("exist").click() + cy.get(".guided-tour-close-button").should("exist").click() cy.byTestID(genSelectors.refreshDrop).should('be.disabled') // get current refreshed time let lastRefresh = Cypress.$("#lastRefresh").text() diff --git a/web/cypress/integration-tests/netflow_zone_multiCluster.cy.ts b/web/cypress/integration-tests/netflow_zone_multiCluster.cy.ts index c42b876c5..6913c411e 100644 --- a/web/cypress/integration-tests/netflow_zone_multiCluster.cy.ts +++ b/web/cypress/integration-tests/netflow_zone_multiCluster.cy.ts @@ -1,10 +1,6 @@ -import { colSelectors, netflowPage, setupTopologyViewWithNamespaceFilter, topologyPage, topologySelectors } from "@views/netflow-page" +import { colSelectors, netflowPage, setupTopologyViewWithNamespaceFilter, topologyPage, topologySelectors, getTopologyScopeURL } from "@views/netflow-page" import { Operator } from "@views/netobserv" -function getTopologyScopeURL(scope: string): string { - return `**/flow/metrics**aggregateBy=${scope}*` -} - describe('Netflow Zone and multiCluster test', { tags: ['Network_Observability'] }, function () { before('any test', function () { @@ -21,7 +17,7 @@ describe('Netflow Zone and multiCluster test', { tags: ['Network_Observability'] }) it("(OCP-71525, aramesha, Network_Observability) should validate zone/multiCluster columns", function () { - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') cy.openColumnsModal().then(col => { @@ -53,9 +49,9 @@ describe('Netflow Zone and multiCluster test', { tags: ['Network_Observability'] fixture: 'flowmetrics/zone.json' }).as('matchedUrl') - topologyPage.selectScopeGroup(scope, null) + topologyPage.selectScopeGroup(scope) cy.wait('@matchedUrl').then(({ response }) => { - expect(response.statusCode).to.eq(200) + expect(response?.statusCode).to.eq(200) }) topologyPage.isViewRendered() @@ -69,9 +65,9 @@ describe('Netflow Zone and multiCluster test', { tags: ['Network_Observability'] fixture: 'flowmetrics/cluster.json' }).as('matchedUrl') - topologyPage.selectScopeGroup(scope, null) + topologyPage.selectScopeGroup(scope) cy.wait('@matchedUrl').then(({ response }) => { - expect(response.statusCode).to.eq(200) + expect(response?.statusCode).to.eq(200) }) topologyPage.isViewRendered() @@ -84,7 +80,7 @@ describe('Netflow Zone and multiCluster test', { tags: ['Network_Observability'] netflowPage.resetClearFilters() }) - after("delete flowcollector and NetObs Operator", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/netobserv_udn.cy.ts b/web/cypress/integration-tests/netobserv_udn.cy.ts index 3d9cc3ab7..b34491dc1 100644 --- a/web/cypress/integration-tests/netobserv_udn.cy.ts +++ b/web/cypress/integration-tests/netobserv_udn.cy.ts @@ -19,7 +19,7 @@ describe('(OCP-81751 Network_Observability) UDN test', { tags: ['Network_Observa }) it("(OCP-81751, aramesha) should verify default Network Name columns", function () { - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') netflowPage.stopAutoRefresh() @@ -34,7 +34,7 @@ describe('(OCP-81751 Network_Observability) UDN test', { tags: ['Network_Observa setupTopologyViewWithNamespaceFilter() const scope = 'network' - topologyPage.selectScopeGroup(scope, null) + topologyPage.selectScopeGroup(scope) topologyPage.isViewRendered() // Filter on empty CUDN @@ -56,7 +56,7 @@ describe('(OCP-81751 Network_Observability) UDN test', { tags: ['Network_Observa }) after("all tests", function () { - cy.adminCLI('oc delete -f cypress/fixtures/test-udn.yaml') + cy.adminCLI('oc delete -f cypress/fixtures/test-udn.yaml --ignore-not-found') Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/network_health.cy.ts b/web/cypress/integration-tests/network_health.cy.ts index af990448c..54c2cd385 100644 --- a/web/cypress/integration-tests/network_health.cy.ts +++ b/web/cypress/integration-tests/network_health.cy.ts @@ -39,11 +39,10 @@ describe('(OCP-84821 Network_Observability) Network Health test', { tags: ['Netw cy.get(networkHealthSelectors.namespace).should('exist') cy.get(networkHealthSelectors.workload).should('exist') - // wait 1min for alert to show up - cy.wait(60000) - cy.get(networkHealthSelectors.namespace).should('exist').click().then(() => { - networkHealth.verifyAlert("dns-traffic") - }) + // Switch to namespace tab and wait for health cards to load + cy.get(networkHealthSelectors.namespace).should('exist').click() + // verifyAlert will wait up to 60s for the card to appear + networkHealth.verifyAlert("dns-traffic") networkHealth.navigateToAlertPage("dns-traffic") // verify Runbooks on the inspect alert page. @@ -65,8 +64,8 @@ describe('(OCP-84821 Network_Observability) Network Health test', { tags: ['Netw cy.get(networkHealthSelectors.sidePanel).should('be.visible') // click the kebab button - cy.get('div.rule-details-row:nth-child(1) button').click().then(() => { - cy.get('button[role="menuitem"]').eq(2).click().then(() => { + cy.get('div.rule-details-row').first().find('button').click().then(() => { + cy.contains('Inspect network traffic').click().then(() => { cy.checkNetflowTraffic() // select Owner group topologyPage.selectGroupWithSlider("Owner") @@ -74,13 +73,13 @@ describe('(OCP-84821 Network_Observability) Network Health test', { tags: ['Netw // click on the NS and check Health tab in sidebar. cy.get('g[data-kind="node"] > g').eq(1).parent().should('exist').click() cy.get('#elementPanel').should('be.visible') - cy.get('#drawer-tabs > ul > li:nth-child(3)').should('exist').click() + cy.get('#drawer-tabs').contains('Health').should('exist').click() cy.get('div .rule-details-row').should('exist') }) }) }) - after("any test", function () { + after("all tests", function () { cy.adminCLI('oc delete -f cypress/fixtures/dns_errors.yaml --ignore-not-found') Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) diff --git a/web/cypress/integration-tests/overview_page.cy.ts b/web/cypress/integration-tests/overview_page.cy.ts index 933811db0..ad0748c73 100644 --- a/web/cypress/integration-tests/overview_page.cy.ts +++ b/web/cypress/integration-tests/overview_page.cy.ts @@ -126,7 +126,7 @@ describe('(OCP-54839 Network_Observability) Netflow Overview page tests', { tags cy.get(overviewSelectors.panelsModal).contains('Save').should('be.disabled'); // select 1 panel and verify its visible on console - cy.get('.pf-v5-c-data-list__check > #top_avg_packet_rates').click(); + cy.get('[data-test="overview-panel-management"]').find('#top_avg_packet_rates').check(); cy.get(overviewSelectors.panelsModal).contains('Save').click(); netflowPage.waitForLokiQuery() cy.checkPanel([overviewSelectors.allPanels[2]]) @@ -145,7 +145,7 @@ describe('(OCP-54839 Network_Observability) Netflow Overview page tests', { tags netflowPage.resetClearFilters() }) - after("after all tests are done", function () { + after("after all tests", function () { cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) }) diff --git a/web/cypress/integration-tests/packet_drop.cy.ts b/web/cypress/integration-tests/packet_drop.cy.ts index a0b9275f2..fa5cb05d0 100644 --- a/web/cypress/integration-tests/packet_drop.cy.ts +++ b/web/cypress/integration-tests/packet_drop.cy.ts @@ -46,7 +46,7 @@ describe('(OCP-66141 Network_Observability) PacketDrop test', { tags: ['Network_ }) it("(OCP-66141, aramesha, Network_Observability) Verify packetDrop Query Options filters", function () { - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') // toggle between drops filter @@ -73,7 +73,7 @@ describe('(OCP-66141 Network_Observability) PacketDrop test', { tags: ['Network_ netflowPage.resetClearFilters() }) - after("Delete flowcollector", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/packet_drop_dashboards.cy.ts b/web/cypress/integration-tests/packet_drop_dashboards.cy.ts index 8c217d32d..953bcac84 100644 --- a/web/cypress/integration-tests/packet_drop_dashboards.cy.ts +++ b/web/cypress/integration-tests/packet_drop_dashboards.cy.ts @@ -1,6 +1,6 @@ import { Operator } from "@views/netobserv" import { netflowPage, querySumSelectors, topologySelectors } from "@views/netflow-page" -import { dashboard, graphSelector } from "@views/dashboards-page" +import { dashboard } from "@views/dashboards-page" const metricType = [ "Bytes", @@ -30,7 +30,7 @@ describe('(OCP-66141 Network_Observability) PacketDrop dashboards test', { tags: it("(OCP-66141, aramesha, Network_Observability) Validate PacketDrop edge labels and Query Summary stats", function () { netflowPage.visit() - cy.get('#tabs-container li:nth-child(3)').click() + cy.get('#tabs-container').contains('Topology').click() cy.get('#drawer').should('not.be.empty') cy.byTestID("show-view-options-button").should('exist').click().then(views => { @@ -85,14 +85,14 @@ describe('(OCP-66141 Network_Observability) PacketDrop dashboards test', { tags: dashboard.visitDashboard("netobserv-main") // verify 'Drops' panel - cy.get('[data-test="drops-chart"]').find(graphSelector.graphBody).should('not.have.class', 'graph-empty-state') + cy.checkDashboards(['drops-chart']) cy.get('#content-scrollable').scrollTo('bottom') cy.checkDashboards(PacketDropPanels) }) - after("Delete flowcollector", function () { + after("all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/prom_datasource_only.cy.ts b/web/cypress/integration-tests/prom_datasource_only.cy.ts index 35b6a7619..5dd5372dd 100644 --- a/web/cypress/integration-tests/prom_datasource_only.cy.ts +++ b/web/cypress/integration-tests/prom_datasource_only.cy.ts @@ -32,9 +32,8 @@ describe('(OCP-74049, OCP-73875 Network_Observability) Prometheus datasource onl cy.byTestID('resource').should('not.exist') }) }) - after("after all tests are done", function () { - // Delete flowcollector - cy.adminCLI(`oc delete flowcollector cluster`) + after("after all tests", function () { + Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) }) diff --git a/web/cypress/integration-tests/quickFilters.cy.ts b/web/cypress/integration-tests/quickFilters.cy.ts index bdb318579..34e558247 100644 --- a/web/cypress/integration-tests/quickFilters.cy.ts +++ b/web/cypress/integration-tests/quickFilters.cy.ts @@ -35,7 +35,7 @@ describe('(OCP-56222 Network_Observability) Quick Filters test', { tags: ['Netwo beforeEach('any netflow table test', function () { netflowPage.visit() - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') }) @@ -49,7 +49,7 @@ describe('(OCP-56222 Network_Observability) Quick Filters test', { tags: ['Netwo }) cy.contains("Quick filters").should('exist').click() cy.get('#quick-filters-dropdown').contains("Test NS") - cy.get('#quick-filters-dropdown').find('.pf-v6-c-check__input').should('exist').click() + cy.contains("Test NS").parent().find('input[type="checkbox"]').should('exist').click() // verify source and destination NS are test-server and test-client respectively cy.get('[data-test-td-column-id=SrcK8S_Namespace]').each((td) => { @@ -59,7 +59,7 @@ describe('(OCP-56222 Network_Observability) Quick Filters test', { tags: ['Netwo expect(td).to.contain(CLIENT_NS) }) - cy.get('#quick-filters-dropdown').find('.pf-v6-c-check__input').should('exist').click() + cy.contains("Test NS").parent().find('input[type="checkbox"]').should('exist').click() cy.get('#filters').should('not.exist') }) diff --git a/web/cypress/integration-tests/static_plugin.cy.ts b/web/cypress/integration-tests/static_plugin.cy.ts index dae8f6f16..1edbc34f7 100644 --- a/web/cypress/integration-tests/static_plugin.cy.ts +++ b/web/cypress/integration-tests/static_plugin.cy.ts @@ -41,7 +41,7 @@ describe('(OCP-84156 Network_Observability) StaticPlugin test', { tags: ['Networ netflowPage.resetClearFilters() }) - after("Delete flowcollector", function () { + after("after all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/integration-tests/table_queryopts.cy.ts b/web/cypress/integration-tests/table_queryopts.cy.ts index 291124dce..65f989730 100644 --- a/web/cypress/integration-tests/table_queryopts.cy.ts +++ b/web/cypress/integration-tests/table_queryopts.cy.ts @@ -18,7 +18,7 @@ describe('(OCP-50532, OCP-50531, OCP-50530, OCP-59408 Network_Observability) Net beforeEach('any netflow table test', function () { netflowPage.visit() - cy.get('#tabs-container li:nth-child(2)').click() + cy.get('#tabs-container').contains('Traffic flows').click() cy.byTestID("table-composable").should('exist') }) @@ -52,42 +52,19 @@ describe('(OCP-50532, OCP-50531, OCP-50530, OCP-59408 Network_Observability) Net }) cy.get(querySumSelectors.flowsCount).should('exist').then(flowsCnt => { - let nflows = 0 - if (warningExists) { - nflows = Number(flowsCnt.text().split('+ Flows')[0]) - } - else { - nflows = Number(flowsCnt.text().split(' ')[0]) - } + // parseFloat handles formats: "123 Flows", "123+ Flows", "1.5k Flows", "1.5k+ Flows" + const nflows = parseFloat(flowsCnt.text()) cy.wait(10) expect(nflows).to.be.greaterThan(0) }) cy.get(querySumSelectors.bytesCount).should('exist').then(bytesCnt => { - let nbytes = 0 - if (warningExists) { - nbytes = Number(bytesCnt.text().split('+ ')[0]) - } - else { - nbytes = Number(bytesCnt.text().split(' ')[0]) - } + const nbytes = parseFloat(bytesCnt.text()) expect(nbytes).to.be.greaterThan(0) }) cy.get(querySumSelectors.packetsCount).should('exist').then(pktsCnt => { - let npkts = 0 - if (warningExists) { - let npktsStr = pktsCnt.text().split('+ ')[0] - if (npktsStr.includes('k')) { - npkts = Number(npktsStr.split('k')[0]) - } - else { - npkts = Number(npktsStr) - } - } - else { - npkts = Number(pktsCnt.text().split(' ')[0]) - } + const npkts = parseFloat(pktsCnt.text()) expect(npkts).to.be.greaterThan(0) }) cy.get('#query-summary-toggle').should('exist').click() @@ -120,12 +97,12 @@ describe('(OCP-50532, OCP-50531, OCP-50530, OCP-59408 Network_Observability) Net // filter on DSCP values cy.get(filterSelectors.filterInput).type("dscp=0" + '{enter}').click() - cy.get('#dscp-0-toggle > span.pf-v6-c-menu-toggle__text').should('contain.text', 'Standard') + cy.get('#dscp-0-toggle').should('contain.text', 'Standard') // Verify DSCP value is Standard for all rows cy.get('[data-test-td-column-id=Dscp]').each((td) => { - expect(td).attr("data-test-td-value").to.contain(0) - cy.get('[data-test-td-column-id=Dscp] > div > div').should('contain.text', 'Standard') + cy.wrap(td).should('have.attr', 'data-test-td-value').and('contain', '0') + cy.wrap(td).should('contain.text', 'Standard') }) netflowPage.clearAllFilters() }) diff --git a/web/cypress/integration-tests/topology_edges_labels.cy.ts b/web/cypress/integration-tests/topology_edges_labels.cy.ts index b1c1db9a1..cf15ba422 100644 --- a/web/cypress/integration-tests/topology_edges_labels.cy.ts +++ b/web/cypress/integration-tests/topology_edges_labels.cy.ts @@ -1,10 +1,6 @@ -import { netflowPage, topologySelectors, topologyPage, setupTopologyViewWithNamespaceFilter } from "@views/netflow-page" +import { netflowPage, topologySelectors, topologyPage, setupTopologyViewWithNamespaceFilter, getTopologyResourceScopeGroupURL } from "@views/netflow-page" import { Operator, project } from "@views/netobserv" -function getTopologyResourceScopeGroupURL(groups: string): string { - return `**/flow/metrics**groups=${groups}*` -} - describe("(OCP-53591 Network_Observability) Netflow Topology edges,labels, badges features", { tags: ['Network_Observability'] }, function () { before('any test', function () { @@ -57,26 +53,26 @@ describe("(OCP-53591 Network_Observability) Netflow Topology edges,labels, badge it("(OCP-53591, memodi, Network_Observability) should verify edges labels can be displayed/hidden", function () { netflowPage.selectSourceNS(project) - topologyPage.selectScopeGroup(null, "none") - topologyPage.selectScopeGroup("namespace", null) + topologyPage.selectScopeGroup(undefined, "none") + topologyPage.selectScopeGroup("namespace") cy.get('#reset-view').should('exist').click() - cy.get('[data-test-id="edge-handler"] g.pf-topology__edge__tag').should("exist") + cy.byLegacyTestID('edge-handler').find('g.pf-topology__edge__tag').should("exist") cy.contains('Display options').should('exist').click() cy.get(topologySelectors.labelToggle).uncheck() - cy.get('[data-test-id="edge-handler"] g.pf-topology__edge__tag').should("not.exist") + cy.byLegacyTestID('edge-handler').find('g.pf-topology__edge__tag').should("not.exist") cy.get(topologySelectors.labelToggle).check() - cy.get('[data-test-id="edge-handler"] g.pf-topology__edge__tag').should("exist") + cy.byLegacyTestID('edge-handler').find('g.pf-topology__edge__tag').should("exist") netflowPage.clearAllFilters() }) it("(OCP-53591, memodi, Network_Observability) should verify badges display/hidden", function () { netflowPage.selectSourceNS(project) - topologyPage.selectScopeGroup(null, "none") - topologyPage.selectScopeGroup("namespace", null) + topologyPage.selectScopeGroup(undefined, "none") + topologyPage.selectScopeGroup("namespace") cy.get('#reset-view').should('exist').click() cy.contains('Display options').should('exist').click() @@ -95,7 +91,7 @@ describe("(OCP-53591 Network_Observability) Netflow Topology edges,labels, badge netflowPage.resetClearFilters() }) - after("after all tests are done", function () { + after("after all tests", function () { cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) }) diff --git a/web/cypress/integration-tests/topology_groups.cy.ts b/web/cypress/integration-tests/topology_groups.cy.ts index c2aa29d65..1f20387e5 100644 --- a/web/cypress/integration-tests/topology_groups.cy.ts +++ b/web/cypress/integration-tests/topology_groups.cy.ts @@ -1,14 +1,6 @@ -import { netflowPage, topologySelectors, topologyPage, setupTopologyViewWithNamespaceFilter } from "@views/netflow-page" +import { netflowPage, topologySelectors, topologyPage, setupTopologyViewWithNamespaceFilter, getTopologyScopeURL, getTopologyResourceScopeGroupURL } from "@views/netflow-page" import { Operator } from "@views/netobserv" -function getTopologyScopeURL(scope: string): string { - return `**/flow/metrics**aggregateBy=${scope}*` -} - -function getTopologyResourceScopeGroupURL(groups: string): string { - return `**/flow/metrics**groups=${groups}*` -} - describe("(OCP-53591 Network_Observability) Netflow Topology groups features", { tags: ['Network_Observability'] }, function () { before('any test', function () { @@ -32,10 +24,10 @@ describe("(OCP-53591 Network_Observability) Netflow Topology groups features", { // selecting something different first // to re-trigger API request on namespace selection - topologyPage.selectScopeGroup("owner", null) - topologyPage.selectScopeGroup(scope, null) + topologyPage.selectScopeGroup("owner") + topologyPage.selectScopeGroup(scope) cy.wait('@matchedUrl').then(({ response }) => { - expect(response.statusCode).to.eq(200) + expect(response?.statusCode).to.eq(200) }) topologyPage.isViewRendered() // verify number of edges and nodes. @@ -52,14 +44,14 @@ describe("(OCP-53591 Network_Observability) Netflow Topology groups features", { // using slider let lastRefresh = Cypress.$("#lastRefresh").text() cy.log(`last refresh is ${lastRefresh}`) - cy.get('.pf-v6-c-progress-stepper').get('#scope-step-2 > div:nth-child(2) > button').click().then(slider => { + cy.get('#scope-step-2 button').click().then(slider => { netflowPage.waitForLokiQuery() cy.wait(3000) cy.get('#lastRefresh').invoke('text').should('not.eq', lastRefresh) }) cy.wait('@matchedUrl').then(({ response }) => { - expect(response.statusCode).to.eq(200) + expect(response?.statusCode).to.eq(200) }) topologyPage.isViewRendered() // verify number of edges and nodes. @@ -70,9 +62,9 @@ describe("(OCP-53591 Network_Observability) Netflow Topology groups features", { it("(OCP-53591, memodi) should verify resource scope", function () { const scope = 'resource' cy.intercept('GET', getTopologyScopeURL(scope), { fixture: 'flowmetrics/resource.json' }).as('matchedUrl') - topologyPage.selectScopeGroup(scope, null) + topologyPage.selectScopeGroup(scope) cy.wait('@matchedUrl').then(({ response }) => { - expect(response.statusCode).to.eq(200) + expect(response?.statusCode).to.eq(200) }) topologyPage.isViewRendered() // verify number of edges and nodes. @@ -121,7 +113,7 @@ describe("(OCP-53591 Network_Observability) Netflow Topology groups features", { netflowPage.resetClearFilters() }) - after("after all tests are done", function () { + after("after all tests", function () { cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) }) diff --git a/web/cypress/integration-tests/topology_view.cy.ts b/web/cypress/integration-tests/topology_view.cy.ts index a0810e2e1..0fb68c278 100644 --- a/web/cypress/integration-tests/topology_view.cy.ts +++ b/web/cypress/integration-tests/topology_view.cy.ts @@ -62,7 +62,9 @@ describe("(OCP-53591 Network_Observability) Netflow Topology view features", { t cy.get('#drawer').should('exist') cy.get('#pageHeader').should('exist').then(() => { - const settings = JSON.parse(localStorage.getItem('netobserv-plugin-settings')) + const rawSettings = localStorage.getItem('netobserv-plugin-settings') + expect(rawSettings, 'netobserv-plugin-settings should exist').to.not.be.null + const settings = JSON.parse(rawSettings as string) const topologySettings = settings['netflow-traffic-topology-options'] expect(settings['netflow-traffic-view-id']).to.be.equal('topology') @@ -83,17 +85,17 @@ describe("(OCP-53591 Network_Observability) Netflow Topology view features", { t cy.get('#elementPanel').should('be.visible') // details tab - cy.get('#drawer-tabs > ul > li:nth-child(1)').should('exist') + cy.get('#drawer-tabs').contains('Details').should('exist') // metrics tab - cy.get('#drawer-tabs > ul > li:nth-child(2)').should('exist').click() - cy.get('div.pf-v6-c-chart').should('exist') + cy.get('#drawer-tabs').contains('Metrics').should('exist').click() + cy.get('.element-metrics-container svg').should('exist') }) afterEach("test", function () { netflowPage.resetClearFilters() }) - after("after all tests are done", function () { + after("after all tests", function () { cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) }) diff --git a/web/cypress/integration-tests/workloads.cy.ts b/web/cypress/integration-tests/workloads.cy.ts index ee0a2adbc..24c01ea60 100644 --- a/web/cypress/integration-tests/workloads.cy.ts +++ b/web/cypress/integration-tests/workloads.cy.ts @@ -23,7 +23,7 @@ describe('(OCP-70972 Network_Observability) Netflow traffic pages on workloads', cy.visitNetflowTrafficTab(page) }) }) - after("after all tests are done", function () { + after("after all tests", function () { cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) }) diff --git a/web/cypress/support/commands.ts b/web/cypress/support/commands.ts index 9037f952b..59da3fdac 100644 --- a/web/cypress/support/commands.ts +++ b/web/cypress/support/commands.ts @@ -333,6 +333,22 @@ Cypress.Commands.add('uiLogout', () => { }) }); +Cypress.Commands.add("cliLogin", (username?: string, password?: string) => { + const kubeconfig = Cypress.env('KUBECONFIG_PATH'); + const loginUsername = username || Cypress.env('LOGIN_USERNAME'); + const loginPassword = password || Cypress.env('LOGIN_PASSWORD'); + cy.exec(`oc whoami --show-server=true --kubeconfig ${kubeconfig}`) + .then(result => { + const hostapi = result.stdout.trim(); + cy.log(hostapi); + cy.exec(`oc login -u ${loginUsername} -p ${loginPassword} ${hostapi} --insecure-skip-tls-verify=true --kubeconfig ${kubeconfig}`, { failOnNonZeroExit: false }) + .then(loginresult => { + cy.log(loginresult.stderr); + cy.log(loginresult.stdout); + }); + }); +}); + Cypress.Commands.add('retryTask', (command, expectedOutput, options?) => { const { retries, interval } = options || DEFAULT_RETRY_OPTIONS; const retryTaskFn = (currentRetries) => { @@ -385,6 +401,7 @@ declare global { adminCLI(command: string, options?: Partial): Chainable uiLogin(provider: string, username: string, password: string): Chainable uiLogout(): Chainable + cliLogin(username?: string, password?: string): Chainable switchPerspective(perspective: string): Chainable checkCommandResult(command: string, expectedoutput: string, options?: { retries?: number; interval?: number }): Chainable retryTask(condition: string, expectedoutput: string, options?: { retries?: number; interval?: number }): Chainable diff --git a/web/cypress/views/dashboards-page.ts b/web/cypress/views/dashboards-page.ts index ff6a798e6..519a641dd 100644 --- a/web/cypress/views/dashboards-page.ts +++ b/web/cypress/views/dashboards-page.ts @@ -6,26 +6,10 @@ export const dashboard = { visitDashboard: (dashboardName: string) => { cy.visit(`/monitoring/dashboards/${dashboardName}`) - // Revert once https://issues.redhat.com/browse/OCPBUGS-57307 is fixed - // cy.get('#refresh-interval-dropdown-dropdown').should('exist').then(btn => { - // cy.wrap(btn).click().then(drop => { - // cy.contains('15 seconds').should('exist').click() - // }) - // }) - cy.get('label[for="refresh-interval-dropdown"]').parent().parent().parent().within(() => { - cy.get('button').click() - }) + cy.contains('label', 'Refresh interval').parent().siblings().find('button').first().click() cy.contains('15 seconds').should('exist').click() - // Revert once https://issues.redhat.com/browse/OCPBUGS-57307 is fixed - // cy.get('#monitoring-time-range-dropdown-dropdown').should('exist').then(btn => { - // cy.wrap(btn).click().then(drop => { - // cy.contains('Last 5 minutes').should('exist').click() - // }) - // }) - cy.get('label[for="monitoring-time-range-dropdown"]').parent().parent().parent().within(() => { - cy.get('button').click() - }) + cy.contains('label', 'Time range').parent().siblings().find('button').first().click() cy.contains('Last 5 minutes').should('exist').click() // to load all the graphs on the dashboard @@ -36,17 +20,17 @@ export const dashboard = { } export namespace dashboardSelectors { - export const flowStatsToggle = '[data-test-id=panel-flowlogs-pipeline-statistics] > div > div > div > button' - export const ebpfStatsToggle = '[data-test-id=panel-e-bpf-agent-statistics]> div > div > div > button' - export const operatorStatsToggle = '[data-test-id=panel-operator-statistics] > div > div > div > button' - export const resourceStatsToggle = '[data-test-id=panel-resource-usage] > div > div > div > button' - export const top10PerRouteToggle = '[data-test-id=panel-top-10-per-route] > div > div > div > button' - export const top10PerNamespaceToggle = '[data-test-id=panel-top-10-per-namespace] > div > div > div > button' - export const top10PerShardToggle = '[data-test-id=panel-top-10-per-shard] > div > div > div > button' + export const flowStatsToggle = '[data-test-id=panel-flowlogs-pipeline-statistics] button:first' + export const ebpfStatsToggle = '[data-test-id=panel-e-bpf-agent-statistics] button:first' + export const operatorStatsToggle = '[data-test-id=panel-operator-statistics] button:first' + export const resourceStatsToggle = '[data-test-id=panel-resource-usage] button:first' + export const top10PerRouteToggle = '[data-test-id=panel-top-10-per-route] button:first' + export const top10PerNamespaceToggle = '[data-test-id=panel-top-10-per-namespace] button:first' + export const top10PerShardToggle = '[data-test-id=panel-top-10-per-shard] button:first' } export const graphSelector = { - graphBody: '.pf-v6-c-card__body > div > div' + graphBody: '[role="region"]' } export const appsInfra = [ @@ -68,7 +52,7 @@ Cypress.Commands.add('checkDashboards', (names) => { // Check that graph body doesn't have empty state - use a custom retry mechanism cy.byTestID(names[i], { timeout: 120000 }).first().within(() => { cy.get(graphSelector.graphBody, { timeout: 120000 }).should($body => { - const hasEmptyState = $body.hasClass('pf-v6-c-empty-state') + const hasEmptyState = $body.find('[data-test="empty-state"]').length > 0 if (hasEmptyState) { throw new Error('Dashboard panel still showing empty state, retrying...') } diff --git a/web/cypress/views/list-page.ts b/web/cypress/views/list-page.ts index 14e911b01..a21ef5761 100644 --- a/web/cypress/views/list-page.ts +++ b/web/cypress/views/list-page.ts @@ -59,9 +59,9 @@ export const listPage = { dvFilter: { byName: (name: string) => { cy.get('[data-ouia-component-id="DataViewFilters"]').within(() => - cy.get('.pf-v6-c-menu-toggle').first().click(), + cy.get('button').first().click(), ); - cy.get('.pf-v6-c-menu__list-item').should('be.visible').contains('Name').click(); + cy.contains('li', 'Name').click(); cy.get('[aria-label="Name filter"]').clear().type(name); }, by: (checkboxLabel: string) => { @@ -92,7 +92,7 @@ export const listPage = { .contains(resourceName) .parents('tr') .within(() => { - cy.get('[data-test-id="kebab-button"]').click(); + cy.byLegacyTestID('kebab-button').click(); }); cy.byTestActionID(actionName).click(); }, @@ -113,9 +113,9 @@ export const listPage = { shouldExist: (resourceName: string) => cy.get('[data-test-rows="resource-row"]').contains(resourceName), clickRowByName: (resourceName: string) => - cy.get(`a[data-test-id="${resourceName}"]`).click({ force: true }), // after applying row filter, resource rows detached from DOM according to cypress, need to force the click + cy.byLegacyTestID(resourceName).filter('a').click({ force: true }), // after applying row filter, resource rows detached from DOM according to cypress, need to force the click shouldNotExist: (resourceName: string) => - cy.get(`[data-test-id="${resourceName}"]`, { timeout: 90000 }).should('not.exist'), + cy.byLegacyTestID(resourceName, { timeout: 90000 }).should('not.exist'), }, dvRows: { getFirstElementName: () => cy.get('[data-test^="data-view-cell-"]').first().find('a'), @@ -136,7 +136,7 @@ export const listPage = { .contains(resourceName) .parents('tr') .within(() => { - cy.get('[data-test-id="kebab-button"]').click(); + cy.byLegacyTestID('kebab-button').click(); }); cy.byTestActionID(actionName).click(); }, diff --git a/web/cypress/views/netflow-page.ts b/web/cypress/views/netflow-page.ts index 6c0640cab..f78ce3a04 100644 --- a/web/cypress/views/netflow-page.ts +++ b/web/cypress/views/netflow-page.ts @@ -18,6 +18,18 @@ declare global { type Group = 'Node' | 'Namespace' | 'Owner' | 'Resource' +export function getTopologyScopeURL(scope: string): string { + return `**/flow/metrics**aggregateBy=${scope}*` +} + +export function getTopologyResourceScopeGroupURL(groups: string): string { + return `**/flow/metrics**groups=${groups}*` +} + +export function getMemoryUsageMB(): number { + return Math.round((window.performance as any).memory?.usedJSHeapSize / 1048576) +} + export const netflowPage = { visit: (clearfilters = true) => { cy.clearLocalStorage() @@ -34,35 +46,24 @@ export const netflowPage = { cy.byTestID('no-results-found').should('not.exist') cy.get('#overview-container').should('exist') }, - toggleFullScreen: () => { - cy.byTestID(genSelectors.moreOpts).should('exist').click().then(() => { - cy.get(genSelectors.expand).click() - }) - }, setAutoRefresh: () => { - cy.byTestID(genSelectors.refreshDrop).should('exist').then($btn => { - // only set refresh if it's OFF - if ($btn.text() == "Refresh off") { - cy.wrap($btn).click({ force: true }) + cy.byTestID(genSelectors.refreshDrop).should('exist').invoke('text').then((text) => { + if (text === "Refresh off") { + cy.byTestID(genSelectors.refreshDrop).click({ force: true }) cy.get('[data-test="15s"]').should('exist').click() } }) }, stopAutoRefresh: () => { - cy.byTestID(genSelectors.refreshDrop).should('exist').then($btn => { - // only stop refresh if it's not already OFF - if ($btn.text() != "Refresh off") { - cy.wrap($btn).click({ force: true }) - // Wait for dropdown menu to be rendered and visible - cy.get('.pf-v5-c-menu').should('be.visible') - cy.get('[data-test="OFF_KEY"]') - .should('be.visible') - .click() + cy.byTestID(genSelectors.refreshDrop).should('exist').invoke('text').then((text) => { + if (!text.includes("Refresh off")) { + cy.byTestID(genSelectors.refreshDrop).click() + cy.byTestID('OFF_KEY').click() } }) }, resetClearFilters: () => { - cy.byTestID("set-default-filters-button").should('exist').click() + cy.byTestID("set-default-filters-button").should('exist').click({ force: true }) }, clearAllFilters: () => { cy.byTestID("clear-all-filters-button").should('exist').click() @@ -73,7 +74,7 @@ export const netflowPage = { selectSourceNS: (project: string) => { // verify Source namespace filter cy.get(filterSelectors.filterInput).type("src_namespace=" + project + '{enter}') - cy.get('#src_namespace-0-toggle > span.pf-v6-c-menu-toggle__text').should('contain.text', `${project}`) + cy.get('#src_namespace-0-toggle').should('contain.text', `${project}`) } } @@ -92,6 +93,9 @@ export const topologyPage = { isViewRendered: () => { cy.get('[data-surface="true"]').should('exist') }, + getOwnerNode: (resourceType: string, resourceName: string, timeout: number = 60000) => { + return cy.get(`g[data-id*="o=${resourceType}.${resourceName}"]`, { timeout }) + }, selectGroupWithSlider: (group: Group) => { let selector switch (group) { @@ -109,7 +113,7 @@ export const topologyPage = { break } cy.get('#lastRefresh').invoke('text').then((lastRefresh) => { - cy.get(`${selector} > div:nth-child(2) > button`).click().then(slider => { + cy.get(`${selector} button`).click().then(slider => { netflowPage.waitForLokiQuery() cy.wait(3000) cy.get('#lastRefresh').invoke('text').should('not.eq', lastRefresh) @@ -126,7 +130,7 @@ export const topologyPage = { export function setupTopologyViewWithNamespaceFilter(namespace?: string) { cy.clearLocalStorage() netflowPage.visit() - cy.get('#tabs-container li:nth-child(3)').click() + cy.get('#tabs-container').contains('Topology').click() if (Cypress.$('[data-surface=true][transform="translate(0, 0) scale(1)]').length > 0) { cy.get('[data-test="filters"] > [data-test="clear-all-filters-button"]').should('exist').click() @@ -136,7 +140,7 @@ export function setupTopologyViewWithNamespaceFilter(namespace?: string) { // Add filter for namespace if provided if (namespace) { cy.get(filterSelectors.filterInput).type("src_namespace=" + namespace + '{enter}') - cy.get('#src_namespace-0-toggle > span.pf-v6-c-menu-toggle__text').should('contain.text', `${namespace}`) + cy.get('#src_namespace-0-toggle').should('contain.text', `${namespace}`) } cy.byTestID("show-view-options-button").should('exist').click().then(() => { @@ -149,19 +153,18 @@ export function setupTopologyViewWithNamespaceFilter(namespace?: string) { } export namespace pluginSelectors { - export const next = '#wizard-container > div > div > footer > button.pf-v6-c-button.pf-m-primary' - export const back = '#wizard-container > div > div > footer > button.pf-v6-c-button.pf-m-secondary' + export const next = 'footer button[type="submit"]' export const save = '[data-test=save-changes]' - export const del = '#editor-toggle-footer > div > div > div > button.pf-v6-c-button.pf-m-danger' - export const confirmDel = '#delete-modal > div > div.modal-footer > div > button.pf-v6-c-button.pf-m-danger' + export const del = '[data-test-id=delete-resource-button]' + export const confirmDel = '[data-test="confirm-action"]' export const openNetworkTraffic = '#open-network-traffic' export const editFlowcollector = '#edit-flow-collector' - export const update = '#editor-toggle-footer > div > div > div > button.pf-v6-c-button.pf-m-primary' - export const privilegedToggle = '#root_spec_agent_ebpf_privileged_field > div > div.pf-m-flex-4 > label > span.pf-v6-c-switch__toggle' + export const update = '[data-test-id=update-resource-button]' + export const privilegedToggle = '[data-test="root_spec_agent_ebpf_privileged"]' export const packetDropEnable = '[data-test-id=root_spec_agent_ebpf_features-PacketDrop]' export const lokiMode = '#root_spec_loki_mode-toggle' export const monolithicMode = '#root_spec_loki_mode-Monolithic' - export const installDemoLoki = '#root_spec_loki_monolithic_installDemoLoki_field > .pf-v6-l-flex > .pf-m-flex-4 > .pf-v6-c-switch > .pf-v6-c-switch__toggle' + export const installDemoLoki = '[data-test="root_spec_loki_monolithic_installDemoLoki"]' } export namespace genSelectors { @@ -170,45 +173,47 @@ export namespace genSelectors { export const refreshBtn = 'refresh-button' export const moreOpts = 'more-options-button' export const fullScreen = '[data-test=fullscreen-button]' - export const expand = '[index="2"] > ul > li > .pf-c-dropdown__menu-item' } +// Helper function to generate table header column selectors +const thCol = (columnId: string): string => `[data-test=th-${columnId}] button`; + export namespace colSelectors { - export const columnsModal = '.modal-content' + export const columnsModal = '#columns-modal' export const save = 'columns-save-button' export const resetDefault = 'columns-reset-button' - export const mac = '[data-test=th-Mac] > .pf-v6-c-table__button' - export const k8sOwner = '[data-test=th-K8S_OwnerObject] > .pf-v6-c-table__button' - export const ipPort = '[data-test=th-AddrPort] > .pf-v6-c-table__button' - export const protocol = '[data-test=th-Proto] > .pf-v6-c-table__button' - export const icmpType = '[data-test=th-IcmpType] > .pf-v6-c-table__button' - export const icmpCode = '[data-test=th-IcmpCode] > .pf-v6-c-table__button' - export const srcNodeIP = '[data-test=th-SrcK8S_HostIP] > .pf-v6-c-table__button' - export const srcNS = '[data-test=th-SrcK8S_Namespace] > .pf-v6-c-table__button' - export const dstNodeIP = '[data-test=th-DstK8S_HostIP] > .pf-v6-c-table__button' - export const direction = '[data-test=th-FlowDirection] > .pf-v6-c-table__button' - export const bytes = '[data-test=th-Bytes] > .pf-v6-c-table__button' - export const packets = '[data-test=th-Packets] > .pf-v6-c-table__button' - export const recordType = '[data-test=th-RecordType] > .pf-v6-c-table__button' - export const conversationID = '[data-test=th-_HashId] > .pf-v6-c-table__button' - export const flowRTT = '[data-test=th-TimeFlowRttMs] > .pf-v6-c-table__button' - export const dscp = '[data-test=th-Dscp] > .pf-v6-c-table__button' - export const dnsLatency = '[data-test=th-DNSLatency] > .pf-v6-c-table__column-help > .pf-v6-c-table__button' - export const dnsResponseCode = '[data-test=th-DNSResponseCode] > .pf-v6-c-table__column-help > .pf-v6-c-table__button' - export const dnsId = '[data-test=th-DNSId] > .pf-v6-c-table__button' - export const dnsError = '[data-test=th-DNSErrNo] > .pf-v6-c-table__button' - export const dnsName = '[data-test=th-DNSName]' - export const srcZone = '[data-test=th-SrcZone] > .pf-v6-c-table__button' - export const dstZone = '[data-test=th-DstZone] > .pf-v6-c-table__button' - export const clusterName = '[data-test=th-ClusterName] > .pf-v6-c-table__button' - export const srcNetworkName = '[data-test=th-SrcNetworkName] > .pf-v6-c-table__button' - export const dstNetworkName = '[data-test=th-DstNetworkName] > .pf-v6-c-table__button' + export const mac = thCol('Mac') + export const k8sOwner = thCol('K8S_OwnerObject') + export const ipPort = thCol('AddrPort') + export const protocol = thCol('Proto') + export const icmpType = thCol('IcmpType') + export const icmpCode = thCol('IcmpCode') + export const srcNodeIP = thCol('SrcK8S_HostIP') + export const srcNS = thCol('SrcK8S_Namespace') + export const dstNodeIP = thCol('DstK8S_HostIP') + export const direction = thCol('FlowDirection') + export const bytes = thCol('Bytes') + export const packets = thCol('Packets') + export const recordType = thCol('RecordType') + export const conversationID = thCol('_HashId') + export const flowRTT = thCol('TimeFlowRttMs') + export const dscp = thCol('Dscp') + export const dnsLatency = thCol('DNSLatency') + export const dnsResponseCode = thCol('DNSResponseCode') + export const dnsId = thCol('DNSId') + export const dnsError = thCol('DNSErrNo') + export const dnsName = thCol('DNSName') + export const srcZone = thCol('SrcZone') + export const dstZone = thCol('DstZone') + export const clusterName = thCol('ClusterName') + export const srcNetworkName = thCol('SrcNetworkName') + export const dstNetworkName = thCol('DstNetworkName') } export namespace filterSelectors { export const filterNames = "#filters p" export const filterInput = '#filter-search-input input' - export const filterDropdown = '#filter-search-input [type="button"]' + export const filterDropdown = '[aria-label="Open advanced search"]' export const columnFilter = '#column-filter-toggle' export const destinationRadio = 'radio-destination' export const sourceRadio = '#radio-source' @@ -227,30 +232,35 @@ export namespace querySumSelectors { export const droppedBytesCount = "#pktDropBytesCount" export const droppedBpsCount = "#pktDropBytesPerSecondsCount" export const droppedPacketsCount = "#pktDropPacketsCount" - export const expandedQuerySummaryPanel = '.pf-c-drawer__panel-main' } +// Helper functions for topology selectors +const topoLayer = (layerId: string): string => `[data-layer-id="${layerId}"]`; +const topoSvg = (attr: 'type' | 'kind', value: string, modifier: string = ''): string => + `g[data-${attr}="${value}"]${modifier}`; +const topoToggle = (toggleId: string): string => `#${toggleId}-switch`; + export namespace topologySelectors { export const metricsFunctionDrop = 'metricFunction-dropdown' export const metricsFunction = '#metricFunction' export const metricTypeDrop = 'metricType-dropdown' export const metricType = '#metricType' - export const optsClose = '.pf-v6-c-drawer__close > .pf-v6-c-button' - export const nGroups = '[data-layer-id="groups"] > g' - export const group = 'g[data-type="group"]' - export const node = 'g[data-kind="node"]:empty' - export const edge = 'g[data-kind="edge"]' - export const groupLayer = '[data-layer-id="groups"]' - export const defaultLayer = '[data-layer-id="default"]' - export const groupToggle = '[for="group-collapsed-switch"] > .pf-v6-c-switch__toggle' - export const edgeToggle = "#edges-switch" - export const labelToggle = '#edges-tag-switch' - export const badgeToggle = '#badge-switch' - export const emptyToggle = '#empty-switch' + export const optsClose = '[aria-label="Close drawer panel"]' + export const nGroups = topoLayer('groups') + ' > g' + export const group = topoSvg('type', 'group') + export const node = topoSvg('kind', 'node', ':empty') + export const edge = topoSvg('kind', 'edge') + export const groupLayer = topoLayer('groups') + export const defaultLayer = topoLayer('default') + export const groupToggle = topoToggle('group-collapsed') + export const edgeToggle = topoToggle('edges') + export const labelToggle = topoToggle('edges-tag') + export const badgeToggle = topoToggle('badge') + export const emptyToggle = topoToggle('empty') } export namespace overviewSelectors { - export const panelsModal = '.modal-content' + export const panelsModal = '#overview-panels-modal' export const resetDefault = 'panels-reset-button' export const save = 'panels-save-button' export const cancel = 'panels-cancel-button' @@ -284,15 +294,13 @@ export const memoryUsage = { } export namespace histogramSelectors { - export const timeRangeContainer = "#chart-histogram > div.pf-v6-l-flex.pf-m-row.histogram-range-container" - export const zoomin = timeRangeContainer + " > div:nth-child(5) > div > div:nth-child(2) > div > button" - export const zoomout = timeRangeContainer + "> div:nth-child(5) > div > div:nth-child(1) > div > button" - const forwardShift = timeRangeContainer + "> div:nth-child(4)" - export const singleRightShift = forwardShift + "> div:nth-child(1) > button" - export const doubleRightShift = forwardShift + "> div:nth-child(2) > button" - const backwardShift = timeRangeContainer + "> div:nth-child(2)" - export const singleLeftShift = backwardShift + "> div:nth-child(2) > button" - export const doubleLeftShift = backwardShift + "> div:nth-child(1) > button" + export const timeRangeContainer = "#chart-histogram .histogram-range-container" + export const zoomin = '[data-test="histogram-zoom-in"]' + export const zoomout = '[data-test="histogram-zoom-out"]' + export const singleRightShift = '[data-test="histogram-single-right"]' + export const doubleRightShift = '[data-test="histogram-double-right"]' + export const singleLeftShift = '[data-test="histogram-single-left"]' + export const doubleLeftShift = '[data-test="histogram-double-left"]' } Cypress.Commands.add('checkPanelsNum', (panels = 2) => { @@ -308,45 +316,25 @@ Cypress.Commands.add('checkPanel', (panelName) => { Cypress.Commands.add('checkPopItems', (id, names) => { for (let i = 0; i < names.length; i++) { - cy.get(id).contains(names[i]) - .closest('.pf-v6-c-data-list__item-row').find('.pf-v6-c-data-list__check'); + cy.get(id).contains('label', names[i]).should('exist'); } }); Cypress.Commands.add('openPanelsModal', () => { cy.showAdvancedOptions(); cy.get('#manage-overview-panels-button').click(); - cy.get('#overview-panels-modal').should('exist'); + cy.get('#overview-panel-management').should('exist'); }); Cypress.Commands.add('openColumnsModal', () => { cy.showAdvancedOptions(); cy.get('#manage-columns-button').click(); - cy.get('#columns-modal').should('exist'); + cy.get('#table-column-management').should('exist'); }); Cypress.Commands.add('checkQuerySummary', (metric) => { - let warningExists = false - let num = 0 - let metricStr: string - - cy.get(querySumSelectors.queryStatsPanel).should('exist').then(() => { - if (Cypress.$(querySumSelectors.queryStatsPanel + ' svg.query-summary-warning').length > 0) { - warningExists = true - } - }) - - if (warningExists) { - metricStr = metric.text().split('+ ')[0] - if (metricStr.includes('k')) { - num = Number(metricStr.split('k')[0]) - } else { - num = Number(metricStr) - } - } else { - num = Number(metric.text().split(' ')[0]) - } - + // parseFloat handles formats: "123 ms", "123+ ms", "1.5k ms", "1.5k+ ms" + const num = parseFloat(metric.text()) expect(num).to.be.greaterThan(0) }); diff --git a/web/cypress/views/netobserv-logo.ts b/web/cypress/views/netobserv-logo.ts new file mode 100644 index 000000000..ef7beeceb --- /dev/null +++ b/web/cypress/views/netobserv-logo.ts @@ -0,0 +1,21 @@ +/** + * Helper function to verify resource SVG icon exists in NetObserv topology view + * Note: As of PF6 migration, icons use PatternFly components (e.g., GlobeRouteIcon for Gateway) + * which may not use inline SVG paths, so we just verify the SVG element exists. + * + * @param resourceType - Kubernetes resource type (e.g., 'Gateway', 'Service') + * @param resourceName - Name of the resource instance + * @param timeout - Timeout in milliseconds (default: 60000) + */ +export function verifyResourceSVGLogo(resourceType: string, resourceName: string, timeout: number = 60000) { + cy.get(`g[data-id*="o=${resourceType}.${resourceName}"]`, { timeout }).then($nodes => { + const nodesWithSvg = $nodes.filter((i, el) => { + return Cypress.$(el).find('svg').length > 0 + }) + + cy.wrap(nodesWithSvg).should('have.length.greaterThan', 0) + cy.wrap(nodesWithSvg).first().should('be.visible').within(() => { + cy.get('svg').should('exist').and('be.visible') + }) + }) +} diff --git a/web/cypress/views/netobserv.ts b/web/cypress/views/netobserv.ts index 19d2368e9..e66be5432 100644 --- a/web/cypress/views/netobserv.ts +++ b/web/cypress/views/netobserv.ts @@ -123,16 +123,20 @@ export const Operator = { cy.visit('k8s/ns/openshift-netobserv-operator/operators.coreos.com~v1alpha1~ClusterServiceVersion') const selector = '[data-test-operator-row="' + Operator.name() + '"]' cy.get(selector).invoke('attr', 'href').then(href => { - cy.visit(href) + if (href) { + cy.visit(href) + } }) cy.contains('Flow Collector').invoke('attr', 'href').then(href => { - cy.visit(href) + if (href) { + cy.visit(href) + } }) }, createFlowcollector: (parameters?: FlowCollectorParameter) => { Operator.visitFlowcollector() - cy.get('div.loading-box__loaded:nth-child(2)').should('exist') + cy.get('div.loading-box__loaded').should('exist') cy.wait(3000) cy.get("#yaml-create").should('exist').then(() => { if ((Cypress.$('td[role="gridcell"]').length > 0) && (parameters != null)) { @@ -142,7 +146,7 @@ export const Operator = { } }) // don't create flowcollector if already exists - cy.get('div.loading-box__loaded:nth-child(2)', { timeout: 60000 }).should('be.visible').then(() => { + cy.get('div.loading-box__loaded', { timeout: 60000 }).should('be.visible').then(() => { if (Cypress.$('td[role="gridcell"]').length == 0) { cy.log("Deploying flowcollector") switch (parameters) { @@ -193,17 +197,13 @@ export const Operator = { // cy.reload(true) cy.intercept('**/copy-login-commands*').as('reload') // wait for all window refresh - cy.wait('@reload', { timeout: 60000 }) + cy.wait('@reload', { timeout: 100000 }) cy.log("Console refreshed successfully") - Operator.visitFlowcollector() - cy.byTestID('status-text').should('exist').should('contain.text', 'Ready') - // Check for loki pod running in netobserv namespace only if Loki is not disabled if (parameters !== "LokiDisabled") { - cy.adminCLI(`oc get pods -n ${project} -l app=loki -o jsonpath="{.items[*].status.phase}"`).then(result => { - const phases = result.stdout.trim().split(' ') - expect(phases).to.include('Running') - }) + cy.adminCLI(`oc wait --for=condition=Ready pod -l app=loki -n ${project} --timeout=180s`) } + Operator.visitFlowcollector() + cy.byTestID('status-text', { timeout: 120000 }).should('exist').should('contain.text', 'Ready') } }) }, @@ -212,7 +212,7 @@ export const Operator = { // Overview tab cy.get(pluginSelectors.next).should('exist').click() // Processing tab - cy.get(pluginSelectors.privilegedToggle).should('exist').click() + cy.get(pluginSelectors.privilegedToggle).should('exist').click({ force: true }) // Enable PacketDrop cy.get(pluginSelectors.packetDropEnable).should('exist').check() cy.get(pluginSelectors.next).should('exist').click() @@ -220,11 +220,10 @@ export const Operator = { cy.get(pluginSelectors.lokiMode).should('exist').click().then(mode => { cy.get(pluginSelectors.monolithicMode).should('exist').click() }) - // Install demoLoki - cy.get(pluginSelectors.installDemoLoki).should('exist').click() - cy.get(pluginSelectors.next).should('exist').click() - // Consumption tab + cy.get(pluginSelectors.installDemoLoki).should('exist').click({ force: true }) cy.get(pluginSelectors.next).should('exist').click() + // Consumption tab - final submit + cy.get('footer').contains('button', 'Submit').should('exist').click() }, deleteFlowCollector: () => { cy.adminCLI(`oc delete flowcollector cluster --ignore-not-found`) @@ -255,7 +254,9 @@ export const Operator = { Cypress.Commands.add('checkStorageClass', (context: Mocha.Context) => { let storageClassCheck = false - cy.adminCLI('oc get sc', { failOnNonZeroExit: false }).then(result => { + const kubeconfig = Cypress.env('KUBECONFIG_PATH'); + expect(kubeconfig, 'KUBECONFIG_PATH').to.be.a('string').and.not.be.empty + cy.exec(`oc get sc --kubeconfig ${kubeconfig}`).then(result => { if (result.stderr.includes('No resources found')) { cy.log('StorageClass not deployed, skipping') storageClassCheck = true diff --git a/web/cypress/views/network-health.ts b/web/cypress/views/network-health.ts index eb69784dd..0bfc97979 100644 --- a/web/cypress/views/network-health.ts +++ b/web/cypress/views/network-health.ts @@ -3,36 +3,31 @@ export namespace networkHealthSelectors { export const node = '[id^="pf-tab-per-node"]' export const namespace = '[id^="pf-tab-per-namespace"]' export const workload = '[id^="pf-tab-per-owner"]' - export const nodeCard = '[id^=health-card-ip]' - export const sidePanel = '.health-gallery-drawer-content' + export const nodeCard = '[data-test^="health-card-"]' + export const sidePanel = '[data-test="health-drawer-content"]' } export const networkHealth = { clickOnAlert: (name: string) => { - // pick the first from the list - cy.get(`label[for^="health-card-selectable-${name}"]`).eq(0).should('be.visible').click() + cy.get(`[data-test^="health-card-${name}"]`, { timeout: 60000 }).eq(0).should('be.visible').find('button').click() }, verifyAlert: (name: string, mode: string = "alert", alertText?: string) => { - // click force since node cards are covered - cy.get(`label[for^="health-card-selectable-${name}"]`).eq(0).should('be.visible').click({ force: true }).then(() => { + cy.get(`[data-test^="health-card-${name}"]`, { timeout: 60000 }).eq(0).should('be.visible').find('button').click({ force: true }).then(() => { cy.get(networkHealthSelectors.sidePanel).should('be.visible') cy.contains(mode).should('exist') if (alertText) { cy.contains(alertText).should('exist') - } - cy.get(`label[for^="health-card-selectable-${name}"]`).eq(0).click() + cy.get(`[data-test^="health-card-${name}"]`).eq(0).find('button').click() cy.get(networkHealthSelectors.sidePanel).should('not.exist') }) }, navigateToAlertPage: (name: string) => { networkHealth.clickOnAlert(name) cy.get(networkHealthSelectors.sidePanel).should('be.visible').then(() => { - // click the kebab button - cy.get('div.rule-details-row:nth-child(1) button').click().then(() => { - cy.get('button[role="menuitem"]').eq(1).click().then(() => { - // "No Alert found" should not show up. + cy.get('button[aria-label="Kebab toggle"]').first().click().then(() => { + cy.contains('Inspect alert').click().then(() => { cy.byTestID('empty-box').should('not.exist') }) }) @@ -41,10 +36,8 @@ export const networkHealth = { navigateToNetflowTrafficPage: (name: string) => { networkHealth.clickOnAlert(name) cy.get(networkHealthSelectors.sidePanel).should('be.visible').then(() => { - // click the kebab button - - cy.get('div.rule-details-row:nth-child(1) button').click().then(() => { - cy.get('button[role="menuitem"]').eq(1).click().then(() => { + cy.get('button[aria-label="Kebab toggle"]').first().click().then(() => { + cy.contains('Inspect network traffic').click().then(() => { }) }) diff --git a/web/cypress/views/operator-hub-page.ts b/web/cypress/views/operator-hub-page.ts index 3d9768503..98bc5d7ac 100644 --- a/web/cypress/views/operator-hub-page.ts +++ b/web/cypress/views/operator-hub-page.ts @@ -44,7 +44,7 @@ export const installedOperators = { export const operatorHubPage = { getAllTileLabels: () => { - return cy.get('.pf-v6-c-badge') + return cy.get('.co-catalog-tile').find('[role="status"]') }, checkCustomCatalog: (name: string) => { sourceActions(name, 'view'); @@ -196,7 +196,7 @@ export const operatorHubPage = { export const operatorHubModal = { clickInstall: () => { - cy.get('[data-test-id="operator-install-btn"]').click({ force: true }); + cy.byLegacyTestID('operator-install-btn').click({ force: true }); }, selectChannel: (channel) => { cy.get('h5').contains('Channel').parent('div').within(() => { diff --git a/web/cypress/views/pages.ts b/web/cypress/views/pages.ts index 35a6e0485..8e525481b 100644 --- a/web/cypress/views/pages.ts +++ b/web/cypress/views/pages.ts @@ -15,7 +15,7 @@ export const Pages = { cy.switchPerspective('Developer'); cy.visit(`/project-details/ns/${namespace}/access`); guidedTour.close(); - cy.get('a[data-test-id="horizontal-link-Project access"]').should('be.visible'); + cy.byLegacyTestID('horizontal-link-Project access').should('be.visible'); }, gotoPVCCreationPage: (namespace: string) => { cy.visit(`/k8s/ns/${namespace}/core~v1~PersistentVolumeClaim`); @@ -90,7 +90,7 @@ export const Pages = { }, gotoClusterDetailspage: () => { cy.visit('settings/cluster'); - cy.get('[data-test-id="horizontal-link-Details"]').should('be.visible'); + cy.byLegacyTestID('horizontal-link-Details').should('be.visible'); }, gotoCRDsList: () => { cy.visit('/k8s/cluster/apiextensions.k8s.io~v1~CustomResourceDefinition'); @@ -98,7 +98,7 @@ export const Pages = { }, gotoOneCRDDetailsPage: (crdname) => { cy.visit(`/k8s/cluster/customresourcedefinitions/${crdname}`); - cy.get('[data-test-id="horizontal-link-Details"]').should('be.visible'); + cy.byLegacyTestID('horizontal-link-Details').should('be.visible'); }, gotoOneNetworkPolicyDetails: (namespace: string, npname: string) => { cy.visit(`/k8s/ns/${namespace}/networkpolicies/${npname}`); @@ -120,7 +120,7 @@ export const Pages = { }, gotoNodeOverviewPage: (nodeName: string) => { cy.visit(`/k8s/cluster/nodes/${nodeName}/`); - cy.get('[data-test-id="dashboard"]').should('be.visible'); + cy.byLegacyTestID('dashboard').should('be.visible'); }, gotoMCPListPage: () => { cy.visit("/k8s/cluster/machineconfiguration.openshift.io~v1~MachineConfigPool"); @@ -130,7 +130,7 @@ export const Pages = { }, gotoMachineConfigDetailsPage: (mc_name: string) => { cy.visit(`/k8s/cluster/machineconfiguration.openshift.io~v1~MachineConfig/${mc_name}`); - cy.get('[data-test-id="resource-summary"]').should('be.visible'); + cy.byLegacyTestID('resource-summary').should('be.visible'); }, gotoCatalogSourcePage: () => { cy.visit('/k8s/ns/openshift-marketplace/operators.coreos.com~v1alpha1~CatalogSource/custom-catalogsource'); diff --git a/web/cypress/views/yaml-editor.ts b/web/cypress/views/yaml-editor.ts index 5d460be35..b92004186 100644 --- a/web/cypress/views/yaml-editor.ts +++ b/web/cypress/views/yaml-editor.ts @@ -20,9 +20,7 @@ export const setEditorContent = (text: string) => { }); }; -// initially yamlEditor loads with all grey text, finished loading when editor is color coded -// class='mtk27' is the light green color of property such as 'apiVersion' -export const isLoaded = () => cy.get("[class='mtk27']").should('exist'); +export const isLoaded = () => cy.get('.monaco-editor .view-lines').should('exist'); // since yaml editor class mtk27 is a font class it doesn't work on an import page with no text // adding a check for the 1st line number, AND providing a wait allowed the load of the full component export const isImportLoaded = () => { @@ -98,8 +96,8 @@ export const verifyFontSizeInEditor = (size: number) => { export const showYAMLSidebar = () => cy.get('[aria-label="Show sidebar"]').click(); export const clickFieldDetailsButton = (fieldName: string) => { cy.contains('h5', `${fieldName}`) - .parents('li') - .find('button.pf-v6-c-button') - .contains('View details') - .click(); + .closest('li') + .within(() => { + cy.contains('button', 'View details').click() + }) };