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

Process killer feature #838

Merged
merged 7 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`opts.processKiller` set for that truthy value, for killing selenium server port.
`opts.processKiller` set to a 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
or use truthy `opts.processKiller` in config
or use `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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A user would ask why they would need to set this given it feels like the expected behavior is that the selenium server or driver shuts down completely when the process is triggered to get shutdown.


```

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
Loading
Loading