diff --git a/tests/playwright/src/index.ts b/tests/playwright/src/index.ts index 4ad5e6c6d6dae..811685a11176d 100644 --- a/tests/playwright/src/index.ts +++ b/tests/playwright/src/index.ts @@ -44,6 +44,7 @@ export * from './model/pages/compose-onboarding/compose-wide-install-page'; export * from './model/pages/container-details-page'; export * from './model/pages/containers-page'; export * from './model/pages/create-kind-cluster-page'; +export * from './model/pages/create-machine-page'; export * from './model/pages/create-pod-page'; export * from './model/pages/dashboard-page'; export * from './model/pages/deploy-to-kubernetes-page'; @@ -52,6 +53,7 @@ export * from './model/pages/extension-card-page'; export * from './model/pages/extension-catalog-card-page'; export * from './model/pages/extension-details-page'; export * from './model/pages/extensions-page'; +export * from './model/pages/forms/machine-creation-form'; export * from './model/pages/image-details-page'; export * from './model/pages/image-edit-page'; export * from './model/pages/images-page'; diff --git a/tests/playwright/src/model/pages/create-machine-page.ts b/tests/playwright/src/model/pages/create-machine-page.ts new file mode 100644 index 0000000000000..ef882ce5ee4a2 --- /dev/null +++ b/tests/playwright/src/model/pages/create-machine-page.ts @@ -0,0 +1,75 @@ +/********************************************************************** + * Copyright (C) 2023 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import type { Locator, Page } from '@playwright/test'; +import { expect as playExpect } from '@playwright/test'; + +import { BasePage } from './base-page'; +import { MachineCreationForm } from './forms/machine-creation-form'; +import { ResourcesPage } from './resources-page'; + +export class CreateMachinePage extends BasePage { + readonly heading: Locator; + readonly machineCreationForm: MachineCreationForm; + readonly closeButton: Locator; + + constructor(page: Page) { + super(page); + this.heading = this.page.getByRole('heading', { name: 'Create Podman Machine' }); + this.machineCreationForm = new MachineCreationForm(this.page); + this.closeButton = this.page.getByRole('button', { name: 'Close' }); + } + + async createMachine( + machineName: string, + { isRootful = true, enableUserNet = false, startNow = true, setAsDefault = true }, + ): Promise { + await this.machineCreationForm.setupAndCreateMachine(machineName, isRootful, enableUserNet, startNow); + + const successfulCreationMessage = this.page.getByText('Successful operation'); + const goBackToResourcesButton = this.page.getByRole('button', { name: 'Go back to resources' }); + + await playExpect(successfulCreationMessage).toBeVisible({ timeout: 60_000 }); + await playExpect(goBackToResourcesButton).toBeVisible(); + + try { + await this.handleConnectionDialog(machineName, setAsDefault); + } catch (error) { + console.log('No handling dialog displayed', error); + } + + await goBackToResourcesButton.click(); + return new ResourcesPage(this.page); + } + + async handleConnectionDialog(machineName: string, setAsDefault: boolean): Promise { + const connectionDialog = this.page.getByRole('dialog', { name: 'Podman' }); + await playExpect(connectionDialog).toBeVisible({ timeout: 10_000 }); + + const dialogMessage = connectionDialog.getByLabel('Dialog Message'); + await playExpect(dialogMessage).toHaveText( + new RegExp( + `Podman Machine '${machineName}' is running but not the default machine .+ Do you want to set it as default?`, + ), + ); + + const handleButtonName = setAsDefault ? 'Yes' : 'Ignore'; + const handleButton = connectionDialog.getByRole('button', { name: handleButtonName }); + await handleButton.click(); + } +} diff --git a/tests/playwright/src/model/pages/forms/machine-creation-form.ts b/tests/playwright/src/model/pages/forms/machine-creation-form.ts new file mode 100644 index 0000000000000..47c43d6d2a8d0 --- /dev/null +++ b/tests/playwright/src/model/pages/forms/machine-creation-form.ts @@ -0,0 +1,82 @@ +/********************************************************************** + * Copyright (C) 2024 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import type { Locator, Page } from '@playwright/test'; +import { expect as playExpect } from '@playwright/test'; + +import { BasePage } from '../base-page'; + +export class MachineCreationForm extends BasePage { + readonly podmanMachineConfiguration: Locator; + readonly podmanMachineName: Locator; + readonly imagePathBox: Locator; + readonly browseImagesButton: Locator; + readonly podmanMachineCPUs: Locator; + readonly podmanMachineMemory: Locator; + readonly podmanMachineDiskSize: Locator; + readonly rootPriviledgesCheckbox: Locator; + readonly userModeNetworkingCheckbox: Locator; + readonly startNowCheckbox: Locator; + readonly createMachineButton: Locator; + + constructor(page: Page) { + super(page); + this.podmanMachineConfiguration = this.page.getByRole('form', { name: 'Properties Information' }); + this.podmanMachineName = this.podmanMachineConfiguration.getByRole('textbox', { name: 'Name' }); + this.imagePathBox = this.podmanMachineConfiguration.getByRole('textbox', { name: 'Image Path (Optional) ' }); + this.browseImagesButton = this.podmanMachineConfiguration.getByRole('button', { + name: 'button-Image Path (Optional)', + }); + this.podmanMachineCPUs = this.podmanMachineConfiguration.getByRole('slider', { name: 'CPU(s)' }); + this.podmanMachineMemory = this.podmanMachineConfiguration.getByRole('slider', { name: 'Memory' }); + this.podmanMachineDiskSize = this.podmanMachineConfiguration.getByRole('slider', { name: 'Disk size' }); + this.rootPriviledgesCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { + name: 'Machine with root privileges', + }); + this.userModeNetworkingCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { + name: 'User mode networking', + }); + this.startNowCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { name: 'Start the machine now' }); + this.createMachineButton = this.podmanMachineConfiguration.getByRole('button', { name: 'Create' }); + } + + async setupAndCreateMachine( + machineName: string, + isRootful: boolean, + enableUserNet: boolean, + startNow: boolean, + ): Promise { + await playExpect(this.podmanMachineConfiguration).toBeVisible({ timeout: 10_000 }); + await this.podmanMachineName.clear(); + await this.podmanMachineName.fill(machineName); + + await this.ensureCheckboxState(isRootful, this.rootPriviledgesCheckbox); + await this.ensureCheckboxState(enableUserNet, this.userModeNetworkingCheckbox); + await this.ensureCheckboxState(startNow, this.startNowCheckbox); + + await playExpect(this.createMachineButton).toBeEnabled(); + await this.createMachineButton.click(); + } + + async ensureCheckboxState(desiredState: boolean, checkbox: Locator): Promise { + if (desiredState !== (await checkbox.isChecked())) { + await checkbox.locator('..').click(); + playExpect(await checkbox.isChecked()).toBe(desiredState); + } + } +} diff --git a/tests/playwright/src/model/pages/podman-onboarding-page.ts b/tests/playwright/src/model/pages/podman-onboarding-page.ts index a56574fe71519..ea86b4053e95e 100644 --- a/tests/playwright/src/model/pages/podman-onboarding-page.ts +++ b/tests/playwright/src/model/pages/podman-onboarding-page.ts @@ -18,21 +18,13 @@ import type { Locator, Page } from '@playwright/test'; +import { MachineCreationForm } from './forms/machine-creation-form'; import { OnboardingPage } from './onboarding-page'; export class PodmanOnboardingPage extends OnboardingPage { readonly podmanAutostartToggle: Locator; readonly createMachinePageTitle: Locator; - readonly podmanMachineConfiguration: Locator; - readonly podmanMachineName: Locator; - readonly podmanMachineCPUs: Locator; - readonly podmanMachineMemory: Locator; - readonly podmanMachineDiskSize: Locator; - readonly podmanMachineImage: Locator; - readonly podmanMachineRootfulCheckbox: Locator; - readonly podmanMachineUserModeNetworkingCheckbox: Locator; - readonly podmanMachineStartAfterCreationCheckbox: Locator; - readonly podmanMachineCreateButton: Locator; + readonly machineCreationForm: MachineCreationForm; readonly podmanMachineShowLogsButton: Locator; readonly goBackButton: Locator; @@ -42,23 +34,7 @@ export class PodmanOnboardingPage extends OnboardingPage { name: 'Autostart Podman engine when launching Podman Desktop', }); this.createMachinePageTitle = this.onboardingComponent.getByLabel('title'); - this.podmanMachineConfiguration = this.page.getByRole('form', { name: 'Properties Information' }); - this.podmanMachineName = this.podmanMachineConfiguration.getByRole('textbox', { name: 'Name' }); - this.podmanMachineCPUs = this.podmanMachineConfiguration.getByRole('slider', { name: 'CPU(s)' }); - this.podmanMachineMemory = this.podmanMachineConfiguration.getByRole('slider', { name: 'Memory' }); - this.podmanMachineDiskSize = this.podmanMachineConfiguration.getByRole('slider', { name: 'Disk size' }); - this.podmanMachineImage = this.podmanMachineConfiguration.getByRole('textbox', { name: 'Image Path (Optional)' }); - this.podmanMachineRootfulCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { - name: 'Machine with root privileges', - }); - this.podmanMachineUserModeNetworkingCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { - name: 'User mode networking', - exact: false, - }); - this.podmanMachineStartAfterCreationCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { - name: 'Start the machine now', - }); - this.podmanMachineCreateButton = this.podmanMachineConfiguration.getByRole('button', { name: 'Create' }); + this.machineCreationForm = new MachineCreationForm(this.page); this.podmanMachineShowLogsButton = this.mainPage.getByRole('button', { name: 'Show Logs' }); this.goBackButton = this.page.getByRole('button', { name: 'Go back to resources' }); } diff --git a/tests/playwright/src/specs/podman-machine-onboarding.spec.ts b/tests/playwright/src/specs/podman-machine-onboarding.spec.ts index ca18b2179170f..a9f1daf2a9871 100644 --- a/tests/playwright/src/specs/podman-machine-onboarding.spec.ts +++ b/tests/playwright/src/specs/podman-machine-onboarding.spec.ts @@ -120,18 +120,20 @@ test.describe.serial('Podman Machine verification', () => { test('Verify default podman machine settings', async () => { await playExpect(podmanOnboardingPage.createMachinePageTitle).toHaveText(`Create a Podman machine`); - await playExpect(podmanOnboardingPage.podmanMachineConfiguration).toBeVisible(); - await playExpect(podmanOnboardingPage.podmanMachineName).toHaveValue('podman-machine-default'); - await playExpect(podmanOnboardingPage.podmanMachineImage).toHaveValue(''); - await playExpect(podmanOnboardingPage.podmanMachineRootfulCheckbox).toBeChecked(); - await playExpect(podmanOnboardingPage.podmanMachineStartAfterCreationCheckbox).toBeChecked(); + await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineConfiguration).toBeVisible(); + await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineName).toHaveValue( + 'podman-machine-default', + ); + await playExpect(podmanOnboardingPage.machineCreationForm.imagePathBox).toHaveValue(''); + await playExpect(podmanOnboardingPage.machineCreationForm.rootPriviledgesCheckbox).toBeChecked(); + await playExpect(podmanOnboardingPage.machineCreationForm.startNowCheckbox).toBeChecked(); if (os.platform() === 'win32') { - await playExpect(podmanOnboardingPage.podmanMachineUserModeNetworkingCheckbox).not.toBeChecked(); + await playExpect(podmanOnboardingPage.machineCreationForm.userModeNetworkingCheckbox).not.toBeChecked(); } else { - await playExpect(podmanOnboardingPage.podmanMachineCPUs).toBeVisible(); - await playExpect(podmanOnboardingPage.podmanMachineMemory).toBeVisible(); - await playExpect(podmanOnboardingPage.podmanMachineDiskSize).toBeVisible(); + await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineCPUs).toBeVisible(); + await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineMemory).toBeVisible(); + await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineDiskSize).toBeVisible(); } }); }); @@ -140,7 +142,7 @@ test.describe.serial('Podman Machine verification', () => { test('Create a default Podman machine', async () => { test.setTimeout(PODMAN_FULL_STARTUP_TIMEOUT); - await podmanOnboardingPage.podmanMachineCreateButton.click(); + await podmanOnboardingPage.machineCreationForm.createMachineButton.click(); await playExpect(podmanOnboardingPage.podmanMachineShowLogsButton).toBeVisible(); await podmanOnboardingPage.podmanMachineShowLogsButton.click(); await playExpect(podmanOnboardingPage.onboardingStatusMessage).toBeVisible({ diff --git a/tests/playwright/src/specs/podman-machine-rootless.spec.ts b/tests/playwright/src/specs/podman-machine-rootless.spec.ts new file mode 100644 index 0000000000000..a61ae0740d10a --- /dev/null +++ b/tests/playwright/src/specs/podman-machine-rootless.spec.ts @@ -0,0 +1,65 @@ +/********************************************************************** + * Copyright (C) 2024 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ +import * as os from 'node:os'; + +import { ResourceElementState } from '../model/core/states'; +import { CreateMachinePage } from '../model/pages/create-machine-page'; +import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; +import { expect as playExpect, test } from '../utility/fixtures'; +import { deletePodmanMachine, handleConfirmationDialog } from '../utility/operations'; + +const PODMAN_MACHINE_NAME: string = 'podman-machine-rootless'; + +test.beforeAll(async ({ runner, welcomePage }) => { + runner.setVideoAndTraceName('podman-rootless-machine-e2e'); + process.env.KEEP_TRACES_ON_PASS = 'true'; + + await welcomePage.handleWelcomePage(true); +}); + +test.afterAll(async ({ runner }) => { + await runner.close(); +}); + +test.skip(os.platform() === 'linux', 'Runs only on Windows and Mac'); +test.describe('Rootless Podman machine Verification', () => { + test('Create a rootless machine', async ({ page, navigationBar }) => { + test.setTimeout(200_000); + + await navigationBar.openSettings(); + const podmanResources = new ResourceConnectionCardPage(page, 'podman'); + await podmanResources.createButton.click(); + + const createMachinePage = new CreateMachinePage(page); + await createMachinePage.createMachine(PODMAN_MACHINE_NAME, { isRootful: false, setAsDefault: false }); + + const machineBox = new ResourceConnectionCardPage(page, 'podman', PODMAN_MACHINE_NAME); //does not work with visible name + await playExpect(machineBox.resourceElementConnectionStatus).toHaveText(ResourceElementState.Running); + }); + test('Clean up rootless machine', async ({ page }) => { + test.setTimeout(150_000); + await deletePodmanMachine(page, PODMAN_MACHINE_NAME); + + try { + await handleConfirmationDialog(page, 'Podman', true, 'Yes'); + await handleConfirmationDialog(page, 'Podman', true, 'OK'); + } catch (error) { + console.log('No handing dialog displayed', error); + } + }); +}); diff --git a/tests/playwright/src/specs/podman-machine-tests.spec.ts b/tests/playwright/src/specs/podman-machine-tests.spec.ts index 47b99462bf812..ccf007c96d81b 100644 --- a/tests/playwright/src/specs/podman-machine-tests.spec.ts +++ b/tests/playwright/src/specs/podman-machine-tests.spec.ts @@ -22,7 +22,7 @@ import { ResourceConnectionCardPage } from '../model/pages/resource-connection-c import { ResourcesPage } from '../model/pages/resources-page'; import { expect as playExpect, test } from '../utility/fixtures'; import { deletePodmanMachine, handleConfirmationDialog } from '../utility/operations'; -import { isLinux, isWindows } from '../utility/platform'; +import { isLinux } from '../utility/platform'; import { waitForPodmanMachineStartup } from '../utility/wait'; const DEFAULT_PODMAN_MACHINE = 'Podman Machine'; @@ -103,34 +103,14 @@ test.describe.serial(`Podman machine switching validation `, () => { }); const podmanMachineCreatePage = new PodmanOnboardingPage(page); - await test.step('Fill in podman machine name input box', async () => { - await playExpect(podmanMachineCreatePage.podmanMachineName).toBeVisible(); - await podmanMachineCreatePage.podmanMachineName.clear(); - await podmanMachineCreatePage.podmanMachineName.fill(ROOTLESS_PODMAN_MACHINE_VISIBLE); - }); - - await test.step('Set podman machine to be rootless', async () => { - await playExpect(podmanMachineCreatePage.podmanMachineRootfulCheckbox).toBeChecked(); - await podmanMachineCreatePage.podmanMachineRootfulCheckbox.locator('..').click(); - await playExpect(podmanMachineCreatePage.podmanMachineRootfulCheckbox).not.toBeChecked(); - }); - - if (isWindows) { - await test.step('Set podman machine to use user mode networking', async () => { - await playExpect(podmanMachineCreatePage.podmanMachineUserModeNetworkingCheckbox).not.toBeChecked(); - await podmanMachineCreatePage.podmanMachineUserModeNetworkingCheckbox.locator('..').click(); - await playExpect(podmanMachineCreatePage.podmanMachineUserModeNetworkingCheckbox).toBeChecked(); - }); - } - - await test.step('Set podman machine to not start after creation', async () => { - await playExpect(podmanMachineCreatePage.podmanMachineStartAfterCreationCheckbox).toBeChecked(); - await podmanMachineCreatePage.podmanMachineStartAfterCreationCheckbox.locator('..').click(); - await playExpect(podmanMachineCreatePage.podmanMachineStartAfterCreationCheckbox).not.toBeChecked(); - }); await test.step('Create podman machine', async () => { - await podmanMachineCreatePage.podmanMachineCreateButton.click(); + await podmanMachineCreatePage.machineCreationForm.setupAndCreateMachine( + ROOTLESS_PODMAN_MACHINE_VISIBLE, + false, + false, + false, + ); await playExpect(podmanMachineCreatePage.goBackButton).toBeEnabled({ timeout: 180_000 }); await podmanMachineCreatePage.goBackButton.click(); }); @@ -222,7 +202,7 @@ test.describe.serial(`Podman machine switching validation `, () => { await handleConfirmationDialog(page, 'Podman', true, 'Yes'); await handleConfirmationDialog(page, 'Podman', true, 'OK'); } catch (error) { - console.log('No handing dialog displayed', error); + console.log('No handling dialog displayed', error); } }); });