feat: migrating electron e2e from spectron (deprecated) to wdio (#32)

This commit is contained in:
Amadou A. DIENE
2022-04-07 09:14:32 +02:00
committed by GitHub
parent 3534d64960
commit d7c7af13f1
18 changed files with 19223 additions and 19134 deletions

3
.gitignore vendored
View File

@@ -88,6 +88,9 @@ typings/
# Electron-Forge
out/
# Allure
allure-results
# System Files
.DS_Store
Thumbs.db

View File

@@ -1,7 +1,9 @@
#!/bin/sh
if [ -z "$husky_skip_init" ]; then
debug () {
[ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1"
if [ "$HUSKY_DEBUG" = "1" ]; then
echo "husky (debug) - $1"
fi
}
readonly hook_name="$(basename "$0")"

View File

@@ -16,8 +16,8 @@ Depending on your need, putting up [Electron](https://www.electronjs.org/) and [
### Main features :
- This project is based on last [Angular 12](https://angular.io/) version with required dependencies for [Electron 13](https://www.electronjs.org/).
- This project is also written in [Typescript 4](https://www.typescriptlang.org/) and includes test samples ([Jasmine](https://jasmine.github.io/) and [Spectron 15](https://www.electronjs.org/spectron)).
- This project is based on last [Angular 12](https://angular.io/) version with required dependencies for [Electron 18](https://www.electronjs.org/).
- This project is also written in [Typescript 4](https://www.typescriptlang.org/) and includes test samples ([WebdriverIO](https://webdriver.io/) and [Jasmine](https://jasmine.github.io/)).
- The app is runnable `on desktop` (with **live-reload** in `development mode`).
- The app is also runnable `on browser` but **without Electron features**.
- You can generate your platform distributables thanks to [`electron-forge`](https://www.electronforge.io/).
@@ -121,8 +121,6 @@ This project architecture is based on [npm workspaces](https://docs.npmjs.com/cl
- all of them
`npm run update-deps`
**NB**: Be sure your `electron` and `spectron` dependencies are matching according to this [version mapping table](https://github.com/electron-userland/spectron#version-map).
### Customizing app icons
```bash

37573
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,11 +11,12 @@
"angular",
"angular 12",
"electron",
"electron 13",
"electron 18",
"typescript",
"typescript 4",
"spectron",
"spectron 15",
"wdio",
"webdriverio",
"chromedriver",
"scss",
"live reload"
],
@@ -39,7 +40,8 @@
"test:e2e": "npm run test:angular-e2e && npm run test:electron-e2e",
"test:angular-e2e": "npm-run-all -p -r start start:angular-e2e",
"start:angular-e2e": "wait-on http://localhost:4200 && cd workspaces/angular-app && npm run cypress:run",
"test:electron-e2e": "npm run package && cross-env X_NODE_ENV=e2e-test node workspaces/electron-e2e/jasmine.js",
"test:electron-e2e": "npm run package && npm run test:electron-e2e:wdio-only",
"test:electron-e2e:wdio-only": "cross-env X_NODE_ENV=e2e-test wdio run workspaces/electron-e2e/wdio.conf.ts --autoCompileOpts.tsNodeOpts.project=workspaces/electron-e2e/tsconfig.json",
"clean": "shx rm -rf .webpack out node_modules workspaces/shared-lib/.dist workspaces/angular-app/node_modules workspaces/angular-app/.dist",
"prepare": "husky install",
"postinstall": "husky install && shx rm -rf .git/hooks && shx ln -s ../.husky .git/hooks",
@@ -100,55 +102,60 @@
}
},
"devDependencies": {
"@commitlint/cli": "^13.1.0",
"@commitlint/config-conventional": "^13.1.0",
"@electron-forge/cli": "^6.0.0-beta.59",
"@electron-forge/maker-deb": "^6.0.0-beta.59",
"@electron-forge/maker-dmg": "^6.0.0-beta.59",
"@electron-forge/maker-rpm": "^6.0.0-beta.59",
"@electron-forge/maker-squirrel": "^6.0.0-beta.59",
"@electron-forge/maker-zip": "^6.0.0-beta.59",
"@electron-forge/plugin-webpack": "6.0.0-beta.59",
"@types/jasmine": "^3.8.2",
"@types/jasminewd2": "^2.0.10",
"@types/lodash": "^4.14.172",
"@types/node": "^16.4.13",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"@vercel/webpack-asset-relocator-loader": "^1.6.0",
"@commitlint/cli": "^16.2.3",
"@commitlint/config-conventional": "^16.2.1",
"@electron-forge/cli": "^6.0.0-beta.63",
"@electron-forge/maker-deb": "^6.0.0-beta.63",
"@electron-forge/maker-dmg": "^6.0.0-beta.63",
"@electron-forge/maker-rpm": "^6.0.0-beta.63",
"@electron-forge/maker-squirrel": "^6.0.0-beta.63",
"@electron-forge/maker-zip": "^6.0.0-beta.63",
"@electron-forge/plugin-webpack": "6.0.0-beta.63",
"@types/lodash": "^4.14.181",
"@types/node": "^17.0.23",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"@vercel/webpack-asset-relocator-loader": "^1.7.2",
"@wdio/allure-reporter": "^7.19.1",
"@wdio/cli": "^7.19.3",
"@wdio/jasmine-framework": "^7.19.3",
"@wdio/local-runner": "^7.19.3",
"@wdio/spec-reporter": "^7.19.1",
"allure-commandline": "^2.17.2",
"chokidar-cli": "^3.0.0",
"copy-webpack-plugin": "^9.0.1",
"chromedriver": "^100.0.0",
"copy-webpack-plugin": "^10.2.4",
"cross-env": "^7.0.3",
"css-loader": "^6.2.0",
"electron": "^13.6.6",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-prettier": "^3.4.0",
"fork-ts-checker-webpack-plugin": "^6.3.1",
"husky": "^7.0.1",
"jasmine": "^3.8.0",
"jasmine-core": "^3.8.0",
"jasmine-spec-reporter": "^7.0.0",
"lint-staged": "^11.1.2",
"css-loader": "^6.7.1",
"electron": "^18.0.3",
"eslint": "^8.12.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0",
"fork-ts-checker-webpack-plugin": "^7.2.3",
"husky": "^7.0.4",
"lint-staged": "^12.3.7",
"node-loader": "^2.0.0",
"npm-check-updates": "^11.8.3",
"npm-check-updates": "^12.5.8",
"npm-run-all": "^4.1.5",
"prettier": "^2.3.2",
"shx": "^0.3.3",
"spectron": "^15.0.0",
"standard-version": "^9.3.1",
"style-loader": "^3.2.1",
"prettier": "^2.6.2",
"shx": "^0.3.4",
"standard-version": "^9.3.2",
"style-loader": "^3.3.1",
"tree-kill": "^1.2.2",
"ts-loader": "^9.2.5",
"ts-node": "^10.1.0",
"typescript": "^4.3.5",
"wait-on": "^6.0.0"
"ts-loader": "^9.2.8",
"ts-node": "^10.7.0",
"typescript": "^4.6.3",
"wait-on": "^6.0.1",
"wdio-chromedriver-service": "^7.3.2",
"wdio-electron-service": "^2.1.0",
"wdio-wait-for": "^2.2.5"
},
"dependencies": {
"@electron/remote": "^2.0.8",
"electron-squirrel-startup": "^1.0.0",
"fs-extra": "^10.0.0",
"winston": "^3.3.3"
"fs-extra": "^10.0.1",
"winston": "^3.7.2"
},
"lint-staged": {
"*.ts": "npm run lint"

View File

@@ -14,7 +14,13 @@
"resolveJsonModule": true,
"paths": {
"*": ["node_modules/*"]
}
},
"types": [
"node",
"webdriverio/async",
"@wdio/jasmine-framework",
"expect-webdriverio"
]
},
"include": ["workspaces/electron-app/**/*", "workspaces/electron-e2e/**/*"]
}

View File

@@ -5,14 +5,24 @@
<h2>{{ 'MULTIPLES.TITLE' | translate }}</h2>
<form [formGroup]="timesTableForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<input type="number" class="form-control" formControlName="input" required />
<input
id="input"
type="number"
class="form-control"
formControlName="input"
required
/>
</div>
<button type="submit" class="btn btn-success" [disabled]="!timesTableForm.valid">
<button
type="submit"
class="btn btn-success"
[disabled]="!timesTableForm.valid"
>
{{ 'MULTIPLES.SUBMIT' | translate }}
</button>
</form>
<h3 *ngFor="let multiple of multiples; let i = index">
<h3 class="results" *ngFor="let multiple of multiples; let i = index">
{{ timesTableForm.value.input }} * {{ 1 + i }} = {{ multiple }}
</h3>
</div>

View File

@@ -1,6 +1,8 @@
import { app, BrowserWindow, shell } from 'electron';
import { Window } from './window';
declare const global: Global;
export class App {
private static _wrapper: Window;
@@ -9,10 +11,6 @@ export class App {
app.on('activate', App.start);
app.on('ready', App.start);
// Fix warning by applying electron new default value for this property
// Further details : https://github.com/electron/electron/issues/18397
app.allowRendererProcessReuse = true;
// Limit navigation and open external links in default browser
app.on('web-contents-created', App.openExternalLinksInDefaultBrowser);
}
@@ -30,7 +28,11 @@ export class App {
private static quit() {
// On MacOS it is common for applications to stay open until the user explicitly quits
if (process.platform !== 'darwin') {
// But WebDriverIO Test Runner does handle that behaviour yet
if (
process.platform !== 'darwin' ||
global.appConfig.configId === 'e2e-test'
) {
app.quit();
}
}

View File

@@ -3,6 +3,7 @@ import * as path from 'path';
import { AbstractService } from '../services/abstract-service';
import { MultiplesService } from '../services/multiples-service';
import { Logger } from '../utils/logger';
import * as remoteMain from '@electron/remote/main';
declare const global: Global;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
@@ -25,21 +26,21 @@ export class Window {
webPreferences: {
// Default behavior in Electron since 5, that
// limits the powers granted to remote content
// except in e2e test when those powers are required by Spectron
// except in e2e test when those powers are required
nodeIntegration: global.appConfig.isNodeIntegration,
// Isolate window context to protect against prototype pollution
// except in e2e test when that access is required by Spectron
// except in e2e test when that access is required
contextIsolation: global.appConfig.isContextIsolation,
// Ensure that JS values can't unsafely cross between worlds
// when using contextIsolation
worldSafeExecuteJavaScript: global.appConfig.isContextIsolation,
// Disable the remote module to enhance security
// except in e2e test when that access is required by Spectron
enableRemoteModule: global.appConfig.isEnableRemoteModule,
// Use a preload script to enhance security
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
},
});
// Disable the remote module to enhance security
// except in e2e test when that access is required
if (global.appConfig.isEnableRemoteModule) {
remoteMain.enable(this._electronWindow.webContents);
}
}
private loadIcon(): Electron.NativeImage | undefined {

View File

@@ -0,0 +1,45 @@
/*import MainPage from './pageobjects/main.page';
describe('My Login application', () => {
it('should login with valid credentials', async () => {
await MainPage.open();
/*await LoginPage.open();
await LoginPage.login('tomsmith', 'SuperSecretPassword!');
await expect(SecurePage.flashAlert).toBeExisting();
await expect(SecurePage.flashAlert).toHaveTextContaining(
'You logged into a secure area!');* /
});
});* /
describe('application loading', () => {
describe('App', () => {
it('should launch the application', async () => {
console.log('==>', await browser.getTitle());
// expect(title).toEqual('Test');
});
// it('should pass args through to the launched application', async () => {
// // custom args are set in the wdio.conf.js file as they need to be set before WDIO starts
// const argv = await app.mainProcess.argv();
// expect(argv).toContain('--foo');
// expect(argv).toContain('--bar=baz');
// });
});
}); */
describe('A simple test to check if app window is opened, visible and with expected title', () => {
describe('App should', () => {
it('show an initial window', async () => {
// Checking there is one visible window
// expect(await browser.).toEqual(true);
// Please note that getWindowHandles() will return 2 if `dev tools` is opened.
expect((await browser.getWindowHandles()).length).toEqual(1);
});
it('have expected title', async () => {
expect(await browser.getTitle()).toEqual('ElectronAngularQuickStart');
});
});
});

View File

@@ -1,23 +0,0 @@
const Jasmine = require('jasmine');
const { SpecReporter } = require('jasmine-spec-reporter');
const jasmine = new Jasmine();
jasmine.loadConfig({
showColors: true,
spec_dir: 'workspaces/electron-e2e',
spec_files: ['./**/*-spec.ts'],
helpers: ['./**/*-helper.ts'],
random: false,
seed: undefined,
stopSpecOnExpectationFailure: false,
});
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json'),
});
jasmine.env.clearReporters();
jasmine.addReporter(
new SpecReporter({ spec: { displayStacktrace: 'pretty' } })
);
jasmine.execute();

View File

@@ -0,0 +1,20 @@
import MultiplesPage from './pageobjects/multiples.page';
describe('A simple test to check if a given input matches with computed multiples', () => {
describe('Multiples component should', () => {
it('show up on startup', async () => {
await expect(MultiplesPage.root).toBeDisplayed();
});
const number = Math.floor(Math.random() * 100) % 10;
it(`display expected results on input (${number})`, async () => {
await MultiplesPage.enterInput(number);
const results = await MultiplesPage.results;
for (let i = 0; i < results.length; i++) {
const ntimes = 1 + i;
const expected = `${number} * ${ntimes} = ${number * ntimes}`;
expect(await results[i].getText()).toEqual(expected);
}
});
});
});

View File

@@ -0,0 +1,32 @@
import AbstractPage from './page';
class MultiplesPage extends AbstractPage {
/**
* Selectors using getter methods
*/
public get root() {
return $('#multiples');
}
public get input() {
return $('#input');
}
public get results() {
return $$('.results');
}
public get buttonSubmit() {
return $('button[type="submit"]');
}
/**
* Wrapper method to interact with the page
*/
public async enterInput(number: number) {
await this.input.setValue(number);
await this.buttonSubmit.click();
}
}
export default new MultiplesPage();

View File

@@ -0,0 +1,7 @@
/**
* Abstract page object containing all methods, selectors and functionality
* that is shared across all page objects
*/
export default abstract class AbstractPage {
// Not implemented yet
}

View File

@@ -1,32 +0,0 @@
import * as path from 'path';
import { Application } from 'spectron';
export async function startApp(): Promise<Application> {
// Path to local electron binary
let electronPath = path.join(
__dirname,
'../../../node_modules/.bin/electron'
);
if (process.platform === 'win32') {
electronPath += '.cmd';
}
// Init local packaged app
const app = new Application({
path: electronPath,
args: ['.webpack/main/index.js'],
});
// Init local app and wait until window loaded
await app.start();
await app.client.waitUntilWindowLoaded();
return app;
}
export async function stopApp(app: Application): Promise<void> {
if (app && app.isRunning()) {
// Wait 1 second and then stop local app
await new Promise((resolve) => setTimeout(resolve, 1000));
await app.stop();
}
}

View File

@@ -1,25 +0,0 @@
import { Application } from 'spectron';
import { startApp, stopApp } from './_hooks';
describe('A simple test to verify a visible window is opened with a title', () => {
let app: Application;
beforeAll(async () => {
app = await startApp();
});
afterAll(async () => {
await stopApp(app);
});
it('shows an initial window', async () => {
// Checking there is one visible window
expect(await app.browserWindow.isVisible()).toEqual(true);
// Please note that getWindowCount() will return 2 if `dev tools` are opened.
expect(await app.client.getWindowCount()).toEqual(1);
});
it('should have expected title', async () => {
expect(await app.client.getTitle()).toEqual('ElectronAngularQuickStart');
});
});

View File

@@ -3,8 +3,13 @@
"compilerOptions": {
"outDir": "./.dist",
"module": "commonjs",
"target": "es5",
"target": "es2020",
"noEmit": true,
"types": ["jasmine", "jasminewd2", "node"]
"types": [
"node",
"webdriverio/async",
"@wdio/jasmine-framework",
"expect-webdriverio"
]
}
}

View File

@@ -0,0 +1,360 @@
import type { Options } from '@wdio/types';
import path from 'path';
// Path to local electron binary
let electronPath = path.join(__dirname, '../../node_modules/.bin/electron');
if (process.platform === 'win32') {
electronPath += '.cmd';
}
// Starting hook
const waitUntilWindowLoaded = async () => {
const timeout = 10000;
await browser.waitUntil(async () => (await browser.isLoading()) === false, {
timeout: timeout,
timeoutMsg: `Expected app to be loaded in less than ${timeout}ms`,
});
};
// Closing hook
const closeApplication = async () => {
if (browser) {
// Wait 1 second and close window
await new Promise((resolve) => setTimeout(resolve, 1000));
await browser.closeWindow();
}
};
export const config: Options.Testrunner = {
//
// ====================
// Runner Configuration
// ====================
//
//
// =====================
// ts-node Configurations
// =====================
//
// You can write tests using TypeScript to get autocompletion and type safety.
// You will need typescript and ts-node installed as devDependencies.
// WebdriverIO will automatically detect if these dependencies are installed
// and will compile your config and tests for you.
// If you need to configure how ts-node runs please use the
// environment variables for ts-node or use wdio config's autoCompileOpts section.
//
autoCompileOpts: {
autoCompile: true,
// see https://github.com/TypeStrong/ts-node#cli-and-programmatic-options
// for all available options
tsNodeOpts: {
transpileOnly: true,
project: 'workspaces/electron-e2e/tsconfig.json',
},
// tsconfig-paths is only used if "tsConfigPathsOpts" are provided, if you
// do please make sure "tsconfig-paths" is installed as dependency
// tsConfigPathsOpts: {
// baseUrl: './'
// }
},
//
// ==================
// Specify Test Files
// ==================
// Define which test specs should run. The pattern is relative to the directory
// from which `wdio` was called.
//
// The specs are defined as an array of spec files (optionally using wildcards
// that will be expanded). The test for each spec file will be run in a separate
// worker process. In order to have a group of spec files run in the same worker
// process simply enclose them in an array within the specs array.
//
// If you are calling `wdio` from an NPM script (see https://docs.npmjs.com/cli/run-script),
// then the current working directory is where your `package.json` resides, so `wdio`
// will be called from there.
//
specs: ['./workspaces/electron-e2e/**/*.e2e-spec.ts'],
// Patterns to exclude.
exclude: [
// 'path/to/excluded/files'
],
//
// ============
// Capabilities
// ============
// Define your capabilities here. WebdriverIO can run multiple capabilities at the same
// time. Depending on the number of capabilities, WebdriverIO launches several test
// sessions. Within your capabilities you can overwrite the spec and exclude options in
// order to group specific specs to a specific capability.
//
// First, you can define how many instances should be started at the same time. Let's
// say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
// set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec
// files and you set maxInstances to 10, all spec files will get tested at the same time
// and 30 processes will get spawned. The property handles how many capabilities
// from the same test should run tests.
//
maxInstances: 10,
//
// If you have trouble getting all important capabilities together, check out the
// Sauce Labs platform configurator - a great tool to configure your capabilities:
// https://saucelabs.com/platform/platform-configurator
//
capabilities: [
{
// maxInstances can get overwritten per capability. So if you have an in-house Selenium
// grid with only 5 firefox instances available you can make sure that not more than
// 5 instances get started at a time.
maxInstances: 5,
//
browserName: 'chrome',
acceptInsecureCerts: true,
'goog:chromeOptions': {
binary: electronPath,
args: ['app=' + '.webpack/main/index.js'],
},
// If outputDir is provided WebdriverIO can capture driver session logs
// it is possible to configure which logTypes to include/exclude.
// excludeDriverLogs: ['*'], // pass '*' to exclude all driver session logs
// excludeDriverLogs: ['bugreport', 'server'],
},
],
//
// ===================
// Test Configurations
// ===================
// Define all options that are relevant for the WebdriverIO instance here
//
// Level of logging verbosity: trace | debug | info | warn | error | silent
logLevel: 'info',
//
// Set specific log levels per logger
// loggers:
// - webdriver, webdriverio
// - @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service
// - @wdio/mocha-framework, @wdio/jasmine-framework
// - @wdio/local-runner
// - @wdio/sumologic-reporter
// - @wdio/cli, @wdio/config, @wdio/utils
// Level of logging verbosity: trace | debug | info | warn | error | silent
// logLevels: {
// webdriver: 'info',
// '@wdio/appium-service': 'info'
// },
//
// If you only want to run your tests until a specific amount of tests have failed use
// bail (default is 0 - don't bail, run all tests).
bail: 0,
//
// Set a base URL in order to shorten url command calls. If your `url` parameter starts
// with `/`, the base url gets prepended, not including the path portion of your baseUrl.
// If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url
// gets prepended directly.
baseUrl: 'http://localhost',
//
// Default timeout for all waitFor* commands.
waitforTimeout: 10000,
//
// Default timeout in milliseconds for request
// if browser driver or grid doesn't send response
connectionRetryTimeout: 120000,
//
// Default request retries count
connectionRetryCount: 3,
//
// Test runner services
// Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process.
services: ['chromedriver'],
// Framework you want to run your specs with.
// The following are supported: Mocha, Jasmine, and Cucumber
// see also: https://webdriver.io/docs/frameworks
//
// Make sure you have the wdio adapter package for the specific framework installed
// before running any tests.
framework: 'jasmine',
//
// The number of times to retry the entire specfile when it fails as a whole
// specFileRetries: 1,
//
// Delay in seconds between the spec file retry attempts
// specFileRetriesDelay: 0,
//
// Whether or not retried specfiles should be retried immediately or deferred to the end of the queue
// specFileRetriesDeferred: false,
//
// Test reporter for stdout.
// The only one supported by default is 'dot'
// see also: https://webdriver.io/docs/dot-reporter
reporters: ['spec', ['allure', { outputDir: 'allure-results' }]],
//
// Options to be passed to Jasmine.
jasmineOpts: {
// Jasmine default timeout
defaultTimeoutInterval: 60000,
//
// The Jasmine framework allows interception of each assertion in order to log the state of the application
// or website depending on the result. For example, it is pretty handy to take a screenshot every time
// an assertion fails.
expectationResultHandler: function (_passed: boolean, _assertion) {
// do something
},
},
//
// =====
// Hooks
// =====
// WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance
// it and to build services around it. You can either apply a single function or an array of
// methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got
// resolved to continue.
/**
* Gets executed once before all workers get launched.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
*/
// onPrepare: function (config, capabilities) {
// },
/**
* Gets executed before a worker process is spawned and can be used to initialise specific service
* for that worker as well as modify runtime environments in an async fashion.
* @param {String} cid capability id (e.g 0-0)
* @param {[type]} caps object containing capabilities for session that will be spawn in the worker
* @param {[type]} specs specs to be run in the worker process
* @param {[type]} args object that will be merged with the main configuration once worker is initialized
* @param {[type]} execArgv list of string arguments passed to the worker process
*/
// onWorkerStart: function (cid, caps, specs, args, execArgv) {
// },
/**
* Gets executed just after a worker process has exited.
* @param {String} cid capability id (e.g 0-0)
* @param {Number} exitCode 0 - success, 1 - fail
* @param {[type]} specs specs to be run in the worker process
* @param {Number} retries number of retries used
*/
// onWorkerEnd: function (cid, exitCode, specs, retries) {
// },
/**
* Gets executed just before initialising the webdriver session and test framework. It allows you
* to manipulate configurations depending on the capability or spec.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
* @param {String} cid worker id (e.g. 0-0)
*/
// beforeSession: function (config, capabilities, specs, cid) {
// },
/**
* Gets executed before test execution begins. At this point you can access to all global
* variables like `browser`. It is the perfect place to define custom commands.
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
* @param {Object} browser instance of created browser/device session
*/
// before: function (capabilities, specs) {
// },
/**
* Runs before a WebdriverIO command gets executed.
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
*/
// beforeCommand: function (commandName, args) {
// },
/**
* Hook that gets executed before the suite starts
* @param {Object} suite suite details
*/
beforeSuite: async (_suite) => {
await waitUntilWindowLoaded();
},
/**
* Function to be executed before a test (in Mocha/Jasmine) starts.
*/
// beforeTest: function (test, context) {
// },
/**
* Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling
* beforeEach in Mocha)
*/
// beforeHook: function (test, context) {
// },
/**
* Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling
* afterEach in Mocha)
*/
// afterHook: function (test, context, { error, result, duration, passed, retries }) {
// },
/**
* Function to be executed after a test (in Mocha/Jasmine only)
* @param {Object} test test object
* @param {Object} context scope object the test was executed with
* @param {Error} result.error error object in case the test fails, otherwise `undefined`
* @param {Any} result.result return object of test function
* @param {Number} result.duration duration of test
* @param {Boolean} result.passed true if test has passed, otherwise false
* @param {Object} result.retries informations to spec related retries, e.g. `{ attempts: 0, limit: 0 }`
*/
afterTest: async function (_test, _context, result) {
// result = { _error, _result, _duration, passed, _retries }
if (!result.passed) {
await browser.takeScreenshot();
}
},
/**
* Hook that gets executed after the suite has ended
* @param {Object} suite suite details
*/
// afterSuite: async function (_suite) {
// },
/**
* Runs after a WebdriverIO command gets executed
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
* @param {Number} result 0 - command success, 1 - command error
* @param {Object} error error object if any
*/
// afterCommand: function (commandName, args, result, error) {
// },
/**
* Gets executed after all tests are done. You still have access to all global variables from
* the test.
* @param {Number} result 0 - test pass, 1 - test fail
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
after: async (_result, _capabilities, _specs) => {
await closeApplication();
},
/**
* Gets executed right after terminating the webdriver session.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// afterSession: function (config, capabilities, specs) {
// },
/**
* Gets executed after all workers got shut down and the process is about to exit. An error
* thrown in the onComplete hook will result in the test run failing.
* @param {Object} exitCode 0 - success, 1 - fail
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {<Object>} results object containing test results
*/
// onComplete: function(_exitCode, _config, _capabilities, _results) {
// },
/**
* Gets executed when a refresh happens.
* @param {String} oldSessionId session ID of the old session
* @param {String} newSessionId session ID of the new session
*/
// onReload: function(oldSessionId, newSessionId) {
// }
};