Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spectron Chromedriver Worker Service #10

Merged
merged 47 commits into from
Jan 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
99e4de6
bumpy, add md copy script
Aug 14, 2021
6ce0c5b
new package who dis
Sep 13, 2021
f4c1d77
package shenanigans
Sep 13, 2021
80ca63e
typescript everything
Sep 13, 2021
99b16e1
make eslint less complainy
Sep 13, 2021
ca28dcb
update for new ts-jest
Sep 13, 2021
2375411
enable coverage
Sep 13, 2021
edb83c0
fix build
Sep 13, 2021
8a6c490
more package shenanigans
Sep 13, 2021
aa048af
quiet eslint
Sep 13, 2021
6c71ab0
TSify
Sep 13, 2021
6d413a2
TSify
Sep 13, 2021
ba20283
more TS
Sep 13, 2021
998f19f
more fixes
Sep 13, 2021
64c2594
move jest-mock-extended back
Sep 13, 2021
1cbed19
more test and linting fixes
Sep 13, 2021
1e383b8
fix clean script
Sep 13, 2021
6b76a34
update ur lock
Sep 13, 2021
ae1293a
update deps
Sep 13, 2021
df8948f
update desc
Sep 14, 2021
0345e46
rework for restart
Sep 14, 2021
4dab329
test:build => test:local
Sep 14, 2021
6d5db82
delete test
Sep 14, 2021
058ba2b
remove eslint exemption
Sep 14, 2021
464188a
update CI pnpm
Sep 14, 2021
ace4403
park it all
Oct 6, 2021
c9ed69e
Merge remote-tracking branch 'origin/master' into chromedriver-service
Oct 24, 2021
31346e5
update the things
Oct 24, 2021
58718d9
Merge branch 'master' into chromedriver-service
goosewobbler Oct 24, 2021
b31ba4f
update ur lock
Oct 24, 2021
93f9729
update deps
Oct 27, 2021
cceedc7
switch service, clean up
Oct 27, 2021
b3d7143
add types to service
Oct 27, 2021
a290193
reference types
Oct 27, 2021
154a7d2
fix teardown
Oct 27, 2021
dcb8b16
clean up
Oct 27, 2021
8d9249d
add mochaOpts and afterTest
Oct 27, 2021
06fa986
wait for window load, back to old tests
Oct 27, 2021
5f9bbb5
add logFileName to spectronOpts
Oct 27, 2021
1946fbf
use the main config outputDir
Oct 27, 2021
fe8a0f1
add eslint-plugin-wdio
Nov 1, 2021
4f1cea1
better typing and some debug
Nov 1, 2021
fd1794f
better match the wdio reference types
Nov 1, 2021
5eb2124
new approach of running WDIO through CLI directly
Nov 1, 2021
0a3964c
get rid of config aftertest
Nov 1, 2021
83d2c6a
update the things
Dec 9, 2021
2b6f793
wdio-electron-service
Dec 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"extends": ["airbnb-base", "prettier", "plugin:node/recommended"],
"plugins": ["import", "node", "prettier", "promise"],
"extends": ["airbnb-base", "prettier", "plugin:node/recommended", "plugin:wdio/recommended"],
"plugins": ["import", "node", "prettier", "promise", "wdio"],
"env": {
"browser": true,
"es2020": true,
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

- uses: pnpm/action-setup@v2.0.1
with:
version: 6.18.0
version: 6.15.0
run_install: true
- name: Run headless test
uses: GabrielBB/xvfb-action@v1
Expand Down
4 changes: 4 additions & 0 deletions common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export interface LooseObject {
}

export interface SpectronClient extends Browser<'async'> {
/**
* Exit the electron application
*/
exitElectronApp(): Promise<void>;
/**
* Wait until the window is no longer loading.
* Takes an optional timeout in milliseconds that defaults to 5000.
Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ The name of your built app. It is combined with the `appPath` value to generate

It needs to be the same as the install directory used by `electron-builder`, which is derived from your `package.json` configuration - either `name` or `productName`. You can find more information regarding this in the `electron-builder` [documentation](https://www.electron.build/configuration/configuration#configuration).

#### `logFileName`

## Migrating configuration from older versions of Spectron

Please see the [migration guide](migration.md#configuration).
6 changes: 3 additions & 3 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ https://www.electronjs.org/docs/latest/tutorial/security

All handling of Chromedriver (CD) is now delegated to the `wdio-chromedriver-service`, which is a WebdriverIO (WDIO) "launcher service". This means that Spectron no longer restarts Chromedriver for each test, which massively speeds up test runs but may mean that some suites experience problems with state leak between tests.

The original behaviour might be reinstated in future through creation of a Spectron "worker service" (e.g. `wdio-spectron-service`) to manage the CD process between tests.
The original behaviour might be reinstated in future through creation of a Spectron "worker service" (e.g. `wdio-electron-service`) to manage the CD process between tests.

#### Further reading:

Expand Down Expand Up @@ -52,15 +52,15 @@ These are the old configuration values (passed into the Spectron constructor) an
- `args` - This was an array of arguments to pass to the Electron application. It has been replaced with an environment variable which needs to be set in the config file so that it is available to WDIO before it starts. See [here](https://sites.google.com/a/chromium.org/chromedriver/capabilities) for details on Chromium arguments.
e.g. `process.env.SPECTRON_APP_ARGS = ['--foo', '--bar=baz'].toString();`
- `chromeDriverArgs` - This was an array of arguments to pass to ChromeDriver. The Chromedriver service [documentation](https://webdriver.io/docs/wdio-chromedriver-service/#configuration) shows an `args` option in their configuration example, though this is not detailed in the "options" section of that page. There is also the WDIO option [`execArgv`](https://webdriver.io/docs/options/#execargv) which applies node arguments to WDIO child processes. No replacement as yet.
- `cwd`- This was the path to the working directory to use for the launched application. It was [passed through](https://github.com/electron-userland/spectron/blob/798cf1401cb7ec0596558ebdc8b4c0e8427a25f7/lib/chrome-driver.js#L41) to the Chromedriver child process. There is no CD service equivalent for this option, though it might be resurrected with a `wdio-spectron-service`. No replacement as yet.
- `cwd`- This was the path to the working directory to use for the launched application. It was [passed through](https://github.com/electron-userland/spectron/blob/798cf1401cb7ec0596558ebdc8b4c0e8427a25f7/lib/chrome-driver.js#L41) to the Chromedriver child process. There is no CD service equivalent for this option, though it might be resurrected with a `wdio-electron-service`. No replacement as yet.
- `env` - This was an object of additional environment variables to set in the launched application. These values were [passed to Chromium](https://github.com/electron-userland/spectron/blob/798cf1401cb7ec0596558ebdc8b4c0e8427a25f7/lib/application.js#L178) in `capabilities` as `spectron-env-foo=bar`. No replacement as yet.
- `host` - This was the host name of the launched Chromedriver process. The CD service has [an option](https://webdriver.io/docs/wdio-chromedriver-service/#hostname) for this. No replacement as yet.
- `port` - This can be set in the config file using the WDIO `port` [option](https://webdriver.io/docs/options/#port).
- `nodePath` - This was a path to a `node` executable to launch Chromedriver with. The CD service has [an option](https://webdriver.io/docs/wdio-chromedriver-service/#chromedrivercustompath) which might fulfil this. No replacement as yet.
- `connectionRetryCount` - This was a number of retry attempts to make when connecting to Chromedriver. WDIO has an [equivalent](https://webdriver.io/docs/options/#connectionretrycount) which _may_ apply to CD.
- `connectionRetryTimeout` - This was a timeout to wait for connections to Chromedriver to be made. WDIO has an [equivalent](https://webdriver.io/docs/options/#connectionretrytimeout) which _may_ apply to CD.
- `quitTimeout` - This was a timeout value to wait for the application quitting. We are now dependent on WDIO WDIO doesn't allow to configure this. Dropped.
- `startTimeout` - This was a timeout value to wait for the start of Chromedriver. There is no Chromedriver service option for this, though it could potentially be reimplemented as part of a `wdio-spectron-service`. No replacement as yet.
- `startTimeout` - This was a timeout value to wait for the start of Chromedriver. There is no Chromedriver service option for this, though it could potentially be reimplemented as part of a `wdio-electron-service`. No replacement as yet.
- `waitTimeout` - This was the timeout value for calls like `waitUntilTextExists` and `waitUntilWindowLoaded` to complete - this value is now passed in on the call itself. The default is still `5000ms`.
- `debuggerAddress` - This was passed through to Chromium config in [`capabilities`](https://github.com/electron-userland/spectron/blob/798cf1401cb7ec0596558ebdc8b4c0e8427a25f7/lib/application.js#L207). No replacement as yet.
- `chromeDriverLogPath` - Chromedriver logs are now output with the other WDIO logs (see `outputDir`) in the `wdio-chromedriver.log` file. No replacement as yet, though the Chromedriver service has two options we can use to resurrect this customisation option - [`outputDir`](https://webdriver.io/docs/wdio-chromedriver-service/#outputdir) and [`logFileName`](https://webdriver.io/docs/wdio-chromedriver-service/#logfilename).
Expand Down
29 changes: 29 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## Testing Spectron

The original Spectron did not have any unit tests, and relied on a series of E2E scenarios built using `mocha` and executed through `xvfb-maybe`. The goal for the new version of Spectron is to have good unit coverage as well as a series of self-documenting E2E specs which cover all the main use cases.

### Test Audit - Migrating the old tests

Here we will discuss the old tests, what they are designed to do and which (parts) of them mignt be beneficial to recreate in the new world.

#### [accessibility-test.js](https://github.com/electron-userland/spectron/blob/master/test/accessibility-test.js)

The accessibility functionality has been [removed](migration.md#accessibility) in favour of modern accessibility testing through devtools-based approaches such as [Axe](https://www.deque.com/axe). As such, these tests are not needed.

#### [application-test.js](https://github.com/electron-userland/spectron/blob/master/test/application-test.js)

- 'launches the application' - Aggregate startup behaviour and valid responses from `browserWindow` API and a bunch of client API methods - This should be split up.
- 'passes through args to the launched app' - testing `args` config value - this can be replicated.
- 'passes through env to the launched app' - testing `env` config value - functionality removed, should be replicated if reinstated.
- 'passes through cwd to the launched app' - testing `cwd` config value - functionality removed, should be replicated if reinstated.
- 'throws an error when no path is specified' - testing empty path to app - this can be replicated as part of testing config validation.
- 'start() rejects with an error if the application does not exist' - testing valid application path - could be replicated but likely to duplicate WDIO / CD functionality
- 'start() rejects with an error if ChromeDriver does not start within the specified timeout' - testing `startTimeout` config value. Should be replicated _if_ the config value is reimplemented.
- 'stop() quits the application' - testing teardown through a logfile - WDIO handles teardown now, not required.
- 'stop() rejects with an error if the application is not running' - WDIO handles teardown now, not required.
- 'restart() restarts the application' - testing CD restart - Not required with new WDIO / CD service. Will be replicated in `wdio-electron-service` if required.
- 'restart() rejects with an error if the application is not running' - testing CD restart fail case - Not required with new WDIO / CD service. Will be replicated in `wdio-electron-service` if required.
- 'getSettings() returns an object with all the configured options' - testing a likely pointless function - functionality removed, should be replicated if reinstated.
- 'getRenderProcessLogs gets the render process console logs and clears them' - testing log retrieval - `getRenderProcessLogs` is just a wrapper for Chromium [`getLogs`](https://webdriver.io/docs/api/chromium/#getlogs) on WDIO browser object. Will replicate test if the wrapper is put back.
- 'getMainProcessLogs gets the main process console logs and clears them' - testing CD logs - this is now handled by CD service, will replicate in `wdio-electron-service` if that route is taken.
- 'getMainProcessLogs does not include any deprecation warnings' - likely added to test the hacky approach to re
28 changes: 17 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,37 @@
"repository": "https://github.com/goosewobbler/spectron",
"bugs": "https://github.com/goosewobbler/spectron/issues",
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.1.0",
"@typescript-eslint/parser": "^5.1.0",
"@types/chromedriver": "^81.0.0",
"@types/fs-extra": "^9.0.12",
"@types/jest": "^27.0.3",
"@types/node": "^16.11.12",
"@typescript-eslint/eslint-plugin": "^5.6.0",
"@typescript-eslint/parser": "^5.6.0",
"@wdio/cli": "^7.16.10",
"cross-env": "^7.0.3",
"eslint": "^8.0.1",
"eslint-config-airbnb-base": "^14.2.1",
"eslint": "^8.4.1",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-base-typescript": "^1.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-typescript": "^2.5.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^25.2.2",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jest": "^25.3.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-promise": "^5.2.0",
"eslint-plugin-wdio": "^7.4.2",
"fs-extra": "^10.0.0",
"husky": "^7.0.4",
"prettier": "^2.4.1",
"typescript": "^4.4.4",
"webdriverio": "^7.16.2"
"prettier": "^2.5.1",
"typescript": "^4.5.2",
"webdriverio": "^7.16.10"
},
"scripts": {
"init": "npm install -g pnpm && pnpm i",
"preinstall": "npx only-allow pnpm",
"build": "pnpm run --filter @goosewobbler/spectron build && pnpm run --filter @goosewobbler/spectron-e2e-app build",
"lint": "pnpm run -r lint && prettier --check \"**/*.{j,t}s\"",
"clean": "pnpm run -r clean",
"clean": "pnpm run -r clean && cross-env rm -f pnpm-lock.yaml",
"format": "prettier --ignore-path .gitignore --write .",
"smoke-test": "pnpm run --filter @goosewobbler/spectron-e2e test",
"unit-test": "pnpm run --filter @goosewobbler/spectron test",
Expand Down
4 changes: 4 additions & 0 deletions packages/spectron/lib/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export async function initSpectron(): Promise<SpectronApp> {
browser.addCommand('getWindowCount', getWindowCount);
browser.addCommand('windowByIndex', windowByIndex);
browser.addCommand('getSelectedText', getSelectedText);
browser.addCommand('exitElectronApp', async () => {
console.log('quitting yo');
await spectronObj.electronApp.quit();
});

spectronObj.client = browser as SpectronClient;

Expand Down
46 changes: 23 additions & 23 deletions packages/spectron/lib/run.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
/* eslint no-process-exit: off, no-console: off */
import Launcher, { RunCommandArguments } from '@wdio/cli';
// import Launcher, { RunCommandArguments } from '@wdio/cli';
import SpectronWorkerService from 'wdio-electron-service';
import { Services } from '@wdio/types';
import { join } from 'path';

type SpectronConfig = {
config: {
spectronOpts: {
appPath: string;
appName: string;
logFileName: string;
};
};
};
Expand Down Expand Up @@ -42,20 +45,19 @@ function stripSpectronOpts(config: SpectronConfig['config']) {
function buildLauncherConfig(
config: SpectronConfig['config'],
chromedriverCustomPath: string,
appPath: string,
appName: string,
spectronOpts: SpectronConfig['config']['spectronOpts'],
chromeArgs: string[],
) {
): WebdriverIO.Config {
const { appPath, appName, logFileName = 'wdio-chromedriver.log' } = spectronOpts;
const filteredConfig = stripSpectronOpts(config);
return {
...filteredConfig,
services: [
[
'chromedriver',
SpectronWorkerService as Services.ServiceClass,
{
port: 9515,
logFileName: 'wdio-chromedriver.log', // default
// outputDir: 'driver-logs', // overwrites the config.outputDir
port: filteredConfig.port,
logFileName,
chromedriverCustomPath,
// args: ['--silent'],
},
Expand All @@ -74,16 +76,17 @@ function buildLauncherConfig(
};
}

export const run = async (...args: unknown[]): Promise<void> => {
export const run = (config: SpectronConfig['config']): WebdriverIO.Config => {
const chromeArgs = [];
const isWin = process.platform === 'win32';

if (process.env.CI) {
chromeArgs.push('window-size=1280,800');
chromeArgs.push('blink-settings=imagesEnabled=false');
chromeArgs.push('enable-automation');
chromeArgs.push('disable-infobars');
chromeArgs.push('disable-extensions');
if (process.platform !== 'win32') {
if (!isWin) {
// chromeArgs.push('headless'); - crashes on linux with xvfb
chromeArgs.push('no-sandbox');
chromeArgs.push('disable-gpu');
Expand All @@ -93,7 +96,6 @@ export const run = async (...args: unknown[]): Promise<void> => {
}
}

const isWin = process.platform === 'win32';
if (isWin) {
process.env.SPECTRON_NODE_PATH = process.execPath;
process.env.SPECTRON_CHROMEDRIVER_PATH = require.resolve('electron-chromedriver/chromedriver');
Expand All @@ -105,10 +107,8 @@ export const run = async (...args: unknown[]): Promise<void> => {
const configFilePath = join(process.cwd(), 'spectron.conf.js');

// https://github.com/mysticatea/eslint-plugin-node/pull/256
const { config }: SpectronConfig = await import(configFilePath); // eslint-disable-line
const {
spectronOpts: { appPath, appName },
} = config;
// const { config }: SpectronConfig = await import(configFilePath); // eslint-disable-line
const { spectronOpts } = config;

if (!config) {
throw new Error(`Unable to read config file: ${configFilePath}`);
Expand All @@ -118,13 +118,13 @@ export const run = async (...args: unknown[]): Promise<void> => {
chromeArgs.push(...process.env.SPECTRON_APP_ARGS.split(','));
}

const launcherConfig = buildLauncherConfig(config, chromedriverCustomPath, appPath, appName, chromeArgs);
const wdio = new Launcher(args[2] as string, launcherConfig as Partial<RunCommandArguments>);
return buildLauncherConfig(config, chromedriverCustomPath, spectronOpts, chromeArgs);
// const wdio = new Launcher(args[2] as string, launcherConfig as Partial<RunCommandArguments>);

try {
const exitCode = await wdio.run();
process.exit(exitCode);
} catch (error) {
console.error('Launcher failed to start the test', (error as Error).stack);
}
// try {
// const exitCode = await wdio.run();
// process.exit(exitCode);
// } catch (error) {
// console.error('Launcher failed to start the test', (error as Error).stack);
// }
};
28 changes: 14 additions & 14 deletions packages/spectron/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@goosewobbler/spectron",
"version": "17.0.0-alpha3",
"version": "17.0.0-alpha4.1",
"description": "Easily test your Electron apps using ChromeDriver and WebdriverIO.",
"main": "./dist/index.js",
"types": "./spectron.d.ts",
Expand Down Expand Up @@ -37,22 +37,22 @@
"author": "Sam Maister",
"license": "MIT",
"dependencies": {
"@wdio/cli": "^7.16.2",
"@wdio/local-runner": "^7.16.2",
"chromedriver": "^94.0.0",
"electron": "^15.3.0",
"electron-chromedriver": "^15.0.0",
"ts-node": "^10.3.1",
"tsconfig-paths": "^3.11.0",
"wdio-chromedriver-service": "^7.2.2",
"webdriverio": "^7.16.2"
"@wdio/cli": "^7.16.10",
"@wdio/local-runner": "^7.16.10",
"chromedriver": "^96.0.0",
"electron": "^16.0.4",
"electron-chromedriver": "^16.0.0",
"ts-node": "^10.4.0",
"tsconfig-paths": "^3.12.0",
"wdio-electron-service": "*",
"webdriverio": "^7.16.10"
},
"devDependencies": {
"@testing-library/dom": "^8.10.1",
"@types/jest": "^27.0.2",
"jest": "^27.3.1",
"@testing-library/dom": "^8.11.1",
"@types/jest": "^27.0.3",
"jest": "^27.4.3",
"jest-mock-extended": "^2.0.4",
"ts-jest": "^27.0.7"
"ts-jest": "^27.1.1"
},
"resolutions": {
"@types/node": "^14.0.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/spectron/spectron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ import { SpectronApp } from './dist/common/types';
declare module '@goosewobbler/spectron' {}

export function initSpectron(): Promise<SpectronApp>;
export function run(): Promise<void>;
export function run(): WebdriverIO.Config;
export { SpectronApp };
30 changes: 30 additions & 0 deletions packages/wdio-electron-service/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module.exports = {
overrides: [
{
files: '*.ts',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
tsconfigRootDir: __dirname,
project: './tsconfig.json',
},
settings: {
'import/resolver': {
typescript: {
project: [`${__dirname}/tsconfig.json`, `${__dirname}/../../common/tsconfig.json`],
alwaysTryTypes: true,
},
},
},
rules: {
'no-unused-vars': 'off',
'import/prefer-default-export': 'off',
'node/no-missing-import': 'off', // duped by import
'node/no-unpublished-import': 'error', // switched on for the NPM package
'node/no-unsupported-features/es-syntax': ['error', { ignores: ['modules'] }],
'import/no-extraneous-dependencies': 'off',
'node/no-extraneous-import': 'off',
},
},
],
};
Loading