From 4e583e90e23117599e7466d214d93dea25f3ecd1 Mon Sep 17 00:00:00 2001 From: Ondrej Dockal Date: Mon, 9 Sep 2024 15:13:08 +0200 Subject: [PATCH] chore(tests): add podman desktop update e2e test (#8653) * chore(test): add podman desktop update e2e test with workflows Signed-off-by: Ondrej Dockal --- .github/workflows/e2e-main.yaml | 70 +++++++++++- .github/workflows/pr-check.yaml | 101 +++++++++++++++++- package.json | 2 + .../src/lib/statusbar/StatusBar.svelte | 4 +- .../src/model/workbench/status-bar.ts | 18 +++- .../specs/installation/update-install.spec.ts | 94 ++++++++++++++++ tests/playwright/src/utility/fixtures.ts | 6 ++ tests/playwright/src/utility/operations.ts | 3 +- 8 files changed, 290 insertions(+), 8 deletions(-) create mode 100644 tests/playwright/src/specs/installation/update-install.spec.ts diff --git a/.github/workflows/e2e-main.yaml b/.github/workflows/e2e-main.yaml index 2acf5b47182da..98ab27a055d86 100644 --- a/.github/workflows/e2e-main.yaml +++ b/.github/workflows/e2e-main.yaml @@ -41,7 +41,7 @@ on: jobs: e2e-tests: - name: Run E2E tests + name: Run All E2E tests runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -85,7 +85,7 @@ jobs: - name: Execute pnpm run: pnpm install - - name: Run All E2E tests + - name: Run E2E tests env: PODMANDESKTOP_CI_BOT_TOKEN: ${{ secrets.PODMANDESKTOP_CI_BOT_TOKEN }} TEST_PODMAN_MACHINE: 'true' @@ -100,3 +100,69 @@ jobs: ./tests/output/ ./tests/**/output/junit*.xml !./tests/**/traces/raw + + win-update-e2e-test: + name: win update e2e tests + runs-on: ${{ matrix.os }} + timeout-minutes: 40 + strategy: + fail-fast: false + matrix: + os: [windows-2022] + steps: + - uses: actions/checkout@v4 + with: + repository: ${{ github.event.inputs.organization }}/${{ github.event.inputs.repositoryName }} + ref: ${{ github.event.inputs.branch }} + if: github.event_name == 'workflow_dispatch' + + - uses: actions/checkout@v4 + if: github.event_name == 'push' + + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + run_install: false + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Execute pnpm + run: pnpm install + + - name: Execute PNPM + run: pnpm install --frozen-lockfile + + - name: Adjust/Downgrade local podman desktop version Windows + if: ${{ matrix.os=='windows-2022'}} + run: | + $version="1.0.0" + jq --arg version "$version" '.version = $version' package.json | Out-File -FilePath package.json_tmp + Move-Item -Path package.json_tmp -Destination package.json -Force + + - name: Build Podman Desktop locally with electron updater included + env: + ELECTRON_ENABLE_INSPECT: true + run: | + pnpm compile:current --win nsis + $path=('./dist/win-unpacked/Podman Desktop.exe' | resolve-path).ProviderPath + echo $path + echo ("PODMAN_DESKTOP_BINARY=" + $path) >> $env:GITHUB_ENV + + - name: Run E2E Update test + env: + UPDATE_PODMAN_DESKTOP: true + run: | + echo "${{ env.PODMAN_DESKTOP_BINARY }}" + pnpm test:e2e:update:run + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: update-e2e-test + path: | + ./tests/output/ + ./tests/**/output/junit*.xml + !./tests/**/traces/raw diff --git a/.github/workflows/pr-check.yaml b/.github/workflows/pr-check.yaml index 106ace0375d8d..5666f884301e1 100644 --- a/.github/workflows/pr-check.yaml +++ b/.github/workflows/pr-check.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2022 Red Hat, Inc. +# Copyright (C) 2022-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. @@ -17,7 +17,9 @@ name: pr-check -on: [pull_request] +on: + pull_request: + types: [labeled, synchronize, opened, reopened] jobs: windows: @@ -324,3 +326,98 @@ jobs: ./tests/output/ ./tests/**/output/junit*.xml !./tests/**/traces/raw + + detect_pnpm_changes: + name: Detect pnpm lock or pr-check files changes + runs-on: ubuntu-24.04 + outputs: + pnpm_lock_changed: ${{ steps.pnpm_changed.outputs.PNPM_LOCK_CHANGED }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Evaluate changes in files + id: pnpm_changed + run: | + git fetch origin ${{ github.event.pull_request.base.ref }} + git diff --name-only origin/${{ github.event.pull_request.base.ref }} HEAD > changes.txt + if grep -q -e 'pnpm-lock.yaml' -e 'pr-check.yaml' changes.txt; then + echo "PNPM_LOCK_CHANGED=true" >> $GITHUB_OUTPUT + else + echo "PNPM_LOCK_CHANGED=false" >> $GITHUB_OUTPUT + fi + + run-update-e2e-test: + name: win update e2e tests + needs: detect_pnpm_changes + if: contains(github.event.pull_request.labels.*.name, 'area/update') || needs.detect_pnpm_changes.outputs.pnpm_lock_changed == 'true' + runs-on: windows-2022 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + run_install: false + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Execute pnpm + run: pnpm install + + - name: Execute PNPM + run: pnpm install --frozen-lockfile + + - name: Adjust/Downgrade local podman desktop version Windows + run: | + $version="1.0.0" + jq --arg version "$version" '.version = $version' package.json | Out-File -FilePath package.json_tmp + Move-Item -Path package.json_tmp -Destination package.json -Force + + - name: Build Podman Desktop locally with electron updater included + env: + ELECTRON_ENABLE_INSPECT: true + run: | + pnpm compile:current --win nsis + $path=('./dist/win-unpacked/Podman Desktop.exe' | resolve-path).ProviderPath + echo $path + echo ("PODMAN_DESKTOP_BINARY=" + $path) >> $env:GITHUB_ENV + + - name: Run E2E Update test + env: + UPDATE_PODMAN_DESKTOP: true + run: | + echo "${{ env.PODMAN_DESKTOP_BINARY }}" + pnpm test:e2e:update:run + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: win-update-e2e-test + path: | + ./tests/**/output/ + ./tests/**/output/junit*.xml + !./tests/**/traces/raw + + update-e2e-test: + name: update E2E test + runs-on: ubuntu-24.04 + needs: run-update-e2e-test + if: | + always() + steps: + - name: Evaluate the Windows Update test results + run: | + echo "Windows updater result: ${{ needs.run-update-e2e-test.result }}" + if [ "${{ needs.run-update-e2e-test.result }}" = "failure" ]; then + echo "Windows udpater test failed..." + exit 1 + else + echo "Windows updater test succeeded or was skipped..." + fi diff --git a/package.json b/package.json index 1d0018eb9ccf8..b2bfefc36d936 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,8 @@ "test:e2e:extension:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test tests/playwright/src/specs/extension-installation.spec.ts", "test:e2e:pw": "npm run test:e2e:build && npm run test:e2e:pw:run", "test:e2e:pw:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test tests/playwright/src/specs/pod-smoke.spec.ts", + "test:e2e:update": "npm run test:e2e:build && npm run test:e2e:update:run", + "test:e2e:update:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test tests/playwright/src/specs/installation/update-install.spec.ts", "test:e2e:copy": "cp ../podman-desktop-extension-bootc/tests/src/bootc-extension.spec.ts tests/src/", "test:main": "vitest run -r packages/main --passWithNoTests --coverage", "test:preload": "vitest run -r packages/preload --passWithNoTests --coverage", diff --git a/packages/renderer/src/lib/statusbar/StatusBar.svelte b/packages/renderer/src/lib/statusbar/StatusBar.svelte index fec9993047fe2..2960b245786bc 100644 --- a/packages/renderer/src/lib/statusbar/StatusBar.svelte +++ b/packages/renderer/src/lib/statusbar/StatusBar.svelte @@ -48,7 +48,9 @@ onMount(async () => {
+ class="flex justify-between px-1 bg-[var(--pd-statusbar-bg)] text-[var(--pd-statusbar-text)] text-sm space-x-2 z-40" + role="contentinfo" + aria-label="Status Bar">
{#each leftEntries as entry} diff --git a/tests/playwright/src/model/workbench/status-bar.ts b/tests/playwright/src/model/workbench/status-bar.ts index 8bf499f6708b8..5277fa5648748 100644 --- a/tests/playwright/src/model/workbench/status-bar.ts +++ b/tests/playwright/src/model/workbench/status-bar.ts @@ -22,15 +22,29 @@ import { handleConfirmationDialog } from '../../utility/operations'; import { BasePage } from '../pages/base-page'; export class StatusBar extends BasePage { + readonly content: Locator; readonly kindInstallationButton: Locator; readonly kubernetesContext: Locator; + readonly versionButton: Locator; + readonly updateButtonTitle: Locator; + readonly shareYourFeedbackButton: Locator; + readonly troubleshootingButton: Locator; + readonly tasksButton: Locator; + readonly helpButton: Locator; constructor(page: Page) { super(page); - this.kindInstallationButton = this.page.getByTitle( + this.content = page.getByRole('contentinfo', { name: 'Status Bar' }); + this.kindInstallationButton = this.content.getByTitle( 'Kind not found on your system, click to download and install it', ); - this.kubernetesContext = this.page.getByTitle('Current Kubernetes Context'); + this.kubernetesContext = this.content.getByTitle('Current Kubernetes Context'); + this.versionButton = this.content.getByRole('button', { name: /^v\d+\.\d+\.\d+(-\w+)?$/ }); + this.updateButtonTitle = this.content.getByRole('button').and(this.content.getByTitle('Update available')); + this.shareYourFeedbackButton = this.content.getByRole('button').and(this.content.getByTitle('Share your feedback')); + this.troubleshootingButton = this.content.getByRole('button').and(this.content.getByTitle('Troubleshooting')); + this.tasksButton = this.content.getByRole('button').and(this.content.getByTitle('Tasks')); + this.helpButton = this.content.getByRole('button').and(this.content.getByTitle('Help')); } public async installKindCLI(): Promise { diff --git a/tests/playwright/src/specs/installation/update-install.spec.ts b/tests/playwright/src/specs/installation/update-install.spec.ts new file mode 100644 index 0000000000000..1520a95532a54 --- /dev/null +++ b/tests/playwright/src/specs/installation/update-install.spec.ts @@ -0,0 +1,94 @@ +/********************************************************************** + * 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 } from '@playwright/test'; + +import type { StatusBar } from '../../model/workbench/status-bar'; +import { expect as playExpect, test } from '../../utility/fixtures'; +import { handleConfirmationDialog } from '../../utility/operations'; + +let sBar: StatusBar; +let updateAvailableDialog: Locator; +let updateDialog: Locator; +let updateDownloadedDialog: Locator; +const performUpdate = process.env.UPDATE_PODMAN_DESKTOP ? process.env.UPDATE_PODMAN_DESKTOP : false; + +test.beforeAll(async ({ runner, page, statusBar }) => { + runner.setVideoAndTraceName('update-e2e'); + + sBar = statusBar; + updateAvailableDialog = page.getByRole('dialog', { name: 'Update Available now' }); + updateDialog = page.getByRole('dialog', { name: 'Update', exact: true }); + updateDownloadedDialog = page.getByRole('dialog', { name: 'Update Downloaded', exact: true }); +}); + +test.afterAll(async ({ runner }) => { + await runner.close(); +}); + +test.describe.serial('Podman Desktop Update Update installation offering', () => { + test('Update is offered automatically on startup', async ({ welcomePage }) => { + await playExpect(updateAvailableDialog).toBeVisible(); + const updateNowButton = updateAvailableDialog.getByRole('button', { name: 'Update Now' }); + await playExpect(updateNowButton).toBeVisible(); + const doNotshowButton = updateAvailableDialog.getByRole('button', { name: 'Do not show again' }); + await playExpect(doNotshowButton).toBeVisible(); + const cancelButton = updateAvailableDialog.getByRole('button', { name: 'Cancel' }); + await playExpect(cancelButton).toBeVisible(); + await cancelButton.click(); + await playExpect(updateAvailableDialog).not.toBeVisible(); + // handle welcome page now + await welcomePage.handleWelcomePage(true); + }); + + test('Version button is visible', async () => { + await playExpect(sBar.content).toBeVisible(); + await playExpect(sBar.versionButton).toBeVisible(); + }); + + test('User initiated update option is available', async ({ page }) => { + await playExpect(sBar.updateButtonTitle).toHaveText(await sBar.versionButton.innerText()); + await sBar.updateButtonTitle.click(); + await handleConfirmationDialog(page, 'Update Available now', false, '', 'Cancel'); + }); +}); +test.describe.serial('Podman Desktop Update installation can be performed', () => { + test.skip(!performUpdate, 'Update test does not run as UPDATE_PODMAN_DESKTOP env. var. is not set'); + test('Update can be initiated', async () => { + await sBar.updateButtonTitle.click(); + await playExpect(updateAvailableDialog).toBeVisible(); + const updateNowButton = updateAvailableDialog.getByRole('button', { name: 'Update now' }); + await playExpect(updateNowButton).toBeVisible(); + await updateNowButton.click(); + await playExpect(updateAvailableDialog).not.toBeVisible(); + }); + + test('Update is in progress', async ({ page }) => { + await sBar.updateButtonTitle.click(); + await playExpect(updateDialog).toBeVisible(); + await handleConfirmationDialog(page, 'Update', true, 'OK', 'Cancel'); + }); + + test('Update is performed and restart offered', async ({ page }) => { + test.setTimeout(150000); + // now it takes some time to perform, in case of failure, PD gets closed + await playExpect(updateDownloadedDialog).toBeVisible({ timeout: 120000 }); + // some buttons + await handleConfirmationDialog(page, 'Update Downloaded', false, 'Restart', 'Cancel'); + }); +}); diff --git a/tests/playwright/src/utility/fixtures.ts b/tests/playwright/src/utility/fixtures.ts index 5ba707a3b0d68..ad5c20a83a0d5 100644 --- a/tests/playwright/src/utility/fixtures.ts +++ b/tests/playwright/src/utility/fixtures.ts @@ -21,6 +21,7 @@ import { test as base } from '@playwright/test'; import { WelcomePage } from '../model/pages/welcome-page'; import { NavigationBar } from '../model/workbench/navigation'; +import { StatusBar } from '../model/workbench/status-bar'; import { Runner } from '../runner/podman-desktop-runner'; import { RunnerOptions } from '../runner/runner-options'; @@ -29,6 +30,7 @@ export type TestFixtures = { navigationBar: NavigationBar; welcomePage: WelcomePage; page: Page; + statusBar: StatusBar; }; export type FixtureOptions = { @@ -52,5 +54,9 @@ export const test = base.extend({ const welcomePage = new WelcomePage(page); await use(welcomePage); }, + statusBar: async ({ page }, use) => { + const statusBar = new StatusBar(page); + await use(statusBar); + }, }); export { expect } from '@playwright/test'; diff --git a/tests/playwright/src/utility/operations.ts b/tests/playwright/src/utility/operations.ts index 372f85c8b02ec..8d7a3604c5dd8 100644 --- a/tests/playwright/src/utility/operations.ts +++ b/tests/playwright/src/utility/operations.ts @@ -160,10 +160,11 @@ export async function handleConfirmationDialog( ): Promise { // wait for dialog to appear using waitFor const dialog = page.getByRole('dialog', { name: dialogTitle, exact: true }); - await dialog.waitFor({ state: 'visible', timeout: 3000 }); + await playExpect(dialog).toBeVisible(); const button = confirm ? dialog.getByRole('button', { name: confirmationButton }) : dialog.getByRole('button', { name: cancelButton }); + await playExpect(button).toBeEnabled(); await button.click(); }