Skip to content

Commit

Permalink
refactor(e2e): Refactor fixtures (#17287)
Browse files Browse the repository at this point in the history
* refactor(e2e): Refactor fixtures

Split it to suiteBase and pageObjects + mocks
fixure file slimmed down
metadataProviderMock.stop() moved to fixture teardown
untangle page fixture
* refactor(e2e): Split of common.ts to common and electron
* refactor(e2e): Pass fixtures obj thru constructors
  • Loading branch information
Vere-Grey authored Feb 28, 2025
1 parent 5c8b991 commit 5c97b17
Show file tree
Hide file tree
Showing 26 changed files with 362 additions and 346 deletions.
117 changes: 1 addition & 116 deletions packages/suite-desktop-core/e2e/support/common.ts
Original file line number Diff line number Diff line change
@@ -1,113 +1,11 @@
/* eslint-disable no-console */

import test, { TestInfo, _electron as electron } from '@playwright/test';
import { readdirSync, removeSync } from 'fs-extra';
import test, { TestInfo } from '@playwright/test';
import { isEqual, omit } from 'lodash';
import path from 'path';

import { TrezorUserEnvLink } from '@trezor/trezor-user-env-link';
import { splitStringEveryNCharacters } from '@trezor/utils';

import { PlaywrightProjects } from '../playwright.config';

// specific version of legacy bridge is requested & expected
export const LEGACY_BRIDGE_VERSION = '2.0.33';
const disableHashCheckArgument = '--state.suite.settings.isFirmwareHashCheckDisabled=true';
const showDebugMenuArgument = `--state.suite.settings.debug.showDebugMenu=true`;

type LaunchSuiteParams = {
rmUserData?: boolean;
bridgeLegacyTest?: boolean;
bridgeDaemon?: boolean;
locale?: string;
colorScheme?: 'light' | 'dark' | 'no-preference' | null | undefined;
videoFolder: string;
viewport: { width: number; height: number };
};

const formatErrorLogMessage = (data: string) => {
const red = '\x1b[31m';
const reset = '\x1b[0m';

return `${red}${data}${reset}`;
};

export const launchSuiteElectronApp = async (params: LaunchSuiteParams) => {
const defaultParams = {
rmUserData: true,
bridgeLegacyTest: true,
bridgeDaemon: false,
};
const options = Object.assign(defaultParams, params);

const appDir = path.join(__dirname, '../../../suite-desktop');
const logLevelArgument = `--log-level=${process.env.LOGLEVEL ?? 'error'}`;
const viewportArgument = `--width=${options.viewport.width} --height=${options.viewport.height}`;
if (!options.bridgeDaemon) {
// TODO: #15646 Find out why currently pw fails to see node-bridge so we default to legacy bridge.
await TrezorUserEnvLink.startBridge(LEGACY_BRIDGE_VERSION);
}
const electronApp = await electron.launch({
cwd: appDir,
args: [
path.join(appDir, './dist/app.js'),
disableHashCheckArgument,
showDebugMenuArgument,
viewportArgument,
logLevelArgument,
...(options.bridgeLegacyTest ? ['--bridge-legacy', '--bridge-test'] : []),
...(options.bridgeDaemon ? ['--bridge-daemon', '--skip-new-bridge-rollout'] : []),
],
colorScheme: params.colorScheme,
locale: params.locale,
recordVideo: { dir: options.videoFolder, size: options.viewport },
});

const localDataDir = await electronApp.evaluate(({ app }) => app.getPath('userData'));

if (options.rmUserData) {
const filesToDelete = readdirSync(localDataDir);
filesToDelete.forEach(file => {
// omitting Cache folder it sometimes prevents the deletion and is not necessary to delete for test idempotency
if (file !== 'Cache') {
try {
removeSync(`${localDataDir}/${file}`);
} catch {
// If files does not exist do nothing.
}
}
});
}

// #15670 Bug in desktop app that loglevel is ignored so we conditionally don't log to stdout
if (process.env.LOGLEVEL) {
electronApp.process().stdout?.on('data', data => console.log(data.toString()));
}
electronApp
.process()
.stderr?.on('data', data => console.error(formatErrorLogMessage(data.toString())));

await electronApp.evaluate(
(_, [resourcesPath]) => {
// This runs in the main Electron process.
// override global variable defined in app.ts
global.resourcesPath = resourcesPath;

return global.resourcesPath;
},
[path.join(appDir, 'build/static')],
);

return electronApp;
};

export const launchSuite = async (params: LaunchSuiteParams) => {
const electronApp = await launchSuiteElectronApp(params);
const window = await electronApp.firstWindow();

return { electronApp, window };
};

export const isDesktopProject = (testInfo: TestInfo) =>
testInfo.project.name === PlaywrightProjects.Desktop;

Expand All @@ -124,19 +22,6 @@ export const getUrl = (testInfo: TestInfo) => {
return apiURL;
};

export const getElectronVideoPath = (videoFolder: string) => {
const videoFilenames = readdirSync(videoFolder).filter(file => file.endsWith('.webm'));
if (videoFilenames.length > 1) {
console.error(
formatErrorLogMessage(
`Warning: More than one electron video file found in the output directory: ${videoFolder}\nAttaching only the first one: ${videoFilenames[0]}`,
),
);
}

return path.join(videoFolder, videoFilenames[0]);
};

// Wraps whole page object methods with test.step
export function step(stepName?: string) {
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
Expand Down
123 changes: 123 additions & 0 deletions packages/suite-desktop-core/e2e/support/electron.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/* eslint-disable no-console */

import { ElectronApplication, Page, _electron as electron } from '@playwright/test';
import { readdirSync, removeSync } from 'fs-extra';
import path from 'path';

import { TrezorUserEnvLink } from '@trezor/trezor-user-env-link';

// specific version of legacy bridge is requested & expected
export const LEGACY_BRIDGE_VERSION = '2.0.33';
const disableHashCheckArgument = '--state.suite.settings.isFirmwareHashCheckDisabled=true';
const showDebugMenuArgument = `--state.suite.settings.debug.showDebugMenu=true`;

type LaunchSuiteParams = {
rmUserData?: boolean;
bridgeLegacyTest?: boolean;
bridgeDaemon?: boolean;
locale?: string;
colorScheme?: 'light' | 'dark' | 'no-preference' | null | undefined;
videoFolder: string;
viewport: { width: number; height: number };
};

export type Suite = {
electronApp: ElectronApplication;
window: Page;
};

const formatErrorLogMessage = (data: string) => {
const red = '\x1b[31m';
const reset = '\x1b[0m';

return `${red}${data}${reset}`;
};

export const launchSuiteElectronApp = async (params: LaunchSuiteParams) => {
const defaultParams = {
rmUserData: true,
bridgeLegacyTest: true,
bridgeDaemon: false,
};
const options = Object.assign(defaultParams, params);

const appDir = path.join(__dirname, '../../../suite-desktop');
const logLevelArgument = `--log-level=${process.env.LOGLEVEL ?? 'error'}`;
const viewportArgument = `--width=${options.viewport.width} --height=${options.viewport.height}`;
if (!options.bridgeDaemon) {
// TODO: #15646 Find out why currently pw fails to see node-bridge so we default to legacy bridge.
await TrezorUserEnvLink.startBridge(LEGACY_BRIDGE_VERSION);
}
const electronApp = await electron.launch({
cwd: appDir,
args: [
path.join(appDir, './dist/app.js'),
disableHashCheckArgument,
showDebugMenuArgument,
viewportArgument,
logLevelArgument,
...(options.bridgeLegacyTest ? ['--bridge-legacy', '--bridge-test'] : []),
...(options.bridgeDaemon ? ['--bridge-daemon', '--skip-new-bridge-rollout'] : []),
],
colorScheme: params.colorScheme,
locale: params.locale,
recordVideo: { dir: options.videoFolder, size: options.viewport },
});

const localDataDir = await electronApp.evaluate(({ app }) => app.getPath('userData'));

if (options.rmUserData) {
const filesToDelete = readdirSync(localDataDir);
filesToDelete.forEach(file => {
// omitting Cache folder it sometimes prevents the deletion and is not necessary to delete for test idempotency
if (file !== 'Cache') {
try {
removeSync(`${localDataDir}/${file}`);
} catch {
// If files does not exist do nothing.
}
}
});
}

// #15670 Bug in desktop app that loglevel is ignored so we conditionally don't log to stdout
if (process.env.LOGLEVEL) {
electronApp.process().stdout?.on('data', data => console.log(data.toString()));
}
electronApp
.process()
.stderr?.on('data', data => console.error(formatErrorLogMessage(data.toString())));

await electronApp.evaluate(
(_, [resourcesPath]) => {
// This runs in the main Electron process.
// override global variable defined in app.ts
global.resourcesPath = resourcesPath;

return global.resourcesPath;
},
[path.join(appDir, 'build/static')],
);

return electronApp;
};

export const launchSuite = async (params: LaunchSuiteParams): Promise<Suite> => {
const electronApp = await launchSuiteElectronApp(params);
const window = await electronApp.firstWindow();

return { electronApp, window };
};

export const getElectronVideoPath = (videoFolder: string) => {
const videoFilenames = readdirSync(videoFolder).filter(file => file.endsWith('.webm'));
if (videoFilenames.length > 1) {
console.error(
formatErrorLogMessage(
`Warning: More than one electron video file found in the output directory: ${videoFolder}\nAttaching only the first one: ${videoFilenames[0]}`,
),
);
}

return path.join(videoFolder, videoFilenames[0]);
};
Loading

0 comments on commit 5c97b17

Please sign in to comment.