From 7f5c9c70a0c8d327c49f67465e95c9e56718d3aa Mon Sep 17 00:00:00 2001 From: cnishina Date: Sat, 11 Aug 2018 17:53:20 -0700 Subject: [PATCH] chore(cli): add cli with yargs - Do not use yarg typings until https://github.com/DefinitelyTyped/DefinitelyTyped/pull/28061 is resolved. - Created e2e test to updates, checks status, starts server, and removes files. - Expose seleniumProcess from the SeleniumServer class. - Moved SIGINT event to start handler method. This will let a user stop the selenium sever with the process pid and without existing their JavaScript process. --- lib/cli/index.ts | 205 +++++++++++++++++------------ lib/cmds/clean.ts | 50 ++++--- lib/cmds/cmds.spec-e2e.ts | 109 +++++++++++++++ lib/cmds/options.ts | 51 ++++--- lib/cmds/start.ts | 59 ++++++--- lib/cmds/status.ts | 51 ++++--- lib/cmds/update.ts | 42 +++--- lib/cmds/utils.spec-unit.ts | 72 ++++++++++ lib/cmds/utils.ts | 177 ++++++++++++++----------- lib/provider/chromedriver.ts | 3 + lib/provider/selenium_server.ts | 20 ++- lib/provider/utils/version_list.ts | 6 +- package-lock.json | 6 - package.json | 4 +- spec/jasmine-e2e.json | 8 ++ 15 files changed, 579 insertions(+), 284 deletions(-) create mode 100644 lib/cmds/cmds.spec-e2e.ts create mode 100644 lib/cmds/utils.spec-unit.ts create mode 100644 spec/jasmine-e2e.json diff --git a/lib/cli/index.ts b/lib/cli/index.ts index 7525f8b..9c6e473 100644 --- a/lib/cli/index.ts +++ b/lib/cli/index.ts @@ -1,86 +1,121 @@ -import * as yargs from 'yargs'; -import * as clean from '../cmds/clean'; -import * as start from '../cmds/start'; -import * as status from '../cmds/status'; -import * as update from '../cmds/update'; - -const chrome = { - describe: 'Install or update chromedriver.', - default: true -}; -const gecko = { - describe: 'Install or update geckodriver.', - default: true -}; -const ie = { - describe: 'Install or update ie driver.', - default: false -}; -const ignoreSSL = { - describe: 'Ignore SSL certificates.' -} -const outDir = { - describe: 'Location of output.', - default: 'downloads' -}; -const proxy = { - describe: 'Use a proxy server to download files.' -}; -const standalone = { - describe: 'Install or update selenium server standalone.', - default: true -}; -const versionsChrome = { - describe: 'The chromedriver version.' -}; -const versionsGecko = { - describe: 'The geckodriver version.', -}; -const versionsIe = { - describe: 'The ie driver version.' -}; -const versionsStandalone = { - describe: 'The selenium server standalone version.' -}; - -yargs - .command('clean', 'Removes downloaded files from the out_dir', { - 'out_dir': outDir - }, (argv: yargs.Arguments) => { - clean.handler(argv); - }) - .command('start', 'Start up the selenium server.', { - 'chrome': chrome, - 'gecko': gecko, - 'ie': ie, - 'out_dir': outDir, - 'standalone': standalone, - 'versions_chrome': versionsChrome, - 'versions_gecko': versionsGecko, - 'versions_ie': versionsIe, - 'versions_standalone': versionsStandalone, - }, (argv: yargs.Arguments) => { - start.handler(argv); - }) - .command('status', 'List the current available binaries.', { - 'out_dir': outDir - }, (argv: yargs.Arguments) => { - status.handler(argv); - }) - .command('update', 'Install or update selected binaries.', { - 'chrome': chrome, - 'gecko': gecko, - 'ie': ie, - 'ignore_ssl': ignoreSSL, - 'out_dir': outDir, - 'proxy': proxy, - 'standalone': standalone, - 'versions.chrome': versionsChrome, - 'versions.gecko': versionsGecko, - 'versions.ie': versionsIe, - 'versions.standalone': versionsStandalone, - }, (argv: yargs.Arguments) => { - update.handler(argv); - }) - .help() +import * as clean from '../cmds/clean'; +import * as start from '../cmds/start'; +import * as status from '../cmds/status'; +import * as update from '../cmds/update'; + +// Not using yarg typings due to: +// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/28061#issuecomment-412365576 +// Although the fix is to cast all my objects into a yargs.Options +// objects, the error is not obvious to debug if an error occurs. +const yargs = require('yargs'); + +// The following constants should be used to match +// the Option provider names. +const CHROME = 'chrome'; +const chromeOption = { + describe: 'Install or update chromedriver.', + default: true, + type: 'boolean' +}; +const GECKO = 'gecko'; +const geckoOption = { + describe: 'Install or update geckodriver.', + default: true, + type: 'boolean' +}; +const IEDRIVER = 'iedriver'; +const ieOption = { + describe: 'Install or update ie driver.', + default: false, + type: 'boolean' +}; +const IGNORE_SSL = 'ignore_ssl'; +const ignoreSSLOption = { + describe: 'Ignore SSL certificates.', + type: 'boolean' +}; +const OUT_DIR = 'out_dir'; +let outDirOption = { + describe: 'Location of output.', + default: 'downloads', + type: 'string' +}; +const PROXY = 'proxy'; +const proxyOption = { + describe: 'Use a proxy server to download files.', + type: 'string' +}; +const STANDALONE = 'standalone'; +const standaloneOption = { + describe: 'Install or update selenium server standalone.', + default: true, + type: 'boolean' +}; +const VERSIONS_CHROME = 'versions.chrome'; +const versionsChromeOption = { + describe: 'The chromedriver version.', + type: 'string' +}; +const VERSIONS_GECKO = 'versions.gecko'; +const versionsGeckoOption = { + describe: 'The geckodriver version.', + type: 'string' +}; +const VERSIONS_IE = 'versions.ie'; +const versionsIeOption = { + describe: 'The ie driver version.', + type: 'string' +}; +const VERSIONS_STANDALONE = 'versions.standalone'; +const versionsStandaloneOption = { + describe: 'The selenium server standalone version.', + type: 'string' +}; + +yargs + .command('clean', 'Removes downloaded files from the out_dir', + (yargs: any) => { + return yargs.option(OUT_DIR, outDirOption) + }, (argv: any) => { + clean.handler(argv); + }) + .command('start', 'Start up the selenium server.', + (yargs: any) => { + return yargs + .option(CHROME, chromeOption) + .option(GECKO, geckoOption) + .option(IEDRIVER, ieOption) + .option(OUT_DIR, outDirOption) + .option(STANDALONE, standaloneOption) + .option(VERSIONS_CHROME, versionsChromeOption) + .option(VERSIONS_GECKO, versionsGeckoOption) + .option(VERSIONS_IE, versionsIeOption) + .option(VERSIONS_STANDALONE, versionsStandaloneOption); + }, (argv: any) => { + start.handler(argv); + }) + .command('status', 'List the current available binaries.', + (yargs: any) => { + return yargs.option(OUT_DIR, outDirOption) + }, (argv: any) => { + status.handler(argv); + }) + .command('update', 'Install or update selected binaries.', + (yargs: any) => { + return yargs.option(OUT_DIR, outDirOption) + .option(CHROME, chromeOption) + .option(GECKO, geckoOption) + .option(IEDRIVER, ieOption) + .option(IGNORE_SSL, ignoreSSLOption) + .option(OUT_DIR, outDirOption) + .option(PROXY, proxyOption) + .option(STANDALONE, standaloneOption) + .option(VERSIONS_CHROME, versionsChromeOption) + .option(VERSIONS_GECKO, versionsGeckoOption) + .option(VERSIONS_IE, versionsIeOption) + .option(VERSIONS_STANDALONE, versionsStandaloneOption); + }, (argv: any) => { + update.handler(argv); + }) + .help() .argv; \ No newline at end of file diff --git a/lib/cmds/clean.ts b/lib/cmds/clean.ts index c6a8144..c4c7ad5 100644 --- a/lib/cmds/clean.ts +++ b/lib/cmds/clean.ts @@ -1,20 +1,30 @@ -import * as yargs from 'yargs'; -import { Options } from './options'; -import { constructAllProviders } from './utils'; - -export function handler(argv: yargs.Arguments) { - let options = constructAllProviders(argv); - console.log(clean(options)); -} - -export function clean(options: Options): string { - let filesCleaned: string[] = []; - for (let provider of options.providers) { - let cleanedFiles = provider.binary.cleanFiles(); - if (cleanedFiles) { - filesCleaned.push(cleanedFiles); - } - } - filesCleaned.push(options.server.binary.cleanFiles()); - return (filesCleaned.sort()).join(); -} +import { Options } from './options'; +import { constructAllProviders } from './utils'; + +/** + * Handles removing files that were downloaded and logs the files. + * @param argv The argv from yargs. + */ +export function handler(argv: any) { + let options = constructAllProviders(argv); + console.log(clean(options)); +} + +/** + * Goes through all the providers and removes the downloaded files. + * @param options The constructed options. + * @returns A list of deleted files. + */ +export function clean(options: Options): string { + let filesCleaned: string[] = []; + for (let provider of options.providers) { + let cleanedFiles = provider.binary.cleanFiles(); + if (cleanedFiles) { + filesCleaned.push(cleanedFiles); + } + } + if (options.server && options.server.binary) { + filesCleaned.push(options.server.binary.cleanFiles()); + } + return (filesCleaned.sort()).join('\n'); +} diff --git a/lib/cmds/cmds.spec-e2e.ts b/lib/cmds/cmds.spec-e2e.ts new file mode 100644 index 0000000..79a2f80 --- /dev/null +++ b/lib/cmds/cmds.spec-e2e.ts @@ -0,0 +1,109 @@ +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import * as rimraf from 'rimraf'; +import { + constructAllProviders, + constructProviders, +} from './utils'; +import { update } from './update'; +import { status } from './status'; +import { start } from './start'; +import { clean } from './clean'; +import { SeleniumServer } from '../provider/selenium_server'; +import { resolve } from 'url'; + +describe('using the cli', () => { + let tmpDir = path.resolve(os.tmpdir(), 'test'); + let origTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + + beforeAll(() => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; + try { + fs.mkdirSync(tmpDir); + } catch (err) {} + }); + + afterAll(() => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = origTimeout; + try { + rimraf.sync(tmpDir); + } catch (err) {} + }); + + describe('a user runs update', () => { + it('should download the files', async() => { + let argv = { + _: ['foobar'], + chrome: true, + standalone: true, + out_dir: tmpDir, + '$0': 'bin\\webdriver-manager' + }; + let options = constructProviders(argv); + await update(options); + const existFiles = fs.readdirSync(tmpDir); + expect(existFiles.length).toBe(7); + }); + }); + + describe('a user runs status', () => { + it('should get the list of versions', () => { + let argv = { + _: ['foobar'], + out_dir: tmpDir, + '$0': 'bin\\webdriver-manager' + }; + let options = constructAllProviders(argv); + let statusLog = status(options); + console.log(statusLog); + let lines = statusLog.split('\n'); + expect(lines.length).toBe(2); + }); + }); + + describe('a user runs start', () => { + it ('should start the selenium server standalone', async() => { + let argv = { + _: ['foobar'], + chrome: true, + standalone: true, + out_dir: tmpDir, + '$0': 'bin\\webdriver-manager' + }; + let options = constructProviders(argv); + // Do not await this promise to start the server since the promise is + // never resolved. + let startProcess = start(options); + + // Arbitrarily wait for the server to start. + await new Promise((resolve, _) => { + setTimeout(resolve, 3000); + }); + let seleniumServer = (options.server.binary as SeleniumServer); + expect(seleniumServer.seleniumProcess).toBeTruthy(); + expect(seleniumServer.seleniumProcess.pid).toBeTruthy(); + + // Kill the server with the pid. + await seleniumServer.stopServer(); + expect(await startProcess).toBe(0); + }); + }); + + describe('a user run clean', () => { + it('should remove the files', () => { + let argv = { + _: ['foobar'], + out_dir: tmpDir, + '$0': 'bin\\webdriver-manager' + }; + let options = constructAllProviders(argv); + let cleanLogs = clean(options); + console.log(cleanLogs); + let lines = cleanLogs.split('\n'); + expect(lines.length).toBe(7); + const existFiles = fs.readdirSync(tmpDir); + expect(existFiles.length).toBe(0); + }); + }); +}); diff --git a/lib/cmds/options.ts b/lib/cmds/options.ts index 6125737..564f898 100644 --- a/lib/cmds/options.ts +++ b/lib/cmds/options.ts @@ -1,20 +1,33 @@ -import { Provider } from '../provider/provider'; - -/** - * An options object to update and start the server. - */ -export interface Options { - providers?: Array<{ - name?: string, - version?: null, - binary?: Provider - }>, - server?: { - name?: string, - version?: null, - binary?: Provider - }, - proxy?: string, - ignoreSSL?: boolean, - outDir?: string +import { Provider } from '../provider/provider'; + +/** + * An options object to update and start the server. + */ +export interface Options { + /** + * A provider contains information about a browser driver not including + * the server. + */ + providers?: Array<{ + // The name of the binary. + name?: string, + // The version which does not have to follow semver. + version?: string, + // The binary provider object. + binary?: Provider + }>, + server?: { + // The name of the server. + name?: string, + // The version which does not have to follow semver. + version?: string, + // The server binary object. + binary?: Provider + }, + // The proxy url (must include protocol with url) + proxy?: string, + // To ignore SSL certs when making requests. + ignoreSSL?: boolean, + // The location where files should be saved. + outDir?: string } \ No newline at end of file diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 22c0016..0644a07 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -1,21 +1,40 @@ -import * as yargs from 'yargs'; -import { Options } from './options'; -import { constructAllProviders } from './utils'; -import { SeleniumServer } from '../provider/selenium_server'; - -export function handler(argv: yargs.Arguments) { - let options = constructAllProviders(argv); - start(options).then(() => {}); -} - -export function start(options: Options): Promise { - let javaOpts: {[key: string]: string} = {}; - for (let provider of options.providers) { - if (provider.binary) { - javaOpts[provider.binary.seleniumFlag] = - provider.binary.getBinaryPath(provider.version); - } - } - return (options.server.binary as SeleniumServer) - .startServer(javaOpts, options.server.version); +import { Options } from './options'; +import { constructAllProviders } from './utils'; +import { SeleniumServer } from '../provider/selenium_server'; + +/** + * Starts the selenium server standalone with the browser driver. + * @param argv The argv from yargs. + */ +export function handler(argv: any) { + let options = constructAllProviders(argv); + process.stdin.resume(); + process.on('SIGINT', () => { + let seleniumServer = options.server.binary as SeleniumServer; + process.kill(seleniumServer.seleniumProcess.pid); + process.exit(process.exitCode); + }); + start(options).then(() => {}); +} + +/** + * Goes through all the providers and creates java options to pass to java + * when starting the selenium server standalone. Also handles the SIGINT + * event when the server is stopped. + * @param options The constructed options. + * @returns Promise to start the selenium server. + */ +export function start(options: Options): Promise { + let javaOpts: {[key: string]: string} = {}; + for (let provider of options.providers) { + if (provider.binary) { + javaOpts[provider.binary.seleniumFlag] = + provider.binary.getBinaryPath(provider.version); + } + } + if (options.server && options.server.binary) { + return (options.server.binary as SeleniumServer) + .startServer(javaOpts, options.server.version); + } + return Promise.reject('Could not start the server'); } \ No newline at end of file diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts index 679e80f..dccf52d 100644 --- a/lib/cmds/status.ts +++ b/lib/cmds/status.ts @@ -1,22 +1,31 @@ -import * as yargs from 'yargs'; -import { Options } from './options'; -import { constructAllProviders } from './utils'; - - -export function handler(argv: yargs.Arguments) { - let options = constructAllProviders(argv); - console.log(status(options)); -} - -export function status(options: Options): string { - let binaryVersions = []; - for (let provider of options.providers) { - let status = provider.binary.getStatus(); - if (status) { - binaryVersions.push(`${provider.name}: ${status}`); - } - } - binaryVersions.push( - `${options.server.name}: ${options.server.binary.getStatus()}`); - return (binaryVersions.sort()).join('\n'); +import { Options } from './options'; +import { constructAllProviders } from './utils'; + +/** + * Displays which versions of providers that have been downloaded. + * @param argv The argv from yargs. + */ +export function handler(argv: any) { + let options = constructAllProviders(argv); + console.log(status(options)); +} + +/** + * Gets a list of versions for server and browser drivers. + * @param options The constructed options. + * @returns A string of the versions downloaded. + */ +export function status(options: Options): string { + let binaryVersions = []; + for (let provider of options.providers) { + let status = provider.binary.getStatus(); + if (status) { + binaryVersions.push(`${provider.name}: ${status}`); + } + } + if (options.server && options.server.binary) { + binaryVersions.push( + `${options.server.name}: ${options.server.binary.getStatus()}`); + } + return (binaryVersions.sort()).join('\n'); } \ No newline at end of file diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index bfc110b..78913d0 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -1,17 +1,27 @@ -import * as yargs from 'yargs'; -import { Options } from './options'; -import { constructProviders } from './utils'; - -export function handler(argv: yargs.Arguments) { - let options = constructProviders(argv); - update(options).then(() => {}); -} - -export function update(options: Options): Promise { - let promises = []; - for (let provider of options.providers) { - promises.push(provider.binary.updateBinary(provider.version)); - } - promises.push(options.server.binary.updateBinary(options.server.version)); - return Promise.all(promises); +import { Options } from './options'; +import { constructProviders } from './utils'; + +/** + * Updates / downloads the providers binaries. + * @param argv The argv from yargs. + */ +export function handler(argv: any) { + let options = constructProviders(argv); + update(options).then(() => {}); +} + +/** + * Updates / downloads the providers binaries. + * @param options The constructed options. + * @returns Promise when binaries are all downloaded. + */ +export function update(options: Options): Promise { + let promises = []; + for (let provider of options.providers) { + promises.push(provider.binary.updateBinary(provider.version)); + } + if (options.server && options.server.binary) { + promises.push(options.server.binary.updateBinary(options.server.version)); + } + return Promise.all(promises); } \ No newline at end of file diff --git a/lib/cmds/utils.spec-unit.ts b/lib/cmds/utils.spec-unit.ts new file mode 100644 index 0000000..46af08c --- /dev/null +++ b/lib/cmds/utils.spec-unit.ts @@ -0,0 +1,72 @@ +import { + constructAllProviders, + constructProviders, +} from './utils'; + +describe('utils', () => { + describe('constructAllProviders', () => { + it('should create all providers', () => { + let argv = { + _: ['foobar'], + proxy: 'http://some.proxy.com', + versions: { gecko: '0.16.0', chrome: '2.20' }, + out_dir: 'foobar_download', + ignore_ssl: false, + '$0': 'bin\\webdriver-manager' + }; + let options = constructAllProviders(argv); + expect(options.providers).toBeTruthy(); + expect(options.providers.length).toBe(3); + for (let provider of options.providers) { + if (provider.name === 'geckodriver') { + expect(provider.version).toBe('0.16.0'); + } + if (provider.name === 'chromedriver') { + expect(provider.version).toBe('2.20'); + } + if (provider.name === 'iedriver') { + expect(provider.version).toBeUndefined(); + } + expect(provider.binary).toBeTruthy(); + } + expect(options.server).toBeTruthy(); + expect(options.server.name).toBe('selenium'); + expect(options.server.version).toBeUndefined(); + expect(options.server.binary).toBeTruthy(); + expect(options.proxy).toBe('http://some.proxy.com'); + expect(options.ignoreSSL).toBeFalsy(); + expect(options.outDir).toBe('foobar_download'); + }); + }); + describe('constructProviders', () => { + it('should create the default providers', () => { + let argv = { + _: ['foobar'], + chrome: true, + gecko: true, + standalone: true, + versions: { gecko: '0.16.0', chrome: '2.20' }, + out_dir: 'foobar_download', + '$0': 'bin\\webdriver-manager' + }; + let options = constructProviders(argv); + expect(options.providers).toBeTruthy(); + expect(options.providers.length).toBe(2); + for (let provider of options.providers) { + if (provider.name === 'geckodriver') { + expect(provider.version).toBe('0.16.0'); + } + if (provider.name === 'chromedriver') { + expect(provider.version).toBe('2.20'); + } + expect(provider.binary).toBeTruthy(); + } + expect(options.server).toBeTruthy(); + expect(options.server.name).toBe('selenium'); + expect(options.server.version).toBeUndefined(); + expect(options.server.binary).toBeTruthy(); + expect(options.proxy).toBeUndefined(); + expect(options.ignoreSSL).toBeUndefined(); + }); + }) +}); \ No newline at end of file diff --git a/lib/cmds/utils.ts b/lib/cmds/utils.ts index e3979fa..5ba6d05 100644 --- a/lib/cmds/utils.ts +++ b/lib/cmds/utils.ts @@ -1,81 +1,98 @@ -import * as yargs from 'yargs'; -import { Options } from './options'; -import { ChromeDriver } from '../provider/chromedriver'; -import { GeckoDriver } from '../provider/geckodriver'; -import { IEDriver } from '../provider/iedriver'; -import { SeleniumServer } from '../provider/selenium_server'; - -export function constructAllProviders(argv: yargs.Arguments): Options { - let providerConfig = { - ignoreSSL: argv.ignore_ssl, - outDir: argv.out_dir, - proxy: argv.proxy - }; - return { - providers: [{ - name: 'chromedriver', - binary: new ChromeDriver(providerConfig), - version: argv.versions_chrome - }, { - name: 'geckodriver', - binary: new GeckoDriver(providerConfig), - version: argv.versions_gecko - }, { - name: 'iedriver', - binary: new IEDriver(providerConfig), - version: argv.versions_ie - }], - server: { - name: 'selenium', - binary: new SeleniumServer(providerConfig), - version: argv.versions_standalone - }, - ignoreSSL: argv.ignore_ssl, - outDir: argv.out_dir, - proxy: argv.proxy, - }; -} - -export function constructProviders(argv: yargs.Arguments): Options { - let options: Options = { - providers: [], - server: {}, - ignoreSSL: argv.ignore_ssl, - outDir: argv.out_dir, - proxy: argv.proxy, - }; - - let providerConfig = { - outDir: options.outDir, - proxy: options.proxy, - ignoreSSL: options.ignoreSSL - }; - - if (argv.chrome) { - options.providers.push({ - name: 'chromedriver', - binary: new ChromeDriver(providerConfig), - version: argv.versions_chrome - }); - } - if (argv.gecko) { - options.providers.push({ - name: 'geckodriver', - binary: new GeckoDriver(providerConfig), - version: argv.versions_gecko - }); - } - if (argv.ie) { - options.providers.push({ - name: 'iedriver', - binary: new IEDriver(providerConfig), - version: argv.versions_ie - }); - } - if (argv.standalone) { - options.server.name = 'selenium'; - options.server.binary = new SeleniumServer(providerConfig); - options.server.version = argv.versions_standalone; - } - return options; +const yargs = require('yargs'); +import { Options } from './options'; +import { ChromeDriver } from '../provider/chromedriver'; +import { GeckoDriver } from '../provider/geckodriver'; +import { IEDriver } from '../provider/iedriver'; +import { SeleniumServer } from '../provider/selenium_server'; + +export function constructAllProviders(argv: any): Options { + let providerConfig = { + ignoreSSL: argv.ignore_ssl, + outDir: argv.out_dir, + proxy: argv.proxy + }; + + let versionsChrome, versionsGecko, versionsIe, versionsStandalone = undefined; + if (argv.versions) { + versionsChrome = argv.versions.chrome; + versionsGecko = argv.versions.gecko; + versionsIe = argv.versions.ie; + versionsStandalone = argv.versions.standalone; + } + + return { + providers: [{ + name: 'chromedriver', + binary: new ChromeDriver(providerConfig), + version: versionsChrome + }, { + name: 'geckodriver', + binary: new GeckoDriver(providerConfig), + version: versionsGecko + }, { + name: 'iedriver', + binary: new IEDriver(providerConfig), + version: versionsIe + }], + server: { + name: 'selenium', + binary: new SeleniumServer(providerConfig), + version: versionsStandalone + }, + ignoreSSL: argv.ignore_ssl, + outDir: argv.out_dir, + proxy: argv.proxy, + }; +} + +export function constructProviders(argv: any): Options { + let options: Options = { + providers: [], + server: {}, + ignoreSSL: argv.ignore_ssl, + outDir: argv.out_dir, + proxy: argv.proxy, + }; + + let providerConfig = { + outDir: options.outDir, + proxy: options.proxy, + ignoreSSL: options.ignoreSSL + }; + + let versionsChrome, versionsGecko, versionsIe, versionsStandalone = undefined; + if (argv.versions) { + versionsChrome = argv.versions.chrome; + versionsGecko = argv.versions.gecko; + versionsIe = argv.versions.ie; + versionsStandalone = argv.versions.standalone; + } + + if (argv.chrome) { + options.providers.push({ + name: 'chromedriver', + binary: new ChromeDriver(providerConfig), + version: versionsChrome + }); + } + if (argv.gecko) { + options.providers.push({ + name: 'geckodriver', + binary: new GeckoDriver(providerConfig), + version: versionsGecko + }); + } + if (argv.ie) { + options.providers.push({ + name: 'iedriver', + binary: new IEDriver(providerConfig), + version: versionsIe + }); + } + if (argv.standalone) { + options.server.name = 'selenium'; + options.server.binary = new SeleniumServer(providerConfig); + options.server.version = versionsStandalone; + } + return options; } \ No newline at end of file diff --git a/lib/provider/chromedriver.ts b/lib/provider/chromedriver.ts index 348187b..9866aa3 100644 --- a/lib/provider/chromedriver.ts +++ b/lib/provider/chromedriver.ts @@ -72,6 +72,9 @@ export class ChromeDriver implements Provider { path.resolve(this.outDir, this.cacheFileName), '.zip', versionParser, semanticVersionParser); + if (version) { + version = version + '.0'; + } let versionObj = getVersion( versionList, osHelper(this.osType, this.osArch), version); diff --git a/lib/provider/selenium_server.ts b/lib/provider/selenium_server.ts index 93577ed..920dcf9 100644 --- a/lib/provider/selenium_server.ts +++ b/lib/provider/selenium_server.ts @@ -26,6 +26,7 @@ export class SeleniumServer implements Provider { outDir = OUT_DIR; proxy: string = null; requestUrl = 'https://selenium-release.storage.googleapis.com/'; + seleniumProcess: childProcess.ChildProcess; constructor(providerConfig?: ProviderConfig) { if (providerConfig) { @@ -97,24 +98,19 @@ export class SeleniumServer implements Provider { * @param version The optional version of the selenium jar file. * @returns A promise so the server can run while awaiting its completion. */ - startServer(opts: {[key:string]: string}, version?: string): Promise { + startServer(opts: {[key:string]: string}, version?: string): Promise { let cmd = this.getCmdStartServer(opts, version); console.log(cmd); - return new Promise((resolve, reject) => { - let seleniumProcess = childProcess.exec(cmd); - console.log(`selenium process id: ${seleniumProcess.pid}`); - seleniumProcess.on('exit', async(code: number) => { + return new Promise((resolve, reject) => { + this.seleniumProcess = childProcess.exec(cmd); + console.log(`selenium process id: ${this.seleniumProcess.pid}`); + this.seleniumProcess.on('exit', (code: number) => { console.log(`Selenium Standalone has exited with code: ${code}`); - process.exit(process.exitCode || code); + resolve(code); }); - seleniumProcess.on('error', (err: Error) => { + this.seleniumProcess.on('error', (err: Error) => { console.log(`Selenium Standalone server encountered an error: ${err}`); }); - process.stdin.resume(); - process.on('SIGINT', async() => { - process.kill(seleniumProcess.pid); - process.exit(process.exitCode); - }); }); } diff --git a/lib/provider/utils/version_list.ts b/lib/provider/utils/version_list.ts index 2da2565..0ec7cfd 100644 --- a/lib/provider/utils/version_list.ts +++ b/lib/provider/utils/version_list.ts @@ -8,7 +8,7 @@ import * as semver from 'semver'; * and os type. */ export interface VersionList { - // The forced version is the semver equivalent version of the + // The forced version is the semver equivalent version of the // actual version number. An example is 2.9 would translate into 2.9.0 [forced_version: string]: { @@ -23,7 +23,7 @@ export interface VersionList { export interface VersionObj { // The file name. name?: string; - + // The content length of the file. size?: number; @@ -31,7 +31,7 @@ export interface VersionObj { url?: string; // The actual version number, not the forced semantic version. - version?: string; + version?: string; } /** diff --git a/package-lock.json b/package-lock.json index 4e027a9..d0a4ecc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -126,12 +126,6 @@ "@types/node": "10.3.3" } }, - "@types/yargs": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-11.1.1.tgz", - "integrity": "sha512-Awgju4dqD8kHXX3jc/B/LaryJC7MsyNfnbN62lIbFzTi0GewH64zrkh4bxo/YTgVEK6r9V3GNecxMhXTJw0+jA==", - "dev": true - }, "adm-zip": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", diff --git a/package.json b/package.json index 31dab93..85b33cc 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "jasmine": "jasmine", "prepare": "tsc", "proxy-server": "tsc && node dist/spec/server/proxy_server.js", - "test": "tsc && npm run test-unit && npm run test-int && npm run test-proxy", + "test": "tsc && npm run test-unit && npm run test-int && npm run test-proxy && npm run test-e2e", + "test-e2e": "tsc && jasmine JASMINE_CONFIG_PATH=spec/jasmine-e2e.json", "test-int": "tsc && jasmine JASMINE_CONFIG_PATH=spec/jasmine-int.json", "test-proxy": "tsc && jasmine JASMINE_CONFIG_PATH=spec/jasmine-proxy.json", "test-unit": "tsc && jasmine JASMINE_CONFIG_PATH=spec/jasmine-unit.json", @@ -47,7 +48,6 @@ "@types/semver": "^5.5.0", "@types/tar": "^4.0.0", "@types/xml2js": "^0.4.3", - "@types/yargs": "^11.1.1", "http-proxy": "^1.17.0", "jasmine": "^3.1.0", "rimraf": "^2.6.2", diff --git a/spec/jasmine-e2e.json b/spec/jasmine-e2e.json new file mode 100644 index 0000000..c7f7665 --- /dev/null +++ b/spec/jasmine-e2e.json @@ -0,0 +1,8 @@ +{ + "spec_dir": "dist", + "spec_files": [ + "**/*.spec-e2e.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +}