Skip to content

Commit 94fed37

Browse files
authored
chore: switch playwright tests to use playwright test runner instead of vitest (podman-desktop#8650)
* chore(test): switch e2e tests to use playwright test runner Signed-off-by: Vladimir Lazar <[email protected]>
1 parent cbabf7f commit 94fed37

23 files changed

+730
-788
lines changed

package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@
3838
"test:unit": "npm run test:main && npm run test:preload && npm run test:preload-docker-extension && npm run test:preload-webview && npm run test:ui && npm run test:renderer && npm run test:scripts:stylesheet && npm run test:tools && npm run test:extensions",
3939
"test:e2e": "npm run test:e2e:build && npm run test:e2e:run",
4040
"test:e2e:build": "cross-env NODE_ENV=development MODE=development DEBUG=pw:browser npm run build",
41-
"test:e2e:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- vitest run tests/playwright/src/specs/ --pool=threads --poolOptions.threads.singleThread --poolOptions.threads.isolate --no-file-parallelism",
41+
"test:e2e:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test tests/playwright/src/specs/",
4242
"test:e2e:smoke": "npm run test:e2e:build && npm run test:e2e:smoke:run",
43-
"test:e2e:smoke:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- vitest run tests/playwright/src/specs/*smoke.spec.ts --pool=threads --poolOptions.threads.singleThread --poolOptions.threads.isolate --no-file-parallelism",
43+
"test:e2e:smoke:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test tests/playwright/src/specs/ -g @smoke",
4444
"test:e2e:extension": "npm run test:e2e:build && npm run test:e2e:extension:run",
45-
"test:e2e:extension:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- vitest run tests/playwright/src/specs/extension-installation.spec.ts --pool=threads --poolOptions.threads.singleThread --poolOptions.threads.isolate --no-file-parallelism",
45+
"test:e2e:extension:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test tests/playwright/src/specs/extension-installation.spec.ts",
46+
"test:e2e:pw": "npm run test:e2e:build && npm run test:e2e:pw:run",
47+
"test:e2e:pw:run": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test tests/playwright/src/specs/container-smoke.spec.ts",
4648
"test:e2e:copy": "cp ../podman-desktop-extension-bootc/tests/src/bootc-extension.spec.ts tests/src/",
4749
"test:main": "vitest run -r packages/main --passWithNoTests --coverage",
4850
"test:preload": "vitest run -r packages/preload --passWithNoTests --coverage",
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**********************************************************************
2-
* Copyright (C) 2023-2024-2024 Red Hat, Inc.
2+
* Copyright (C) 2024 Red Hat, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,15 +16,25 @@
1616
* SPDX-License-Identifier: Apache-2.0
1717
***********************************************************************/
1818

19-
import { afterEach, beforeEach, onTestFailed } from 'vitest';
19+
import { defineConfig, devices } from '@playwright/test';
2020

21-
import type { RunnerTestContext } from '../testContext/runner-test-context';
22-
import { takeScreenshotHook } from './extended-hooks-utils';
21+
export default defineConfig({
22+
outputDir: 'tests/playwright/output/',
23+
workers: 1,
2324

24-
afterEach(async (context: RunnerTestContext) => {
25-
onTestFailed(async () => await takeScreenshotHook(context.pdRunner, context.task.name));
26-
});
25+
reporter: [
26+
['list'],
27+
['junit', { outputFile: 'tests/playwright/output/junit-results.xml' }],
28+
['json', { outputFile: 'tests/playwright/output/json-results.json' }],
29+
['html', { open: 'never', outputFolder: 'tests/playwright/output/html-results/' }],
30+
],
2731

28-
beforeEach(async (context: RunnerTestContext) => {
29-
context?.pdRunner?.setTestPassed(true);
32+
projects: [
33+
{
34+
name: 'chromium',
35+
use: {
36+
...devices['Desktop Chrome'],
37+
},
38+
},
39+
],
3040
});

tests/playwright/src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
// export core modules
2020
export * from './globalSetup/global-setup';
2121
export * from './runner/podman-desktop-runner';
22-
export * from './setupFiles/extended-hooks-utils';
2322
export * from './setupFiles/setup-registry';
2423
export type { RunnerTestContext } from './testContext/runner-test-context';
2524
export * from './utility/cleanup';

tests/playwright/src/model/pages/extensions-page.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import { expect as playExpect } from '@playwright/test';
2121

2222
import { ExtensionCardPage } from './extension-card-page';
2323
import type { ExtensionDetailsPage } from './extension-details-page';
24-
import { MainPage } from './main-page';
2524

26-
export class ExtensionsPage extends MainPage {
25+
export class ExtensionsPage {
26+
readonly page: Page;
2727
readonly heading: Locator;
2828
readonly header: Locator;
2929
readonly content: Locator;
@@ -33,7 +33,7 @@ export class ExtensionsPage extends MainPage {
3333
readonly installExtensionFromOCIImageButton: Locator;
3434

3535
constructor(page: Page) {
36-
super(page, 'extensions');
36+
this.page = page;
3737
this.header = page.getByRole('region', { name: 'header' });
3838
this.content = page.getByRole('region', { name: 'content' });
3939
this.heading = this.header.getByLabel('Title').getByText('extensions');

tests/playwright/src/runner/podman-desktop-runner.ts

+4-20
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2020
import { dirname, join, resolve } from 'node:path';
2121

2222
import type { ElectronApplication, JSHandle, Page } from '@playwright/test';
23-
import { _electron as electron } from '@playwright/test';
23+
import { _electron as electron, test } from '@playwright/test';
2424
import type { BrowserWindow } from 'electron';
2525

2626
import { waitWhile } from '../utility/wait';
@@ -42,8 +42,6 @@ export class PodmanDesktopRunner {
4242
private _videoAndTraceName: string | undefined;
4343
private _autoUpdate: boolean;
4444
private _autoCheckUpdate: boolean;
45-
private _testPassed: boolean;
46-
private _suitePassed: boolean;
4745

4846
constructor({
4947
profile = '',
@@ -53,13 +51,11 @@ export class PodmanDesktopRunner {
5351
}: { profile?: string; customFolder?: string; autoUpdate?: boolean; autoCheckUpdate?: boolean } = {}) {
5452
this._running = false;
5553
this._profile = profile;
56-
this._testOutput = join('tests', 'output', this._profile);
54+
this._testOutput = join('tests', 'playwright', 'output', this._profile);
5755
this._customFolder = join(this._testOutput, customFolder);
5856
this._videoAndTraceName = undefined;
5957
this._autoUpdate = autoUpdate;
6058
this._autoCheckUpdate = autoCheckUpdate;
61-
this._testPassed = false;
62-
this._suitePassed = true;
6359

6460
// Options setting always needs to be last action in constructor in order to apply settings correctly
6561
this._options = this.defaultOptions();
@@ -138,7 +134,7 @@ export class PodmanDesktopRunner {
138134
}
139135

140136
public async startTracing(): Promise<void> {
141-
await this.getPage().context().tracing.start({ screenshots: true, snapshots: true });
137+
await this.getPage().context().tracing.start({ screenshots: true, snapshots: true, sources: true });
142138
}
143139

144140
public async stopTracing(): Promise<void> {
@@ -237,7 +233,7 @@ export class PodmanDesktopRunner {
237233
rmSync(rawTracesPath, { recursive: true, force: true, maxRetries: 5 });
238234
}
239235

240-
if (!this._testPassed || !this._suitePassed || !this._videoAndTraceName) return;
236+
if (test.info().status === 'failed') return;
241237

242238
if (!process.env.KEEP_TRACES_ON_PASS) {
243239
const tracesPath = join(this._testOutput, 'traces', `${this._videoAndTraceName}_trace.zip`);
@@ -342,18 +338,6 @@ export class PodmanDesktopRunner {
342338
return this._testOutput;
343339
}
344340

345-
public getTestPassed(): boolean {
346-
return this._testPassed;
347-
}
348-
349-
public setTestPassed(value: boolean): void {
350-
this._testPassed = value;
351-
}
352-
353-
public setSuitePassed(value: boolean): void {
354-
this._suitePassed = value;
355-
}
356-
357341
public get options(): object {
358342
return this._options;
359343
}

tests/playwright/src/setupFiles/extended-hooks-utils.ts

-45
This file was deleted.

tests/playwright/src/specs/compose-onboarding-smoke.spec.ts

+13-15
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,8 @@
1616
* SPDX-License-Identifier: Apache-2.0
1717
***********************************************************************/
1818

19-
import { afterEach } from 'node:test';
20-
2119
import type { Page } from '@playwright/test';
22-
import { expect as playExpect } from '@playwright/test';
23-
import { afterAll, beforeAll, beforeEach, describe, test } from 'vitest';
20+
import { expect as playExpect, test } from '@playwright/test';
2421

2522
import { CLIToolsPage } from '../model/pages/cli-tools-page';
2623
import { ComposeLocalInstallPage } from '../model/pages/compose-onboarding/compose-local-install-page';
@@ -33,7 +30,6 @@ import { SettingsBar } from '../model/pages/settings-bar';
3330
import { WelcomePage } from '../model/pages/welcome-page';
3431
import { NavigationBar } from '../model/workbench/navigation';
3532
import { PodmanDesktopRunner } from '../runner/podman-desktop-runner';
36-
import type { RunnerTestContext } from '../testContext/runner-test-context';
3733
import { isCI, isLinux } from '../utility/platform';
3834

3935
const RESOURCE_NAME: string = 'Compose';
@@ -45,7 +41,9 @@ let composeVersion: string;
4541
// property that will make sure that on linux we can run only partial tests, by default this is turned off
4642
const composePartialInstallation = process.env.COMPOSE_PARTIAL_INSTALL ? process.env.COMPOSE_PARTIAL_INSTALL : false;
4743

48-
beforeAll(async () => {
44+
test.skip(!!isCI && isLinux, 'Tests suite should not run on Linux platform');
45+
46+
test.beforeAll(async () => {
4947
pdRunner = new PodmanDesktopRunner();
5048
page = await pdRunner.start();
5149
pdRunner.setVideoAndTraceName('compose-onboarding-e2e');
@@ -55,16 +53,12 @@ beforeAll(async () => {
5553
navBar = new NavigationBar(page);
5654
});
5755

58-
afterAll(async () => {
56+
test.afterAll(async () => {
5957
await pdRunner.close();
6058
});
6159

62-
beforeEach<RunnerTestContext>(async ctx => {
63-
ctx.pdRunner = pdRunner;
64-
});
65-
66-
describe.skipIf(isCI && isLinux)('Compose onboarding workflow verification', async () => {
67-
afterEach(async () => {
60+
test.describe.serial('Compose onboarding workflow verification @smoke', () => {
61+
test.afterEach(async () => {
6862
await navBar.openDashboard();
6963
});
7064

@@ -125,7 +119,9 @@ describe.skipIf(isCI && isLinux)('Compose onboarding workflow verification', asy
125119
await skipOkButton.click();
126120
});
127121

128-
test.skipIf(composePartialInstallation)('Can install Compose system-wide', async () => {
122+
test('Can install Compose system-wide', async () => {
123+
test.skip(!!composePartialInstallation, 'Partial installation of Compose is enabled');
124+
129125
const onboardingPage = await openComposeOnboarding(page);
130126
await onboardingPage.nextStepButton.click();
131127

@@ -140,7 +136,9 @@ describe.skipIf(isCI && isLinux)('Compose onboarding workflow verification', asy
140136
await playExpect(resourcesPage.heading).toBeVisible();
141137
});
142138

143-
test.skipIf(composePartialInstallation)('Verify Compose was installed', async () => {
139+
test('Verify Compose was installed', async () => {
140+
test.skip(!!composePartialInstallation, 'Partial installation of Compose is enabled');
141+
144142
await navBar.openSettings();
145143
const settingsBar = new SettingsBar(page);
146144
const resourcesPage = await settingsBar.openTabPage(ResourcesPage);

tests/playwright/src/specs/container-smoke.spec.ts

+15-12
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
***********************************************************************/
1818

1919
import type { Page } from '@playwright/test';
20-
import { expect as playExpect } from '@playwright/test';
21-
import { afterAll, beforeAll, beforeEach, describe, test } from 'vitest';
20+
import { expect as playExpect, test } from '@playwright/test';
2221

2322
import { ContainerState } from '../model/core/states';
2423
import type { ContainerInteractiveParams } from '../model/core/types';
@@ -28,7 +27,6 @@ import type { ImagesPage } from '../model/pages/images-page';
2827
import { WelcomePage } from '../model/pages/welcome-page';
2928
import { NavigationBar } from '../model/workbench/navigation';
3029
import { PodmanDesktopRunner } from '../runner/podman-desktop-runner';
31-
import type { RunnerTestContext } from '../testContext/runner-test-context';
3230
import { deleteContainer, deleteImage } from '../utility/operations';
3331
import { waitForPodmanMachineStartup, waitWhile } from '../utility/wait';
3432

@@ -40,7 +38,7 @@ const containerToRun = 'alpine-container';
4038
const containerList = ['first', 'second', 'third'];
4139
const containerStartParams: ContainerInteractiveParams = { attachTerminal: false };
4240

43-
beforeAll(async () => {
41+
test.beforeAll(async () => {
4442
pdRunner = new PodmanDesktopRunner();
4543
page = await pdRunner.start();
4644
pdRunner.setVideoAndTraceName('containers-e2e');
@@ -67,11 +65,9 @@ beforeAll(async () => {
6765
}
6866
});
6967

70-
beforeEach<RunnerTestContext>(async ctx => {
71-
ctx.pdRunner = pdRunner;
72-
});
68+
test.afterAll(async () => {
69+
test.setTimeout(90000);
7370

74-
afterAll(async () => {
7571
try {
7672
await deleteContainer(page, containerToRun);
7773
for (const container of containerList) {
@@ -82,10 +78,14 @@ afterAll(async () => {
8278
} finally {
8379
await pdRunner.close();
8480
}
85-
}, 90000);
81+
});
82+
83+
test.describe.serial('Verification of container creation workflow @smoke', () => {
84+
test.describe.configure({ retries: 2 });
85+
86+
test(`Pulling of '${imageToPull}:${imageTag}' image`, async () => {
87+
test.setTimeout(90000);
8688

87-
describe('Verification of container creation workflow', async () => {
88-
test(`Pulling of '${imageToPull}:${imageTag}' image`, { retry: 2, timeout: 90000 }, async () => {
8989
const navigationBar = new NavigationBar(page);
9090
let images = await navigationBar.openImages();
9191
const pullImagePage = await images.openPullImage();
@@ -114,6 +114,7 @@ describe('Verification of container creation workflow', async () => {
114114
images = await navigationBar.openImages();
115115
playExpect(await images.getCurrentStatusOfImage(imageToPull)).toBe('USED');
116116
});
117+
117118
test('Test navigation between pages', async () => {
118119
const navigationBar = new NavigationBar(page);
119120
const containers = await navigationBar.openContainers();
@@ -253,6 +254,8 @@ describe('Verification of container creation workflow', async () => {
253254
});
254255

255256
test('Prune containers', async () => {
257+
test.setTimeout(120000);
258+
256259
const navigationBar = new NavigationBar(page);
257260
//Start 3 containers
258261
for (const container of containerList) {
@@ -300,5 +303,5 @@ describe('Verification of container creation workflow', async () => {
300303
.poll(async () => await containersPage.containerExists(container), { timeout: 30000 })
301304
.toBeFalsy();
302305
}
303-
}, 120000);
306+
});
304307
});

0 commit comments

Comments
 (0)