-
Notifications
You must be signed in to change notification settings - Fork 702
CONSOLE-5299: migrate dev-console Cypress tests to Playwright (batch 1) #16475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Cragsmann
wants to merge
1
commit into
openshift:main
Choose a base branch
from
Cragsmann:CONSOLE-5299
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import type { Page } from '@playwright/test'; | ||
|
|
||
| import BasePage from '../base-page'; | ||
|
|
||
| export class ConfigMapPage extends BasePage { | ||
| private readonly createButton = this.page.locator('[data-test="item-create"]'); | ||
| private readonly nameInput = this.page.getByTestId('configmap-name'); | ||
| private readonly initialKeyInput = this.page.getByTestId('key-0'); | ||
| private readonly secondKeyInput = this.page.getByTestId('key-1'); | ||
| private readonly valueTextarea = this.page.locator('[data-test-id="file-input-textarea"]'); | ||
| private readonly addKeyValueButton = this.page.getByTestId('add-key-value-button'); | ||
| private readonly submitButton = this.page.locator('[data-test-id="submit-button"]'); | ||
| private readonly dataSectionBody = this.page.locator('.co-m-pane__body'); | ||
|
|
||
| constructor(page: Page) { | ||
| super(page); | ||
| } | ||
|
|
||
| async clickCreate(): Promise<void> { | ||
| await this.robustClick(this.createButton); | ||
| } | ||
|
|
||
| async fillName(name: string): Promise<void> { | ||
| await this.nameInput.fill(name); | ||
| } | ||
|
|
||
| async fillKey(key: string): Promise<void> { | ||
| await this.initialKeyInput.scrollIntoViewIfNeeded(); | ||
| await this.initialKeyInput.fill(key); | ||
| } | ||
|
|
||
| async fillValue(value: string): Promise<void> { | ||
| await this.valueTextarea.scrollIntoViewIfNeeded(); | ||
| await this.valueTextarea.fill(value); | ||
| } | ||
|
|
||
| async submitForm(): Promise<void> { | ||
| await this.robustClick(this.submitButton); | ||
| } | ||
|
|
||
| async addKeyValue(): Promise<void> { | ||
| await this.robustClick(this.addKeyValueButton.first()); | ||
| } | ||
|
|
||
| async fillSecondKey(key: string): Promise<void> { | ||
| await this.secondKeyInput.scrollIntoViewIfNeeded(); | ||
| await this.secondKeyInput.fill(key); | ||
| } | ||
|
|
||
| async createConfigMap(name: string, key = 'test-key', value = 'test-value'): Promise<void> { | ||
| await this.clickCreate(); | ||
| await this.fillName(name); | ||
| await this.fillKey(key); | ||
| await this.fillValue(value); | ||
| await this.submitForm(); | ||
| } | ||
|
|
||
| async expectDataSectionToContain(text: string): Promise<void> { | ||
| const { expect } = await import('@playwright/test'); | ||
| await expect(this.dataSectionBody).toContainText(text); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| import type { Page } from '@playwright/test'; | ||
| import { expect } from '@playwright/test'; | ||
|
|
||
| import BasePage from '../base-page'; | ||
|
|
||
| export class CustomizationPage extends BasePage { | ||
| private readonly successAlert = this.page.locator('[aria-label="Success Alert"]'); | ||
| private readonly clusterLink = this.page.getByTestId('cluster'); | ||
| private readonly customizeAction = this.page.locator('[data-test-action="Customize"]'); | ||
| private readonly actionsMenuButton = this.page.locator('[data-test-id="actions-menu-button"]'); | ||
|
|
||
| constructor(page: Page) { | ||
| super(page); | ||
| } | ||
|
|
||
| async navigateToConsoles(): Promise<void> { | ||
| await this.goTo('/search?kind=console.operator.openshift.io~v1~Console'); | ||
| await this.waitForLoadingComplete(); | ||
| } | ||
|
|
||
| async clickCluster(): Promise<void> { | ||
| await this.robustClick(this.clusterLink); | ||
| } | ||
|
|
||
| async openCustomization(): Promise<void> { | ||
| await this.robustClick(this.actionsMenuButton); | ||
| await this.robustClick(this.customizeAction); | ||
| await this.waitForLoadingComplete(); | ||
| } | ||
|
|
||
| async clickDeveloperTab(): Promise<void> { | ||
| const tab = this.page.locator('[role="presentation"]').filter({ hasText: 'Developer' }); | ||
| await this.robustClick(tab); | ||
| } | ||
|
|
||
| async disableAllSoftwareCatalogItems(): Promise<void> { | ||
| const formSection = this.page.getByTestId('catalog-types form-section'); | ||
| const removeAll = formSection.locator('[aria-label="Remove all"]'); | ||
| await removeAll.scrollIntoViewIfNeeded(); | ||
| if (await removeAll.isEnabled()) { | ||
| await removeAll.click(); | ||
| } | ||
| const addAll = formSection.locator('[aria-label="Add all"]'); | ||
| await addAll.scrollIntoViewIfNeeded(); | ||
| await addAll.click(); | ||
| } | ||
|
|
||
| async disableSoftwareCatalogItem(itemName: string): Promise<void> { | ||
| const formSection = this.page.getByTestId('catalog-types form-section'); | ||
| await formSection.locator('[aria-label="Remove all"]').click(); | ||
| await formSection.locator('[aria-label="Available search input"]').fill(itemName); | ||
| const option = formSection.locator('[role="option"]').filter({ hasText: itemName }); | ||
| await option.scrollIntoViewIfNeeded(); | ||
| await option.click(); | ||
| await formSection.locator('[aria-label="Add selected"]').scrollIntoViewIfNeeded(); | ||
| await formSection.locator('[aria-label="Add selected"]').click(); | ||
| } | ||
|
|
||
| async enableOnlySoftwareCatalogItem(itemName: string): Promise<void> { | ||
| const formSection = this.page.getByTestId('catalog-types form-section'); | ||
| await formSection.locator('[aria-label="Add all"]').click(); | ||
| await formSection.locator('[aria-label="Chosen search input"]').fill(itemName); | ||
| const option = formSection.locator('[role="option"]').filter({ hasText: itemName }); | ||
| await option.click(); | ||
| await formSection.locator('[aria-label="Remove selected"]').click(); | ||
| } | ||
|
|
||
| async disableAllAddPageItems(): Promise<void> { | ||
| const formSection = this.page.getByTestId('add-page form-section'); | ||
| await formSection.locator('[aria-label="Add all"]').scrollIntoViewIfNeeded(); | ||
| await formSection.locator('[aria-label="Add all"]').click(); | ||
| } | ||
|
|
||
| async disableAddPageItem(itemName: string): Promise<void> { | ||
| const formSection = this.page.getByTestId('add-page form-section'); | ||
| await formSection.locator('[aria-label="Remove all"]').click(); | ||
| await formSection.locator('[aria-label="Available search input"]').fill(itemName); | ||
| const option = formSection.locator('[role="option"]').filter({ hasText: itemName }); | ||
| await option.scrollIntoViewIfNeeded(); | ||
| await option.click(); | ||
| await formSection.locator('[aria-label="Add selected"]').scrollIntoViewIfNeeded(); | ||
| await formSection.locator('[aria-label="Add selected"]').click(); | ||
| } | ||
|
|
||
| async expectSaveMessage(): Promise<void> { | ||
| await this.successAlert.scrollIntoViewIfNeeded(); | ||
| await expect(this.successAlert).toBeVisible(); | ||
| } | ||
|
|
||
| async expectPinnedResourceSection(): Promise<void> { | ||
| await expect(this.page.getByTestId('pinned-resource form-section')).toBeVisible(); | ||
| } | ||
|
|
||
| async expectPerspectiveDropdownValue(value: string): Promise<void> { | ||
| const perspectiveGroup = this.page.locator('[data-test="perspectives form-group"]').nth(1); | ||
| await expect(perspectiveGroup.locator('button[class*="menu-toggle"]')).toContainText(value); | ||
| } | ||
|
|
||
| async selectPerspectiveState(state: string): Promise<void> { | ||
| const perspectiveGroup = this.page.locator('[data-test="perspectives form-group"]').nth(1); | ||
| const toggle = perspectiveGroup | ||
| .locator('button') | ||
| .filter({ hasText: /Disabled|Enabled|AccessReview/ }); | ||
| await this.robustClick(toggle); | ||
| await expect(this.page.locator('[role="listbox"]')).toBeVisible(); | ||
| const option = this.page.locator('button[role="option"]').filter({ hasText: state }); | ||
| await this.robustClick(option); | ||
| } | ||
|
|
||
| async expectSuccessAlert(): Promise<void> { | ||
| await expect(this.page.getByTestId('success-alert')).toBeVisible(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import type { Page } from '@playwright/test'; | ||
| import { expect } from '@playwright/test'; | ||
|
|
||
| import BasePage from '../base-page'; | ||
|
|
||
| export class HealthChecksPage extends BasePage { | ||
| private readonly healthChecksForm = this.page.locator('div.odc-heath-check-probe-form'); | ||
| private readonly successText = this.page.locator('span.odc-heath-check-probe__successText'); | ||
| private readonly checkIcon = this.page.locator('[data-test-id="check-icon"]'); | ||
| private readonly addButton = this.page.locator('[data-test-id="submit-button"]'); | ||
| private readonly saveButton = this.page.locator('[data-test-id="submit-button"]'); | ||
| private readonly typeToggle = this.page.getByTestId('console-select-menu-toggle'); | ||
|
|
||
| constructor(page: Page) { | ||
| super(page); | ||
| } | ||
|
|
||
| async clickAddProbe(probeName: string): Promise<void> { | ||
| const probeButton = this.page.getByRole('button', { name: probeName }); | ||
| await probeButton.scrollIntoViewIfNeeded(); | ||
| await this.robustClick(probeButton); | ||
| await expect(this.healthChecksForm).toBeVisible(); | ||
| } | ||
|
|
||
| async selectProbeType(type: string): Promise<void> { | ||
| await this.robustClick(this.typeToggle); | ||
| const option = this.page.getByTestId('console-select-item').filter({ hasText: type }); | ||
| await this.robustClick(option); | ||
|
|
||
| if (type === 'Container command') { | ||
| const argInput = this.page.locator('[placeholder="argument"]'); | ||
| if ((await argInput.count()) > 0) { | ||
| await argInput.fill('example'); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| async clickCheckIcon(): Promise<void> { | ||
| await this.robustClick(this.checkIcon); | ||
| } | ||
|
|
||
| async clickAddButton(): Promise<void> { | ||
| await this.robustClick(this.addButton); | ||
| } | ||
|
|
||
| async clickSaveButton(): Promise<void> { | ||
| await this.robustClick(this.saveButton); | ||
| } | ||
|
|
||
| async removeProbe(probeName: string): Promise<void> { | ||
| const probeSuccess = this.page.getByRole('button', { name: probeName }); | ||
| const removeIcon = probeSuccess.locator('..').locator('..').locator('[role="img"]'); | ||
| await this.robustClick(removeIcon); | ||
| } | ||
|
|
||
| async expectProbesAdded(count: number): Promise<void> { | ||
| await expect(this.successText).toHaveCount(count); | ||
| } | ||
|
|
||
| async expectProbeAdded(probeName: string): Promise<void> { | ||
| await expect(this.successText.filter({ hasText: probeName })).toBeVisible(); | ||
| } | ||
|
|
||
| async expectEditHealthChecksTitle(): Promise<void> { | ||
| await expect(this.page.locator('[data-test="page-heading"] h1')).toContainText( | ||
| 'Edit health checks', | ||
| ); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| import type { Page } from '@playwright/test'; | ||
|
|
||
| import BasePage from '../base-page'; | ||
|
|
||
| export class PerspectivePage extends BasePage { | ||
| private readonly perspectiveSwitcherToggle = this.page.locator( | ||
| '[data-test-id="perspective-switcher-toggle"]', | ||
| ); | ||
| private readonly perspectiveSwitcherMenu = this.page.locator( | ||
| '[data-test-id="perspective-switcher-menu"]', | ||
| ); | ||
| private readonly sidebar = this.page.locator('#page-sidebar'); | ||
| private readonly projectDropdown = this.page.locator('[data-test-id="namespace-bar-dropdown"]'); | ||
| private readonly projectFilterInput = this.page.locator( | ||
| '[data-test="namespace-dropdown-filter"]', | ||
| ); | ||
| private readonly createProjectButton = this.page.locator( | ||
| '[data-test="namespace-dropdown-create-project"]', | ||
| ); | ||
|
|
||
| constructor(page: Page) { | ||
| super(page); | ||
| } | ||
|
|
||
| private async switchToPerspective(perspectiveName: string): Promise<void> { | ||
| await this.perspectiveSwitcherToggle.waitFor({ state: 'visible', timeout: 60_000 }); | ||
| const currentText = await this.perspectiveSwitcherToggle.textContent(); | ||
| if (currentText?.includes(perspectiveName)) { | ||
| return; | ||
| } | ||
| await this.robustClick(this.perspectiveSwitcherToggle); | ||
| const option = this.perspectiveSwitcherMenu | ||
| .locator('[data-test-id="perspective-switcher-menu-option"]') | ||
| .filter({ hasText: perspectiveName }); | ||
| await this.robustClick(option); | ||
| await this.waitForLoadingComplete(); | ||
| } | ||
|
|
||
| async switchToDeveloper(): Promise<void> { | ||
| await this.switchToPerspective('Developer'); | ||
| } | ||
|
|
||
| async switchToAdministrator(): Promise<void> { | ||
| await this.switchToPerspective('Administrator'); | ||
| // Also handle "Core platform" label (OpenShift 5.0+) | ||
| const currentText = await this.perspectiveSwitcherToggle.textContent(); | ||
| if ( | ||
| !currentText?.includes('Administrator') && | ||
| !currentText?.includes('Core platform') | ||
| ) { | ||
| await this.switchToPerspective('Core platform'); | ||
| } | ||
| } | ||
|
|
||
| async selectOrCreateProject(name: string): Promise<void> { | ||
| await this.robustClick(this.projectDropdown); | ||
| await this.projectFilterInput.fill(name); | ||
|
|
||
| const projectLink = this.page.locator(`[id="${name}-link"]`); | ||
| if ((await projectLink.count()) > 0) { | ||
| await this.robustClick(projectLink); | ||
| } else { | ||
| await this.robustClick(this.createProjectButton); | ||
| await this.page.locator('#input-name').fill(name); | ||
| await this.robustClick(this.page.locator('[data-test="confirm-action"]')); | ||
| } | ||
| await this.waitForLoadingComplete(); | ||
| } | ||
|
|
||
| async navigateToDevMenu(menuItem: string): Promise<void> { | ||
| const menuLink = this.sidebar.locator(`[data-test-id="${menuItem}-header"]`); | ||
| await this.robustClick(menuLink); | ||
| await this.waitForLoadingComplete(); | ||
| } | ||
|
|
||
| async navigateToAdd(): Promise<void> { | ||
| await this.navigateToDevMenu('+Add'); | ||
| } | ||
|
|
||
| async navigateToTopology(): Promise<void> { | ||
| await this.navigateToDevMenu('topology'); | ||
| } | ||
|
|
||
| async navigateToSearch(): Promise<void> { | ||
| await this.navigateToDevMenu('search'); | ||
| } | ||
|
|
||
| async navigateToProject(): Promise<void> { | ||
| await this.navigateToDevMenu('project-details'); | ||
| } | ||
|
|
||
| async navigateToRoutes(): Promise<void> { | ||
| const routesLink = this.sidebar.locator('a[href*="Route"]'); | ||
| await this.robustClick(routesLink); | ||
| await this.waitForLoadingComplete(); | ||
| } | ||
|
|
||
| async navigateToConfigMaps(): Promise<void> { | ||
| const configMapsLink = this.sidebar.locator('a[href*="ConfigMap"]'); | ||
| await this.robustClick(configMapsLink); | ||
| await this.waitForLoadingComplete(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import type { Page } from '@playwright/test'; | ||
| import { expect } from '@playwright/test'; | ||
|
|
||
| import BasePage from '../base-page'; | ||
|
|
||
| export class PodListPage extends BasePage { | ||
| private readonly manageColumnsButton = this.page.locator('button[data-test="manage-columns"]'); | ||
| private readonly createdColumnCheckbox = this.page.locator('input[id="created"]'); | ||
| private readonly receivingTrafficCheckbox = this.page.locator('input[id="trafficStatus"]'); | ||
| private readonly confirmActionButton = this.page.locator('button[data-test="confirm-action"]'); | ||
| private readonly receivingTrafficColumnLabel = this.page.locator( | ||
| '[data-label="Receiving Traffic"]', | ||
| ); | ||
|
|
||
| constructor(page: Page) { | ||
| super(page); | ||
| } | ||
|
|
||
| async enableReceivingTrafficColumn(): Promise<void> { | ||
| await this.robustClick(this.manageColumnsButton); | ||
| await this.createdColumnCheckbox.uncheck(); | ||
| await this.receivingTrafficCheckbox.check(); | ||
| await this.robustClick(this.confirmActionButton); | ||
| } | ||
|
|
||
| async expectReceivingTrafficColumnVisible(): Promise<void> { | ||
| await expect(this.receivingTrafficColumnLabel).toBeVisible(); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix fallback order for Administrator/Core platform switching.
On Line 44,
switchToPerspective('Administrator')can fail before theCore platformfallback runs (Lines 46–52) when only the new label is present.Proposed fix
async switchToAdministrator(): Promise<void> { - await this.switchToPerspective('Administrator'); - // Also handle "Core platform" label (OpenShift 5.0+) - const currentText = await this.perspectiveSwitcherToggle.textContent(); - if ( - !currentText?.includes('Administrator') && - !currentText?.includes('Core platform') - ) { - await this.switchToPerspective('Core platform'); - } + const currentText = await this.perspectiveSwitcherToggle.textContent(); + if (currentText?.includes('Administrator') || currentText?.includes('Core platform')) { + return; + } + + await this.robustClick(this.perspectiveSwitcherToggle); + const options = this.perspectiveSwitcherMenu.locator( + '[data-test-id="perspective-switcher-menu-option"]', + ); + const administratorOption = options.filter({ hasText: 'Administrator' }); + const corePlatformOption = options.filter({ hasText: 'Core platform' }); + + if ((await administratorOption.count()) > 0) { + await this.robustClick(administratorOption); + } else { + await this.robustClick(corePlatformOption); + } + await this.waitForLoadingComplete(); }🤖 Prompt for AI Agents