diff --git a/tests/playwright/package.json b/tests/playwright/package.json index 076e306b..a70abbf1 100644 --- a/tests/playwright/package.json +++ b/tests/playwright/package.json @@ -2,9 +2,10 @@ "name": "bootc-tests-playwright", "version": "0.0.1", "description": "Podman Desktop BootC extension Playwright E2E tests", + "type": "module", "scripts": { "test:e2e:setup": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' --", - "test:e2e": "npm run test:e2e:setup vitest run src/ --pool=threads --poolOptions.threads.singleThread --poolOptions.threads.isolate --no-file-parallelism" + "test:e2e": "npm run test:e2e:setup npx playwright test src/" }, "author": "Red Hat", "license": "Apache-2.0", diff --git a/tests/playwright/src/setupFiles/extended-hooks.ts b/tests/playwright/playwright.config.ts similarity index 59% rename from tests/playwright/src/setupFiles/extended-hooks.ts rename to tests/playwright/playwright.config.ts index 92d44ee4..796f3a5e 100644 --- a/tests/playwright/src/setupFiles/extended-hooks.ts +++ b/tests/playwright/playwright.config.ts @@ -16,9 +16,25 @@ * SPDX-License-Identifier: Apache-2.0 ***********************************************************************/ -import { afterEach, onTestFailed } from 'vitest'; -import { takeScreenshotHook, type RunnerTestContext } from '@podman-desktop/tests-playwright'; +import { defineConfig, devices } from '@playwright/test'; -afterEach(async (context: RunnerTestContext) => { - onTestFailed(async () => await takeScreenshotHook(context.pdRunner, context.task.name)); +export default defineConfig({ + outputDir: 'tests/playwright/output/', + workers: 1, + + reporter: [ + ['list'], + ['junit', { outputFile: 'tests/playwright/output/junit-results.xml' }], + ['json', { outputFile: 'tests/playwright/output/json-results.json' }], + ['html', { open: 'never', outputFolder: 'tests/playwright/output/html-results/' }], + ], + + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + ], }); diff --git a/tests/playwright/src/bootc-extension.spec.ts b/tests/playwright/src/bootc-extension.spec.ts index 20626958..9cb14b14 100644 --- a/tests/playwright/src/bootc-extension.spec.ts +++ b/tests/playwright/src/bootc-extension.spec.ts @@ -17,7 +17,6 @@ ***********************************************************************/ import type { Page } from '@playwright/test'; -import { afterAll, beforeAll, test, describe, beforeEach } from 'vitest'; import { NavigationBar, PodmanDesktopRunner, @@ -26,12 +25,12 @@ import { removeFolderIfExists, waitForPodmanMachineStartup, } from '@podman-desktop/tests-playwright'; -import { expect as playExpect } from '@playwright/test'; -import { RunnerTestContext } from '@podman-desktop/tests-playwright'; +import { expect as playExpect, test } from '@playwright/test'; import * as path from 'node:path'; import * as os from 'node:os'; import { BootcPage } from './model/bootc-page'; import { ArchitectureType } from '@podman-desktop/tests-playwright'; +import { fileURLToPath } from 'node:url'; let pdRunner: PodmanDesktopRunner; let page: Page; @@ -45,6 +44,8 @@ const extensionLabel = 'redhat.bootc'; const extensionHeading = 'Bootable Container'; const isLinux = os.platform() === 'linux'; const isWindows = os.platform() === 'win32'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const containerFilePath = path.resolve(__dirname, '..', 'resources', 'bootable-containerfile'); const contextDirectory = path.resolve(__dirname, '..', 'resources'); const skipInstallation = process.env.SKIP_INSTALLATION; @@ -52,11 +53,7 @@ const buildISOImage = process.env.BUILD_ISO_IMAGE; let timeoutForBuild = 900000; let imageBuildFailed = true; -beforeEach(async ctx => { - ctx.pdRunner = pdRunner; -}); - -beforeAll(async () => { +test.beforeAll(async () => { await removeFolderIfExists('tests/output/images'); pdRunner = new PodmanDesktopRunner({ customFolder: 'bootc-tests-pd', autoUpdate: false, autoCheckUpdate: false }); page = await pdRunner.start(); @@ -68,7 +65,8 @@ beforeAll(async () => { await waitForPodmanMachineStartup(page); }); -afterAll(async () => { +test.afterAll(async () => { + test.setTimeout(180000); try { await deleteImage(page, imageName); } catch (error) { @@ -77,40 +75,40 @@ afterAll(async () => { await removeFolderIfExists('tests/output/images'); await pdRunner.close(); } -}, 180000); +}); -describe('BootC Extension', async () => { +test.describe('BootC Extension', () => { test('Go to settings and check if extension is already installed', async () => { const extensionsPage = await navBar.openExtensions(); if (await extensionsPage.extensionIsInstalled(extensionLabel)) extensionInstalled = true; }); - test.runIf(extensionInstalled && !skipInstallation)( - 'Uninstalled previous version of bootc extension', - async () => { - console.log('Extension found already installed, trying to remove!'); - await ensureBootcIsRemoved(); - }, - 200000, - ); - - test.runIf(!skipInstallation)( - 'Install extension through Extension page', - async () => { - const extensionsPage = await navBar.openExtensions(); - await extensionsPage.installExtensionFromOCIImage('ghcr.io/containers/podman-desktop-extension-bootc'); - - await playExpect - .poll(async () => await extensionsPage.extensionIsInstalled(extensionLabel), { timeout: 30000 }) - .toBeTruthy(); - }, - 200000, - ); - - describe.each([ArchitectureType.ARM64, ArchitectureType.AMD64])( - 'Bootc images for architecture: %s', - async architecture => { - test('Build bootc image from containerfile', async () => { + test('Uninstalled previous version of bootc extension', async () => { + test.skip(!extensionInstalled && !!skipInstallation); + test.setTimeout(200000); + console.log('Extension found already installed, trying to remove!'); + await ensureBootcIsRemoved(); + }); + + test('Install extension through Extension page', async () => { + test.skip(!!skipInstallation); + test.setTimeout(200000); + + const extensionsPage = await navBar.openExtensions(); + await extensionsPage.installExtensionFromOCIImage('ghcr.io/containers/podman-desktop-extension-bootc'); + + await playExpect + .poll(async () => await extensionsPage.extensionIsInstalled(extensionLabel), { timeout: 30000 }) + .toBeTruthy(); + }); + + const architectures = [ArchitectureType.AMD64, ArchitectureType.ARM64]; + + for (const architecture of architectures) { + test.describe.serial(`Bootc images for architecture: ${architecture}`, () => { + test(`Build bootc image from containerfile for architecture: ${architecture}`, async () => { + test.setTimeout(210000); + imageBuildFailed = true; let imagesPage = await navBar.openImages(); await playExpect(imagesPage.heading).toBeVisible(); @@ -128,55 +126,70 @@ describe('BootC Extension', async () => { await playExpect.poll(async () => await imagesPage.waitForImageExists(imageName)).toBeTruthy(); imageBuildFailed = false; - }, 210000); - - describe.skipIf(isLinux).each(['QCOW2', 'AMI', 'RAW', 'VMDK', 'ISO'])('Building images ', async type => { - test(`Building bootable image type: ${type}`, async context => { - if (imageBuildFailed) { - console.log('Image build failed, skipping test'); - context.skip(); - } - - if (type === 'ISO') { - if (buildISOImage) { - timeoutForBuild = 1200000; - console.log(`Building ISO image requested, extending timeout to ${timeoutForBuild}`); + }); + + const types = ['QCOW2', 'AMI', 'RAW', 'VMDK', 'ISO']; + + for (const type of types) { + test.describe.serial('Building images ', () => { + test(`Building bootable image type: ${type}`, async () => { + test.skip(isLinux); + test.setTimeout(1250000); + + if (imageBuildFailed) { + console.log('Image build failed, skipping test'); + test.skip(); + } + + if (type === 'ISO') { + if (buildISOImage) { + timeoutForBuild = 1200000; + console.log(`Building ISO image requested, extending timeout to ${timeoutForBuild}`); + } else { + console.log(`Building ISO image not requested, skipping test`); + test.skip(); + } + } + + const imagesPage = await navBar.openImages(); + await playExpect(imagesPage.heading).toBeVisible(); + + const imageDetailPage = await imagesPage.openImageDetails(imageName); + await playExpect(imageDetailPage.heading).toBeVisible(); + + const pathToStore = path.resolve( + __dirname, + '..', + 'tests', + 'playwright', + 'output', + 'images', + `${type}-${architecture}`, + ); + [page, webview] = await handleWebview(); + const bootcPage = new BootcPage(page, webview); + const result = await bootcPage.buildDiskImage( + `${imageName}:${imageTag}`, + pathToStore, + type, + architecture, + timeoutForBuild, + ); + console.log( + `Building disk image for platform ${os.platform()} and architecture ${architecture} and type ${type} is ${result}`, + ); + if (isWindows && architecture === ArchitectureType.ARM64) { + console.log('Expected to fail on Windows for ARM64'); + playExpect(result).toBeFalsy(); } else { - console.log(`Building ISO image not requested, skipping test`); - context.skip(); + console.log('Expected to pass on Linux, Windows and macOS'); + playExpect(result).toBeTruthy(); } - } - - const imagesPage = await navBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const imageDetailPage = await imagesPage.openImageDetails(imageName); - await playExpect(imageDetailPage.heading).toBeVisible(); - - const pathToStore = path.resolve(__dirname, '..', 'tests', 'output', 'images', `${type}-${architecture}`); - [page, webview] = await handleWebview(); - const bootcPage = new BootcPage(page, webview); - const result = await bootcPage.buildDiskImage( - `${imageName}:${imageTag}`, - pathToStore, - type, - architecture, - timeoutForBuild, - ); - console.log( - `Building disk image for platform ${os.platform()} and architecture ${architecture} and type ${type} is ${result}`, - ); - if (isWindows && architecture === ArchitectureType.ARM64) { - console.log('Expected to fail on Windows for ARM64'); - playExpect(result).toBeFalsy(); - } else { - console.log('Expected to pass on Linux, Windows and macOS'); - playExpect(result).toBeTruthy(); - } - }, 1250000); - }); - }, - ); + }); + }); + } + }); + } test('Remove bootc extension through Settings', async () => { await ensureBootcIsRemoved();