diff --git a/README.md b/README.md index fc38234..25d282b 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,15 @@ npm install --save-dev karma-local-wd-launcher ## Drivers -This launcher assumes that drivers such as `chromedriver`, `geckodriver`, and -`safaridriver` (Mac only) are already installed, executable, and in your `PATH`. +This launcher will use the +[`webdriver-installer`](https://www.npmjs.com/package/webdriver-installer) +module to automatically download and install driver binaries for your local +browsers. These will be cached in a folder called `.webdriver-installer-cache` +in your home directory. Alternately, you may use the environment variables `CHROMEDRIVER_PATH`, `GECKODRIVER_PATH`, or `SAFARIDRIVER_PATH` to indicate where these drivers are -installed. +already installed. ## Configuration diff --git a/index.js b/index.js index 3768d23..1a34bd6 100644 --- a/index.js +++ b/index.js @@ -15,10 +15,19 @@ * Supports Chrome, Firefox, and Safari. */ +const fs = require('fs'); const os = require('os'); +const path = require('path'); const url = require('url'); const wd = require('wd'); +const {installWebDrivers} = require('webdriver-installer'); + +const DRIVER_CACHE = path.join(os.homedir(), '.webdriver-installer-cache'); +fs.mkdirSync(DRIVER_CACHE, {recursive: true}); + +let driversInstalledPromise = null; + // Map nodejs OS names to Selenium platform names. const PLATFORM_MAP = { 'darwin': 'Mac', @@ -32,6 +41,14 @@ const LocalWebDriverBase = function( this.browserName = browserName; + if (driverCommand[0] == '/') { + // Absolute path. Keep it. + } else { + // File name. Assume it's in our driver cache. + driverCommand = path.join(DRIVER_CACHE, driverCommand); + } + + // Checked by the base class to determine what command to run. this.DEFAULT_CMD = { linux: driverCommand, darwin: driverCommand, @@ -43,7 +60,8 @@ const LocalWebDriverBase = function( // Called by the base class to get arguments to pass to the driver command. this._getOptions = () => argsFromPort(port.toString()); - this.ENV_CMD = driverCommand.toUpperCase() + '_PATH'; + // An environment variable that can be used to override the command path. + this.ENV_CMD = driverCommand.toUpperCase().replace('-', '_') + '_PATH'; const config = { protocol: 'http:', @@ -110,7 +128,18 @@ const LocalWebDriverBase = function( const originalStart = this.start; let previousUrl = null; - this.start = (url) => { + this.start = async (url) => { + // If we haven't installed drivers yet in this session, start the + // installation process now. + if (!driversInstalledPromise) { + // TODO: Tie logging for this to karma log settings. + driversInstalledPromise = + installWebDrivers(DRIVER_CACHE, /* logging= */ false); + } + + // Wait for drivers to be installed for all local browsers. + await driversInstalledPromise; + previousUrl = url; originalStart.call(this, url); }; diff --git a/package-lock.json b/package-lock.json index a309d30..b6fafce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.2", "license": "Apache-2.0", "dependencies": { - "wd": "^1.14.0" + "wd": "^1.14.0", + "webdriver-installer": "^1.0.0" }, "peerDependencies": { "karma": "^6.2.0" @@ -872,6 +873,14 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1541,6 +1550,25 @@ "node": ">= 0.6" } }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1603,6 +1631,11 @@ "node": ">=0.10.0" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -2135,6 +2168,11 @@ "node": ">=0.8" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2313,6 +2351,30 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/webdriver-installer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/webdriver-installer/-/webdriver-installer-1.0.0.tgz", + "integrity": "sha512-bbwXpOqbLOUUzl2exIn474ZTeOXS7Pxst4qkbgEPbSDtfayIZV1g4sTr6ct9VEZQP6GuQ5PTEDinHBvu3ZIVZA==", + "dependencies": { + "node-fetch": "^2.6.7", + "tar-stream": "^2.2.0", + "yauzl": "^2.10.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -2392,6 +2454,15 @@ "node": ">=10" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/zip-stream": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", @@ -3105,6 +3176,14 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -3628,6 +3707,14 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "peer": true }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3672,6 +3759,11 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -4079,6 +4171,11 @@ "punycode": "^1.4.1" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4207,6 +4304,30 @@ } } }, + "webdriver-installer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/webdriver-installer/-/webdriver-installer-1.0.0.tgz", + "integrity": "sha512-bbwXpOqbLOUUzl2exIn474ZTeOXS7Pxst4qkbgEPbSDtfayIZV1g4sTr6ct9VEZQP6GuQ5PTEDinHBvu3ZIVZA==", + "requires": { + "node-fetch": "^2.6.7", + "tar-stream": "^2.2.0", + "yauzl": "^2.10.0" + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -4257,6 +4378,15 @@ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "peer": true }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "zip-stream": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", diff --git a/package.json b/package.json index d7b3c98..1bee6e2 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ ], "author": "Joey Parrish ", "dependencies": { - "wd": "^1.14.0" + "wd": "^1.14.0", + "webdriver-installer": "^1.0.0" }, "peerDependencies": { "karma": "^6.2.0"