chore(test): refactoring of runner factory class (#17045)

* chore(test): refactoring of runner factory class
This commit is contained in:
Vladimir Lazar 2026-04-14 13:31:41 +02:00 committed by GitHub
parent a0191823a1
commit 0eaf131dd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 63 additions and 41 deletions

1
.gitignore vendored
View file

@ -16,3 +16,4 @@ yarn.lock
extensions/podman/assets/
extensions-extra/
__mocks__/@podman-desktop/api.ts
contexts/meta/

View file

@ -23,10 +23,9 @@ import type { ElectronApplication, JSHandle, Page } from '@playwright/test';
import { _electron as electron } from '@playwright/test';
import type { BrowserWindow } from 'electron';
import { Runner } from '/@/runner/podman-desktop-runner';
import { RunnerFactory } from '/@/runner/runner-factory';
import { Runner } from './podman-desktop-runner';
import type { RunnerOptions } from './runner-options';
import type { RunnerOptions } from '/@/runner/runner-options';
type WindowState = {
isVisible: boolean;

View file

@ -15,42 +15,58 @@
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
/** biome-ignore-all lint/complexity/noStaticOnlyClass: <Singleton pattern> */
import { ChromeDevToolsProtocolRunner } from '/@/runner/chrome-dev-tools-protocol-runner';
import { ElectronRunner } from '/@/runner/electron-runner';
import type { Runner } from '/@/runner/podman-desktop-runner';
import { RunnerOptions } from '/@/runner/runner-options';
export class RunnerFactory {
protected static _instance: Runner | undefined;
private static _instance: Runner | undefined;
private static _initializing: Promise<Runner> | undefined;
static async getInstance({
runnerOptions = new RunnerOptions(),
}: {
runnerOptions?: RunnerOptions;
} = {}): Promise<Runner> {
if (!this._instance) {
const pdArgs = process.env.PODMAN_DESKTOP_ARGS;
const pdBinary = process.env.PODMAN_DESKTOP_BINARY;
const debugPort = process.env.DEBUGGING_PORT;
if (
(pdArgs && !pdBinary && !debugPort) ||
(pdBinary && !pdArgs && !debugPort) ||
(!pdArgs && !pdBinary && !debugPort)
) {
this._instance = new ElectronRunner({ runnerOptions });
} else if (pdBinary && debugPort) {
this._instance = new ChromeDevToolsProtocolRunner({ runnerOptions });
} else {
throw new Error(
'Allowed combinations are standalone PODMAN_DESKTOP_ARGS or PODMAN_DESKTOP_BINARY or neither of them for electron runner. Or PODMAN_DESKTOP_BINARY and DEBUGGING_PORT for CDP runner...',
);
}
await this._instance.start();
static async getInstance(runnerOptions?: RunnerOptions): Promise<Runner> {
if (RunnerFactory._instance) return RunnerFactory._instance;
if (RunnerFactory._initializing) return RunnerFactory._initializing;
RunnerFactory._initializing = RunnerFactory.create(runnerOptions ?? new RunnerOptions());
try {
RunnerFactory._instance = await RunnerFactory._initializing;
return RunnerFactory._instance;
} finally {
RunnerFactory._initializing = undefined;
}
return this._instance;
}
private static async create(runnerOptions: RunnerOptions): Promise<Runner> {
const runner = RunnerFactory.resolveRunner(runnerOptions);
await runner.start();
return runner;
}
private static resolveRunner(runnerOptions: RunnerOptions): Runner {
const pdArgs = process.env.PODMAN_DESKTOP_ARGS;
const pdBinary = process.env.PODMAN_DESKTOP_BINARY;
const debugPort = process.env.DEBUGGING_PORT;
if (pdArgs && pdBinary) {
throw new Error('PODMAN_DESKTOP_ARGS and PODMAN_DESKTOP_BINARY are mutually exclusive');
}
if (pdArgs && debugPort) {
throw new Error('DEBUGGING_PORT requires PODMAN_DESKTOP_BINARY, not PODMAN_DESKTOP_ARGS');
}
if (debugPort && !pdBinary) {
throw new Error('DEBUGGING_PORT requires PODMAN_DESKTOP_BINARY to be set');
}
if (pdBinary && debugPort) {
return new ChromeDevToolsProtocolRunner({ runnerOptions });
}
return new ElectronRunner({ runnerOptions });
}
static dispose(): void {
this._instance = undefined;
RunnerFactory._instance = undefined;
RunnerFactory._initializing = undefined;
}
}

View file

@ -116,18 +116,24 @@ test.describe
await playExpect(imagesPage.heading).toBeVisible({ timeout: 5_000 });
await navigationBar.goBack(); // Now on Containers with forward available
await playExpect(containerPage.heading).toBeVisible({ timeout: 5_000 });
// Simulate trackpad swipe left (deltaX > 30 for forward)
await page.evaluate(() => {
const event = new WheelEvent('wheel', {
deltaX: 50, // > 30 threshold for forward navigation
deltaY: 0,
bubbles: true,
});
document.body.dispatchEvent(event);
});
// Verify navigation to Images
await playExpect(imagesPage.heading).toBeVisible({ timeout: 5_000 });
const forwardButton = page.getByRole('button', { name: 'Forward (hold for history)' });
await playExpect(forwardButton).toBeEnabled({ timeout: 5_000 });
// The app has a 500ms swipe cooldown (NavigationButtons.handleWheel) that may
// still be active from the previous trackpad-swipe-left test. Poll the dispatch
// so the event is retried once the cooldown expires.
await playExpect
.poll(
async () => {
await page.evaluate(() => {
document.body.dispatchEvent(new WheelEvent('wheel', { deltaX: 50, deltaY: 0, bubbles: true }));
});
return imagesPage.heading.isVisible();
},
{ timeout: 5_000 },
)
.toBeTruthy();
});
test('Navigation shortcuts blocked when focus in input field', async ({ navigationBar, page }) => {

View file

@ -41,7 +41,7 @@ export type FixtureOptions = {
export const test = base.extend<TestFixtures & FixtureOptions>({
runnerOptions: [new RunnerOptions(), { option: true }],
runner: async ({ runnerOptions }, use) => {
const runner = await RunnerFactory.getInstance({ runnerOptions });
const runner = await RunnerFactory.getInstance(runnerOptions);
await use(runner);
},
page: async ({ runner }, use) => {