Skip to content

Commit

Permalink
Process killer feature (#838)
Browse files Browse the repository at this point in the history
* processKiller feature

* changed changelog

* linter fixes

* linter fix

* test fix

* killing selenium port if port is busy

* processKiller in config by default
  • Loading branch information
udarrr committed Aug 28, 2023
1 parent 7553f2f commit e6135ab
Show file tree
Hide file tree
Showing 11 changed files with 549 additions and 56 deletions.
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,13 @@ updates:
update-types: ["version-update:semver-major"]
- dependency-name: "eslint"
update-types: ["version-update:semver-major"]
- dependency-name: "execa"
update-types: ["version-update:semver-major"]
- dependency-name: "find-process"
update-types: ["version-update:semver-major"]
- dependency-name: "fkill"
update-types: ["version-update:semver-major"]
- dependency-name: "prettier"
update-types: ["version-update:semver-major"]


6 changes: 6 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 9.1.0 (2023-08-26)
* added new feature processKiller

# 9.0.6 (2023-08-24)
* support new storage for the latest chromedriver, since v116 (channel: stable, beta, dev, canary)

# 7.1.0 (2021-07-01)
* support Apple M1 for chrome and gecko drivers #558
* prevent selenium process from hagning if spawned programmatically without stdout/stderr handlers
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
⚠️ __ATTENTION:__ we are looking for people taking on maintenance for this package. Read more in [`webdriverio/selenium-standalone#813`](https://github.com/webdriverio/selenium-standalone/issues/813).

Node.js Selenium Standalone [![Test](https://github.com/webdriverio/selenium-standalone/actions/workflows/test.yml/badge.svg?branch=main&event=push)](https://github.com/webdriverio/selenium-standalone/actions/workflows/test.yml)
Node.js Selenium Standalone [![Test](https://github.com/webdriverio/selenium-standalone/actions/workflows/test.yml/badge.svg?branch=main&event=push)](https://github.com/webdriverio/selenium-standalone/actions/workflows/test.yml) ![Supported node versions](https://img.shields.io/badge/node-12%2C%2013%2C%2014%2C%2015%2C%2016%2C%2017%2C%2018%2C%2019%2C%2020-green)
===========================

> A node based CLI library for launching [Selenium](http://www.seleniumhq.org/download/) with [WebDriver](https://w3c.github.io/webdriver/) support.
Expand All @@ -13,6 +13,13 @@ Supported Drivers:
* [Edge WebDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/#downloads)
* [Chromium Edge WebDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/#downloads)

## Available browsers

By default, Google Chrome, Firefox and Microsoft Edge are available when installed on the host system.

Starting from `v6.22` chrome, edgechromium, and geckodriver support `latest` as version.

Starting from `v9.0.6` support changes regarding new storage for `latest` versions of chromedriver.

## Install & Run

Expand Down Expand Up @@ -68,12 +75,6 @@ See [CLI](./docs/CLI.md) docs

See [API](./docs/API.md) docs

## Available browsers

By default, Google Chrome, Firefox and Microsoft Edge are available when installed on the host system.

Starting from `v6.22` chrome, edgechromium, and geckodriver support `latest` as version.

## Tips

- [Start Selenium whenever your (ubuntu) machine starts!](./docs/run-when-system-starts.md)
Expand Down
4 changes: 4 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ By default all drivers are loaded, you only control and change the versions or a

`opts.requestOpts` can be any valid [`got` options object](https://www.npmjs.com/package/got#proxies). You can use this for example to set a timeout.

`opts.processKiller` set for that truthy value, for killing selenium server port.

returns `Promise<ChildProcess>`

## Error: Port 4444 is already in use.
Expand All @@ -121,6 +123,8 @@ If you're getting this error, it means that you didn't shut down the server succ
pkill -f selenium-standalone
```

or use truthy `opts.processKiller` in config

## Set `selenium-standalone` Version as NodeJS environment parameter

You can set any version by `process.env.SELENIUM_VERSION=3.141.59` before starting selenium-standalone. Default values are here: [lib/default-config.js](../lib/default-config.js)
3 changes: 3 additions & 0 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ selenium-standalone start --singleDriverStart=chrome
selenium-standalone install --config=/path/to/config.json
selenium-standalone start --config=./config/seleniumConfig.js

# killing process before starting
selenium-standalone start --processKiller=true

```

Config file can be a JSON file or a [module file](https://nodejs.org/api/modules.html#modules_file_modules) that exports options as an object:
Expand Down
5 changes: 2 additions & 3 deletions lib/default-config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module.exports = () => {
const config = {
return {
baseURL: 'https://github.com/SeleniumHQ/selenium/releases/download',
version: process.env.SELENIUM_VERSION || '4.9.0',
processKiller: false,
drivers: {
chrome: {
version: 'latest',
Expand Down Expand Up @@ -31,6 +32,4 @@ module.exports = () => {
},
},
};

return config;
};
86 changes: 86 additions & 0 deletions lib/processKiller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const fkill = require('fkill');
const findProcess = require('find-process');
const { command } = require('execa');

function getConfigProcessesName(drivers) {
const driversName = Object.keys(drivers);
const processesName = [];

if (driversName && driversName.length) {
for (const driverName of driversName) {
if (driverName === 'chrome') {
processesName.push('chromedriver');
} else if (driverName === 'firefox') {
processesName.push('geckodriver');
} else if (driverName === 'chromiumedge') {
processesName.push('msedgedriver');
} else if (driverName === 'ie') {
processesName.push('IEDriverServer');
} else if (driverName === 'safari') {
processesName.push('safaridriver');
}
}
}
return processesName;
}

async function processKiller(drivers, portValue) {
if (portValue) {
if (!Number.isNaN(Number(`${portValue}`.startsWith(':') ? `${portValue}`.substring(1) : `${portValue}`))) {
const portCast = `${portValue}`.startsWith(':') ? portValue : `:${portValue}`;

await killProcessByFkill([portCast]);
await killProcessByCmd([`${portValue}`.startsWith(':') ? `${portValue}`.substring(1) : portValue], 'port');
}
}
if (drivers && typeof drivers === 'object' && Object.keys(drivers).length) {
await killProcess(getConfigProcessesName(drivers), 'name');
}
}

async function killProcess(processesNameArr, type) {
await killProcessByFkill(processesNameArr);
await killProcessByCmd(processesNameArr, type);
}

async function killProcessByCmd(processes, type) {
if (processes && processes.length) {
for (const processSingle of processes) {
const results = await findProcess(type, processSingle, true);

if (results && results.length) {
for (const result of results) {
try {
if (process.platform === 'win32' && !result.name.includes('node')) {
await command(`taskkill /F /IM ${result.name} /T`);

console.log(`Killed process: "${processSingle}" system name is "${result.name}"`);
} else if (!process.name.includes('node')) {
await command(`pkill -f ${result.name}`);

console.log(`Killed process: "${processSingle}" system name is "${result.name}"`);
}
} catch (_) {
// eslint-disable-next-line no-empty
}
}
}
}
}
}

async function killProcessByFkill(processes) {
for (const process of processes) {
try {
await fkill([process], { force: true, tree: true, ignoreCase: true });

console.log(`Killed process: "${process}"`);
} catch (_) {
// eslint-disable-next-line no-empty
}
}
}

module.exports = {
processKiller,
};
12 changes: 11 additions & 1 deletion lib/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const defaultConfig = require('./default-config')();
const { checkArgs } = require('./check-args');
const { isSelenium4 } = require('./isSelenium4');
const noop = require('./noop');
const { processKiller } = require('./processKiller.js');

async function start(_opts) {
const opts = checkArgs('Start API', _opts);
Expand Down Expand Up @@ -147,8 +148,17 @@ async function start(_opts) {
await checkPathsExistence(Object.keys(fsPaths).map((name) => fsPaths[name].installPath));

const seleniumStatusUrl = statusUrl.getSeleniumStatusUrl(args, opts);

if (await isPortReachable(seleniumStatusUrl.port, { timeout: 100 })) {
throw new Error(`Port ${seleniumStatusUrl.port} is already in use.`);
if ('processKiller' in opts && opts.processKiller) {
await processKiller(opts.drivers, seleniumStatusUrl.port);

if (await isPortReachable(seleniumStatusUrl.port, { timeout: 100 })) {
throw new Error(`Port ${seleniumStatusUrl.port} is already in use.`);
}
} else {
throw new Error(`Port ${seleniumStatusUrl.port} is already in use.`);
}
}

if (!opts.spawnOptions.stdio) {
Expand Down

0 comments on commit e6135ab

Please sign in to comment.