Skip to content
Open
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
15 changes: 6 additions & 9 deletions cypress/e2e/vulnerability.cy.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { securityPage, KNOWN_VULNERABLE_IMAGE, CLEAN_IMAGE } from '../views/securityPage'
import { common } from '../views/common'
import { common, deviceDetailsFleetRow } from '../views/common'

/**
* Vulnerability Reporting End-to-End Tests
Expand Down Expand Up @@ -135,10 +135,11 @@ describe('Vulnerability Reporting', () => {
.clear()
.type(FLEET_NAME)

// Add device selector label
// Add device selector label. OCP 4.20: button lives inside LabelGroup.
// OCP 4.22: EditableLabelControl renders it as a sibling of LabelGroup.
cy.contains('.pf-v6-c-form__label-text', 'Device selector')
.parents('.pf-v6-c-form__group')
.find('.pf-v6-c-label-group button[aria-label="Add label"]')
.contains('button', 'Add label')
.click()

cy.wait(500)
Expand Down Expand Up @@ -350,9 +351,7 @@ describe('Vulnerability Reporting', () => {
cy.contains('.pf-v6-c-label', fleetLabel).should('exist')

// Verify fleet connection appears on device details (fleet propagation can take up to 30s)
cy.contains('.fctl-device-details-tab__label', 'Fleet name', { timeout: 30000 })
.closest('.pf-v6-l-stack')
.should('contain', FLEET_NAME)
deviceDetailsFleetRow(30000).should('contain', FLEET_NAME)

// Patch device status to set vulnerable image digest directly (like e2e tests).
// Uses cy.task (Node.js binary) instead of cy.exec (bin/flightctl wrapper) because
Expand Down Expand Up @@ -606,9 +605,7 @@ describe('Vulnerability Reporting', () => {
cy.wait(2000)

// Verify fleet disconnected
cy.contains('.fctl-device-details-tab__label', 'Fleet name', { timeout: 10000 })
.closest('.pf-v6-l-stack')
.should('contain', 'None')
deviceDetailsFleetRow(10000).should('contain', 'None')

// Patch device status to clear vulnerable digest (like e2e tests).
cy.task('patchDeviceStatus', {
Expand Down
17 changes: 17 additions & 0 deletions cypress/views/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
* Common utilities for test operations
*/

/**
* Device details "Fleet" field. Newer flightctl-ui (OCP 4.22) uses a DescriptionList
* term labeled "Fleet"; OCP 4.20 still uses `.fctl-device-details-tab__label` + "Fleet name".
* Returns a chainable scoped to the field's parent row.
*/
export const deviceDetailsFleetRow = (timeout = 60000) => {
const termOrLegacyLabel = [
'dt.pf-v6-c-description-list__term',
'.fctl-device-details-tab__label',
].join(', ')
const row = [
'.pf-v6-c-description-list__group',
'.pf-v6-l-stack',
].join(', ')
return cy.contains(termOrLegacyLabel, /^(Fleet name|Fleet)$/, { timeout }).closest(row)
}

/** True after org selection was handled or confirmed absent — only run once per spec (first navigateTo). */
let organizationSelectionHandled = false

Expand Down
47 changes: 23 additions & 24 deletions cypress/views/devicesPage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { common } from './common'
import { common, deviceDetailsFleetRow } from './common'

/** Default table row index for flows that assume a single primary device row */
const ROW_0 = 0
Expand All @@ -17,8 +17,9 @@ const DEVICE_EVENTS_NORMAL = [
/** Events list body on device details — Events tab */
const EVENTS_CONTAINER = '[data-testid="device-events-list"]'

/** Device details → Applications table (standalone UI expandable VM/apps table) */
const DEVICE_APPLICATIONS_TABLE = '#fctl-applications-table'
/** Device details → Applications table (id on 4.20; 4.22 also has the card + aria-label). */
const DEVICE_APPLICATIONS_TABLE =
'#fctl-applications-table, #device-applications-card table, table[aria-label="Applications"]'

/** Device details → Terminal tab → VM serial console (Open console) */
const APP_CONSOLE_TERMINAL = '[data-testid="app-console-terminal"]'
Expand Down Expand Up @@ -628,14 +629,14 @@ export const devicesPage = {
.click({ force: true })
cy.get('[data-testid="device-details-title"]', { timeout: 120000 }).should('be.visible')
cy.get('[data-testid="device-details-tab-details"]').should('be.visible')
cy.contains('.fctl-device-details-tab__label', 'Fleet name', { timeout: 60000 })
.closest('.pf-v6-l-stack')
.find('.fctl-resource-link__text', { timeout: 60000 })
.invoke('text')
.as('expectedFleetName')
cy.contains('.fctl-device-details-tab__label', 'Fleet name')
.closest('.pf-v6-l-stack')
.should('not.contain', 'None')
deviceDetailsFleetRow(60000).then(($row) => {
const linkEl = $row.find('.fctl-resource-link__text, a').first()
const fleet = linkEl.length
? linkEl.text().trim()
: $row.text().replace(/Fleet\s*(name)?/i, '').trim()
cy.wrap(fleet).as('expectedFleetName')
})
deviceDetailsFleetRow().should('not.contain', 'None')

cy.get('@expectedFleetName').then((fleetName) => {
const fleet = String(fleetName).trim()
Expand Down Expand Up @@ -666,15 +667,11 @@ export const devicesPage = {
},

expectDeviceDetailsFleetConnected: (fleetName = SCALE_FLEET_NAME) => {
cy.contains('.fctl-device-details-tab__label', 'Fleet name', { timeout: 120000 })
.closest('.pf-v6-l-stack')
.should('contain', fleetName)
deviceDetailsFleetRow(120000).should('contain', fleetName)
},

expectDeviceDetailsFleetDisconnected: () => {
cy.contains('.fctl-device-details-tab__label', 'Fleet name', { timeout: 120000 })
.closest('.pf-v6-l-stack')
.should('contain', 'None')
deviceDetailsFleetRow(120000).should('contain', 'None')
},

removeFleetLabelOnDeviceDetails: (labelText = SCALE_FLEET_LABEL_TEXT) => {
Expand Down Expand Up @@ -713,8 +710,9 @@ export const devicesPage = {

waitForVmAppRunning: (deviceName = 'test-device', appName = 'test-vm', timeoutMs = 600000) => {
cy.get('[data-testid="device-details-title"]', { timeout: 30000 }).should('contain', deviceName)
cy.get(DEVICE_APPLICATIONS_TABLE, { timeout: timeoutMs }).scrollIntoView({ block: 'center' })
cy.get(DEVICE_APPLICATIONS_TABLE, { timeout: timeoutMs }).first().scrollIntoView({ block: 'center' })
cy.get(DEVICE_APPLICATIONS_TABLE)
.first()
.find('td[data-label="Name"]')
.contains(new RegExp(`^${appName}$`), { timeout: timeoutMs })
.parents('tr')
Expand All @@ -726,8 +724,9 @@ export const devicesPage = {
common.navigateTo('Devices')
enrolledDeviceLinkByAlias(deviceName).click()
cy.get('[data-testid="device-details-title"]').should('contain', deviceName)
cy.get(DEVICE_APPLICATIONS_TABLE).scrollIntoView({ block: 'center' })
cy.get(DEVICE_APPLICATIONS_TABLE).first().scrollIntoView({ block: 'center' })
cy.get(DEVICE_APPLICATIONS_TABLE)
.first()
.find('td[data-label="Name"]')
.contains(new RegExp(`^${appName}$`))
.parents('tr')
Expand Down Expand Up @@ -767,20 +766,20 @@ export const devicesPage = {
},

loginVmSerialConsole: (user = 'fedora', password = 'fedora') => {
cy.get(APP_CONSOLE_XTERM_INPUT, { timeout: 30000 }).should('exist').click({ force: true }).type('{enter}', { force: true })
cy.get(APP_CONSOLE_XTERM_INPUT, { timeout: 30000 }).first().should('exist').click({ force: true }).type('{enter}', { force: true })
cy.get(APP_CONSOLE_XTERM_ROWS, { timeout: 120000 }).should(($el) => {
expect($el.text()).to.match(/login:/i)
})
cy.get(APP_CONSOLE_XTERM_INPUT).click({ force: true }).type(`${user}{enter}`, { force: true, delay: 50 })
cy.get(APP_CONSOLE_XTERM_INPUT).first().click({ force: true }).type(`${user}{enter}`, { force: true, delay: 50 })
cy.get(APP_CONSOLE_XTERM_ROWS, { timeout: 30000 }).should(($el) => {
expect($el.text()).to.match(/Password:/i)
})
cy.get(APP_CONSOLE_XTERM_INPUT).type(`${password}{enter}`, { force: true, delay: 50, log: false })
cy.get(APP_CONSOLE_XTERM_INPUT).type('{enter}', { force: true })
cy.get(APP_CONSOLE_XTERM_INPUT).first().type(`${password}{enter}`, { force: true, delay: 50, log: false })
cy.get(APP_CONSOLE_XTERM_INPUT).first().type('{enter}', { force: true })
cy.get(APP_CONSOLE_XTERM_ROWS, { timeout: 60000 }).should(($el) => {
expect($el.text()).to.match(new RegExp(`${user}@`))
})
cy.get(APP_CONSOLE_XTERM_INPUT).type('whoami{enter}', { force: true, delay: 50 })
cy.get(APP_CONSOLE_XTERM_INPUT).first().type('whoami{enter}', { force: true, delay: 50 })
cy.get(APP_CONSOLE_XTERM_ROWS, { timeout: 30000 }).should(($el) => {
expect($el.text()).to.match(new RegExp(`whoami[\\s\\S]*${user}`))
})
Expand Down
13 changes: 8 additions & 5 deletions cypress/views/fleetsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,11 @@ export const fleetsPage = {
cy.get('[data-testid="rich-validation-field-name"]').should('be.visible')
cy.get('[data-testid="rich-validation-field-name"]').type(fleetname)
cy.get('[data-testid="rich-validation-field-name"]').should('have.value', 'test-fleet')
cy.get('.pf-v6-l-stack__item > .pf-v6-c-label-group > .pf-v6-c-label-group__main > .pf-v6-c-label-group__list').click()
cy.get('[data-testid="wizard-next-button"]').click()
// Blur the name field so Formik validates. OCP 4.20 clicked the LabelGroup list
// (removed in newer PF / EditableLabelControl). Do not add a selector label here —
// createFleet historically submitted with an empty device selector.
cy.get('[data-testid="rich-validation-field-name"]').blur()
cy.get('[data-testid="wizard-next-button"]').should('not.be.disabled').click()
cy.get('[data-testid="textfield-osSpec"]').should('be.visible')
cy.get('[data-testid="textfield-osSpec"]').type(img)
cy.get('[data-testid="textfield-osSpec"]').should('have.value', img)
Expand All @@ -96,7 +99,7 @@ export const fleetsPage = {

cy.get(`[data-testid="fleet-row-actions-${fleetname}"] .pf-v6-c-menu-toggle`).should('be.visible').click()
cy.contains('.pf-v6-c-menu__item-text', 'Edit fleet configurations').should('be.visible').click()
cy.get(':nth-child(1) > .pf-v6-c-form__group-label > .pf-v6-c-form__label > .pf-v6-c-form__label-text').should('contain', 'Fleet name')
cy.get('.pf-v6-c-form__label-text').first().should('contain', 'Fleet name')
cy.get('[data-testid="wizard-next-button"]').click()
cy.get('[data-testid="textfield-osSpec"]').should('be.visible')
cy.get('[data-testid="textfield-osSpec"]').clear()
Expand All @@ -114,8 +117,8 @@ export const fleetsPage = {
deleteFleet: (fleetname = Cypress.env('fleetname')) => {
common.navigateTo('Fleets')

cy.get('[data-label="Name"]').contains(fleetname)
cy.get('.pf-v6-c-table__tbody > .pf-v6-c-table__tr > .pf-v6-c-table__check > label > input').click()
cy.contains('td', fleetname, { timeout: 10000 }).should('be.visible')
cy.contains('td', fleetname).closest('tr').find('input[type="checkbox"]').click()
cy.get('[data-testid="toolbar-delete-fleets"]').should('be.visible')
cy.get('[data-testid="toolbar-delete-fleets"]').click()
cy.get('[data-testid="modal-delete-fleets-confirm"]').should('be.visible')
Expand Down
Loading