mirror of
https://github.com/podman-desktop/podman-desktop
synced 2026-04-21 17:47:22 +00:00
chore(tests): introduce globalSetup file and extract shared configuration (#3291)
* feat(tests): introduce globalSetup, extract shared configuration, page object model and podman desktop runner fixes #3288, #3290, #3302 Signed-off-by: Ondrej Dockal <odockal@redhat.com>
This commit is contained in:
parent
5b79dca233
commit
13d2c3f4c6
8 changed files with 360 additions and 50 deletions
|
|
@ -1,51 +1,46 @@
|
|||
/**********************************************************************
|
||||
* 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 { BrowserWindow } from 'electron';
|
||||
import type { ElectronApplication, JSHandle, Page } from 'playwright';
|
||||
import { _electron as electron } from 'playwright';
|
||||
import type { JSHandle, Page } from 'playwright';
|
||||
import { afterAll, beforeAll, expect, test, describe } from 'vitest';
|
||||
import { expect as playExpect } from '@playwright/test';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { rm } from 'node:fs/promises';
|
||||
import { PodmanDesktopRunner } from './runner/podman-desktop-runner';
|
||||
import { WelcomePage } from './model/pages/welcome-page';
|
||||
import { DashboardPage } from './model/pages/dashboard-page';
|
||||
|
||||
const navBarItems = ['Dashboard', 'Containers', 'Images', 'Pods', 'Volumes', 'Settings'];
|
||||
let electronApp: ElectronApplication;
|
||||
let pdRunner: PodmanDesktopRunner;
|
||||
let page: Page;
|
||||
|
||||
beforeAll(async () => {
|
||||
// remove all videos/screenshots
|
||||
if (existsSync('tests/output')) {
|
||||
console.log('Cleaning up output folder...');
|
||||
await rm('tests/output', { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const env: { [key: string]: string } = Object.assign({}, process.env as { [key: string]: string });
|
||||
env.PODMAN_DESKTOP_HOME_DIR = 'tests/output/podman-desktop';
|
||||
|
||||
electronApp = await electron.launch({
|
||||
args: ['.'],
|
||||
env,
|
||||
recordVideo: {
|
||||
dir: 'tests/output/videos',
|
||||
size: {
|
||||
width: 1050,
|
||||
height: 700,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
page = await electronApp.firstWindow();
|
||||
pdRunner = new PodmanDesktopRunner();
|
||||
page = await pdRunner.start();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await electronApp.close();
|
||||
await pdRunner.close();
|
||||
});
|
||||
|
||||
describe('Basic e2e verification of podman desktop start', async () => {
|
||||
describe('Welcome page handling', async () => {
|
||||
test('Check the Welcome page is displayed', async () => {
|
||||
// Direct Electron console to Node terminal.
|
||||
page.on('console', console.log);
|
||||
|
||||
const window: JSHandle<BrowserWindow> = await electronApp.browserWindow(page);
|
||||
const window: JSHandle<BrowserWindow> = await pdRunner.getBrowserWindow();
|
||||
|
||||
const windowState = await window.evaluate(
|
||||
(mainWindow): Promise<{ isVisible: boolean; isDevToolsOpened: boolean; isCrashed: boolean }> => {
|
||||
|
|
@ -69,40 +64,37 @@ describe('Basic e2e verification of podman desktop start', async () => {
|
|||
expect(windowState.isCrashed, 'The app has crashed').toBeFalsy();
|
||||
expect(windowState.isVisible, 'The main window was not visible').toBeTruthy();
|
||||
|
||||
await page.screenshot({ path: 'tests/output/screenshots/screenshot-welcome-page-init.png', fullPage: true });
|
||||
await pdRunner.screenshot('welcome-page-init.png');
|
||||
|
||||
const welcomeMessage = page.locator('text=/Welcome to Podman Desktop.*/');
|
||||
await playExpect(welcomeMessage).toBeVisible();
|
||||
const welcomePage = new WelcomePage(page);
|
||||
await playExpect(welcomePage.welcomeMessage).toBeVisible();
|
||||
});
|
||||
|
||||
test('Telemetry checkbox is present, set to true, consent can be changed', async () => {
|
||||
// wait for the initial screen to be loaded
|
||||
const telemetryConsent = page.getByText('Telemetry');
|
||||
expect(telemetryConsent).not.undefined;
|
||||
expect(await telemetryConsent.isChecked()).to.be.true;
|
||||
const welcomePage = new WelcomePage(page);
|
||||
await playExpect(welcomePage.telemetryConsent).toBeVisible();
|
||||
playExpect(await welcomePage.telemetryConsent.isChecked()).toBeTruthy();
|
||||
|
||||
await telemetryConsent.click();
|
||||
expect(await telemetryConsent.isChecked()).to.be.false;
|
||||
await welcomePage.turnOffTelemetry();
|
||||
playExpect(await welcomePage.telemetryConsent.isChecked()).toBeFalsy();
|
||||
});
|
||||
|
||||
test('Redirection from Welcome page to Dashboard works', async () => {
|
||||
const goToPodmanDesktopButton = page.locator('button:text("Go to Podman Desktop")');
|
||||
const welcomePage = new WelcomePage(page);
|
||||
// wait for visibility
|
||||
await goToPodmanDesktopButton.waitFor({ state: 'visible' });
|
||||
await welcomePage.goToPodmanDesktopButton.waitFor({ state: 'visible' });
|
||||
|
||||
await page.screenshot({ path: 'tests/output/screenshots/screenshot-welcome-page-display.png', fullPage: true });
|
||||
await pdRunner.screenshot('welcome-page-display.png');
|
||||
|
||||
// click on the button
|
||||
await goToPodmanDesktopButton.click();
|
||||
await welcomePage.goToPodmanDesktopButton.click();
|
||||
|
||||
await page.screenshot({
|
||||
path: 'tests/output/screenshots/screenshot-welcome-page-redirect-to-dashboard.png',
|
||||
fullPage: true,
|
||||
});
|
||||
await pdRunner.screenshot('welcome-page-redirect-to-dashboard.png');
|
||||
|
||||
// check we have the dashboard page
|
||||
const dashboardTitle = page.getByRole('heading', { name: 'Dashboard' });
|
||||
await playExpect(dashboardTitle).toBeVisible();
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await playExpect(dashboardPage.heading).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
37
tests/src/globalSetup/global-setup.ts
Normal file
37
tests/src/globalSetup/global-setup.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/**********************************************************************
|
||||
* 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 { removeFolderIfExists } from '../utility/cleanup';
|
||||
|
||||
let setupCalled = false;
|
||||
let teardownCalled = false;
|
||||
|
||||
export async function setup() {
|
||||
if (!setupCalled) {
|
||||
// remove all previous testing output files
|
||||
await removeFolderIfExists('tests/output');
|
||||
setupCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
export async function teardown() {
|
||||
if (!teardownCalled) {
|
||||
// here comes teardown logic
|
||||
teardownCalled = true;
|
||||
}
|
||||
}
|
||||
31
tests/src/model/pages/base-page.ts
Normal file
31
tests/src/model/pages/base-page.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**********************************************************************
|
||||
* 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 { Page } from 'playwright';
|
||||
|
||||
export class PodmanDesktopPage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public getPage(): Page {
|
||||
return this.page;
|
||||
}
|
||||
}
|
||||
29
tests/src/model/pages/dashboard-page.ts
Normal file
29
tests/src/model/pages/dashboard-page.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**********************************************************************
|
||||
* 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';
|
||||
import { PodmanDesktopPage } from './base-page';
|
||||
|
||||
export class DashboardPage extends PodmanDesktopPage {
|
||||
readonly heading: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.heading = page.getByRole('heading', { name: 'Dashboard' });
|
||||
}
|
||||
}
|
||||
58
tests/src/model/pages/welcome-page.ts
Normal file
58
tests/src/model/pages/welcome-page.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**********************************************************************
|
||||
* 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';
|
||||
import { PodmanDesktopPage } from './base-page';
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
export class WelcomePage extends PodmanDesktopPage {
|
||||
readonly welcomeMessage: Locator;
|
||||
readonly telemetryConsent: Locator;
|
||||
readonly goToPodmanDesktopButton: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.welcomeMessage = page.getByText('Welcome to Podman Desktop');
|
||||
this.telemetryConsent = page.getByText('Telemetry');
|
||||
this.goToPodmanDesktopButton = page.getByRole('button', { name: 'Go to Podman Desktop', exact: true });
|
||||
}
|
||||
|
||||
async turnOffTelemetry() {
|
||||
await this.telemetryConsent.uncheck();
|
||||
}
|
||||
|
||||
async closeWelcomePage() {
|
||||
await this.goToPodmanDesktopButton.click();
|
||||
}
|
||||
|
||||
async waitForInitialization() {
|
||||
// wait for an application to initialize
|
||||
const checkLoader = this.page.getByRole('heading', { name: 'Initializing...' });
|
||||
await expect(checkLoader).toHaveCount(0, { timeout: 5000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for application to initialize, turn off telemetry and closes welcome page
|
||||
*/
|
||||
async handleWelcomePage() {
|
||||
await this.waitForInitialization();
|
||||
await this.turnOffTelemetry();
|
||||
await this.closeWelcomePage();
|
||||
await expect(this.welcomeMessage).toHaveCount(0, { timeout: 3000 });
|
||||
}
|
||||
}
|
||||
131
tests/src/runner/podman-desktop-runner.ts
Normal file
131
tests/src/runner/podman-desktop-runner.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/**********************************************************************
|
||||
* 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 { ElectronApplication, JSHandle, Page } from 'playwright';
|
||||
import { _electron as electron } from 'playwright';
|
||||
import { join } from 'node:path';
|
||||
import type { BrowserWindow } from 'electron';
|
||||
|
||||
export class PodmanDesktopRunner {
|
||||
private _options: object;
|
||||
private _running: boolean;
|
||||
private _app: ElectronApplication | undefined;
|
||||
private _page: Page | undefined;
|
||||
private readonly _profile: string;
|
||||
private readonly _testOutput: string;
|
||||
|
||||
constructor(profile = '') {
|
||||
this._running = false;
|
||||
this._profile = profile;
|
||||
this._testOutput = join('tests', 'output', this._profile);
|
||||
this._options = this.defaultOptions();
|
||||
}
|
||||
|
||||
public async start(): Promise<Page> {
|
||||
if (this.isRunning()) {
|
||||
throw Error('Podman Desktop is already running');
|
||||
}
|
||||
|
||||
// start the app with given properties
|
||||
this._app = await electron.launch({
|
||||
...this._options,
|
||||
});
|
||||
// setup state
|
||||
this._running = true;
|
||||
this._page = await this.getElectronApp().firstWindow();
|
||||
// Direct Electron console to Node terminal.
|
||||
this.getPage().on('console', console.log);
|
||||
|
||||
return this._page;
|
||||
}
|
||||
|
||||
public getPage(): Page {
|
||||
if (this._page) {
|
||||
return this._page;
|
||||
} else {
|
||||
throw Error('Application was not started yet');
|
||||
}
|
||||
}
|
||||
|
||||
public getElectronApp(): ElectronApplication {
|
||||
if (this._app) {
|
||||
return this._app;
|
||||
} else {
|
||||
throw Error('Application was not started yet');
|
||||
}
|
||||
}
|
||||
|
||||
public async getBrowserWindow(): Promise<JSHandle<BrowserWindow>> {
|
||||
return await this.getElectronApp().browserWindow(this.getPage());
|
||||
}
|
||||
|
||||
public async screenshot(filename: string) {
|
||||
await this.getPage().screenshot({ path: join(this._testOutput, 'screenshots', filename), fullPage: true });
|
||||
}
|
||||
|
||||
public async close() {
|
||||
if (!this.isRunning()) {
|
||||
throw Error('Podman Desktop is not running');
|
||||
}
|
||||
if (this.getElectronApp()) {
|
||||
await this.getElectronApp().close();
|
||||
}
|
||||
this._running = false;
|
||||
}
|
||||
|
||||
private defaultOptions() {
|
||||
const directory = join(this._testOutput, 'videos');
|
||||
console.log(`video will be written to: ${directory}`);
|
||||
const env = this.setupPodmanDesktopCustomFolder();
|
||||
return {
|
||||
args: ['.'],
|
||||
env,
|
||||
recordVideo: {
|
||||
dir: directory,
|
||||
size: {
|
||||
width: 1050,
|
||||
height: 700,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private setupPodmanDesktopCustomFolder(): object {
|
||||
const env: { [key: string]: string } = Object.assign({}, process.env as { [key: string]: string });
|
||||
const dir = join(this._testOutput, 'podman-desktop');
|
||||
console.log(`podman desktop custom config will be written to: ${dir}`);
|
||||
env.PODMAN_DESKTOP_HOME_DIR = dir;
|
||||
return env;
|
||||
}
|
||||
|
||||
public isRunning(): boolean {
|
||||
return this._running;
|
||||
}
|
||||
|
||||
public setOptions(value: object) {
|
||||
this._options = value;
|
||||
}
|
||||
|
||||
public getTestOutput(): string {
|
||||
return this._testOutput;
|
||||
}
|
||||
|
||||
public get options(): object {
|
||||
return this._options;
|
||||
}
|
||||
}
|
||||
31
tests/src/utility/cleanup.ts
Normal file
31
tests/src/utility/cleanup.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**********************************************************************
|
||||
* 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 { existsSync } from 'node:fs';
|
||||
import { rm } from 'node:fs/promises';
|
||||
|
||||
/**
|
||||
* Force remove recursively folder, if exists
|
||||
* @param path path to a folder to be force removed recursively
|
||||
*/
|
||||
export async function removeFolderIfExists(path: string) {
|
||||
if (existsSync(path)) {
|
||||
console.log(`Cleaning up folder: ${path}`);
|
||||
await rm(path, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ const config = {
|
|||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
globalSetup: './tests/src/globalSetup/global-setup.ts',
|
||||
/**
|
||||
* By default, vitest search test files in all packages.
|
||||
* For e2e tests have sense search only is project root tests folder
|
||||
|
|
|
|||
Loading…
Reference in a new issue