From 1820fbc46ddc45b70911fb1678f1d99247ec7028 Mon Sep 17 00:00:00 2001 From: Craig Date: Fri, 11 Mar 2016 10:39:00 -0800 Subject: [PATCH 01/79] Initial commit --- LICENSE | 21 +++++++++++++++++++++ README.md | 1 + 2 files changed, 22 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..eeabde90 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Craig + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..4ebce531 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# webdriver-manager \ No newline at end of file From 9a2a36d4b840f0f7cc9bf4fa011ed9a9a27d0bfd Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 11 Mar 2016 17:04:40 -0800 Subject: [PATCH 02/79] chore(bin): binaries separated from webdriver-manager with unit tests --- .clang-format | 3 + .gitignore | 4 + README.md | 68 +++++++++- config.json | 13 ++ gulpfile.js | 60 +++++++++ lib/binaries/binary.ts | 75 +++++++++++ lib/binaries/chromeDriver.ts | 48 +++++++ lib/binaries/ieDriver.ts | 46 +++++++ lib/binaries/index.ts | 4 + lib/binaries/standAlone.ts | 34 +++++ lib/cli/cli.ts | 135 ++++++++++++++++++++ lib/cli/index.ts | 3 + lib/cli/options.ts | 28 +++++ lib/cli/programs.ts | 223 +++++++++++++++++++++++++++++++++ lib/cmds/clean.ts | 38 ++++++ lib/cmds/opts.ts | 21 ++++ lib/cmds/start.ts | 108 ++++++++++++++++ lib/cmds/status.ts | 62 +++++++++ lib/cmds/update.ts | 119 ++++++++++++++++++ lib/config.ts | 55 ++++++++ lib/files/downloadedBinary.ts | 18 +++ lib/files/downloader.ts | 119 ++++++++++++++++++ lib/files/fileManager.ts | 179 ++++++++++++++++++++++++++ lib/files/index.ts | 3 + lib/webdriver.ts | 33 +++++ package.json | 34 +++++ spec/files/fileManager_spec.ts | 152 ++++++++++++++++++++++ spec/support/jasmine.json | 8 ++ spec/webdriver_spec.ts | 34 +++++ tsconfig.json | 19 +++ typings.json | 10 ++ 31 files changed, 1755 insertions(+), 1 deletion(-) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 config.json create mode 100644 gulpfile.js create mode 100644 lib/binaries/binary.ts create mode 100644 lib/binaries/chromeDriver.ts create mode 100644 lib/binaries/ieDriver.ts create mode 100644 lib/binaries/index.ts create mode 100644 lib/binaries/standAlone.ts create mode 100644 lib/cli/cli.ts create mode 100644 lib/cli/index.ts create mode 100644 lib/cli/options.ts create mode 100644 lib/cli/programs.ts create mode 100644 lib/cmds/clean.ts create mode 100644 lib/cmds/opts.ts create mode 100644 lib/cmds/start.ts create mode 100644 lib/cmds/status.ts create mode 100644 lib/cmds/update.ts create mode 100644 lib/config.ts create mode 100644 lib/files/downloadedBinary.ts create mode 100644 lib/files/downloader.ts create mode 100644 lib/files/fileManager.ts create mode 100644 lib/files/index.ts create mode 100644 lib/webdriver.ts create mode 100644 package.json create mode 100644 spec/files/fileManager_spec.ts create mode 100644 spec/support/jasmine.json create mode 100644 spec/webdriver_spec.ts create mode 100644 tsconfig.json create mode 100644 typings.json diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..8d1c3c31 --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +Language: JavaScript +BasedOnStyle: Google +ColumnLimit: 100 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2e7e84c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +built/ +node_modules/ +selenium/ +typings/ diff --git a/README.md b/README.md index 4ebce531..70ce8527 100644 --- a/README.md +++ b/README.md @@ -1 +1,67 @@ -# webdriver-manager \ No newline at end of file +# webdriver-manager + +### Setup + +``` +npm install +``` + +### Testing + +``` +gulp test +``` + +### Running webdriver +``` +node built/lib/webdriver.js +``` + +``` +Usage:webdriver-manager [options] + +Commands: + clean removes all downloaded driver files from the out_dir + start start up the selenium server + status list the current available drivers + update install or update selected binaries + +Options: + --out_dir Location to output/expect [default: /src/webdriver-manager/selenium] + --seleniumPort Optional port for the selenium standalone server + --versions_standalone Optional seleniuim standalone server version [default: 2.52.0] + --versions_chrome Optional chrome driver version [default: 2.21] + --ignore_ssl Ignore SSL certificates + --proxy Proxy to use for the install or update command + --alternate_cnd Alternate CDN to binaries + --standalone Install or update selenium standalone [default: true] + --chrome Install or update chromedriver [default: true] +``` + +### Running a specific command + +running update +``` +node built/lib/cmds/update.js update-run +``` + +help update +``` +node built/lib/cmds/update.js update-help +``` + +``` +Usage: update-run [options] + update-help +Description: install or update selected binaries + +Options: + --out_dir Location to output/expect [default: /src/webdriver-manager/selenium] + --ignore_ssl Ignore SSL certificates + --proxy Proxy to use for the install or update command + --alternate_cnd Alternate CDN to binaries + --standalone Install or update selenium standalone [default: true] + --chrome Install or update chromedriver [default: true] + --versions_standalone Optional seleniuim standalone server version [default: 2.52.0] + --versions_chrome Optional chrome driver version [default: 2.21] + ``` diff --git a/config.json b/config.json new file mode 100644 index 00000000..0c377acf --- /dev/null +++ b/config.json @@ -0,0 +1,13 @@ +{ + "webdriverVersions": { + "selenium": "2.52.0", + "chromedriver": "2.21", + "iedriver": "2.52.0" + }, + "cdnUrls": { + "selenium": "https://selenium-release.storage.googleapis.com/", + "chromedriver": "https://chromedriver.storage.googleapis.com/", + "iedriver": "https://selenium-release.storage.googleapis.com/" + } + +} diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..bfd92519 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,60 @@ +'use strict'; + +var gulp = require('gulp'); +var clangFormat = require('clang-format'); +var gulpFormat = require('gulp-clang-format'); +var runSequence = require('run-sequence'); +var spawn = require('child_process').spawn; + +var runSpawn = function(done, task, opt_arg) { + opt_arg = typeof opt_arg !== 'undefined' ? opt_arg : []; + var child = spawn(task, opt_arg, {stdio: 'inherit'}); + var running = false; + child.on('close', function() { + if (!running) { + running = true; + done(); + } + }); + child.on('error', function() { + if (!running) { + console.error('gulp encountered a child error'); + running = true; + done(); + } + }); +}; + +gulp.task('copy', function() { + return gulp.src(['config.json', 'package.json']) + .pipe(gulp.dest('built/')); +}); + +gulp.task('clang', function() { + return gulp.src(['src/**/*.ts']) + .pipe(gulpFormat.checkFormat('file', clangFormat)) + .on('warning', function(e) { + console.log(e); + }); +}); + +gulp.task('typings', function(done) { + runSpawn(done, 'node', ['node_modules/typings/dist/bin.js', 'install']); +}); + +gulp.task('tsc', function(done) { + runSpawn(done, 'node', ['node_modules/typescript/bin/tsc']); +}); + +gulp.task('prepublish', function(done) { + runSequence(['typings', 'clang'], 'tsc', 'copy', done); +}); + +gulp.task('default',['prepublish']); +gulp.task('build',['prepublish']); + +gulp.task('test', ['build'], function(done) { + var opt_arg = []; + opt_arg.push('node_modules/jasmine/bin/jasmine.js'); + runSpawn(done, 'node', opt_arg); +}); diff --git a/lib/binaries/binary.ts b/lib/binaries/binary.ts new file mode 100644 index 00000000..75e445b7 --- /dev/null +++ b/lib/binaries/binary.ts @@ -0,0 +1,75 @@ +/** + * operating system enum + */ +export enum OS { + Windows_NT, + Linux, + Darwin +} + +/** + * Dictionary to map the binary's id to the binary object + */ +export interface BinaryMap { [id: string]: T; } + + +/** + * The binary object base class + */ +export class Binary { + static os: Array; // the operating systems, the binary can run on + static id: string; // the binaries key identifier + static isDefault: boolean; // to download by default + static versionDefault: string; // a static default version variable + static shortName: Array; // the names used for a binary download + name: string; // used for logging to console + prefixDefault: string; // start of the file name + versionCustom: string; // version of file + suffixDefault: string; // file type for downloading + cdn: string; // url protocol and host + + /** + * @param ostype The operating system. + * @returns The executable file type. + */ + executableSuffix(ostype: string): string { + if (ostype == 'Windows_NT') { + return '.exe'; + } else { + return ''; + } + } + + /** + * @param ostype The operating system. + * @returns The file name for the executable. + */ + executableFilename(ostype: string): string { + return this.prefix() + this.version() + this.executableSuffix(ostype); + } + + prefix(): string { return this.prefixDefault; } + + version(): string { return this.versionCustom; } + + suffix(ostype?: string, arch?: string): string { return this.suffixDefault; } + + filename(ostype?: string, arch?: string): string { return this.prefix() + this.version() + this.suffix(ostype, arch); } + + shortVersion(version: string): string { return version.slice(0, version.lastIndexOf('.')); } + + /** + * A base class method that should be overridden. + */ + id(): string { return 'not implemented'; } + + /** + * A base class method that should be overridden. + */ + versionDefault(): string { return 'not implemented'; } + + /** + * A base class method that should be overridden. + */ + url(ostype?: string, arch?: string): string { return 'not implemented'; } +} diff --git a/lib/binaries/chromeDriver.ts b/lib/binaries/chromeDriver.ts new file mode 100644 index 00000000..538ea29a --- /dev/null +++ b/lib/binaries/chromeDriver.ts @@ -0,0 +1,48 @@ +import {arch, type} from 'os'; + +import {Binary, OS} from './binary'; +import {Config} from '../config'; + +/** + * The chrome driver binary. + */ +export class ChromeDriver extends Binary { + static os = [OS.Windows_NT, OS.Linux, OS.Darwin]; + static id = 'chrome'; + static versionDefault = Config.binaryVersions().chrome; + static isDefault = true; + static shortName = ['chrome']; + + constructor() { + super(); + this.name = 'chromedriver'; + this.versionCustom = ChromeDriver.versionDefault; + this.prefixDefault = 'chromedriver_'; + this.suffixDefault = '.zip'; + this.cdn = Config.cdnUrls().chrome; + } + + id(): string { return ChromeDriver.id; } + + versionDefault(): string { return ChromeDriver.versionDefault; } + + suffix(ostype: string, arch: string): string { + if (ostype === 'Darwin') { + return 'mac32' + this.suffixDefault; + } else if (ostype === 'Linux') { + if (arch === 'x64') { + return 'linux64' + this.suffixDefault; + } else { + return 'linux32' + this.suffixDefault; + } + } else if (ostype === 'Windows_NT') { + return 'win32' + this.suffixDefault; + } + } + + url(ostype: string, arch: string): string { + let urlBase = this.cdn + this.version() + '/'; + let filename = this.prefix() + this.suffix(ostype, arch); + return urlBase + filename; + } +} diff --git a/lib/binaries/ieDriver.ts b/lib/binaries/ieDriver.ts new file mode 100644 index 00000000..3d76c509 --- /dev/null +++ b/lib/binaries/ieDriver.ts @@ -0,0 +1,46 @@ +import {arch, type} from 'os'; + +import {Binary, OS} from './binary'; +import {Config} from '../config'; + +/** + * The internet explorer binary. + */ +export class IEDriver extends Binary { + static os = [OS.Windows_NT]; + static id = 'ie'; + static versionDefault = Config.binaryVersions().ie; + static isDefault = false; + static shortName = ['ie', 'ie32']; + + constructor() { + super(); + this.name = 'IEDriver'; + this.versionCustom = IEDriver.versionDefault; + this.prefixDefault = 'IEDriverServer'; + this.suffixDefault = '.zip'; + this.cdn = Config.cdnUrls().ie; + } + + id(): string { return IEDriver.id; } + + versionDefault(): string { return IEDriver.versionDefault; } + + version(): string { + if (type() == 'Windows_NT') { + if (arch() == 'x64') { + return '_x64_' + this.versionCustom; + } else { + return '_Win32_' + this.versionCustom; + ; + } + } + return ''; + } + + url(): string { + let urlBase = this.cdn + this.shortVersion(this.version()) + '/'; + let filename = this.prefix() + this.version() + this.suffix(); + return urlBase + filename; + } +} diff --git a/lib/binaries/index.ts b/lib/binaries/index.ts new file mode 100644 index 00000000..5db61ed1 --- /dev/null +++ b/lib/binaries/index.ts @@ -0,0 +1,4 @@ +export * from './binary'; +export * from './chromeDriver'; +export * from './ieDriver'; +export * from './standAlone'; diff --git a/lib/binaries/standAlone.ts b/lib/binaries/standAlone.ts new file mode 100644 index 00000000..06566743 --- /dev/null +++ b/lib/binaries/standAlone.ts @@ -0,0 +1,34 @@ +import {Binary, OS} from './binary'; +import {Config} from '../config'; + +/** + * The selenium server jar. + */ +export class StandAlone extends Binary { + static os = [OS.Windows_NT, OS.Linux, OS.Darwin]; + static id = 'standalone'; + static versionDefault = Config.binaryVersions().selenium; + static isDefault = true; + static shortName = ['standalone']; + + constructor() { + super(); + this.name = 'selenium standalone'; + this.versionCustom = StandAlone.versionDefault; + this.prefixDefault = 'selenium-server-standalone-'; + this.suffixDefault = '.jar'; + this.cdn = Config.cdnUrls().selenium; + } + + id(): string { return StandAlone.id; } + + versionDefault(): string { return StandAlone.versionDefault; } + + url(): string { + let urlBase = this.cdn + this.shortVersion(this.version()) + '/'; + let filename = this.prefix() + this.version() + this.suffix(); + return urlBase + filename; + } + + executableSuffix(ostype?: string): string { return '.jar'; } +} diff --git a/lib/cli/cli.ts b/lib/cli/cli.ts new file mode 100644 index 00000000..2a30df56 --- /dev/null +++ b/lib/cli/cli.ts @@ -0,0 +1,135 @@ +import {Programs, Program} from './programs'; +import {Args, Options} from './options'; + +/** + * The Cli contains the usage and the collection of programs. + * + * Printing help for all the programs in the following order: + * usage, commands, and options. If the options are used in multiple programs, + * it will list it once. + */ +export class Cli { + programs: Programs = {}; + usageText: string; + + /** + * Register a program to the command line interface. + * @returns The cli for method chaining. + */ + program(prog: Program): Cli { + this.programs[prog.cmd] = prog; + return this; + } + + /** + * Add a usage for the command line interface. + * @returns The cli for method chaining. + */ + usage(usageText: string): Cli { + this.usageText = usageText; + return this; + } + + /** + * Prints help for the programs registered to the cli. + */ + printHelp(): void { + console.log('Usage:' + this.usageText); + console.log('\nCommands:'); + let cmdDescriptionPos = this.posCmdDescription(); + for (let cmd in this.programs) { + let prog = this.programs[cmd]; + prog.printCmd(cmdDescriptionPos); + } + let descriptionPos = this.posDescription(); + let defaultPos = this.posDefault(); + let extOptions: Options = {}; + console.log('\nOptions:'); + // print all options + for (let cmd in this.programs) { + let prog = this.programs[cmd]; + prog.printOptions(descriptionPos, defaultPos, extOptions); + } + } + + /** + * For commands, gets the position where the description should start so they + * are aligned. + * @returns The position where the command description should start. + */ + posCmdDescription(): number { + let position = -1; + for (let cmd in this.programs) { + position = Math.max(position, cmd.length + 6); + } + return position; + } + + /** + * For options, gets the position where the description should start so they + * are aligned. + * @returns The position where the option description should start. + */ + posDescription(): number { + let position = -1; + for (let cmd in this.programs) { + let prog = this.programs[cmd]; + position = Math.max(position, prog.posDescription()); + } + return position; + } + + /** + * For options, get the position where the default values should start so they + * are aligned. + * @returns The position where the option default values should start. + */ + posDefault(): number { + let position = -1; + for (let cmd in this.programs) { + let prog = this.programs[cmd]; + position = Math.max(position, prog.posDefault()); + } + return position; + } + + /** + * Go through all programs and add options to the collection. + * @returns The options used in the programs. + */ + getOptions(): Options { + let allOptions: Options = {}; + for (let cmd in this.programs) { + let prog = this.programs[cmd]; + allOptions = prog.getOptions_(allOptions); + } + return allOptions; + } + + /** + * Get the options used by the programs and create the minimist options + * to ensure that minimist parses the values properly. + * @returns The options for minimist. + */ + getMinimistOptions(): Object { + let allOptions = this.getOptions(); + let minimistOptions: Args = {} + let minimistBoolean: Array = []; + let minimistString: Array = []; + let minimistNumber: Array = []; + for (let opt in allOptions) { + let option = allOptions[opt]; + if (option.type === 'boolean') { + minimistBoolean.push(option.opt); + } else if (option.type === 'string') { + minimistString.push(option.opt); + } else if (option.type === 'number') { + minimistNumber.push(option.opt); + } + } + minimistOptions['boolean'] = minimistBoolean; + minimistOptions['string'] = minimistString; + minimistOptions['number']= minimistNumber; + return minimistOptions; + } +} diff --git a/lib/cli/index.ts b/lib/cli/index.ts new file mode 100644 index 00000000..ac08d148 --- /dev/null +++ b/lib/cli/index.ts @@ -0,0 +1,3 @@ +export * from './cli'; +export * from './options'; +export * from './programs'; diff --git a/lib/cli/options.ts b/lib/cli/options.ts new file mode 100644 index 00000000..0c463c97 --- /dev/null +++ b/lib/cli/options.ts @@ -0,0 +1,28 @@ +export interface Args { [opt: string]: any } + +export interface Options { [opt: string]: Option; } + +export class Option { + opt: string; + description: string; + type: string; + defaultValue: any; + value: any; + + constructor(opt: string, description: string, type: string, defaultValue?: any) { + this.opt = opt; + this.description = description; + this.type = type; + if (defaultValue) { + this.defaultValue = defaultValue; + } + } + + getValue(): any { + if (this.value) { + return this.value; + } else { + return this.defaultValue; + } + } +} diff --git a/lib/cli/programs.ts b/lib/cli/programs.ts new file mode 100644 index 00000000..4917d5ce --- /dev/null +++ b/lib/cli/programs.ts @@ -0,0 +1,223 @@ +import * as minimist from 'minimist'; +import {Args, Options, Option} from './options'; + +/** + * Dictionary that maps the command and the program. + */ +export interface Programs { [cmd: string]: Program; } + +/** + * A program has a command, a description, options, and a run method + */ +export class Program { + static MIN_SPACING: number = 4; + cmd: string; + cmdDescription: string + options: Options = {}; + runMethod: Function; + helpDescription: string; + + /** + * Register a command and the description. + * @param cmd The command. + * @param cmdDescription The description of the command. + * @returns The program for method chaining. + */ + command(cmd: string, cmdDescription: string): Program { + this.cmd = cmd; + this.cmdDescription = cmdDescription; + return this; + } + + /** + * Register a new option. + * @param opt The option. + * @param description The description of the option. + * @param defaultValue The option's default value. + * @returns The program for method chaining. + */ + option(opt: string, description: string, opt_defaultValue?: any): Program { + this.options[opt] = new Option(opt, description, opt_defaultValue); + return this; + } + + /** + * Adds an option to the program. + * @param option The option. + * @returns The program for method chaining. + */ + addOption(option: Option): Program { + this.options[option.opt] = option; + return this; + } + + /** + * Registers a method that will be used to run the program. + * @param runMethod The method that will be used to run the program. + * @returns The program for method chaining. + */ + action(runMethod: Function): Program { + this.runMethod = runMethod; + return this; + } + + /** + * Adds the value to the options and passes the updated options to the run + * method. + * @param args The arguments that will be parsed to run the method. + */ + run(args: Args): void { + for (let arg in args) { + if (this.options[arg]) { + this.options[arg].value = args[arg]; + } + } + this.runMethod(this.options); + } + + /** + * Prints the command with the description. The description will have spaces + * between the cmd so that the starting position is "posDescription". If the + * gap between the cmd and the description is less than MIN_SPACING or + * posDescription is undefined, the spacing will be MIN_SPACING. + * + * @param opt_postDescription Starting position of the description. + */ + printCmd(opt_posDescription?: number): void { + let log = ' ' + this.cmd; + let spacing = Program.MIN_SPACING; + + if (opt_posDescription) { + let diff = opt_posDescription - log.length; + if (diff < Program.MIN_SPACING) { + spacing = Program.MIN_SPACING; + } else { + spacing = diff; + } + } + log += Array(spacing).join(' ') + this.cmdDescription; + console.log(log); + } + + /** + * Prints the options with the option descriptions and default values. + * The posDescription and posDefault is the starting position for the option + * description. If extOptions are provided, check to see if we have already + * printed those options. Also, once we print the option, add them to the extOptions. + * + * @param posDescription Position to start logging the description. + * @param posDefault Position to start logging the default value. + * @param opt_extOptions A collection of options that will be updated. + */ + printOptions(posDescription: number, posDefault: number, opt_extOptions?: Options): void { + for (let opt in this.options) { + // we have already logged it + if (opt_extOptions && opt_extOptions[opt]) { + continue; + } + + let option = this.options[opt]; + let log = ' --' + option.opt; + let spacing = Program.MIN_SPACING; + + // description + let diff = posDescription - log.length; + if (diff < Program.MIN_SPACING) { + spacing = Program.MIN_SPACING; + } else { + spacing = diff; + } + log += Array(spacing).join(' ') + option.description; + + // default value + if (option.defaultValue) { + spacing = Program.MIN_SPACING; + let diff = posDefault - log.length - 1; + if (diff <= Program.MIN_SPACING) { + spacing = Program.MIN_SPACING; + } else { + spacing = diff; + } + log += Array(spacing).join(' '); + log += '[default: ' + option.defaultValue + ']'; + } + + console.log(log); + if (opt_extOptions) { + opt_extOptions[option.opt] = option; + } + } + } + + /** + * Assuming that the this program can run by itself, to print out the program's + * help. Also assuming that the commands are called cmd-run and cmd-help. + */ + printHelp(): void { + console.log( + '\n' + + 'Usage: ' + this.cmd + '-run [options]\n' + + ' ' + this.cmd + '-help\n' + + 'Description: ' + this.cmdDescription + '\n'); + console.log('Options:'); + this.printOptions(this.posDescription(), this.posDefault()); + } + + posDescription(): number { return this.lengthOf_('opt') + 2 * Program.MIN_SPACING; } + + posDefault(): number { + return this.posDescription() + this.lengthOf_('description') + Program.MIN_SPACING; + } + + lengthOf_(param: string): number { + let maxLength = -1; + for (let opt in this.options) { + let option = this.options[opt]; + if (param === 'description') { + maxLength = Math.max(maxLength, option.description.length); + } else if (param === 'opt') { + maxLength = Math.max(maxLength, option.opt.length); + } + } + return maxLength; + } + + /** + * Create a collection of options used by this program. + * @returns The options used in the programs. + */ + getOptions_(allOptions: Options): Options { + for (let opt in this.options) { + allOptions[opt] = this.options[opt]; + } + return allOptions; + } + + /** + * Get the options used by the program and create the minimist options + * to ensure that minimist parses the values properly. + * @returns The options for minimist. + */ + getMinimistOptions() { + let allOptions: Options = {}; + allOptions = this.getOptions_(allOptions); + let minimistOptions: Args = {} + let minimistBoolean: Array = []; + let minimistString: Array = []; + let minimistNumber: Array = []; + for (let opt in allOptions) { + let option = allOptions[opt]; + if (option.type === 'boolean') { + minimistBoolean.push(option.opt); + } else if (option.type === 'string') { + minimistString.push(option.opt); + } else if (option.type === 'number') { + minimistNumber.push(option.opt); + } + } + minimistOptions['boolean'] = minimistBoolean; + minimistOptions['string'] = minimistString; + minimistOptions['number']= minimistNumber; + return minimistOptions; + } +} diff --git a/lib/cmds/clean.ts b/lib/cmds/clean.ts new file mode 100644 index 00000000..1656e042 --- /dev/null +++ b/lib/cmds/clean.ts @@ -0,0 +1,38 @@ +import * as minimist from 'minimist'; +import * as path from 'path'; + +import {Opts} from './opts'; +import {Config} from '../config'; +import {FileManager} from '../files'; +import {Options, Program} from '../cli'; + +let prog = new Program() + .command('clean', 'removes all downloaded driver files from the out_dir') + .action(clean) + .addOption(Opts.outputDir); + +export var program = prog; + +// stand alone runner +let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); +if (argv._[0] === 'clean-run') { + program.run(argv); +} else if (argv._[0] === 'clean-help') { + program.printHelp(); +} + +/** + * Parses the options and cleans the output directory of binaries. + * @param: options + */ +function clean(options: Options): void { + let outputDir = Config.SELENIUM_DIR; + if (options['out_dir'].getValue()) { + if (path.isAbsolute(options['out_dir'].getValue())) { + outputDir = options['out_dir'].getValue(); + } else { + outputDir = path.resolve(Config.BASE_DIR, options['out_dir'].getValue()); + } + } + FileManager.removeExistingFiles(outputDir); +} diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts new file mode 100644 index 00000000..c5511cd7 --- /dev/null +++ b/lib/cmds/opts.ts @@ -0,0 +1,21 @@ +import {Config} from '../config'; +import {Cli, Option, Options} from '../cli'; +import {ChromeDriver, IEDriver, StandAlone} from '../binaries'; + +/** + * The options used by the commands. + */ +export class Opts { + static outputDir = new Option('out_dir', 'Location to output/expect', 'string', Config.SELENIUM_DIR); + static seleniumPort = new Option('seleniumPort', 'Optional port for the selenium standalone server', 'string'); + static ignoreSsl = new Option('ignore_ssl', 'Ignore SSL certificates', 'boolean', false); + static proxy = new Option('proxy', 'Proxy to use for the install or update command', 'string'); + static alternateCdn = new Option('alternate_cnd', 'Alternate CDN to binaries', 'string'); + static standalone = new Option('standalone', 'Install or update selenium standalone', 'boolean', StandAlone.isDefault); + static chrome = new Option('chrome', 'Install or update chromedriver', 'boolean', ChromeDriver.isDefault); + static ie = new Option('ie', 'Install or update ie driver', 'boolean', IEDriver.isDefault); + static ie32 = new Option('ie32', 'Install or update 32-bit ie driver', 'boolean', IEDriver.isDefault); + static versionsChrome = new Option('versions_chrome', 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); + static versionsStandAlone = new Option('versions_standalone', 'Optional seleniuim standalone server version', 'string', StandAlone.versionDefault); + static versionsIe = new Option('versions_ie', 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); +} diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts new file mode 100644 index 00000000..bea1f404 --- /dev/null +++ b/lib/cmds/start.ts @@ -0,0 +1,108 @@ +import * as minimist from 'minimist'; +import * as path from 'path'; +import * as os from 'os'; +import * as childProcess from 'child_process'; +import * as http from 'http'; + +import {Opts} from './opts'; +import {Config} from '../config'; +import {FileManager} from '../files'; +import {Options, Program} from '../cli'; +import {BinaryMap, ChromeDriver, IEDriver, StandAlone} from '../binaries'; + +let prog = new Program() + .command('start', 'start up the selenium server') + .action(start) + .addOption(Opts.outputDir) + .addOption(Opts.seleniumPort) + .addOption(Opts.versionsStandAlone) + .addOption(Opts.versionsChrome); + +if (os.type() === 'Windows_NT') { + prog.addOption(Opts.versionsIe); +} + +export var program = prog; + +// stand alone runner +let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); +if (argv._[0] === 'start-run') { + prog.run(argv); +} else if (argv._[0] === 'start-help') { + prog.printHelp(); +} + +/** + * Parses the options and starts the selenium standalone server. + * @param options + */ +function start(options: Options) { + let osType = os.type(); + let binaries = FileManager.setupBinaries(); + let seleniumPort = options['seleniumPort'].getValue(); + let outputDir = Config.SELENIUM_DIR; + if (options['out_dir'].getValue()) { + if (path.isAbsolute(options['out_dir'].getValue())) { + outputDir = options['out_dir'].getValue(); + } else { + outputDir = path.resolve(Config.BASE_DIR, options['out_dir'].getValue()); + } + } + binaries[StandAlone.id].versionCustom = options['versions_standalone'].getValue(); + binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getValue(); + if (options['versions_ie']) { + binaries[IEDriver.id].versionCustom = options['versions_ie'].getValue(); + } + let downloadedBinaries = FileManager.downloadedBinaries(outputDir); + + if (downloadedBinaries[StandAlone.id] == null) { + console.error( + 'Selenium Standalone is not present. Install with ' + + 'webdriver-manager update --standalone'); + process.exit(1); + } + let args: Array = ['-jar', path.join(outputDir, binaries[StandAlone.id].filename())]; + if (seleniumPort) { + args.push('-port', seleniumPort); + } + if (downloadedBinaries[ChromeDriver.id] != null) { + args.push( + '-Dwebdriver.chrome.driver=' + + path.join(outputDir, binaries[ChromeDriver.id].executableFilename(osType))); + } + if (downloadedBinaries[IEDriver.id] != null) { + args.push( + '-Dwebdriver.ie.driver=' + + path.join(outputDir, binaries[IEDriver.id].executableFilename(osType))); + } + + // log the command to launch selenium server + let argsToString = ''; + for (let arg in args) { + argsToString += ' ' + args[arg]; + } + console.log('java' + argsToString); + + let seleniumProcess = spawnCommand('java', args); + console.log('seleniumProcess.pid: ' + seleniumProcess.pid); + seleniumProcess.on('exit', (code: number) => { + console.log('Selenium Standalone has exited with code ' + code); + process.exit(code); + }); + process.stdin.resume(); + process.stdin.on('data', (chunk: Buffer) => { + console.log('Attempting to shut down selenium nicely'); + http.get('http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer'); + }); + process.on('SIGINT', () => { + console.log('Staying alive until the Selenium Standalone process exits'); + }); +} + +function spawnCommand(command: string, args?: any) { + let win32 = process.platform === 'win32'; + let winCommand = win32 ? 'cmd' : command; + let finalArgs = win32 ? ['/c'].concat(command, args) : args; + + return childProcess.spawn(winCommand, finalArgs, {stdio: 'inherit'}); +} diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts new file mode 100644 index 00000000..428b75f1 --- /dev/null +++ b/lib/cmds/status.ts @@ -0,0 +1,62 @@ +import * as minimist from 'minimist'; +import * as path from 'path'; + +import {Opts} from './opts'; +import {Config} from '../config'; +import {FileManager} from '../files'; +import {Options, Program} from '../cli'; + +let prog = new Program() + .command('status', 'list the current available drivers') + .addOption(Opts.outputDir) + .action(status); + +export var program = prog; + +// stand alone runner +let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); +if (argv._[0] === 'status-run') { + prog.run(argv); +} else if (argv._[0] === 'status-help') { + prog.printHelp(); +} + +/** + * Parses the options and logs the status of the binaries downloaded. + * @param options + */ +function status(options: Options) { + let binaries = FileManager.setupBinaries(); + let outputDir = Config.SELENIUM_DIR; + if (options['out_dir'].value) { + if (path.isAbsolute(options['out_dir'].getValue())) { + outputDir = options['out_dir'].getValue(); + } else { + outputDir = path.resolve(Config.BASE_DIR, options['out_dir'].getValue()); + } + } + let downloadedBinaries = FileManager.downloadedBinaries(outputDir); + // log which binaries have been downloaded + for (let bin in downloadedBinaries) { + let downloaded = downloadedBinaries[bin]; + let log = downloaded.name + ' '; + log += downloaded.versions.length == 1 ? 'version available: ' : 'versions available: '; + for (let ver in downloaded.versions) { + let version = downloaded.versions[ver]; + log += version; + if (downloaded.binary.versionDefault() === version) { + log += ' [default]'; + } + if (+ver != downloaded.versions.length - 1) { + log += ', '; + } + } + console.log(log); + } + // for binaries that are available for the operating system, show them here + for (let bin in binaries) { + if (downloadedBinaries[bin] == null) { + console.log(binaries[bin].name + ' is not present'); + } + } +} diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts new file mode 100644 index 00000000..26c9ce39 --- /dev/null +++ b/lib/cmds/update.ts @@ -0,0 +1,119 @@ +import * as AdmZip from 'adm-zip'; +import * as fs from 'fs'; +import * as minimist from 'minimist'; +import * as os from 'os'; +import * as path from 'path'; + +import {Opts} from './opts'; +import {Config} from '../config'; +import {Binary, ChromeDriver, IEDriver, StandAlone} from '../binaries'; +import {FileManager, Downloader} from '../files'; +import {Options, Program} from '../cli'; + +var prog = new Program() + .command('update', 'install or update selected binaries') + .action(update) + .addOption(Opts.outputDir) + .addOption(Opts.ignoreSsl) + .addOption(Opts.proxy) + .addOption(Opts.alternateCdn) + .addOption(Opts.standalone) + .addOption(Opts.chrome); + +if (os.type() === 'Windows_NT') { + prog.addOption(Opts.ie).addOption(Opts.ie32); +} + +prog + .addOption(Opts.versionsStandAlone) + .addOption(Opts.versionsChrome); + +if (os.type() === 'Windows_NT') { + prog.addOption(Opts.versionsIe); +} +export var program = prog; + +// stand alone runner +let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); +if (argv._[0] === 'update-run') { + program.run(argv); +} else if (argv._[0] === 'update-help') { + program.printHelp(); +} + +/** + * Parses the options and downloads binaries if they do not exist. + * @param options + */ +function update(options: Options): void { + let standalone = options['standalone'].getValue() != undefined; + let chrome = options['chrome'].getValue() != undefined; + let ie: boolean = false; + let ie32: boolean = false; + if (options['ie']) { + ie = options['ie'].getValue() != undefined; + } + if (options['ie32']) { + ie32 = options['ie32'].getValue() != undefined; + } + let outputDir = Config.SELENIUM_DIR; + if (options['out_dir'].getValue()) { + if (path.isAbsolute(options['out_dir'].getValue())) { + outputDir = options['out_dir'].getValue(); + } else { + outputDir = path.resolve(Config.BASE_DIR, options['out_dir'].getValue()); + } + } + let ignoreSSL = options['ignore_ssl'].getValue() != undefined; + let proxy = options['proxy'].getValue(); + + // setup versions for binaries + let binaries = FileManager.setupBinaries(); + binaries[StandAlone.id].versionCustom = options['versions_standalone'].getValue(); + binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getValue(); + if (options['versions_ie']) { + binaries[IEDriver.id].versionCustom = options['versions.ie'].getValue(); + } + + // do the update + if (standalone) { + if (FileManager.toDownload(binaries[StandAlone.id], outputDir)) { + Downloader.downloadBinary(binaries[StandAlone.id], outputDir); + } else { + console.log(binaries[StandAlone.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); + } + } + if (chrome) { + if (FileManager.toDownload(binaries[ChromeDriver.id], outputDir)) { + Downloader.downloadBinary( + binaries[ChromeDriver.id], outputDir, proxy, ignoreSSL, unzip); + } else { + console.log(binaries[ChromeDriver.id].name + ' ' + binaries[ChromeDriver.id].versionCustom + ' up to date'); + } + } + if (ie) { + if (FileManager.toDownload(binaries[IEDriver.id], outputDir)) { + Downloader.downloadBinary( + binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); + } else { + console.log(binaries[IEDriver.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); + } + } + if (ie32) { + if (FileManager.toDownload(binaries[IEDriver.id], outputDir)) { + Downloader.downloadBinary( + binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); + } else { + console.log(binaries[IEDriver.id].name + ' 32-bit ' + binaries[StandAlone.id].versionCustom + ' up to date'); + } + } +} + +function unzip(binary: T, outputDir: string, filename: string): void { + let zip = new AdmZip(filename); + let osType = os.type(); + zip.extractAllTo(outputDir, true); + let mv = + path.join(outputDir, binary.prefix() + binary.version() + binary.executableSuffix(osType)); + fs.renameSync(path.join(outputDir, binary.name + binary.executableSuffix(osType)), mv); +} diff --git a/lib/config.ts b/lib/config.ts new file mode 100644 index 00000000..ff37abbd --- /dev/null +++ b/lib/config.ts @@ -0,0 +1,55 @@ +import * as path from 'path'; + +/** + * Dictionary map of the different binaries. + */ +export interface ConfigFile { + selenium?: string; + chrome?: string; + ie?: string; +} + +/** + * The configuration for webdriver-manager. + */ +export class Config { + static CONFIG_PATH: string = '../config.json'; + static PACKAGE_PATH: string = '../package.json'; + static BASE_DIR: string = path.resolve(__dirname, '../../'); + static SELENIUM_DIR: string = path.resolve(Config.BASE_DIR, 'selenium'); + + /** + * Get the binary versions from the configuration file. + * @returns A map of the versions defined in the configuration file. + */ + static binaryVersions(): ConfigFile { + let configFile = require(Config.CONFIG_PATH); + let configVersions: ConfigFile = {}; + configVersions.selenium = configFile.webdriverVersions.selenium; + configVersions.chrome = configFile.webdriverVersions.chromedriver; + configVersions.ie = configFile.webdriverVersions.iedriver; + return configVersions; + } + + /** + * Get the CDN urls from the configuration file. + * @returns A map of the CDN versions defined in the configuration file. + */ + static cdnUrls(): ConfigFile { + let configFile = require(Config.CONFIG_PATH); + let configCdnUrls: ConfigFile = {}; + configCdnUrls.selenium = configFile.cdnUrls.selenium; + configCdnUrls.chrome = configFile.cdnUrls.chromedriver; + configCdnUrls.ie = configFile.cdnUrls.iedriver; + return configCdnUrls; + } + + /** + * Get the current version of webdriver-manager from the package.json + * @returns The webdriver-manager version. + */ + static version(): string { + let packageJson = require(Config.PACKAGE_PATH); + return packageJson.version; + } +} diff --git a/lib/files/downloadedBinary.ts b/lib/files/downloadedBinary.ts new file mode 100644 index 00000000..df2e7f3a --- /dev/null +++ b/lib/files/downloadedBinary.ts @@ -0,0 +1,18 @@ +import {Binary} from '../binaries/binary'; + +/** + * The downloaded binary is the binary with the list of versions downloaded. + */ +export class DownloadedBinary extends Binary { + versions: Array = []; + binary: Binary; + + constructor(binary: Binary) { + super(); + this.binary = binary; + this.name = binary.name; + this.versionCustom = binary.versionCustom; + } + + id(): string { return this.binary.id(); } +} diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts new file mode 100644 index 00000000..dbda39d9 --- /dev/null +++ b/lib/files/downloader.ts @@ -0,0 +1,119 @@ +import * as os from 'os'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as request from 'request'; +import * as url from 'url'; + +import {Binary} from '../binaries/binary'; + +/** + * The file downloader. + */ +export class Downloader { + + /** + * Download the binary file. + * @param binary The binary of interest. + * @param outputDir The directory where files are downloaded and stored. + * @param opt_proxy The proxy for downloading files. + * @param opt_ignoreSSL To ignore SSL. + * @param opt_callback Callback method to be executed after the file is downloaded. + */ + static downloadBinary( + binary: Binary, outputDir: string, opt_proxy?: string, + opt_ignoreSSL?: boolean, opt_callback?: Function): void { + console.log('Updating ' + binary.name + ' to version ' + binary.version()); + var url = binary.url(os.type(), os.arch()); + if (!url) { + console.error(binary.name + ' v' + binary.version() + ' is not available for your system.'); + return; + } + Downloader.httpGetFile_( + url, binary.filename(os.type(), os.arch()), outputDir, opt_proxy, opt_ignoreSSL, (downloaded: boolean) => { + if (opt_callback) { + opt_callback(binary, outputDir, downloaded); + } + }); + } + + /** + * Resolves proxy based on values set + * @param fileUrl The url to download the file. + * @param opt_proxy The proxy to connect to to download files. + * @return Either undefined or the proxy. + */ + static resolveProxy_(fileUrl: string, opt_proxy?: string): string { + let protocol = url.parse(fileUrl).protocol; + if (opt_proxy) { + return opt_proxy; + } else if (protocol === 'https:') { + return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || + process.env.http_proxy; + } else if (protocol === 'http:') { + return process.env.HTTP_PROXY || process.env.http_proxy; + } + }; + + /** + * Ceates the GET request for the file name. + * @param fileUrl The url to download the file. + * @param fileName The name of the file to download. + * @param opt_proxy The proxy to connect to to download files. + * @param opt_ignoreSSL To ignore SSL. + */ + static httpGetFile_( + fileUrl: string, fileName: string, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, + callback?: Function): void { + console.log('downloading ' + fileUrl + '...'); + let filePath = path.join(outputDir, fileName); + let file = fs.createWriteStream(filePath); + let contentLength = 0; + + if (opt_ignoreSSL) { + console.log('Ignoring SSL certificate'); + } + + let options = { + url: fileUrl, + strictSSL: !opt_ignoreSSL, + rejectUnauthorized: !opt_ignoreSSL, + proxy: Downloader.resolveProxy_(fileUrl, opt_proxy) + }; + + request(options) + .on('response', + function(response) { + if (response.statusCode !== 200) { + fs.unlink(filePath); + console.error('Error: Got code ' + response.statusCode + ' from ' + fileUrl); + } + contentLength = response.headers['content-length']; + }) + .on('error', + function(error) { + console.error('Error: Got error ' + error + ' from ' + fileUrl); + fs.unlink(filePath); + }) + .pipe(file); + + file.on('close', function() { + fs.stat(filePath, function(err, stats) { + if (err) { + console.error('Error: Got error ' + err + ' from ' + fileUrl); + return; + } + if (stats.size != contentLength) { + console.error( + 'Error: corrupt download for ' + fileName + + '. Please re-run webdriver-manager update'); + fs.unlink(filePath); + return; + } + console.log(fileName + ' downloaded to ' + filePath); + if (callback) { + callback(filePath); + } + }); + }); + } +} diff --git a/lib/files/fileManager.ts b/lib/files/fileManager.ts new file mode 100644 index 00000000..5499ae92 --- /dev/null +++ b/lib/files/fileManager.ts @@ -0,0 +1,179 @@ +import * as os from 'os'; +import * as path from 'path'; +import * as fs from 'fs'; + +import {Binary, BinaryMap, ChromeDriver, IEDriver, StandAlone, OS} from '../binaries'; +import {DownloadedBinary} from './'; + +/** + * The File Manager class is where the webdriver manager will compile a list of + * binaries that could be downloaded and get a list of previously downloaded + * file versions. + */ +export class FileManager { + static makeOutputDirectory(outputDir: string) { + if (!fs.existsSync(outputDir) || !fs.statSync(outputDir).isDirectory()) { + fs.mkdirSync(outputDir); + } + } + + /** + * For the operating system, check against the list of operating systems that the + * binary is available for. + * @param osType The operating system. + * @param binary The class type to have access to the static properties. + * @returns If the binary is available for the operating system. + */ + static checkOS_(osType: string, binary: typeof Binary): boolean { + console.log(binary.os); + for (let os in binary.os) { + if (OS[os] == osType) { + return true; + } + } + return false; + } + + /** + * For the operating system, create a list that includes the binaries + * for selenium standalone, chrome, and internet explorer. + * @param osType The operating system. + * @returns A binary map that are available for the operating system. + */ + static compileBinaries_(osType: string): BinaryMap { + let binaries: BinaryMap = {}; + if (FileManager.checkOS_(osType, StandAlone)) { + binaries[StandAlone.id] = new StandAlone(); + } + if (FileManager.checkOS_(osType, ChromeDriver)) { + binaries[ChromeDriver.id] = new ChromeDriver(); + } + if (FileManager.checkOS_(osType, IEDriver)) { + binaries[IEDriver.id] = new IEDriver(); + } + return binaries; + } + + /** + * Look up the operating system and compile a list of binaries that are available + * for the system. + * @returns A binary map that is available for the operating system. + */ + static setupBinaries(): BinaryMap { return FileManager.compileBinaries_(os.type()); } + + /** + * Get the list of existing files from the output directory + * @param outputDir The directory where binaries are saved + * @returns A list of existing files. + */ + static getExistngFiles(outputDir: string): Array { return fs.readdirSync(outputDir); } + + /** + * For the binary, operating system, and system architecture, look through + * the existing files and the downloaded binary + * @param binary The binary of interest + * @param osType The operating system. + * @param existingFiles A list of existing files. + * @returns The downloaded binary with all the versions found. + */ + static downloadedVersions_( + binary: Binary, osType: string, arch: string, + existingFiles: Array): DownloadedBinary { + let versions: Array = []; + for (let existPos in existingFiles) { + let existFile: string = existingFiles[existPos]; + // use only files that have a prefix and suffix that we care about + if (existFile.indexOf(binary.prefix()) === 0) { + let editExistFile = existFile.replace(binary.prefix(), ''); + // if the suffix matches the executable suffix, add it + if (binary.suffix(osType, arch) === binary.executableSuffix(osType)) { + versions.push(editExistFile.replace(binary.suffix(osType, arch), '')); + } + // if the suffix does not match the executable, + // the binary is something like: .exe and .zip + else if (existFile.indexOf(binary.suffix(osType, arch)) === -1) { + editExistFile = editExistFile.replace(binary.executableSuffix(osType), ''); + editExistFile = editExistFile.indexOf('_') === 0 ? + editExistFile.substring(1, editExistFile.length) : + editExistFile; + versions.push(editExistFile); + } + } + } + if (versions.length === 0) { + return null; + } + let downloadedBinary = new DownloadedBinary(binary); + downloadedBinary.versions = versions; + return downloadedBinary; + } + + /** + * Finds all the downloaded binary versions stored in the output directory. + * @param outputDir The directory where files are downloaded and stored. + * @returns An dictionary map of all the downloaded binaries found in the output folder. + */ + static downloadedBinaries(outputDir: string): BinaryMap { + let ostype = os.type(); + let arch = os.arch(); + let binaries = FileManager.setupBinaries(); + let existingFiles = FileManager.getExistngFiles(outputDir); + let downloaded: BinaryMap = {}; + for (let bin in binaries) { + let binary = FileManager.downloadedVersions_(binaries[bin], ostype, arch, existingFiles); + if (binary != null) { + downloaded[binary.id()] = binary; + } + } + return downloaded; + } + + /** + * Check to see if the binary version should be downloaded. + * @param binary The binary of interest. + * @param outputDir The directory where files are downloaded and stored. + * @returns If the file should be downloaded. + */ + static toDownload(binary: T, outputDir: string): boolean { + let downloaded: BinaryMap = FileManager.downloadedBinaries(outputDir); + if (downloaded[binary.id()]) { + let versions = downloaded[binary.id()].versions; + let version = binary.version(); + for (let index in versions) { + let v = versions[index]; + if (v === version) { + return false; + } + } + } + return true; + } + + /** + * Removes the existing files found in the output directory that match the + * binary prefix names. + * @param outputDir The directory where files are downloaded and stored. + */ + static removeExistingFiles(outputDir: string): void { + // folder exists + if (!fs.existsSync(outputDir)) { + console.log('The out_dir path ' + outputDir + ' does not exist.'); + return; + } + let existingFiles = FileManager.getExistngFiles(outputDir); + if (existingFiles.length === 0) { + console.log('No files found in out_dir: ' + outputDir + '.'); + return; + } + + let binaries = FileManager.setupBinaries(); + existingFiles.forEach((file) => { + for (let binPos in binaries) { + let bin: Binary = binaries[binPos]; + if (file.indexOf(bin.prefix()) !== -1) { + fs.unlinkSync(path.join(outputDir, file)); + } + } + }) + } +} diff --git a/lib/files/index.ts b/lib/files/index.ts new file mode 100644 index 00000000..ca4d93a6 --- /dev/null +++ b/lib/files/index.ts @@ -0,0 +1,3 @@ +export * from './downloadedBinary'; +export * from './downloader'; +export * from './fileManager'; diff --git a/lib/webdriver.ts b/lib/webdriver.ts new file mode 100644 index 00000000..d3c2746e --- /dev/null +++ b/lib/webdriver.ts @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +import * as minimist from 'minimist'; +import * as clean from './cmds/clean'; +import * as start from './cmds/start'; +import * as status from './cmds/status'; +import * as update from './cmds/update'; + +import {Cli} from './cli'; + +let commandline = new Cli() + .usage('webdriver-manager [options]') + .program(clean.program) + .program(start.program) + .program(status.program) + .program(update.program); + +let minimistOptions = commandline.getMinimistOptions(); +let argv = minimist(process.argv.slice(2), minimistOptions); +let cmd = argv._; +if (commandline.programs[cmd[0]]) { + if (cmd[1] === 'help') { + commandline.programs[cmd[0]].printHelp(); + } else { + commandline.programs[cmd[0]].run(argv); + } +} else if (cmd[0] === 'help'){ + commandline.printHelp(); +} else { + // do nothing +} + +export var cli = commandline; diff --git a/package.json b/package.json new file mode 100644 index 00000000..571ca499 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "webdriver-manager", + "version": "1.0.0", + "description": "Webdriver Manager for E2E protractor tests", + "scripts": { + "prepublish": "gulp prepublish", + "test": "gulp test" + }, + "keywords": [ + "angular", + "test", + "testing", + "protractor", + "webdriver", + "webdriverjs", + "selenium" + ], + "author": "Craig Nishina ", + "license": "MIT", + "dependencies": { + "adm-zip": "^0.4.7", + "clang-format": "^1.0.35", + "gulp": "^3.9.1", + "gulp-clang-format": "^1.0.23", + "http": "0.0.0", + "jasmine": "^2.4.1", + "minimist": "^1.2.0", + "request": "^2.69.0", + "run-sequence": "^1.1.5", + "typescript": "^1.8.7", + "typings": "^0.7.7", + "url": "^0.11.0" + } +} diff --git a/spec/files/fileManager_spec.ts b/spec/files/fileManager_spec.ts new file mode 100644 index 00000000..e2d97300 --- /dev/null +++ b/spec/files/fileManager_spec.ts @@ -0,0 +1,152 @@ +/// + +import * as fs from 'fs'; +import * as path from 'path'; +import {Binary, ChromeDriver, IEDriver, StandAlone} from '../../lib/binaries'; +import {DownloadedBinary, FileManager} from '../../lib/files'; + + +describe('file manager', () => { + + describe('setting up for windows', () => { + let osType = 'Windows_NT'; + + it('should find correct binaries', () => { + expect(FileManager.checkOS_(osType, ChromeDriver)).toBe(true); + expect(FileManager.checkOS_(osType, IEDriver)).toBe(true); + expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); + }); + + it('should return the binary array', () => { + let binaries = FileManager.compileBinaries_(osType); + expect(binaries[StandAlone.id].name).toBe((new StandAlone()).name); + expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); + expect(binaries[IEDriver.id].name).toBe((new IEDriver()).name); + }); + }); + + describe('setting up for linux', () => { + let osType = 'Linux'; + + it('should find correct binaries', () => { + expect(FileManager.checkOS_(osType, ChromeDriver)).toBe(true); + expect(FileManager.checkOS_(osType, IEDriver)).toBe(false); + expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); + }); + + it('should return the binary array', () => { + let binaries = FileManager.compileBinaries_(osType); + expect(binaries[StandAlone.id].name).toBe((new StandAlone()).name); + expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); + expect(binaries[IEDriver.id]).toBeUndefined(); + }); + }); + + describe('setting up for mac', () => { + let osType = 'Darwin'; + + it('should find correct binaries', () => { + expect(FileManager.checkOS_(osType, ChromeDriver)).toBe(true); + expect(FileManager.checkOS_(osType, IEDriver)).toBe(false); + expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); + }); + + it('should return the binary array', () => { + let binaries = FileManager.compileBinaries_(osType); + expect(binaries[StandAlone.id].name).toBe((new StandAlone()).name); + expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); + expect(binaries[IEDriver.id]).toBeUndefined(); + }); + }); + + describe('downloaded version checks', () => { + let existingFiles: Array; + let selenium = new StandAlone(); + let chrome = new ChromeDriver(); + let ie = new IEDriver(); + let ostype: string; + let arch: string; + + function setup(osType: string): void { + ostype = osType; + arch = 'x64'; + existingFiles = [ + selenium.prefix() + '2.51.0' + selenium.executableSuffix(), + selenium.prefix() + '2.52.0' + selenium.executableSuffix()]; + existingFiles.push(chrome.prefix() + '2.20' + chrome.suffix(ostype, arch)); + existingFiles.push(chrome.prefix() + '2.20' + chrome.executableSuffix(ostype)); + existingFiles.push(chrome.prefix() + '2.21' + chrome.suffix(ostype, arch)); + existingFiles.push(chrome.prefix() + '2.21' + chrome.executableSuffix(ostype)); + if (ostype == 'Windows_NT') { + existingFiles.push(ie.prefix() + '_Win32_2.51.0' + ie.suffix()); + existingFiles.push(ie.prefix() + '_Win32_2.51.0' + ie.executableSuffix(ostype)); + existingFiles.push(ie.prefix() + '_x64_2.51.0' + ie.suffix()); + existingFiles.push(ie.prefix() + '_x64_2.51.0' + ie.executableSuffix(ostype)); + existingFiles.push(ie.prefix() + '_Win32_2.52.0' + ie.suffix()); + existingFiles.push(ie.prefix() + '_Win32_2.52.0' + ie.executableSuffix(ostype)); + existingFiles.push(ie.prefix() + '_x64_2.52.0' + ie.suffix()); + existingFiles.push(ie.prefix() + '_x64_2.52.0' + ie.executableSuffix(ostype)); + } + } + + describe('versions for selenium', () => { + it('should find the correct version for windows', () => { + setup('Windows_NT'); + let downloaded = FileManager.downloadedVersions_(selenium, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('2.51.0'); + expect(downloaded.versions[1]).toBe('2.52.0'); + }); + it('should find the correct version for mac', () => { + setup('Darwin'); + let downloaded = FileManager.downloadedVersions_(selenium, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('2.51.0'); + expect(downloaded.versions[1]).toBe('2.52.0'); + }); + it('should find the correct version for mac', () => { + setup('Linux'); + let downloaded = FileManager.downloadedVersions_(selenium, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('2.51.0'); + expect(downloaded.versions[1]).toBe('2.52.0'); + }); + }); + + describe('versions for chrome', () => { + it('should find the correct version for windows', () => { + setup('Windows_NT'); + let downloaded = FileManager.downloadedVersions_(chrome, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('2.20'); + expect(downloaded.versions[1]).toBe('2.21'); + }); + it('should find the correct version for mac', () => { + setup('Darwin'); + let downloaded = FileManager.downloadedVersions_(chrome, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('2.20'); + expect(downloaded.versions[1]).toBe('2.21'); + }); + it('should find the correct version for linux', () => { + setup('Linux'); + let downloaded = FileManager.downloadedVersions_(chrome, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('2.20'); + expect(downloaded.versions[1]).toBe('2.21'); + }); + }); + + describe('versions for ie on windows', () => { + it('should find the correct version for windows', () => { + setup('Windows_NT'); + let downloaded = FileManager.downloadedVersions_(ie, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(4); + expect(downloaded.versions[0]).toBe('Win32_2.51.0'); + expect(downloaded.versions[1]).toBe('x64_2.51.0'); + expect(downloaded.versions[2]).toBe('Win32_2.52.0'); + expect(downloaded.versions[3]).toBe('x64_2.52.0'); + }); + }); + }); +}); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 00000000..0564d8cb --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,8 @@ +{ + "spec_dir": "built/spec", + "spec_files": [ + "**/*_spec.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +} diff --git a/spec/webdriver_spec.ts b/spec/webdriver_spec.ts new file mode 100644 index 00000000..81450664 --- /dev/null +++ b/spec/webdriver_spec.ts @@ -0,0 +1,34 @@ +/// + +import * as child_process from 'child_process'; +import * as path from 'path'; +import {cli} from '../lib/webdriver'; + +function runSpawn(task: string, opt_arg: Array): Array { + opt_arg = typeof opt_arg !== 'undefined' ? opt_arg : []; + let child = child_process.spawnSync(task, opt_arg, {stdio: 'pipe'}); + return child.output[1].toString().split('\n'); +}; + +describe('cli', () => { + describe('help', () => { + it ('should have usage and commands', () => { + let lines = runSpawn('node', ['built/lib/webdriver.js', 'help']); + + // very specific to make sure the + let index = 0 + expect(lines[index++].indexOf('Usage:')).toBe(0); + index++; + expect(lines[index++].indexOf('Commands:')).toBe(0); + for (let cmd in cli.programs) { + expect(lines[index++].indexOf(cmd)).toBe(2); + } + index++; + expect(lines[index++].indexOf('Options:')).toBe(0); + let options = cli.getOptions(); + for (let opt in options) { + expect(lines[index++].indexOf('--' + opt)).toBe(2); + } + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..cb334d95 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": false, + "declaration": true, + "removeComments": false, + "noImplicitAny": true, + "outDir": "built/" + }, + "exclude": [ + "built", + "node_modules", + "typings/browser", + "typings/browser.d.ts", + "typings/main" + ] +} diff --git a/typings.json b/typings.json new file mode 100644 index 00000000..53b8281c --- /dev/null +++ b/typings.json @@ -0,0 +1,10 @@ +{ + "ambientDependencies": { + "adm-zip": "registry:dt/adm-zip#0.0.0+20160211023517", + "form-data": "registry:dt/form-data#0.0.0+20150826060748", + "jasmine": "registry:dt/jasmine#2.2.0+20160308082659", + "minimist": "registry:dt/minimist#1.1.3+20151229171613", + "node": "registry:dt/node#4.0.0+20160226132328", + "request": "registry:dt/request#0.0.0+20160311134650" + } +} From 0eb0c4fcb8e61861ee1bbd8fb72a14c5c0fdda92 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Tue, 22 Mar 2016 15:22:11 -0700 Subject: [PATCH 03/79] chore(style): fix to typescript style guide --- bin/webdriver-tool | 3 ++ lib/binaries/binary.ts | 4 +-- .../{chromeDriver.ts => chrome_driver.ts} | 0 lib/binaries/{ieDriver.ts => ie_driver.ts} | 0 lib/binaries/index.ts | 6 ++-- .../{standAlone.ts => stand_alone.ts} | 0 lib/cli/cli.ts | 10 +++--- lib/cli/options.ts | 33 ++++++++++++++++--- lib/cli/programs.ts | 15 +++++---- lib/cmds/clean.ts | 10 +++--- lib/cmds/opts.ts | 2 +- lib/cmds/start.ts | 29 ++++++++-------- lib/cmds/status.ts | 8 ++--- lib/cmds/update.ts | 28 ++++++++-------- lib/config.ts | 14 ++++---- ...wnloadedBinary.ts => downloaded_binary.ts} | 2 +- lib/files/downloader.ts | 4 +-- lib/files/{fileManager.ts => file_manager.ts} | 19 +++++------ lib/files/index.ts | 4 +-- package.json | 23 ++++++++----- spec/files/fileManager_spec.ts | 2 +- spec/webdriver_spec.ts | 2 +- 22 files changed, 126 insertions(+), 92 deletions(-) create mode 100644 bin/webdriver-tool rename lib/binaries/{chromeDriver.ts => chrome_driver.ts} (100%) rename lib/binaries/{ieDriver.ts => ie_driver.ts} (100%) rename lib/binaries/{standAlone.ts => stand_alone.ts} (100%) rename lib/files/{downloadedBinary.ts => downloaded_binary.ts} (92%) rename lib/files/{fileManager.ts => file_manager.ts} (91%) diff --git a/bin/webdriver-tool b/bin/webdriver-tool new file mode 100644 index 00000000..ae4878a2 --- /dev/null +++ b/bin/webdriver-tool @@ -0,0 +1,3 @@ +!#/usr/bin/env node + +require('../built/lib/webdriver.js'); diff --git a/lib/binaries/binary.ts b/lib/binaries/binary.ts index 75e445b7..b3974056 100644 --- a/lib/binaries/binary.ts +++ b/lib/binaries/binary.ts @@ -17,11 +17,11 @@ export interface BinaryMap { [id: string]: T; } * The binary object base class */ export class Binary { - static os: Array; // the operating systems, the binary can run on + static os: OS[]; // the operating systems, the binary can run on static id: string; // the binaries key identifier static isDefault: boolean; // to download by default static versionDefault: string; // a static default version variable - static shortName: Array; // the names used for a binary download + static shortName: string[]; // the names used for a binary download name: string; // used for logging to console prefixDefault: string; // start of the file name versionCustom: string; // version of file diff --git a/lib/binaries/chromeDriver.ts b/lib/binaries/chrome_driver.ts similarity index 100% rename from lib/binaries/chromeDriver.ts rename to lib/binaries/chrome_driver.ts diff --git a/lib/binaries/ieDriver.ts b/lib/binaries/ie_driver.ts similarity index 100% rename from lib/binaries/ieDriver.ts rename to lib/binaries/ie_driver.ts diff --git a/lib/binaries/index.ts b/lib/binaries/index.ts index 5db61ed1..1d45e1c6 100644 --- a/lib/binaries/index.ts +++ b/lib/binaries/index.ts @@ -1,4 +1,4 @@ export * from './binary'; -export * from './chromeDriver'; -export * from './ieDriver'; -export * from './standAlone'; +export * from './chrome_driver'; +export * from './ie_driver'; +export * from './stand_alone'; diff --git a/lib/binaries/standAlone.ts b/lib/binaries/stand_alone.ts similarity index 100% rename from lib/binaries/standAlone.ts rename to lib/binaries/stand_alone.ts diff --git a/lib/cli/cli.ts b/lib/cli/cli.ts index 2a30df56..a9c134d2 100644 --- a/lib/cli/cli.ts +++ b/lib/cli/cli.ts @@ -1,5 +1,5 @@ import {Programs, Program} from './programs'; -import {Args, Options} from './options'; +import {MinimistArgs, Options} from './options'; /** * The Cli contains the usage and the collection of programs. @@ -113,10 +113,10 @@ export class Cli { */ getMinimistOptions(): Object { let allOptions = this.getOptions(); - let minimistOptions: Args = {} - let minimistBoolean: Array = []; - let minimistString: Array = []; - let minimistNumber: Array = []; + let minimistOptions: MinimistArgs = {} + let minimistBoolean: string[] = []; + let minimistString: string[] = []; + let minimistNumber: string[] = []; for (let opt in allOptions) { let option = allOptions[opt]; if (option.type === 'boolean') { diff --git a/lib/cli/options.ts b/lib/cli/options.ts index 0c463c97..187c4286 100644 --- a/lib/cli/options.ts +++ b/lib/cli/options.ts @@ -1,4 +1,6 @@ -export interface Args { [opt: string]: any } +export interface MinimistArgs { [opt: string]: string[] } + +export interface Args { [opt: string]: number|string|boolean } export interface Options { [opt: string]: Option; } @@ -6,10 +8,10 @@ export class Option { opt: string; description: string; type: string; - defaultValue: any; - value: any; + defaultValue: number|string|boolean; + value: number|string|boolean; - constructor(opt: string, description: string, type: string, defaultValue?: any) { + constructor(opt: string, description: string, type: string, defaultValue?: number|string|boolean) { this.opt = opt; this.description = description; this.type = type; @@ -18,11 +20,32 @@ export class Option { } } - getValue(): any { + getValue_(): number|string|boolean { if (this.value) { return this.value; } else { return this.defaultValue; } } + + getNumber(): number { + let value = this.getValue_(); + if (value && typeof value === 'number') { + return +value; + } + return null; + } + + getString(): string { + let value = this.getValue_(); + if (value && typeof value === 'string') { + return '' + value; + } + return null; + } + + getBoolean(): boolean { + let value = this.getValue_(); + return value ? true : false; + } } diff --git a/lib/cli/programs.ts b/lib/cli/programs.ts index 4917d5ce..839284f6 100644 --- a/lib/cli/programs.ts +++ b/lib/cli/programs.ts @@ -1,5 +1,5 @@ import * as minimist from 'minimist'; -import {Args, Options, Option} from './options'; +import {Args, MinimistArgs, Options, Option} from './options'; /** * Dictionary that maps the command and the program. @@ -33,11 +33,12 @@ export class Program { * Register a new option. * @param opt The option. * @param description The description of the option. + * @param type The type of value expected: boolean, number, or string * @param defaultValue The option's default value. * @returns The program for method chaining. */ - option(opt: string, description: string, opt_defaultValue?: any): Program { - this.options[opt] = new Option(opt, description, opt_defaultValue); + option(opt: string, description: string, type: string, opt_defaultValue?: number|string|boolean): Program { + this.options[opt] = new Option(opt, description, type, opt_defaultValue); return this; } @@ -201,10 +202,10 @@ export class Program { getMinimistOptions() { let allOptions: Options = {}; allOptions = this.getOptions_(allOptions); - let minimistOptions: Args = {} - let minimistBoolean: Array = []; - let minimistString: Array = []; - let minimistNumber: Array = []; + let minimistOptions: MinimistArgs = {} + let minimistBoolean: string[] = []; + let minimistString: string[] = []; + let minimistNumber: string[] = []; for (let opt in allOptions) { let option = allOptions[opt]; if (option.type === 'boolean') { diff --git a/lib/cmds/clean.ts b/lib/cmds/clean.ts index 1656e042..389ca7cf 100644 --- a/lib/cmds/clean.ts +++ b/lib/cmds/clean.ts @@ -26,12 +26,12 @@ if (argv._[0] === 'clean-run') { * @param: options */ function clean(options: Options): void { - let outputDir = Config.SELENIUM_DIR; - if (options['out_dir'].getValue()) { - if (path.isAbsolute(options['out_dir'].getValue())) { - outputDir = options['out_dir'].getValue(); + let outputDir = Config.seleniumDir; + if (options['out_dir'].getString()) { + if (path.isAbsolute(options['out_dir'].getString())) { + outputDir = options['out_dir'].getString(); } else { - outputDir = path.resolve(Config.BASE_DIR, options['out_dir'].getValue()); + outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); } } FileManager.removeExistingFiles(outputDir); diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index c5511cd7..62b553d2 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -6,7 +6,7 @@ import {ChromeDriver, IEDriver, StandAlone} from '../binaries'; * The options used by the commands. */ export class Opts { - static outputDir = new Option('out_dir', 'Location to output/expect', 'string', Config.SELENIUM_DIR); + static outputDir = new Option('out_dir', 'Location to output/expect', 'string', Config.seleniumDir); static seleniumPort = new Option('seleniumPort', 'Optional port for the selenium standalone server', 'string'); static ignoreSsl = new Option('ignore_ssl', 'Ignore SSL certificates', 'boolean', false); static proxy = new Option('proxy', 'Proxy to use for the install or update command', 'string'); diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index bea1f404..c7240751 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -39,19 +39,19 @@ if (argv._[0] === 'start-run') { function start(options: Options) { let osType = os.type(); let binaries = FileManager.setupBinaries(); - let seleniumPort = options['seleniumPort'].getValue(); - let outputDir = Config.SELENIUM_DIR; - if (options['out_dir'].getValue()) { - if (path.isAbsolute(options['out_dir'].getValue())) { - outputDir = options['out_dir'].getValue(); + let seleniumPort = options['seleniumPort'].getString(); + let outputDir = Config.seleniumDir; + if (options['out_dir'].getString()) { + if (path.isAbsolute(options['out_dir'].getString())) { + outputDir = options['out_dir'].getString(); } else { - outputDir = path.resolve(Config.BASE_DIR, options['out_dir'].getValue()); + outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); } } - binaries[StandAlone.id].versionCustom = options['versions_standalone'].getValue(); - binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getValue(); + binaries[StandAlone.id].versionCustom = options['versions_standalone'].getString(); + binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getString(); if (options['versions_ie']) { - binaries[IEDriver.id].versionCustom = options['versions_ie'].getValue(); + binaries[IEDriver.id].versionCustom = options['versions_ie'].getString(); } let downloadedBinaries = FileManager.downloadedBinaries(outputDir); @@ -61,7 +61,7 @@ function start(options: Options) { 'webdriver-manager update --standalone'); process.exit(1); } - let args: Array = ['-jar', path.join(outputDir, binaries[StandAlone.id].filename())]; + let args: string[] = ['-jar', path.join(outputDir, binaries[StandAlone.id].filename())]; if (seleniumPort) { args.push('-port', seleniumPort); } @@ -99,10 +99,11 @@ function start(options: Options) { }); } -function spawnCommand(command: string, args?: any) { - let win32 = process.platform === 'win32'; - let winCommand = win32 ? 'cmd' : command; - let finalArgs = win32 ? ['/c'].concat(command, args) : args; +function spawnCommand(command: string, args?: string[]) { + let osType = os.type(); + let windows: boolean = osType === 'Windows_NT'; + let winCommand = windows ? 'cmd' : command; + let finalArgs: string[] = windows ? ['/c'].concat([command],args) : args; return childProcess.spawn(winCommand, finalArgs, {stdio: 'inherit'}); } diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts index 428b75f1..6bfc1eb3 100644 --- a/lib/cmds/status.ts +++ b/lib/cmds/status.ts @@ -27,12 +27,12 @@ if (argv._[0] === 'status-run') { */ function status(options: Options) { let binaries = FileManager.setupBinaries(); - let outputDir = Config.SELENIUM_DIR; + let outputDir = Config.seleniumDir; if (options['out_dir'].value) { - if (path.isAbsolute(options['out_dir'].getValue())) { - outputDir = options['out_dir'].getValue(); + if (path.isAbsolute(options['out_dir'].getString())) { + outputDir = options['out_dir'].getString(); } else { - outputDir = path.resolve(Config.BASE_DIR, options['out_dir'].getValue()); + outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); } } let downloadedBinaries = FileManager.downloadedBinaries(outputDir); diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 26c9ce39..15bee119 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -46,33 +46,33 @@ if (argv._[0] === 'update-run') { * @param options */ function update(options: Options): void { - let standalone = options['standalone'].getValue() != undefined; - let chrome = options['chrome'].getValue() != undefined; + let standalone = options['standalone'].getBoolean(); + let chrome = options['chrome'].getBoolean(); let ie: boolean = false; let ie32: boolean = false; if (options['ie']) { - ie = options['ie'].getValue() != undefined; + ie = options['ie'].getBoolean(); } if (options['ie32']) { - ie32 = options['ie32'].getValue() != undefined; + ie32 = options['ie32'].getBoolean(); } - let outputDir = Config.SELENIUM_DIR; - if (options['out_dir'].getValue()) { - if (path.isAbsolute(options['out_dir'].getValue())) { - outputDir = options['out_dir'].getValue(); + let outputDir = Config.seleniumDir; + if (options['out_dir'].getString()) { + if (path.isAbsolute(options['out_dir'].getString())) { + outputDir = options['out_dir'].getString(); } else { - outputDir = path.resolve(Config.BASE_DIR, options['out_dir'].getValue()); + outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); } } - let ignoreSSL = options['ignore_ssl'].getValue() != undefined; - let proxy = options['proxy'].getValue(); + let ignoreSSL = options['ignore_ssl'].getBoolean(); + let proxy = options['proxy'].getString(); // setup versions for binaries let binaries = FileManager.setupBinaries(); - binaries[StandAlone.id].versionCustom = options['versions_standalone'].getValue(); - binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getValue(); + binaries[StandAlone.id].versionCustom = options['versions_standalone'].getString(); + binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getString(); if (options['versions_ie']) { - binaries[IEDriver.id].versionCustom = options['versions.ie'].getValue(); + binaries[IEDriver.id].versionCustom = options['versions.ie'].getString(); } // do the update diff --git a/lib/config.ts b/lib/config.ts index ff37abbd..318bedab 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -13,17 +13,17 @@ export interface ConfigFile { * The configuration for webdriver-manager. */ export class Config { - static CONFIG_PATH: string = '../config.json'; - static PACKAGE_PATH: string = '../package.json'; - static BASE_DIR: string = path.resolve(__dirname, '../../'); - static SELENIUM_DIR: string = path.resolve(Config.BASE_DIR, 'selenium'); + static configPath: string = '../config.json'; + static packagePath: string = '../package.json'; + static baseDir: string = path.resolve(__dirname, '../../'); + static seleniumDir: string = path.resolve(Config.baseDir, 'selenium'); /** * Get the binary versions from the configuration file. * @returns A map of the versions defined in the configuration file. */ static binaryVersions(): ConfigFile { - let configFile = require(Config.CONFIG_PATH); + let configFile = require(Config.configPath); let configVersions: ConfigFile = {}; configVersions.selenium = configFile.webdriverVersions.selenium; configVersions.chrome = configFile.webdriverVersions.chromedriver; @@ -36,7 +36,7 @@ export class Config { * @returns A map of the CDN versions defined in the configuration file. */ static cdnUrls(): ConfigFile { - let configFile = require(Config.CONFIG_PATH); + let configFile = require(Config.configPath); let configCdnUrls: ConfigFile = {}; configCdnUrls.selenium = configFile.cdnUrls.selenium; configCdnUrls.chrome = configFile.cdnUrls.chromedriver; @@ -49,7 +49,7 @@ export class Config { * @returns The webdriver-manager version. */ static version(): string { - let packageJson = require(Config.PACKAGE_PATH); + let packageJson = require(Config.packagePath); return packageJson.version; } } diff --git a/lib/files/downloadedBinary.ts b/lib/files/downloaded_binary.ts similarity index 92% rename from lib/files/downloadedBinary.ts rename to lib/files/downloaded_binary.ts index df2e7f3a..ff295b95 100644 --- a/lib/files/downloadedBinary.ts +++ b/lib/files/downloaded_binary.ts @@ -4,7 +4,7 @@ import {Binary} from '../binaries/binary'; * The downloaded binary is the binary with the list of versions downloaded. */ export class DownloadedBinary extends Binary { - versions: Array = []; + versions: string[] = []; binary: Binary; constructor(binary: Binary) { diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index dbda39d9..ccf05b45 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -82,7 +82,7 @@ export class Downloader { request(options) .on('response', - function(response) { + (response) => { if (response.statusCode !== 200) { fs.unlink(filePath); console.error('Error: Got code ' + response.statusCode + ' from ' + fileUrl); @@ -90,7 +90,7 @@ export class Downloader { contentLength = response.headers['content-length']; }) .on('error', - function(error) { + (error) => { console.error('Error: Got error ' + error + ' from ' + fileUrl); fs.unlink(filePath); }) diff --git a/lib/files/fileManager.ts b/lib/files/file_manager.ts similarity index 91% rename from lib/files/fileManager.ts rename to lib/files/file_manager.ts index 5499ae92..291bb457 100644 --- a/lib/files/fileManager.ts +++ b/lib/files/file_manager.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import * as fs from 'fs'; import {Binary, BinaryMap, ChromeDriver, IEDriver, StandAlone, OS} from '../binaries'; -import {DownloadedBinary} from './'; +import {DownloadedBinary} from './downloaded_binary'; /** * The File Manager class is where the webdriver manager will compile a list of @@ -25,7 +25,6 @@ export class FileManager { * @returns If the binary is available for the operating system. */ static checkOS_(osType: string, binary: typeof Binary): boolean { - console.log(binary.os); for (let os in binary.os) { if (OS[os] == osType) { return true; @@ -40,8 +39,8 @@ export class FileManager { * @param osType The operating system. * @returns A binary map that are available for the operating system. */ - static compileBinaries_(osType: string): BinaryMap { - let binaries: BinaryMap = {}; + static compileBinaries_(osType: string): BinaryMap { + let binaries: BinaryMap = {}; if (FileManager.checkOS_(osType, StandAlone)) { binaries[StandAlone.id] = new StandAlone(); } @@ -59,14 +58,14 @@ export class FileManager { * for the system. * @returns A binary map that is available for the operating system. */ - static setupBinaries(): BinaryMap { return FileManager.compileBinaries_(os.type()); } + static setupBinaries(): BinaryMap { return FileManager.compileBinaries_(os.type()); } /** * Get the list of existing files from the output directory * @param outputDir The directory where binaries are saved * @returns A list of existing files. */ - static getExistngFiles(outputDir: string): Array { return fs.readdirSync(outputDir); } + static getExistngFiles(outputDir: string): string[] { return fs.readdirSync(outputDir); } /** * For the binary, operating system, and system architecture, look through @@ -78,8 +77,8 @@ export class FileManager { */ static downloadedVersions_( binary: Binary, osType: string, arch: string, - existingFiles: Array): DownloadedBinary { - let versions: Array = []; + existingFiles: string[]): DownloadedBinary { + let versions: string[] = []; for (let existPos in existingFiles) { let existFile: string = existingFiles[existPos]; // use only files that have a prefix and suffix that we care about @@ -113,12 +112,12 @@ export class FileManager { * @param outputDir The directory where files are downloaded and stored. * @returns An dictionary map of all the downloaded binaries found in the output folder. */ - static downloadedBinaries(outputDir: string): BinaryMap { + static downloadedBinaries(outputDir: string): BinaryMap { let ostype = os.type(); let arch = os.arch(); let binaries = FileManager.setupBinaries(); let existingFiles = FileManager.getExistngFiles(outputDir); - let downloaded: BinaryMap = {}; + let downloaded: BinaryMap = {}; for (let bin in binaries) { let binary = FileManager.downloadedVersions_(binaries[bin], ostype, arch, existingFiles); if (binary != null) { diff --git a/lib/files/index.ts b/lib/files/index.ts index ca4d93a6..74536c2e 100644 --- a/lib/files/index.ts +++ b/lib/files/index.ts @@ -1,3 +1,3 @@ -export * from './downloadedBinary'; +export * from './downloaded_binary'; export * from './downloader'; -export * from './fileManager'; +export * from './file_manager'; diff --git a/package.json b/package.json index 571ca499..6ae1522d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "webdriver-manager", - "version": "1.0.0", - "description": "Webdriver Manager for E2E protractor tests", + "name": "webdriver-tool", + "version": "0.0.1", + "description": "Webdriver tool to manage selenium standalone server", "scripts": { "prepublish": "gulp prepublish", "test": "gulp test" @@ -15,20 +15,27 @@ "webdriverjs", "selenium" ], + "repository": { + "type": "git", + "url": "git://github.com/angular/webdriver-tool.git" + }, + "bin": { + "webdriver-tool": "built/lib/webdriver.js" + }, + "main": "built/lib/webdriver.js", "author": "Craig Nishina ", "license": "MIT", "dependencies": { "adm-zip": "^0.4.7", + "minimist": "^1.2.0" + }, + "devDependencies": { "clang-format": "^1.0.35", "gulp": "^3.9.1", "gulp-clang-format": "^1.0.23", - "http": "0.0.0", "jasmine": "^2.4.1", - "minimist": "^1.2.0", - "request": "^2.69.0", "run-sequence": "^1.1.5", "typescript": "^1.8.7", - "typings": "^0.7.7", - "url": "^0.11.0" + "typings": "^0.7.7" } } diff --git a/spec/files/fileManager_spec.ts b/spec/files/fileManager_spec.ts index e2d97300..9201a7b8 100644 --- a/spec/files/fileManager_spec.ts +++ b/spec/files/fileManager_spec.ts @@ -60,7 +60,7 @@ describe('file manager', () => { }); describe('downloaded version checks', () => { - let existingFiles: Array; + let existingFiles: string[]; let selenium = new StandAlone(); let chrome = new ChromeDriver(); let ie = new IEDriver(); diff --git a/spec/webdriver_spec.ts b/spec/webdriver_spec.ts index 81450664..fbfb19e3 100644 --- a/spec/webdriver_spec.ts +++ b/spec/webdriver_spec.ts @@ -4,7 +4,7 @@ import * as child_process from 'child_process'; import * as path from 'path'; import {cli} from '../lib/webdriver'; -function runSpawn(task: string, opt_arg: Array): Array { +function runSpawn(task: string, opt_arg: string[]): string[] { opt_arg = typeof opt_arg !== 'undefined' ? opt_arg : []; let child = child_process.spawnSync(task, opt_arg, {stdio: 'pipe'}); return child.output[1].toString().split('\n'); From 3abaad470d9978c0fc7ef3d156d9e1d070cbd80b Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Thu, 24 Mar 2016 15:07:25 -0700 Subject: [PATCH 04/79] chore(cleanup): clean up rename from webdriver-manager to webdriver-tool --- .npmignore | 6 ++++++ README.md | 10 +++++----- bin/webdriver-tool | 2 +- lib/webdriver.ts | 2 +- npm-debug.log | 26 ++++++++++++++++++++++++++ package.json | 5 +++-- 6 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 .npmignore create mode 100644 npm-debug.log diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..d897ed0c --- /dev/null +++ b/.npmignore @@ -0,0 +1,6 @@ +lib/ + +.clang-format +.gitignore +.npmignore + diff --git a/README.md b/README.md index 70ce8527..07aa1aab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# webdriver-manager +# webdriver-tool ### Setup @@ -14,11 +14,11 @@ gulp test ### Running webdriver ``` -node built/lib/webdriver.js +webdriver-tool ``` ``` -Usage:webdriver-manager [options] +Usage:webdriver-tool [options] Commands: clean removes all downloaded driver files from the out_dir @@ -27,7 +27,7 @@ Commands: update install or update selected binaries Options: - --out_dir Location to output/expect [default: /src/webdriver-manager/selenium] + --out_dir Location to output/expect [default: /src/webdriver-tool/selenium] --seleniumPort Optional port for the selenium standalone server --versions_standalone Optional seleniuim standalone server version [default: 2.52.0] --versions_chrome Optional chrome driver version [default: 2.21] @@ -56,7 +56,7 @@ Usage: update-run [options] Description: install or update selected binaries Options: - --out_dir Location to output/expect [default: /src/webdriver-manager/selenium] + --out_dir Location to output/expect [default: /src/webdriver-tool/selenium] --ignore_ssl Ignore SSL certificates --proxy Proxy to use for the install or update command --alternate_cnd Alternate CDN to binaries diff --git a/bin/webdriver-tool b/bin/webdriver-tool index ae4878a2..1ea912e1 100644 --- a/bin/webdriver-tool +++ b/bin/webdriver-tool @@ -1,3 +1,3 @@ -!#/usr/bin/env node +#!/usr/bin/env node require('../built/lib/webdriver.js'); diff --git a/lib/webdriver.ts b/lib/webdriver.ts index d3c2746e..e106de71 100644 --- a/lib/webdriver.ts +++ b/lib/webdriver.ts @@ -9,7 +9,7 @@ import * as update from './cmds/update'; import {Cli} from './cli'; let commandline = new Cli() - .usage('webdriver-manager [options]') + .usage('webdriver-tool [options]') .program(clean.program) .program(start.program) .program(status.program) diff --git a/npm-debug.log b/npm-debug.log new file mode 100644 index 00000000..76315619 --- /dev/null +++ b/npm-debug.log @@ -0,0 +1,26 @@ +0 info it worked if it ends with ok +1 verbose cli [ '/Users/cnishina/.nvm/versions/node/v4.2.4/bin/node', +1 verbose cli '/Users/cnishina/.nvm/versions/node/v4.2.4/bin/npm', +1 verbose cli 'adduser' ] +2 info using npm@2.14.12 +3 info using node@v4.2.4 +4 verbose stack Error: canceled +4 verbose stack at Interface. (/Users/cnishina/.nvm/versions/node/v4.2.4/lib/node_modules/npm/node_modules/read/lib/read.js:66:13) +4 verbose stack at emitNone (events.js:67:13) +4 verbose stack at Interface.emit (events.js:166:7) +4 verbose stack at Interface._ttyWrite (readline.js:690:16) +4 verbose stack at ReadStream.onkeypress (readline.js:105:10) +4 verbose stack at emitTwo (events.js:87:13) +4 verbose stack at ReadStream.emit (events.js:172:7) +4 verbose stack at emitKeys (readline.js:1250:14) +4 verbose stack at next (native) +4 verbose stack at ReadStream.onData (readline.js:918:36) +5 verbose cwd /Users/cnishina/webdriver-tool +6 error Darwin 15.4.0 +7 error argv "/Users/cnishina/.nvm/versions/node/v4.2.4/bin/node" "/Users/cnishina/.nvm/versions/node/v4.2.4/bin/npm" "adduser" +8 error node v4.2.4 +9 error npm v2.14.12 +10 error canceled +11 error If you need help, you may report this error at: +11 error +12 verbose exit [ 1, true ] diff --git a/package.json b/package.json index 6ae1522d..30a6a9ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-tool", - "version": "0.0.1", + "version": "0.0.2", "description": "Webdriver tool to manage selenium standalone server", "scripts": { "prepublish": "gulp prepublish", @@ -27,7 +27,8 @@ "license": "MIT", "dependencies": { "adm-zip": "^0.4.7", - "minimist": "^1.2.0" + "minimist": "^1.2.0", + "request": "^2.69.0" }, "devDependencies": { "clang-format": "^1.0.35", From 1c42ed8db2475e9efde39342c49688d9d5307d7c Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Thu, 24 Mar 2016 15:43:02 -0700 Subject: [PATCH 05/79] fix(update): create the directory prior to downloading --- .npmignore | 2 ++ LICENSE | 13 +++++---- README.md | 70 +++++++++++++++------------------------------- lib/cmds/update.ts | 1 + npm-debug.log | 26 ----------------- package.json | 2 +- 6 files changed, 34 insertions(+), 80 deletions(-) delete mode 100644 npm-debug.log diff --git a/.npmignore b/.npmignore index d897ed0c..cacc253e 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,6 @@ lib/ +spec/ +typings/ .clang-format .gitignore diff --git a/LICENSE b/LICENSE index eeabde90..4662726f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -The MIT License (MIT) +The MIT License -Copyright (c) 2016 Craig +Copyright (c) 2010-2015 Google, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,14 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/README.md b/README.md index 07aa1aab..b328491f 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,43 @@ -# webdriver-tool +Webdriver Tool +============== -### Setup +Getting Started +--------------- ``` -npm install +npm install -g webdriver-tool ``` -### Testing +Setting up a Selenium Server +---------------------------- -``` -gulp test -``` +Prior to starting the selenium server, download the selenium server jar and driver binaries. By default it will download the selenium server jar and chromedriver binary. -### Running webdriver ``` -webdriver-tool +bin/webdriver-tool update ``` -``` -Usage:webdriver-tool [options] +Starting the Selenium Server +---------------------------- -Commands: - clean removes all downloaded driver files from the out_dir - start start up the selenium server - status list the current available drivers - update install or update selected binaries - -Options: - --out_dir Location to output/expect [default: /src/webdriver-tool/selenium] - --seleniumPort Optional port for the selenium standalone server - --versions_standalone Optional seleniuim standalone server version [default: 2.52.0] - --versions_chrome Optional chrome driver version [default: 2.21] - --ignore_ssl Ignore SSL certificates - --proxy Proxy to use for the install or update command - --alternate_cnd Alternate CDN to binaries - --standalone Install or update selenium standalone [default: true] - --chrome Install or update chromedriver [default: true] -``` +By default, the selenium server will run on `http://localhost:4444/wd/hub`. -### Running a specific command -running update ``` -node built/lib/cmds/update.js update-run +bin/webdriver-tool start ``` -help update +Other useful commands +--------------------- + +View different versions of server and driver files: + ``` -node built/lib/cmds/update.js update-help +bin/webdriver-tool status ``` -``` -Usage: update-run [options] - update-help -Description: install or update selected binaries +Clear out the server and driver files. If `webdriver-tool start` does not work, try to clear out the saved files. -Options: - --out_dir Location to output/expect [default: /src/webdriver-tool/selenium] - --ignore_ssl Ignore SSL certificates - --proxy Proxy to use for the install or update command - --alternate_cnd Alternate CDN to binaries - --standalone Install or update selenium standalone [default: true] - --chrome Install or update chromedriver [default: true] - --versions_standalone Optional seleniuim standalone server version [default: 2.52.0] - --versions_chrome Optional chrome driver version [default: 2.21] - ``` +``` +bin/webdriver-tool clean +``` diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 15bee119..55998eb8 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -63,6 +63,7 @@ function update(options: Options): void { } else { outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); } + FileManager.makeOutputDirectory(outputDir); } let ignoreSSL = options['ignore_ssl'].getBoolean(); let proxy = options['proxy'].getString(); diff --git a/npm-debug.log b/npm-debug.log deleted file mode 100644 index 76315619..00000000 --- a/npm-debug.log +++ /dev/null @@ -1,26 +0,0 @@ -0 info it worked if it ends with ok -1 verbose cli [ '/Users/cnishina/.nvm/versions/node/v4.2.4/bin/node', -1 verbose cli '/Users/cnishina/.nvm/versions/node/v4.2.4/bin/npm', -1 verbose cli 'adduser' ] -2 info using npm@2.14.12 -3 info using node@v4.2.4 -4 verbose stack Error: canceled -4 verbose stack at Interface. (/Users/cnishina/.nvm/versions/node/v4.2.4/lib/node_modules/npm/node_modules/read/lib/read.js:66:13) -4 verbose stack at emitNone (events.js:67:13) -4 verbose stack at Interface.emit (events.js:166:7) -4 verbose stack at Interface._ttyWrite (readline.js:690:16) -4 verbose stack at ReadStream.onkeypress (readline.js:105:10) -4 verbose stack at emitTwo (events.js:87:13) -4 verbose stack at ReadStream.emit (events.js:172:7) -4 verbose stack at emitKeys (readline.js:1250:14) -4 verbose stack at next (native) -4 verbose stack at ReadStream.onData (readline.js:918:36) -5 verbose cwd /Users/cnishina/webdriver-tool -6 error Darwin 15.4.0 -7 error argv "/Users/cnishina/.nvm/versions/node/v4.2.4/bin/node" "/Users/cnishina/.nvm/versions/node/v4.2.4/bin/npm" "adduser" -8 error node v4.2.4 -9 error npm v2.14.12 -10 error canceled -11 error If you need help, you may report this error at: -11 error -12 verbose exit [ 1, true ] diff --git a/package.json b/package.json index 30a6a9ec..58f0f754 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-tool", - "version": "0.0.2", + "version": "0.0.3", "description": "Webdriver tool to manage selenium standalone server", "scripts": { "prepublish": "gulp prepublish", From aebc2789b629a09f0d1900a6ef8b302025c36101 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Thu, 24 Mar 2016 17:03:38 -0700 Subject: [PATCH 06/79] chore(cli): add version and clean up version --- lib/cli/cli.ts | 21 ++++++++++++++++++++- lib/cli/programs.ts | 5 +++-- lib/cmds/clean.ts | 4 ++-- lib/cmds/update.ts | 5 +++-- lib/webdriver.ts | 16 ++++++++++++---- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/cli/cli.ts b/lib/cli/cli.ts index a9c134d2..df1e5f96 100644 --- a/lib/cli/cli.ts +++ b/lib/cli/cli.ts @@ -11,6 +11,7 @@ import {MinimistArgs, Options} from './options'; export class Cli { programs: Programs = {}; usageText: string; + version: string; /** * Register a program to the command line interface. @@ -30,11 +31,20 @@ export class Cli { return this; } + + /** + * Register the version for the cli + */ + setVersion(version: string): Cli { + this.version = version; + return this; + } + /** * Prints help for the programs registered to the cli. */ printHelp(): void { - console.log('Usage:' + this.usageText); + console.log('Usage: ' + this.usageText); console.log('\nCommands:'); let cmdDescriptionPos = this.posCmdDescription(); for (let cmd in this.programs) { @@ -52,6 +62,15 @@ export class Cli { } } + /** + * Print the version + */ + printVersion(): void { + if (this.version) { + console.log('Version ' + this.version); + } + } + /** * For commands, gets the position where the description should start so they * are aligned. diff --git a/lib/cli/programs.ts b/lib/cli/programs.ts index 839284f6..c9e6eecb 100644 --- a/lib/cli/programs.ts +++ b/lib/cli/programs.ts @@ -16,6 +16,7 @@ export class Program { options: Options = {}; runMethod: Function; helpDescription: string; + version: string; /** * Register a command and the description. @@ -157,8 +158,8 @@ export class Program { printHelp(): void { console.log( '\n' + - 'Usage: ' + this.cmd + '-run [options]\n' + - ' ' + this.cmd + '-help\n' + + 'Usage: ' + this.cmd + ' [options]\n' + + ' ' + this.cmd + ' help\n' + 'Description: ' + this.cmdDescription + '\n'); console.log('Options:'); this.printOptions(this.posDescription(), this.posDefault()); diff --git a/lib/cmds/clean.ts b/lib/cmds/clean.ts index 389ca7cf..35e32c73 100644 --- a/lib/cmds/clean.ts +++ b/lib/cmds/clean.ts @@ -16,9 +16,9 @@ export var program = prog; // stand alone runner let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); if (argv._[0] === 'clean-run') { - program.run(argv); + prog.run(argv); } else if (argv._[0] === 'clean-help') { - program.printHelp(); + prog.printHelp(); } /** diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 55998eb8..e5d47a57 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -36,11 +36,12 @@ export var program = prog; // stand alone runner let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); if (argv._[0] === 'update-run') { - program.run(argv); + prog.run(argv); } else if (argv._[0] === 'update-help') { - program.printHelp(); + prog.printHelp(); } + /** * Parses the options and downloads binaries if they do not exist. * @param options diff --git a/lib/webdriver.ts b/lib/webdriver.ts index e106de71..d0e5e6c2 100644 --- a/lib/webdriver.ts +++ b/lib/webdriver.ts @@ -7,9 +7,11 @@ import * as status from './cmds/status'; import * as update from './cmds/update'; import {Cli} from './cli'; +import {Config} from './config'; let commandline = new Cli() .usage('webdriver-tool [options]') + .setVersion(Config.version()) .program(clean.program) .program(start.program) .program(status.program) @@ -19,15 +21,21 @@ let minimistOptions = commandline.getMinimistOptions(); let argv = minimist(process.argv.slice(2), minimistOptions); let cmd = argv._; if (commandline.programs[cmd[0]]) { - if (cmd[1] === 'help') { + if (cmd[0] === 'version') { + commandline.printVersion(); + } + else if (cmd[0] === 'help') { + commandline.printHelp(); + } + else if (cmd[1] === 'help' || argv['help'] || argv['h']) { commandline.programs[cmd[0]].printHelp(); } else { commandline.programs[cmd[0]].run(argv); } -} else if (cmd[0] === 'help'){ - commandline.printHelp(); +} else if (argv['version'] || argv['v']) { + commandline.printVersion(); } else { - // do nothing + commandline.printHelp(); } export var cli = commandline; From 15ae0e815270c8af2441002492e3165edd3140df Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 25 Mar 2016 00:11:45 -0700 Subject: [PATCH 07/79] fix(chmod): set permissions to 755 --- lib/cmds/update.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index e5d47a57..1396a3f3 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -118,4 +118,7 @@ function unzip(binary: T, outputDir: string, filename: string) let mv = path.join(outputDir, binary.prefix() + binary.version() + binary.executableSuffix(osType)); fs.renameSync(path.join(outputDir, binary.name + binary.executableSuffix(osType)), mv); + if (osType !== 'Windows_NT') { + fs.chmodSync(mv, '0755'); + } } From f0622d2e173b68e4afcd409f9c0356c8a1c2652a Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 25 Mar 2016 00:13:41 -0700 Subject: [PATCH 08/79] feat(logs): add chrome logs command line option --- lib/cmds/opts.ts | 1 + lib/cmds/start.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index 62b553d2..72ff054f 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -18,4 +18,5 @@ export class Opts { static versionsChrome = new Option('versions_chrome', 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); static versionsStandAlone = new Option('versions_standalone', 'Optional seleniuim standalone server version', 'string', StandAlone.versionDefault); static versionsIe = new Option('versions_ie', 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); + static chromeLogs = new Option('chrome_logs', 'File path to chrome logs', 'string', undefined); } diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index c7240751..f39a2337 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -16,7 +16,8 @@ let prog = new Program() .addOption(Opts.outputDir) .addOption(Opts.seleniumPort) .addOption(Opts.versionsStandAlone) - .addOption(Opts.versionsChrome); + .addOption(Opts.versionsChrome) + .addOption(Opts.chromeLogs); if (os.type() === 'Windows_NT') { prog.addOption(Opts.versionsIe); @@ -48,6 +49,14 @@ function start(options: Options) { outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); } } + let chromeLogs: string = null; + if (options['chrome_logs'].getString()) { + if (path.isAbsolute(options['chrome_logs'].getString())) { + chromeLogs = options['chrome_logs'].getString(); + } else { + chromeLogs = path.resolve(Config.baseDir, options['chrome_logs'].getString()); + } + } binaries[StandAlone.id].versionCustom = options['versions_standalone'].getString(); binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getString(); if (options['versions_ie']) { @@ -69,6 +78,9 @@ function start(options: Options) { args.push( '-Dwebdriver.chrome.driver=' + path.join(outputDir, binaries[ChromeDriver.id].executableFilename(osType))); + if (chromeLogs != null) { + args.push('-Dwebdriver.chrome.logfile=' + chromeLogs); + } } if (downloadedBinaries[IEDriver.id] != null) { args.push( From 27d3fca74b4ed27d1b9e096129f23ce509cf8a41 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 25 Mar 2016 16:21:15 -0700 Subject: [PATCH 09/79] chore(readme): add info and remove bin paths from the readme closes #3 and #4 --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b328491f..9c6fe548 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Webdriver Tool ============== +Webdriver tool is a manager for selenium standalone server jar and browser drivers executables. This is the same tool as `webdriver-manager` from the [Protractor](https://github.com/angular/protractor) repository. + Getting Started --------------- @@ -14,7 +16,7 @@ Setting up a Selenium Server Prior to starting the selenium server, download the selenium server jar and driver binaries. By default it will download the selenium server jar and chromedriver binary. ``` -bin/webdriver-tool update +webdriver-tool update ``` Starting the Selenium Server @@ -24,7 +26,7 @@ By default, the selenium server will run on `http://localhost:4444/wd/hub`. ``` -bin/webdriver-tool start +webdriver-tool start ``` Other useful commands @@ -33,11 +35,11 @@ Other useful commands View different versions of server and driver files: ``` -bin/webdriver-tool status +webdriver-tool status ``` Clear out the server and driver files. If `webdriver-tool start` does not work, try to clear out the saved files. ``` -bin/webdriver-tool clean +webdriver-tool clean ``` From 8c472918ac73390890bbc39fcc4c7a2e86d3b262 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 25 Mar 2016 16:22:34 -0700 Subject: [PATCH 10/79] feat(local): use the local version of webdriver-tool if it is installed closes #5 --- bin/webdriver-tool | 28 +++++++++++++++++++++++++++- lib/cli/cli.ts | 27 ++++++++++++++++----------- lib/cli/util.ts | 19 +++++++++++++++++++ lib/config.ts | 20 +++++++++++++++----- lib/webdriver.ts | 1 - package.json | 3 ++- typings.json | 1 + 7 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 lib/cli/util.ts diff --git a/bin/webdriver-tool b/bin/webdriver-tool index 1ea912e1..58c9252b 100644 --- a/bin/webdriver-tool +++ b/bin/webdriver-tool @@ -1,3 +1,29 @@ #!/usr/bin/env node -require('../built/lib/webdriver.js'); +var path = require('path'); +var fs = require('fs'); +var chalk = require('chalk'); + +var Logger = require('../built/lib/cli/util').Logger; +var Config = require('../built/lib/config').Config; + +var globalNpm = process.env.NPM_BIN || process.env.NVM_BIN; +globalPath = path.resolve(globalNpm, '../lib/node_modules/webdriver-tool/built/lib/'); +localPath = path.resolve(__dirname, '../node_modules/webdriver-tool/built/lib/'); + +try { + if (fs.statSync(localPath).isDirectory()) { + Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion()));; + } + try { + if (fs.statSync(globalPath).isDirectory()) { + Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion())); + } + } catch (err) { } + require(path.resolve(localPath, 'webdriver.js')); +} +// we are using the global version +catch (err) { + Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion())); + require(path.resolve(globalPath, 'webdriver.js')); +} diff --git a/lib/cli/cli.ts b/lib/cli/cli.ts index df1e5f96..a4f13948 100644 --- a/lib/cli/cli.ts +++ b/lib/cli/cli.ts @@ -1,6 +1,12 @@ +import * as chalk from 'chalk'; +import * as path from 'path'; + +import {Config} from '../config'; +import {Logger} from './util'; import {Programs, Program} from './programs'; import {MinimistArgs, Options} from './options'; + /** * The Cli contains the usage and the collection of programs. * @@ -31,15 +37,6 @@ export class Cli { return this; } - - /** - * Register the version for the cli - */ - setVersion(version: string): Cli { - this.version = version; - return this; - } - /** * Prints help for the programs registered to the cli. */ @@ -66,8 +63,16 @@ export class Cli { * Print the version */ printVersion(): void { - if (this.version) { - console.log('Version ' + this.version); + let localVersion = Config.localVersion(); + Logger.info(chalk.green('local version: ' + localVersion)); + + let globalVersion = Config.globalVersion(); + if (globalVersion) { + Logger.info(chalk.cyan('global version: ' + globalVersion)); + + if (globalVersion !== localVersion) { + Logger.info(chalk.yellow('warning version mismatch')); + } } } diff --git a/lib/cli/util.ts b/lib/cli/util.ts new file mode 100644 index 00000000..5c9583fb --- /dev/null +++ b/lib/cli/util.ts @@ -0,0 +1,19 @@ +import * as chalk from 'chalk'; + +export enum LOG_LEVEL { DEBUG, WARN, INFO } + +export class Logger { + static info(message: string, opt_noTimestamp?: boolean): void { + if (!opt_noTimestamp) { + message = Logger.timestamp() + ' ' + message; + } + console.log(message); + } + + static timestamp(): string { + let d = new Date(); + return '[' + chalk.gray(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()) + ']'; + } +} + +export class Req {} diff --git a/lib/config.ts b/lib/config.ts index 318bedab..924b1936 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,3 +1,4 @@ +import * as fs from 'fs'; import * as path from 'path'; /** @@ -44,12 +45,21 @@ export class Config { return configCdnUrls; } - /** - * Get the current version of webdriver-manager from the package.json - * @returns The webdriver-manager version. - */ - static version(): string { + static localVersion(): string { let packageJson = require(Config.packagePath); return packageJson.version; } + + static globalVersion(): string { + let globalNpm = process.env.NPM_BIN || process.env.NVM_BIN; + let globalPackagePath = path.resolve(globalNpm, '../lib/node_modules/webdriver-tool/built/package.json'); + try { + if (fs.statSync(globalPackagePath).isFile()) { + let globalPackageJson = require(globalPackagePath); + return globalPackageJson.version; + } + } catch(err) { + return null; + } + } } diff --git a/lib/webdriver.ts b/lib/webdriver.ts index d0e5e6c2..27168efa 100644 --- a/lib/webdriver.ts +++ b/lib/webdriver.ts @@ -11,7 +11,6 @@ import {Config} from './config'; let commandline = new Cli() .usage('webdriver-tool [options]') - .setVersion(Config.version()) .program(clean.program) .program(start.program) .program(status.program) diff --git a/package.json b/package.json index 58f0f754..9891596e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-tool", - "version": "0.0.3", + "version": "0.0.4", "description": "Webdriver tool to manage selenium standalone server", "scripts": { "prepublish": "gulp prepublish", @@ -31,6 +31,7 @@ "request": "^2.69.0" }, "devDependencies": { + "chalk": "^1.1.1", "clang-format": "^1.0.35", "gulp": "^3.9.1", "gulp-clang-format": "^1.0.23", diff --git a/typings.json b/typings.json index 53b8281c..37d6f8bc 100644 --- a/typings.json +++ b/typings.json @@ -1,6 +1,7 @@ { "ambientDependencies": { "adm-zip": "registry:dt/adm-zip#0.0.0+20160211023517", + "chalk": "registry:dt/chalk#0.4.0+20160317120654", "form-data": "registry:dt/form-data#0.0.0+20150826060748", "jasmine": "registry:dt/jasmine#2.2.0+20160308082659", "minimist": "registry:dt/minimist#1.1.3+20151229171613", From 4a0caf5a69cacda01df87d4b4cc35092e519d267 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 25 Mar 2016 16:29:35 -0700 Subject: [PATCH 11/79] fix(dep): fix dependency for chalk --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9891596e..9ebf0ea9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-tool", - "version": "0.0.4", + "version": "0.0.5", "description": "Webdriver tool to manage selenium standalone server", "scripts": { "prepublish": "gulp prepublish", @@ -27,11 +27,11 @@ "license": "MIT", "dependencies": { "adm-zip": "^0.4.7", + "chalk": "^1.1.1", "minimist": "^1.2.0", "request": "^2.69.0" }, "devDependencies": { - "chalk": "^1.1.1", "clang-format": "^1.0.35", "gulp": "^3.9.1", "gulp-clang-format": "^1.0.23", From 3623a54c79674de5d51aa1cf2dbc5820e3a416e9 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 25 Mar 2016 16:47:56 -0700 Subject: [PATCH 12/79] chore(release): bump version and release --- bin/webdriver-tool | 9 ++------- package.json | 6 +++--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/bin/webdriver-tool b/bin/webdriver-tool index 58c9252b..cf8986ae 100644 --- a/bin/webdriver-tool +++ b/bin/webdriver-tool @@ -13,17 +13,12 @@ localPath = path.resolve(__dirname, '../node_modules/webdriver-tool/built/lib/') try { if (fs.statSync(localPath).isDirectory()) { - Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion()));; + Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion()) + '\n');; } - try { - if (fs.statSync(globalPath).isDirectory()) { - Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion())); - } - } catch (err) { } require(path.resolve(localPath, 'webdriver.js')); } // we are using the global version catch (err) { - Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion())); + Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion()) + '\n'); require(path.resolve(globalPath, 'webdriver.js')); } diff --git a/package.json b/package.json index 9ebf0ea9..f74b9be0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-tool", - "version": "0.0.5", + "version": "0.0.6", "description": "Webdriver tool to manage selenium standalone server", "scripts": { "prepublish": "gulp prepublish", @@ -20,9 +20,9 @@ "url": "git://github.com/angular/webdriver-tool.git" }, "bin": { - "webdriver-tool": "built/lib/webdriver.js" + "webdriver-tool": "bin/webdriver-tool" }, - "main": "built/lib/webdriver.js", + "main": "bin/webdriver-tool", "author": "Craig Nishina ", "license": "MIT", "dependencies": { From c34b05cc66849708a2fc515bc455a6a661c867d6 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Mon, 28 Mar 2016 11:35:58 -0700 Subject: [PATCH 13/79] fix(bin): local, project, and global usage --- bin/webdriver-tool | 40 +++++++++++++++++++++++++++++++--------- lib/cli/cli.ts | 17 ----------------- lib/config.ts | 32 ++++++++++++++++++++++++++++++-- lib/webdriver.ts | 7 +------ package.json | 2 +- 5 files changed, 63 insertions(+), 35 deletions(-) mode change 100644 => 100755 bin/webdriver-tool diff --git a/bin/webdriver-tool b/bin/webdriver-tool old mode 100644 new mode 100755 index cf8986ae..887c55ad --- a/bin/webdriver-tool +++ b/bin/webdriver-tool @@ -8,17 +8,39 @@ var Logger = require('../built/lib/cli/util').Logger; var Config = require('../built/lib/config').Config; var globalNpm = process.env.NPM_BIN || process.env.NVM_BIN; -globalPath = path.resolve(globalNpm, '../lib/node_modules/webdriver-tool/built/lib/'); -localPath = path.resolve(__dirname, '../node_modules/webdriver-tool/built/lib/'); -try { - if (fs.statSync(localPath).isDirectory()) { - Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion()) + '\n');; +var globalPath = path.resolve(globalNpm, '../lib/node_modules/webdriver-tool/built/lib/'); +var localPath = path.resolve(__dirname, '../../../node_modules/webdriver-tool/built/lib/'); + +// check to see if this is webdriver-tool (the project), if it is, run this +var projectPath = path.resolve('.'); +var parentPath = path.resolve(projectPath, '..'); +var folder = projectPath.replace(parentPath + '/', ''); + +if (folder === 'webdriver-tool') { + Logger.info('webdriver-tool: using ' + chalk.magenta('project version ' + Config.projectVersion())); + // compare project version to global + if (Config.globalVersion() && Config.projectVersion() !== Config.globalVersion()) { + Logger.info(chalk.yellow('warning: project version [' + Config.projectVersion() + + '] mismatch with global version [' + Config.globalVersion() + ']\n')); } - require(path.resolve(localPath, 'webdriver.js')); + require(path.resolve(projectPath, 'built/lib/webdriver.js')); } -// we are using the global version -catch (err) { - Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion()) + '\n'); +else if (localPath === globalPath) { + // we are using the global version + Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion())); require(path.resolve(globalPath, 'webdriver.js')); +} else { + try { + // try to stat the local path + var stat = fs.statSync(localPath); + Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion())); + if (Config.globalVersion() && Config.localVersion() !== Config.globalVerison()) { + Logger.info(chalk.yellow('warning: local version [' + Config.localVersion() + + '] mismatch with global version [' + Config.globalVersion() + ']\n')); + } + require(path.resolve(localPath, 'webdriver.js')); + } catch (err) { + console.error('error') + } } diff --git a/lib/cli/cli.ts b/lib/cli/cli.ts index a4f13948..e3a8940e 100644 --- a/lib/cli/cli.ts +++ b/lib/cli/cli.ts @@ -59,23 +59,6 @@ export class Cli { } } - /** - * Print the version - */ - printVersion(): void { - let localVersion = Config.localVersion(); - Logger.info(chalk.green('local version: ' + localVersion)); - - let globalVersion = Config.globalVersion(); - if (globalVersion) { - Logger.info(chalk.cyan('global version: ' + globalVersion)); - - if (globalVersion !== localVersion) { - Logger.info(chalk.yellow('warning version mismatch')); - } - } - } - /** * For commands, gets the position where the description should start so they * are aligned. diff --git a/lib/config.ts b/lib/config.ts index 924b1936..627e4729 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -45,11 +45,39 @@ export class Config { return configCdnUrls; } + /** + * If installed as a node module, return the local version. + */ static localVersion(): string { - let packageJson = require(Config.packagePath); - return packageJson.version; + try { + if (fs.statSync(Config.packagePath).isFile()) { + let packageJson = require(Config.packagePath); + return packageJson.version; + } + } catch(err) { + return null; + } + } + + /** + * If running from the project directory, get the project's version. + */ + static projectVersion(): string { + let projectPath = path.resolve('.'); + let projectPackagePath = path.resolve(projectPath, 'package.json'); + try { + if (fs.statSync(projectPackagePath).isFile()) { + let projectJson = require(projectPackagePath); + return projectJson.version; + } + } catch(err) { + return null; + } } + /** + * If installed, returns the globally installed webdriver-tool. + */ static globalVersion(): string { let globalNpm = process.env.NPM_BIN || process.env.NVM_BIN; let globalPackagePath = path.resolve(globalNpm, '../lib/node_modules/webdriver-tool/built/package.json'); diff --git a/lib/webdriver.ts b/lib/webdriver.ts index 27168efa..338ac9f6 100644 --- a/lib/webdriver.ts +++ b/lib/webdriver.ts @@ -20,10 +20,7 @@ let minimistOptions = commandline.getMinimistOptions(); let argv = minimist(process.argv.slice(2), minimistOptions); let cmd = argv._; if (commandline.programs[cmd[0]]) { - if (cmd[0] === 'version') { - commandline.printVersion(); - } - else if (cmd[0] === 'help') { + if (cmd[0] === 'help') { commandline.printHelp(); } else if (cmd[1] === 'help' || argv['help'] || argv['h']) { @@ -31,8 +28,6 @@ if (commandline.programs[cmd[0]]) { } else { commandline.programs[cmd[0]].run(argv); } -} else if (argv['version'] || argv['v']) { - commandline.printVersion(); } else { commandline.printHelp(); } diff --git a/package.json b/package.json index f74b9be0..b5419d7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-tool", - "version": "0.0.6", + "version": "0.0.9", "description": "Webdriver tool to manage selenium standalone server", "scripts": { "prepublish": "gulp prepublish", From e85255c3e470a240d7b6a867e5caa6080b99654c Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Mon, 28 Mar 2016 13:07:37 -0700 Subject: [PATCH 14/79] chore(bin): debug local, project, and global version usage --- bin/webdriver-tool | 16 +++++----------- lib/config.ts | 10 ++-------- package.json | 2 +- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/bin/webdriver-tool b/bin/webdriver-tool index 887c55ad..5fcde7b3 100755 --- a/bin/webdriver-tool +++ b/bin/webdriver-tool @@ -31,16 +31,10 @@ else if (localPath === globalPath) { Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion())); require(path.resolve(globalPath, 'webdriver.js')); } else { - try { - // try to stat the local path - var stat = fs.statSync(localPath); - Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion())); - if (Config.globalVersion() && Config.localVersion() !== Config.globalVerison()) { - Logger.info(chalk.yellow('warning: local version [' + Config.localVersion() + - '] mismatch with global version [' + Config.globalVersion() + ']\n')); - } - require(path.resolve(localPath, 'webdriver.js')); - } catch (err) { - console.error('error') + Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion())); + if (Config.globalVersion() && Config.localVersion() !== Config.globalVersion()) { + Logger.info(chalk.yellow('warning: local version [' + Config.localVersion() + + '] mismatch with global version [' + Config.globalVersion() + ']\n')); } + require(path.resolve(localPath, 'webdriver.js')); } diff --git a/lib/config.ts b/lib/config.ts index 627e4729..c02eaeb2 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -49,14 +49,8 @@ export class Config { * If installed as a node module, return the local version. */ static localVersion(): string { - try { - if (fs.statSync(Config.packagePath).isFile()) { - let packageJson = require(Config.packagePath); - return packageJson.version; - } - } catch(err) { - return null; - } + let packageJson = require(Config.packagePath); + return packageJson.version; } /** diff --git a/package.json b/package.json index b5419d7a..27814561 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-tool", - "version": "0.0.9", + "version": "0.1.0", "description": "Webdriver tool to manage selenium standalone server", "scripts": { "prepublish": "gulp prepublish", From 6ccb9d8b9ac6daf79388c44e6d53f1d3d71fd3f8 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Mon, 28 Mar 2016 17:46:44 -0700 Subject: [PATCH 15/79] fix(versions): versions option should stay consistent with existing webdriver-manager closes #6 --- lib/cli/programs.ts | 23 ++++++++++++++----- lib/cmds/clean.ts | 15 +++++++------ lib/cmds/index.ts | 1 + lib/cmds/opts.ts | 46 ++++++++++++++++++++++++------------- lib/cmds/start.ts | 41 ++++++++++++++++----------------- lib/cmds/status.ts | 13 ++++++----- lib/cmds/update.ts | 55 +++++++++++++++++++++++---------------------- lib/webdriver.ts | 2 +- 8 files changed, 115 insertions(+), 81 deletions(-) create mode 100644 lib/cmds/index.ts diff --git a/lib/cli/programs.ts b/lib/cli/programs.ts index c9e6eecb..3327f289 100644 --- a/lib/cli/programs.ts +++ b/lib/cli/programs.ts @@ -68,15 +68,28 @@ export class Program { * method. * @param args The arguments that will be parsed to run the method. */ - run(args: Args): void { - for (let arg in args) { - if (this.options[arg]) { - this.options[arg].value = args[arg]; - } + run(json: JSON): void { + for (let opt in this.options) { + this.options[opt].value = this.getValue_(opt, json); } this.runMethod(this.options); } + private getValue_(key: string, json: JSON): string { + let keyList: string[] = key.split('.'); + let tempJson:any = json; + while (keyList.length > 0) { + let keyItem = keyList[0]; + if (tempJson[keyItem]) { + tempJson = tempJson[keyItem]; + keyList = keyList.slice(1); + } else { + return undefined; + } + } + return tempJson.toString(); + } + /** * Prints the command with the description. The description will have spaces * between the cmd so that the starting position is "posDescription". If the diff --git a/lib/cmds/clean.ts b/lib/cmds/clean.ts index 35e32c73..db0858d1 100644 --- a/lib/cmds/clean.ts +++ b/lib/cmds/clean.ts @@ -1,22 +1,23 @@ import * as minimist from 'minimist'; import * as path from 'path'; -import {Opts} from './opts'; import {Config} from '../config'; import {FileManager} from '../files'; import {Options, Program} from '../cli'; +import {Opts} from './opts'; +import * as Opt from './'; let prog = new Program() .command('clean', 'removes all downloaded driver files from the out_dir') .action(clean) - .addOption(Opts.outputDir); + .addOption(Opts[Opt.OUT_DIR]); export var program = prog; // stand alone runner let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); if (argv._[0] === 'clean-run') { - prog.run(argv); + prog.run(JSON.parse(JSON.stringify(argv))); } else if (argv._[0] === 'clean-help') { prog.printHelp(); } @@ -27,11 +28,11 @@ if (argv._[0] === 'clean-run') { */ function clean(options: Options): void { let outputDir = Config.seleniumDir; - if (options['out_dir'].getString()) { - if (path.isAbsolute(options['out_dir'].getString())) { - outputDir = options['out_dir'].getString(); + if (options[Opt.OUT_DIR].getString()) { + if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { + outputDir = options[Opt.OUT_DIR].getString(); } else { - outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); + outputDir = path.resolve(Config.baseDir, options[Opt.OUT_DIR].getString()); } } FileManager.removeExistingFiles(outputDir); diff --git a/lib/cmds/index.ts b/lib/cmds/index.ts new file mode 100644 index 00000000..d6ee736f --- /dev/null +++ b/lib/cmds/index.ts @@ -0,0 +1 @@ +export * from './opts'; diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index 72ff054f..4bc64667 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -2,21 +2,37 @@ import {Config} from '../config'; import {Cli, Option, Options} from '../cli'; import {ChromeDriver, IEDriver, StandAlone} from '../binaries'; +export const OUT_DIR = 'out_dir'; +export const SELENIUM_PORT = 'seleniumPort'; +export const IGNORE_SSL = 'ignore_ssl'; +export const PROXY = 'proxy'; +export const ALTERNATE_CDN = 'alternate_cdn'; +export const STANDALONE = 'standalone'; +export const CHROME = 'chrome'; +export const IE = 'ie'; +export const IE32 = 'ie32'; +export const VERSIONS_CHROME = 'versions.chrome'; +export const VERSIONS_STANDALONE = 'versions.standalone'; +export const VERSIONS_IE = 'versions.ie'; +export const CHROME_LOGS = 'chrome_logs'; + + /** * The options used by the commands. */ -export class Opts { - static outputDir = new Option('out_dir', 'Location to output/expect', 'string', Config.seleniumDir); - static seleniumPort = new Option('seleniumPort', 'Optional port for the selenium standalone server', 'string'); - static ignoreSsl = new Option('ignore_ssl', 'Ignore SSL certificates', 'boolean', false); - static proxy = new Option('proxy', 'Proxy to use for the install or update command', 'string'); - static alternateCdn = new Option('alternate_cnd', 'Alternate CDN to binaries', 'string'); - static standalone = new Option('standalone', 'Install or update selenium standalone', 'boolean', StandAlone.isDefault); - static chrome = new Option('chrome', 'Install or update chromedriver', 'boolean', ChromeDriver.isDefault); - static ie = new Option('ie', 'Install or update ie driver', 'boolean', IEDriver.isDefault); - static ie32 = new Option('ie32', 'Install or update 32-bit ie driver', 'boolean', IEDriver.isDefault); - static versionsChrome = new Option('versions_chrome', 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); - static versionsStandAlone = new Option('versions_standalone', 'Optional seleniuim standalone server version', 'string', StandAlone.versionDefault); - static versionsIe = new Option('versions_ie', 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); - static chromeLogs = new Option('chrome_logs', 'File path to chrome logs', 'string', undefined); -} +var opts: Options = {}; +opts[OUT_DIR] = new Option(OUT_DIR, 'Location to output/expect', 'string', Config.seleniumDir); +opts[SELENIUM_PORT] = new Option(SELENIUM_PORT, 'Optional port for the selenium standalone server', 'string'); +opts[IGNORE_SSL] = new Option(IGNORE_SSL, 'Ignore SSL certificates', 'boolean', false); +opts[PROXY] = new Option(PROXY, 'Proxy to use for the install or update command', 'string'); +opts[ALTERNATE_CDN] = new Option(ALTERNATE_CDN, 'Alternate CDN to binaries', 'string'); +opts[STANDALONE] = new Option(STANDALONE, 'Install or update selenium standalone', 'boolean', StandAlone.isDefault); +opts[CHROME] = new Option(CHROME, 'Install or update chromedriver', 'boolean', ChromeDriver.isDefault); +opts[IE] = new Option(IE, 'Install or update ie driver', 'boolean', IEDriver.isDefault); +opts[IE32] = new Option(IE32, 'Install or update 32-bit ie driver', 'boolean', IEDriver.isDefault); +opts[VERSIONS_CHROME] = new Option(VERSIONS_CHROME, 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); +opts[VERSIONS_STANDALONE] = new Option(VERSIONS_STANDALONE, 'Optional seleniuim standalone server version', 'string', StandAlone.versionDefault); +opts[VERSIONS_IE] = new Option(VERSIONS_IE, 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); +opts[CHROME_LOGS] = new Option(CHROME_LOGS, 'File path to chrome logs', 'string', undefined); + +export var Opts = opts; diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index f39a2337..4017c00c 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -5,6 +5,7 @@ import * as childProcess from 'child_process'; import * as http from 'http'; import {Opts} from './opts'; +import * as Opt from './'; import {Config} from '../config'; import {FileManager} from '../files'; import {Options, Program} from '../cli'; @@ -13,14 +14,14 @@ import {BinaryMap, ChromeDriver, IEDriver, StandAlone} from '../binaries'; let prog = new Program() .command('start', 'start up the selenium server') .action(start) - .addOption(Opts.outputDir) - .addOption(Opts.seleniumPort) - .addOption(Opts.versionsStandAlone) - .addOption(Opts.versionsChrome) - .addOption(Opts.chromeLogs); + .addOption(Opts[Opt.OUT_DIR]) + .addOption(Opts[Opt.SELENIUM_PORT]) + .addOption(Opts[Opt.VERSIONS_STANDALONE]) + .addOption(Opts[Opt.VERSIONS_CHROME]) + .addOption(Opts[Opt.CHROME_LOGS]); if (os.type() === 'Windows_NT') { - prog.addOption(Opts.versionsIe); + prog.addOption(Opts[Opt.VERSIONS_IE]); } export var program = prog; @@ -28,7 +29,7 @@ export var program = prog; // stand alone runner let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); if (argv._[0] === 'start-run') { - prog.run(argv); + prog.run(JSON.parse(JSON.stringify(argv))); } else if (argv._[0] === 'start-help') { prog.printHelp(); } @@ -40,27 +41,27 @@ if (argv._[0] === 'start-run') { function start(options: Options) { let osType = os.type(); let binaries = FileManager.setupBinaries(); - let seleniumPort = options['seleniumPort'].getString(); + let seleniumPort = options[Opt.SELENIUM_PORT].getString(); let outputDir = Config.seleniumDir; - if (options['out_dir'].getString()) { - if (path.isAbsolute(options['out_dir'].getString())) { - outputDir = options['out_dir'].getString(); + if (options[Opt.OUT_DIR].getString()) { + if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { + outputDir = options[Opt.OUT_DIR].getString(); } else { - outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); + outputDir = path.resolve(Config.baseDir, options[Opt.OUT_DIR].getString()); } } let chromeLogs: string = null; - if (options['chrome_logs'].getString()) { - if (path.isAbsolute(options['chrome_logs'].getString())) { - chromeLogs = options['chrome_logs'].getString(); + if (options[Opt.CHROME_LOGS].getString()) { + if (path.isAbsolute(options[Opt.CHROME_LOGS].getString())) { + chromeLogs = options[Opt.CHROME_LOGS].getString(); } else { - chromeLogs = path.resolve(Config.baseDir, options['chrome_logs'].getString()); + chromeLogs = path.resolve(Config.baseDir, options[Opt.CHROME_LOGS].getString()); } } - binaries[StandAlone.id].versionCustom = options['versions_standalone'].getString(); - binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getString(); - if (options['versions_ie']) { - binaries[IEDriver.id].versionCustom = options['versions_ie'].getString(); + binaries[StandAlone.id].versionCustom = options[Opt.VERSIONS_STANDALONE].getString(); + binaries[ChromeDriver.id].versionCustom = options[Opt.VERSIONS_CHROME].getString(); + if (options[Opt.VERSIONS_IE]) { + binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString(); } let downloadedBinaries = FileManager.downloadedBinaries(outputDir); diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts index 6bfc1eb3..b58c033b 100644 --- a/lib/cmds/status.ts +++ b/lib/cmds/status.ts @@ -2,13 +2,14 @@ import * as minimist from 'minimist'; import * as path from 'path'; import {Opts} from './opts'; +import * as Opt from './'; import {Config} from '../config'; import {FileManager} from '../files'; import {Options, Program} from '../cli'; let prog = new Program() .command('status', 'list the current available drivers') - .addOption(Opts.outputDir) + .addOption(Opts[Opt.OUT_DIR]) .action(status); export var program = prog; @@ -16,7 +17,7 @@ export var program = prog; // stand alone runner let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); if (argv._[0] === 'status-run') { - prog.run(argv); + prog.run(JSON.parse(JSON.stringify(argv))); } else if (argv._[0] === 'status-help') { prog.printHelp(); } @@ -28,11 +29,11 @@ if (argv._[0] === 'status-run') { function status(options: Options) { let binaries = FileManager.setupBinaries(); let outputDir = Config.seleniumDir; - if (options['out_dir'].value) { - if (path.isAbsolute(options['out_dir'].getString())) { - outputDir = options['out_dir'].getString(); + if (options[Opt.OUT_DIR].value) { + if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { + outputDir = options[Opt.OUT_DIR].getString(); } else { - outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); + outputDir = path.resolve(Config.baseDir, options[Opt.OUT_DIR].getString()); } } let downloadedBinaries = FileManager.downloadedBinaries(outputDir); diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 1396a3f3..6451e0a4 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -5,6 +5,7 @@ import * as os from 'os'; import * as path from 'path'; import {Opts} from './opts'; +import * as Opt from './'; import {Config} from '../config'; import {Binary, ChromeDriver, IEDriver, StandAlone} from '../binaries'; import {FileManager, Downloader} from '../files'; @@ -13,30 +14,30 @@ import {Options, Program} from '../cli'; var prog = new Program() .command('update', 'install or update selected binaries') .action(update) - .addOption(Opts.outputDir) - .addOption(Opts.ignoreSsl) - .addOption(Opts.proxy) - .addOption(Opts.alternateCdn) - .addOption(Opts.standalone) - .addOption(Opts.chrome); + .addOption(Opts[Opt.OUT_DIR]) + .addOption(Opts[Opt.IGNORE_SSL]) + .addOption(Opts[Opt.PROXY]) + .addOption(Opts[Opt.ALTERNATE_CDN]) + .addOption(Opts[Opt.STANDALONE]) + .addOption(Opts[Opt.CHROME]); if (os.type() === 'Windows_NT') { - prog.addOption(Opts.ie).addOption(Opts.ie32); + prog.addOption(Opts[Opt.IE]).addOption(Opts[Opt.IE32]); } prog - .addOption(Opts.versionsStandAlone) - .addOption(Opts.versionsChrome); + .addOption(Opts[Opt.VERSIONS_STANDALONE]) + .addOption(Opts[Opt.VERSIONS_CHROME]); if (os.type() === 'Windows_NT') { - prog.addOption(Opts.versionsIe); + prog.addOption(Opts[Opt.VERSIONS_IE]); } export var program = prog; // stand alone runner let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); if (argv._[0] === 'update-run') { - prog.run(argv); + prog.run(JSON.parse(JSON.stringify(argv))); } else if (argv._[0] === 'update-help') { prog.printHelp(); } @@ -47,34 +48,34 @@ if (argv._[0] === 'update-run') { * @param options */ function update(options: Options): void { - let standalone = options['standalone'].getBoolean(); - let chrome = options['chrome'].getBoolean(); + let standalone = options[Opt.STANDALONE].getBoolean(); + let chrome = options[Opt.CHROME].getBoolean(); let ie: boolean = false; let ie32: boolean = false; - if (options['ie']) { - ie = options['ie'].getBoolean(); + if (options[Opt.IE]) { + ie = options[Opt.IE].getBoolean(); } - if (options['ie32']) { - ie32 = options['ie32'].getBoolean(); + if (options[Opt.IE32]) { + ie32 = options[Opt.IE32].getBoolean(); } let outputDir = Config.seleniumDir; - if (options['out_dir'].getString()) { - if (path.isAbsolute(options['out_dir'].getString())) { - outputDir = options['out_dir'].getString(); + if (options[Opt.OUT_DIR].getString()) { + if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { + outputDir = options[Opt.OUT_DIR].getString(); } else { - outputDir = path.resolve(Config.baseDir, options['out_dir'].getString()); + outputDir = path.resolve(Config.baseDir, options[Opt.OUT_DIR].getString()); } FileManager.makeOutputDirectory(outputDir); } - let ignoreSSL = options['ignore_ssl'].getBoolean(); - let proxy = options['proxy'].getString(); + let ignoreSSL = options[Opt.IGNORE_SSL].getBoolean(); + let proxy = options[Opt.PROXY].getString(); // setup versions for binaries let binaries = FileManager.setupBinaries(); - binaries[StandAlone.id].versionCustom = options['versions_standalone'].getString(); - binaries[ChromeDriver.id].versionCustom = options['versions_chrome'].getString(); - if (options['versions_ie']) { - binaries[IEDriver.id].versionCustom = options['versions.ie'].getString(); + binaries[StandAlone.id].versionCustom = options[Opt.VERSIONS_STANDALONE].getString(); + binaries[ChromeDriver.id].versionCustom = options[Opt.VERSIONS_CHROME].getString(); + if (options[Opt.VERSIONS_IE]) { + binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString(); } // do the update diff --git a/lib/webdriver.ts b/lib/webdriver.ts index 338ac9f6..80aae2fd 100644 --- a/lib/webdriver.ts +++ b/lib/webdriver.ts @@ -26,7 +26,7 @@ if (commandline.programs[cmd[0]]) { else if (cmd[1] === 'help' || argv['help'] || argv['h']) { commandline.programs[cmd[0]].printHelp(); } else { - commandline.programs[cmd[0]].run(argv); + commandline.programs[cmd[0]].run(JSON.parse(JSON.stringify(argv))); } } else { commandline.printHelp(); From 5d617130a2e01f27aabbf079bb5cc22da0f9c358 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Tue, 29 Mar 2016 11:34:11 -0700 Subject: [PATCH 16/79] chore(log): improve logging messages closes #10 --- bin/webdriver-tool | 4 ++-- lib/cli/index.ts | 1 + lib/cli/util.ts | 14 ++++++++++++++ lib/cmds/start.ts | 14 +++++++------- lib/cmds/status.ts | 6 +++--- lib/cmds/update.ts | 10 +++++----- lib/files/downloader.ts | 19 ++++++++++--------- lib/files/file_manager.ts | 6 ++++-- 8 files changed, 46 insertions(+), 28 deletions(-) diff --git a/bin/webdriver-tool b/bin/webdriver-tool index 5fcde7b3..a9d7e0db 100755 --- a/bin/webdriver-tool +++ b/bin/webdriver-tool @@ -33,8 +33,8 @@ else if (localPath === globalPath) { } else { Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion())); if (Config.globalVersion() && Config.localVersion() !== Config.globalVersion()) { - Logger.info(chalk.yellow('warning: local version [' + Config.localVersion() + - '] mismatch with global version [' + Config.globalVersion() + ']\n')); + Logger.warn('warning: local version [' + Config.localVersion() + + '] mismatch with global version [' + Config.globalVersion() + ']\n'); } require(path.resolve(localPath, 'webdriver.js')); } diff --git a/lib/cli/index.ts b/lib/cli/index.ts index ac08d148..0032c817 100644 --- a/lib/cli/index.ts +++ b/lib/cli/index.ts @@ -1,3 +1,4 @@ export * from './cli'; export * from './options'; export * from './programs'; +export * from './util'; diff --git a/lib/cli/util.ts b/lib/cli/util.ts index 5c9583fb..e42af5b8 100644 --- a/lib/cli/util.ts +++ b/lib/cli/util.ts @@ -10,6 +10,20 @@ export class Logger { console.log(message); } + static warn(message: string, opt_noTimestamp?: boolean): void { + if (!opt_noTimestamp) { + message = Logger.timestamp() + ' ' + chalk.yellow(message); + } + console.log(message); + } + + static error(message: string, opt_noTimestamp?: boolean): void { + if (!opt_noTimestamp) { + message = Logger.timestamp() + ' ' + chalk.red(message); + } + console.log(message); + } + static timestamp(): string { let d = new Date(); return '[' + chalk.gray(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()) + ']'; diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 4017c00c..47f67a3f 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -8,7 +8,7 @@ import {Opts} from './opts'; import * as Opt from './'; import {Config} from '../config'; import {FileManager} from '../files'; -import {Options, Program} from '../cli'; +import {Logger, Options, Program} from '../cli'; import {BinaryMap, ChromeDriver, IEDriver, StandAlone} from '../binaries'; let prog = new Program() @@ -66,7 +66,7 @@ function start(options: Options) { let downloadedBinaries = FileManager.downloadedBinaries(outputDir); if (downloadedBinaries[StandAlone.id] == null) { - console.error( + Logger.error( 'Selenium Standalone is not present. Install with ' + 'webdriver-manager update --standalone'); process.exit(1); @@ -94,21 +94,21 @@ function start(options: Options) { for (let arg in args) { argsToString += ' ' + args[arg]; } - console.log('java' + argsToString); + Logger.info('java' + argsToString); let seleniumProcess = spawnCommand('java', args); - console.log('seleniumProcess.pid: ' + seleniumProcess.pid); + Logger.info('seleniumProcess.pid: ' + seleniumProcess.pid); seleniumProcess.on('exit', (code: number) => { - console.log('Selenium Standalone has exited with code ' + code); + Logger.info('Selenium Standalone has exited with code ' + code); process.exit(code); }); process.stdin.resume(); process.stdin.on('data', (chunk: Buffer) => { - console.log('Attempting to shut down selenium nicely'); + Logger.info('Attempting to shut down selenium nicely'); http.get('http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer'); }); process.on('SIGINT', () => { - console.log('Staying alive until the Selenium Standalone process exits'); + Logger.info('Staying alive until the Selenium Standalone process exits'); }); } diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts index b58c033b..db087d36 100644 --- a/lib/cmds/status.ts +++ b/lib/cmds/status.ts @@ -5,7 +5,7 @@ import {Opts} from './opts'; import * as Opt from './'; import {Config} from '../config'; import {FileManager} from '../files'; -import {Options, Program} from '../cli'; +import {Logger, Options, Program} from '../cli'; let prog = new Program() .command('status', 'list the current available drivers') @@ -52,12 +52,12 @@ function status(options: Options) { log += ', '; } } - console.log(log); + Logger.info(log); } // for binaries that are available for the operating system, show them here for (let bin in binaries) { if (downloadedBinaries[bin] == null) { - console.log(binaries[bin].name + ' is not present'); + Logger.info(binaries[bin].name + ' is not present'); } } } diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 6451e0a4..fff0b276 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -9,7 +9,7 @@ import * as Opt from './'; import {Config} from '../config'; import {Binary, ChromeDriver, IEDriver, StandAlone} from '../binaries'; import {FileManager, Downloader} from '../files'; -import {Options, Program} from '../cli'; +import {Logger, Options, Program} from '../cli'; var prog = new Program() .command('update', 'install or update selected binaries') @@ -83,7 +83,7 @@ function update(options: Options): void { if (FileManager.toDownload(binaries[StandAlone.id], outputDir)) { Downloader.downloadBinary(binaries[StandAlone.id], outputDir); } else { - console.log(binaries[StandAlone.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); + Logger.info(binaries[StandAlone.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); } } if (chrome) { @@ -91,7 +91,7 @@ function update(options: Options): void { Downloader.downloadBinary( binaries[ChromeDriver.id], outputDir, proxy, ignoreSSL, unzip); } else { - console.log(binaries[ChromeDriver.id].name + ' ' + binaries[ChromeDriver.id].versionCustom + ' up to date'); + Logger.info(binaries[ChromeDriver.id].name + ' ' + binaries[ChromeDriver.id].versionCustom + ' up to date'); } } if (ie) { @@ -99,7 +99,7 @@ function update(options: Options): void { Downloader.downloadBinary( binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); } else { - console.log(binaries[IEDriver.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); + Logger.info(binaries[IEDriver.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); } } if (ie32) { @@ -107,7 +107,7 @@ function update(options: Options): void { Downloader.downloadBinary( binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); } else { - console.log(binaries[IEDriver.id].name + ' 32-bit ' + binaries[StandAlone.id].versionCustom + ' up to date'); + Logger.info(binaries[IEDriver.id].name + ' 32-bit ' + binaries[StandAlone.id].versionCustom + ' up to date'); } } } diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index ccf05b45..e75274ba 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -5,6 +5,7 @@ import * as request from 'request'; import * as url from 'url'; import {Binary} from '../binaries/binary'; +import {Logger} from '../cli'; /** * The file downloader. @@ -22,10 +23,10 @@ export class Downloader { static downloadBinary( binary: Binary, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, opt_callback?: Function): void { - console.log('Updating ' + binary.name + ' to version ' + binary.version()); + Logger.info('Updating ' + binary.name + ' to version ' + binary.version()); var url = binary.url(os.type(), os.arch()); if (!url) { - console.error(binary.name + ' v' + binary.version() + ' is not available for your system.'); + Logger.error(binary.name + ' v' + binary.version() + ' is not available for your system.'); return; } Downloader.httpGetFile_( @@ -64,13 +65,13 @@ export class Downloader { static httpGetFile_( fileUrl: string, fileName: string, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, callback?: Function): void { - console.log('downloading ' + fileUrl + '...'); + Logger.info('downloading ' + fileUrl + '...'); let filePath = path.join(outputDir, fileName); let file = fs.createWriteStream(filePath); let contentLength = 0; if (opt_ignoreSSL) { - console.log('Ignoring SSL certificate'); + Logger.info('Ignoring SSL certificate'); } let options = { @@ -85,13 +86,13 @@ export class Downloader { (response) => { if (response.statusCode !== 200) { fs.unlink(filePath); - console.error('Error: Got code ' + response.statusCode + ' from ' + fileUrl); + Logger.error('Error: Got code ' + response.statusCode + ' from ' + fileUrl); } contentLength = response.headers['content-length']; }) .on('error', (error) => { - console.error('Error: Got error ' + error + ' from ' + fileUrl); + Logger.error('Error: Got error ' + error + ' from ' + fileUrl); fs.unlink(filePath); }) .pipe(file); @@ -99,17 +100,17 @@ export class Downloader { file.on('close', function() { fs.stat(filePath, function(err, stats) { if (err) { - console.error('Error: Got error ' + err + ' from ' + fileUrl); + Logger.error('Error: Got error ' + err + ' from ' + fileUrl); return; } if (stats.size != contentLength) { - console.error( + Logger.error( 'Error: corrupt download for ' + fileName + '. Please re-run webdriver-manager update'); fs.unlink(filePath); return; } - console.log(fileName + ' downloaded to ' + filePath); + Logger.info(fileName + ' downloaded to ' + filePath); if (callback) { callback(filePath); } diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 291bb457..8e897eca 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -4,6 +4,7 @@ import * as fs from 'fs'; import {Binary, BinaryMap, ChromeDriver, IEDriver, StandAlone, OS} from '../binaries'; import {DownloadedBinary} from './downloaded_binary'; +import {Logger} from '../cli'; /** * The File Manager class is where the webdriver manager will compile a list of @@ -156,12 +157,12 @@ export class FileManager { static removeExistingFiles(outputDir: string): void { // folder exists if (!fs.existsSync(outputDir)) { - console.log('The out_dir path ' + outputDir + ' does not exist.'); + Logger.warn('The out_dir path ' + outputDir + ' does not exist.'); return; } let existingFiles = FileManager.getExistngFiles(outputDir); if (existingFiles.length === 0) { - console.log('No files found in out_dir: ' + outputDir + '.'); + Logger.warn('No files found in out_dir: ' + outputDir); return; } @@ -171,6 +172,7 @@ export class FileManager { let bin: Binary = binaries[binPos]; if (file.indexOf(bin.prefix()) !== -1) { fs.unlinkSync(path.join(outputDir, file)); + Logger.info('Removed ' + file); } } }) From 374c3e719fce18a2f0a1b751b19bffb7d266cc69 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Wed, 30 Mar 2016 01:10:23 -0700 Subject: [PATCH 17/79] feat(length): on update, check to see the file is the correct length closes #8 --- lib/cmds/update.ts | 46 ++++++++++++++++++++++++--------------- lib/files/downloader.ts | 31 +++++++++++++++++++++++--- lib/files/file_manager.ts | 38 ++++++++++++++++++++++++++------ package.json | 1 + typings.json | 1 + 5 files changed, 89 insertions(+), 28 deletions(-) diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index fff0b276..1838f5a4 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -80,35 +80,45 @@ function update(options: Options): void { // do the update if (standalone) { - if (FileManager.toDownload(binaries[StandAlone.id], outputDir)) { - Downloader.downloadBinary(binaries[StandAlone.id], outputDir); - } else { - Logger.info(binaries[StandAlone.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); - } + FileManager.toDownload(binaries[StandAlone.id], outputDir).then((value: boolean) => { + if (value) { + Downloader.downloadBinary(binaries[StandAlone.id], outputDir); + } else { + Logger.info(binaries[StandAlone.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); + } + }); } if (chrome) { - if (FileManager.toDownload(binaries[ChromeDriver.id], outputDir)) { + FileManager.toDownload(binaries[ChromeDriver.id], outputDir).then((value: boolean) => { + if (value) { Downloader.downloadBinary( binaries[ChromeDriver.id], outputDir, proxy, ignoreSSL, unzip); } else { + // check to see if the executable is as expected + // check for correct file permissions Logger.info(binaries[ChromeDriver.id].name + ' ' + binaries[ChromeDriver.id].versionCustom + ' up to date'); } + }); } if (ie) { - if (FileManager.toDownload(binaries[IEDriver.id], outputDir)) { - Downloader.downloadBinary( - binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); - } else { - Logger.info(binaries[IEDriver.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); - } + FileManager.toDownload(binaries[IEDriver.id], outputDir).then((value: boolean) => { + if (value) { + Downloader.downloadBinary( + binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); + } else { + Logger.info(binaries[IEDriver.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); + } + }); } if (ie32) { - if (FileManager.toDownload(binaries[IEDriver.id], outputDir)) { - Downloader.downloadBinary( - binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); - } else { - Logger.info(binaries[IEDriver.id].name + ' 32-bit ' + binaries[StandAlone.id].versionCustom + ' up to date'); - } + FileManager.toDownload(binaries[IEDriver.id], outputDir).then((value: boolean) => { + if (value) { + Downloader.downloadBinary( + binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); + } else { + Logger.info(binaries[IEDriver.id].name + ' 32-bit ' + binaries[StandAlone.id].versionCustom + ' up to date'); + } + }); } } diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index e75274ba..db2b48e4 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -3,6 +3,8 @@ import * as path from 'path'; import * as fs from 'fs'; import * as request from 'request'; import * as url from 'url'; +import * as q from 'q'; +import * as http from 'http'; import {Binary} from '../binaries/binary'; import {Logger} from '../cli'; @@ -23,7 +25,7 @@ export class Downloader { static downloadBinary( binary: Binary, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, opt_callback?: Function): void { - Logger.info('Updating ' + binary.name + ' to version ' + binary.version()); + Logger.info('downloading ' + binary.name + ' to version ' + binary.version()); var url = binary.url(os.type(), os.arch()); if (!url) { Logger.error(binary.name + ' v' + binary.version() + ' is not available for your system.'); @@ -53,7 +55,30 @@ export class Downloader { } else if (protocol === 'http:') { return process.env.HTTP_PROXY || process.env.http_proxy; } - }; + } + + static httpHeadContentLength(fileUrl: string, opt_proxy?: string, opt_ignoreSSL?: boolean): q.Promise { + let deferred = q.defer(); + if (opt_ignoreSSL) { + Logger.info('ignoring SSL certificate'); + } + + let options = { + method: 'HEAD', + url: fileUrl, + strictSSL: !opt_ignoreSSL, + rejectUnauthorized: !opt_ignoreSSL, + proxy: Downloader.resolveProxy_(fileUrl, opt_proxy) + }; + + let contentLength = 0; + request(options) + .on('response', (response) => { + contentLength = response.headers['content-length']; + deferred.resolve(contentLength); + }); + return deferred.promise; + } /** * Ceates the GET request for the file name. @@ -65,7 +90,7 @@ export class Downloader { static httpGetFile_( fileUrl: string, fileName: string, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, callback?: Function): void { - Logger.info('downloading ' + fileUrl + '...'); + Logger.info('curl -o ' + outputDir + '/' + fileName + ' ' + fileUrl); let filePath = path.join(outputDir, fileName); let file = fs.createWriteStream(filePath); let contentLength = 0; diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 8e897eca..dc048ae7 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -1,9 +1,11 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; +import * as q from 'q'; import {Binary, BinaryMap, ChromeDriver, IEDriver, StandAlone, OS} from '../binaries'; import {DownloadedBinary} from './downloaded_binary'; +import {Downloader} from './downloader'; import {Logger} from '../cli'; /** @@ -134,19 +136,41 @@ export class FileManager { * @param outputDir The directory where files are downloaded and stored. * @returns If the file should be downloaded. */ - static toDownload(binary: T, outputDir: string): boolean { + static toDownload(binary: T, outputDir: string): q.Promise { + let osType = os.type(); + let osArch = os.arch(); + let filePath: string; + let readData: Buffer; + let deferred = q.defer(); let downloaded: BinaryMap = FileManager.downloadedBinaries(outputDir); + if (downloaded[binary.id()]) { - let versions = downloaded[binary.id()].versions; + let downloadedBinary = downloaded[binary.id()]; + let versions = downloadedBinary.versions; let version = binary.version(); for (let index in versions) { let v = versions[index]; if (v === version) { - return false; + filePath = path.resolve(outputDir, binary.filename(osType, osArch)); + readData = fs.readFileSync(filePath); + + // we have the version, verify it is the correct file size + let contentLength = Downloader.httpHeadContentLength(binary.url(osType, osArch)); + return contentLength.then((value: any): boolean => { + if (value == readData.length) { + return false; + } else { + Logger.warn(path.basename(filePath) + ' expected length ' + value + + ', found ' + readData.length); + Logger.warn('removing file: ' + filePath); + return true; + } + }); } } } - return true; + deferred.resolve(true); + return deferred.promise; } /** @@ -157,12 +181,12 @@ export class FileManager { static removeExistingFiles(outputDir: string): void { // folder exists if (!fs.existsSync(outputDir)) { - Logger.warn('The out_dir path ' + outputDir + ' does not exist.'); + Logger.warn('the out_dir path ' + outputDir + ' does not exist'); return; } let existingFiles = FileManager.getExistngFiles(outputDir); if (existingFiles.length === 0) { - Logger.warn('No files found in out_dir: ' + outputDir); + Logger.warn('no files found in out_dir: ' + outputDir); return; } @@ -172,7 +196,7 @@ export class FileManager { let bin: Binary = binaries[binPos]; if (file.indexOf(bin.prefix()) !== -1) { fs.unlinkSync(path.join(outputDir, file)); - Logger.info('Removed ' + file); + Logger.info('removed ' + file); } } }) diff --git a/package.json b/package.json index 27814561..161ccbc4 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "adm-zip": "^0.4.7", "chalk": "^1.1.1", "minimist": "^1.2.0", + "q": "^1.4.1", "request": "^2.69.0" }, "devDependencies": { diff --git a/typings.json b/typings.json index 37d6f8bc..a66d99d7 100644 --- a/typings.json +++ b/typings.json @@ -6,6 +6,7 @@ "jasmine": "registry:dt/jasmine#2.2.0+20160308082659", "minimist": "registry:dt/minimist#1.1.3+20151229171613", "node": "registry:dt/node#4.0.0+20160226132328", + "q": "registry:dt/q#0.0.0+20160323171452", "request": "registry:dt/request#0.0.0+20160311134650" } } From 744424a53b8f32becd321c7323ead2ca351ddb91 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 1 Apr 2016 17:08:58 -0700 Subject: [PATCH 18/79] chore(download): update downloader for NO_PROXY fix from protractor closes #18 --- lib/files/downloader.ts | 31 +++++++++-- spec/files/downloader_spec.ts | 99 +++++++++++++++++++++++++++++++++++ typings.json | 12 ++--- 3 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 spec/files/downloader_spec.ts diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index db2b48e4..da60e978 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -47,14 +47,35 @@ export class Downloader { */ static resolveProxy_(fileUrl: string, opt_proxy?: string): string { let protocol = url.parse(fileUrl).protocol; + let hostname = url.parse(fileUrl).hostname; + if (opt_proxy) { return opt_proxy; - } else if (protocol === 'https:') { - return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || - process.env.http_proxy; - } else if (protocol === 'http:') { - return process.env.HTTP_PROXY || process.env.http_proxy; + } else { + // If the NO_PROXY environment variable exists and matches the host name, + // to ignore the resolve proxy. + // the checks to see if it exists and equal to empty string is to help with testing + let noProxy: string = process.env.NO_PROXY || process.env.no_proxy; + if (noProxy) { + // array of hostnames/domain names listed in the NO_PROXY environment variable + let noProxyTokens = noProxy.split(','); + // check if the fileUrl hostname part does not end with one of the + // NO_PROXY environment variable's hostnames/domain names + for (let noProxyToken of noProxyTokens) { + if (hostname.indexOf(noProxyToken) !== -1) { + return undefined; + } + } + } + + // If the HTTPS_PROXY and HTTP_PROXY environment variable is set, use that as the proxy + if (protocol === 'https:') { + return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy; + } else if (protocol === 'http:') { + return process.env.HTTP_PROXY || process.env.http_proxy; + } } + return null; } static httpHeadContentLength(fileUrl: string, opt_proxy?: string, opt_ignoreSSL?: boolean): q.Promise { diff --git a/spec/files/downloader_spec.ts b/spec/files/downloader_spec.ts new file mode 100644 index 00000000..8d74c525 --- /dev/null +++ b/spec/files/downloader_spec.ts @@ -0,0 +1,99 @@ +/// + +import {Downloader} from '../../lib/files'; + +describe('downloader', () => { + let fileUrlHttp = 'http://foobar.com'; + let fileUrlHttps = 'https://foobar.com'; + let argProxy = 'http://foobar.arg'; + let envNoProxy = 'http://foobar.com'; + let envHttpProxy = 'http://foobar.env'; + let envHttpsProxy = 'https://foobar.env'; + + it ('should return undefined when proxy arg is not used', () => { + let proxy = Downloader.resolveProxy_(fileUrlHttp); + expect(proxy).toBeUndefined(); + }); + + describe('proxy arg', () => { + let opt_proxy = 'http://bar.foo'; + it('should return the proxy arg', () => { + let proxy = Downloader.resolveProxy_(fileUrlHttp, opt_proxy); + expect(proxy).toBe(opt_proxy); + }); + + it('should always return the proxy arg with env var set', () => { + process.env.HTTP_PROXY = envHttpProxy; + process.env.HTTPS_PROXY = envHttpsProxy; + process.env.NO_PROXY = envNoProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttp, opt_proxy); + expect(proxy).toBe(opt_proxy); + }); + }); + + describe('environment varialbes', () => { + beforeEach(() => { + delete process.env.HTTP_PROXY; + delete process.env.http_proxy; + delete process.env.HTTPS_PROXY; + delete process.env.https_proxy; + delete process.env.NO_PROXY; + delete process.env.no_proxy; + }); + + it('should return the HTTP env variable', () => { + process.env.HTTP_PROXY = envHttpProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttp); + expect(proxy).toBe(envHttpProxy); + }); + + it('should return the http env variable', () => { + process.env.http_proxy = envHttpProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttp); + expect(proxy).toBe(envHttpProxy); + }); + + it('should return the HTTPS env variable for https protocol', () => { + process.env.HTTPS_PROXY = envHttpsProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttps); + expect(proxy).toBe(envHttpsProxy); + }); + + it('should return the https env variable for https protocol', () => { + process.env.https_proxy = envHttpsProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttps); + expect(proxy).toBe(envHttpsProxy); + }); + + it('should return the HTTP env variable for https protocol', () => { + process.env.HTTP_PROXY = envHttpProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttps); + expect(proxy).toBe(envHttpProxy); + }); + + it('should return the https env variable for https protocol', () => { + process.env.http_proxy = envHttpProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttps); + expect(proxy).toBe(envHttpProxy); + }); + + describe('NO_PROXY environment variable', () => { + beforeEach(() => { + delete process.env.NO_PROXY; + delete process.env.no_proxy; + }); + + it('should return null when the NO_PROXY matches the fileUrl', () => { + process.env.NO_PROXY = envNoProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttp); + expect(proxy).toBeUndefined(); + }); + + it('should return null when the no_proxy matches the fileUrl', () => { + process.env.no_proxy = envNoProxy; + let proxy = Downloader.resolveProxy_(fileUrlHttp); + expect(proxy).toBeUndefined(); + }); + }); + }); +}); diff --git a/typings.json b/typings.json index a66d99d7..a1607003 100644 --- a/typings.json +++ b/typings.json @@ -1,12 +1,12 @@ { "ambientDependencies": { - "adm-zip": "registry:dt/adm-zip#0.0.0+20160211023517", + "adm-zip": "registry:dt/adm-zip#0.0.0+20160317120654", "chalk": "registry:dt/chalk#0.4.0+20160317120654", - "form-data": "registry:dt/form-data#0.0.0+20150826060748", - "jasmine": "registry:dt/jasmine#2.2.0+20160308082659", - "minimist": "registry:dt/minimist#1.1.3+20151229171613", - "node": "registry:dt/node#4.0.0+20160226132328", + "form-data": "registry:dt/form-data#0.0.0+20160316155526", + "jasmine": "registry:dt/jasmine#2.2.0+20160317120654", + "minimist": "registry:dt/minimist#1.1.3+20160317120654", + "node": "registry:dt/node#4.0.0+20160330064709", "q": "registry:dt/q#0.0.0+20160323171452", - "request": "registry:dt/request#0.0.0+20160311134650" + "request": "registry:dt/request#0.0.0+20160316155526" } } From a073fd0e9d0290e52ac3a808643b069c71b196c3 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Tue, 5 Apr 2016 10:34:37 -0700 Subject: [PATCH 19/79] fix(npm): use global, local, and project without env closes #20 --- bin/webdriver-tool | 54 ++++++++++++++++++++-------------------------- lib/config.ts | 30 +++++++++++--------------- 2 files changed, 35 insertions(+), 49 deletions(-) mode change 100755 => 100644 bin/webdriver-tool diff --git a/bin/webdriver-tool b/bin/webdriver-tool old mode 100755 new mode 100644 index a9d7e0db..31b3d1c1 --- a/bin/webdriver-tool +++ b/bin/webdriver-tool @@ -2,39 +2,31 @@ var path = require('path'); var fs = require('fs'); -var chalk = require('chalk'); -var Logger = require('../built/lib/cli/util').Logger; -var Config = require('../built/lib/config').Config; - -var globalNpm = process.env.NPM_BIN || process.env.NVM_BIN; - -var globalPath = path.resolve(globalNpm, '../lib/node_modules/webdriver-tool/built/lib/'); -var localPath = path.resolve(__dirname, '../../../node_modules/webdriver-tool/built/lib/'); - -// check to see if this is webdriver-tool (the project), if it is, run this -var projectPath = path.resolve('.'); -var parentPath = path.resolve(projectPath, '..'); -var folder = projectPath.replace(parentPath + '/', ''); +var cwd = process.cwd(); +var dir = __dirname; +var localInstall = path.resolve(cwd, 'node_modules/webdriver-tool/'); +var parentPath = path.resolve(cwd, '..'); +var folder = cwd.replace(parentPath, '').substring(1); +// Project version. if (folder === 'webdriver-tool') { - Logger.info('webdriver-tool: using ' + chalk.magenta('project version ' + Config.projectVersion())); - // compare project version to global - if (Config.globalVersion() && Config.projectVersion() !== Config.globalVersion()) { - Logger.info(chalk.yellow('warning: project version [' + Config.projectVersion() + - '] mismatch with global version [' + Config.globalVersion() + ']\n')); - } - require(path.resolve(projectPath, 'built/lib/webdriver.js')); + var chalk = require('chalk'); + console.log('Webdriver Tool: using ' + chalk.magenta('project version ' + + require(path.resolve('package.json')).version)); + require(path.resolve('built/lib/webdriver.js')); +} +// Local version. +else if (fs.statSync(path.resolve(localInstall)).isDirectory()) { + var chalk = require(path.resolve(localInstall, 'node_modules/chalk/index.js')); + console.log('Webdriver Tool: using ' + chalk.cyan('local installed version ' + + require(path.resolve(localInstall, 'package.json')).version)); + require(path.resolve(localInstall, 'built/lib/webdriver.js')); } -else if (localPath === globalPath) { - // we are using the global version - Logger.info('webdriver-tool: using ' + chalk.cyan('global version ' + Config.globalVersion())); - require(path.resolve(globalPath, 'webdriver.js')); -} else { - Logger.info('webdriver-tool: using ' + chalk.green('local version ' + Config.localVersion())); - if (Config.globalVersion() && Config.localVersion() !== Config.globalVersion()) { - Logger.warn('warning: local version [' + Config.localVersion() + - '] mismatch with global version [' + Config.globalVersion() + ']\n'); - } - require(path.resolve(localPath, 'webdriver.js')); +// Global version. +else { + var chalk = require(path.resolve(dir, '../node_modules/chalk/index.js')); + console.log('Webdriver Tool: using ' + chalk.green('global installed version ', + require(path.resolve(dir, '../package.json')).version)); + require(path.resolve(dir, '../built/lib/webdriver.js')); } diff --git a/lib/config.ts b/lib/config.ts index c02eaeb2..d481fe0c 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -49,20 +49,13 @@ export class Config { * If installed as a node module, return the local version. */ static localVersion(): string { - let packageJson = require(Config.packagePath); - return packageJson.version; - } - - /** - * If running from the project directory, get the project's version. - */ - static projectVersion(): string { - let projectPath = path.resolve('.'); - let projectPackagePath = path.resolve(projectPath, 'package.json'); + var cwd = process.cwd(); + var localInstall = path.resolve(cwd, 'node_modules/webdriver-tool/'); try { - if (fs.statSync(projectPackagePath).isFile()) { - let projectJson = require(projectPackagePath); - return projectJson.version; + if (fs.statSync(localInstall).isDirectory()) { + return require(path.resolve(localInstall, 'package.json')).version; + } else { + return null; } } catch(err) { return null; @@ -73,12 +66,13 @@ export class Config { * If installed, returns the globally installed webdriver-tool. */ static globalVersion(): string { - let globalNpm = process.env.NPM_BIN || process.env.NVM_BIN; - let globalPackagePath = path.resolve(globalNpm, '../lib/node_modules/webdriver-tool/built/package.json'); + var dir = __dirname; + let globalPackageJson = path.resolve(dir, '../package.json'); try { - if (fs.statSync(globalPackagePath).isFile()) { - let globalPackageJson = require(globalPackagePath); - return globalPackageJson.version; + if (fs.statSync(globalPackageJson).isFile()) { + return require(globalPackageJson).version; + } else { + return null; } } catch(err) { return null; From 0ec14435379161259435edc7c766388941f1a846 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Fri, 1 Apr 2016 13:18:34 -0700 Subject: [PATCH 20/79] fix(binary): file type, unzipping, and permissions closes #7, #16 --- lib/binaries/binary.ts | 1 + lib/binaries/ie_driver.ts | 7 ++-- lib/cmds/update.ts | 77 ++++++++++++++++++++++----------------- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/lib/binaries/binary.ts b/lib/binaries/binary.ts index b3974056..5445fdf4 100644 --- a/lib/binaries/binary.ts +++ b/lib/binaries/binary.ts @@ -27,6 +27,7 @@ export class Binary { versionCustom: string; // version of file suffixDefault: string; // file type for downloading cdn: string; // url protocol and host + arch: string; /** * @param ostype The operating system. diff --git a/lib/binaries/ie_driver.ts b/lib/binaries/ie_driver.ts index 3d76c509..06bef433 100644 --- a/lib/binaries/ie_driver.ts +++ b/lib/binaries/ie_driver.ts @@ -1,4 +1,4 @@ -import {arch, type} from 'os'; +import * as os from 'os'; import {Binary, OS} from './binary'; import {Config} from '../config'; @@ -20,6 +20,7 @@ export class IEDriver extends Binary { this.prefixDefault = 'IEDriverServer'; this.suffixDefault = '.zip'; this.cdn = Config.cdnUrls().ie; + this.arch = os.arch(); } id(): string { return IEDriver.id; } @@ -27,8 +28,8 @@ export class IEDriver extends Binary { versionDefault(): string { return IEDriver.versionDefault; } version(): string { - if (type() == 'Windows_NT') { - if (arch() == 'x64') { + if (os.type() == 'Windows_NT') { + if (this.arch == 'x64') { return '_x64_' + this.versionCustom; } else { return '_Win32_' + this.versionCustom; diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 1838f5a4..3f897eb6 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -78,58 +78,67 @@ function update(options: Options): void { binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString(); } - // do the update + // if the file has not been completely downloaded, download it + // else if the file has already been downloaded, unzip the file, rename it, and give it permissions if (standalone) { - FileManager.toDownload(binaries[StandAlone.id], outputDir).then((value: boolean) => { + let binary = binaries[StandAlone.id]; + FileManager.toDownload(binary, outputDir).then((value: boolean) => { if (value) { - Downloader.downloadBinary(binaries[StandAlone.id], outputDir); + Downloader.downloadBinary(binary, outputDir); } else { - Logger.info(binaries[StandAlone.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); + Logger.info(binary.name + ' - file exists: ' + binary.filename(os.type(), os.arch())); + Logger.info(binary.name + ' - v' + binary.versionCustom + ' up to date'); } }); } if (chrome) { - FileManager.toDownload(binaries[ChromeDriver.id], outputDir).then((value: boolean) => { - if (value) { - Downloader.downloadBinary( - binaries[ChromeDriver.id], outputDir, proxy, ignoreSSL, unzip); - } else { - // check to see if the executable is as expected - // check for correct file permissions - Logger.info(binaries[ChromeDriver.id].name + ' ' + binaries[ChromeDriver.id].versionCustom + ' up to date'); - } - }); + let binary = binaries[ChromeDriver.id]; + updateBinary(binary, outputDir, proxy, ignoreSSL); } if (ie) { - FileManager.toDownload(binaries[IEDriver.id], outputDir).then((value: boolean) => { - if (value) { - Downloader.downloadBinary( - binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); - } else { - Logger.info(binaries[IEDriver.id].name + ' ' + binaries[StandAlone.id].versionCustom + ' up to date'); - } - }); + let binary = binaries[IEDriver.id]; + binary.arch = os.arch(); // Win32 or x64 + updateBinary(binary, outputDir, proxy, ignoreSSL); } if (ie32) { - FileManager.toDownload(binaries[IEDriver.id], outputDir).then((value: boolean) => { - if (value) { - Downloader.downloadBinary( - binaries[IEDriver.id], outputDir, proxy, ignoreSSL, unzip); - } else { - Logger.info(binaries[IEDriver.id].name + ' 32-bit ' + binaries[StandAlone.id].versionCustom + ' up to date'); - } - }); + let binary = binaries[IEDriver.id]; + binary.arch = 'Win32'; + updateBinary(binary, outputDir, proxy, ignoreSSL); } } -function unzip(binary: T, outputDir: string, filename: string): void { - let zip = new AdmZip(filename); +function updateBinary(binary: Binary, outputDir: string, proxy: string, ignoreSSL: boolean) { + FileManager.toDownload(binary, outputDir).then((value: boolean) => { + if (value) { + Downloader.downloadBinary(binary, outputDir, proxy, ignoreSSL, unzip); + } else { + Logger.info(binary.name + ' - file exists: ' + binary.filename(os.type(), os.arch())); + let fileName = binary.filename(os.type(), os.arch()); + unzip(binary, outputDir, fileName); + Logger.info(binary.name + ' - v' + binary.versionCustom + ' up to date'); + } + }); +} + +function unzip(binary: T, outputDir: string, fileName: string): void { + // remove the previously saved file and unzip it let osType = os.type(); + let mv = path.join(outputDir, binary.executableFilename(osType)); + try { + fs.unlinkSync(mv); + } catch(err) {} + + // unzip the file + Logger.info(binary.name + ' - unzipping ' + fileName); + let zip = new AdmZip(path.resolve(outputDir, fileName)); zip.extractAllTo(outputDir, true); - let mv = - path.join(outputDir, binary.prefix() + binary.version() + binary.executableSuffix(osType)); + + // rename fs.renameSync(path.join(outputDir, binary.name + binary.executableSuffix(osType)), mv); + + // set permissions if (osType !== 'Windows_NT') { + Logger.info(binary.name + ' - setting permissions to 0755 for ' + mv); fs.chmodSync(mv, '0755'); } } From f002b18959e4dd1bf3797adf9f2c9048ecdbeefc Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Thu, 7 Apr 2016 15:00:47 -0700 Subject: [PATCH 21/79] chore(migrate): update for migrating webdriver-tool to webdriver-manager closes #1 --- LICENSE | 2 +- README.md | 18 +++++++++--------- bin/{webdriver-tool => webdriver-manager} | 8 ++++---- lib/cli/util.ts | 11 +++++++---- lib/cmds/update.ts | 4 ++-- lib/files/downloader.ts | 5 ++--- lib/webdriver.ts | 2 +- package.json | 10 +++++----- 8 files changed, 31 insertions(+), 29 deletions(-) rename bin/{webdriver-tool => webdriver-manager} (77%) mode change 100644 => 100755 diff --git a/LICENSE b/LICENSE index 4662726f..5cbc7da3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2010-2015 Google, Inc. +Copyright (c) 2016 Google, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9c6fe548..af828f6a 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -Webdriver Tool -============== +Webdriver Manager +================= -Webdriver tool is a manager for selenium standalone server jar and browser drivers executables. This is the same tool as `webdriver-manager` from the [Protractor](https://github.com/angular/protractor) repository. +A selenium server and browser driver manager for your end to end tests. This is the same tool as `webdriver-manager` from the [Protractor](https://github.com/angular/protractor) repository. Getting Started --------------- ``` -npm install -g webdriver-tool +npm install -g webdriver-manager ``` Setting up a Selenium Server @@ -16,7 +16,7 @@ Setting up a Selenium Server Prior to starting the selenium server, download the selenium server jar and driver binaries. By default it will download the selenium server jar and chromedriver binary. ``` -webdriver-tool update +webdriver-manager update ``` Starting the Selenium Server @@ -26,7 +26,7 @@ By default, the selenium server will run on `http://localhost:4444/wd/hub`. ``` -webdriver-tool start +webdriver-manager start ``` Other useful commands @@ -35,11 +35,11 @@ Other useful commands View different versions of server and driver files: ``` -webdriver-tool status +webdriver-manager status ``` -Clear out the server and driver files. If `webdriver-tool start` does not work, try to clear out the saved files. +Clear out the server and driver files. If `webdriver-manager start` does not work, try to clear out the saved files. ``` -webdriver-tool clean +webdriver-manager clean ``` diff --git a/bin/webdriver-tool b/bin/webdriver-manager old mode 100644 new mode 100755 similarity index 77% rename from bin/webdriver-tool rename to bin/webdriver-manager index 31b3d1c1..13a51455 --- a/bin/webdriver-tool +++ b/bin/webdriver-manager @@ -10,23 +10,23 @@ var parentPath = path.resolve(cwd, '..'); var folder = cwd.replace(parentPath, '').substring(1); // Project version. -if (folder === 'webdriver-tool') { +if (folder === 'webdriver-manager') { var chalk = require('chalk'); - console.log('Webdriver Tool: using ' + chalk.magenta('project version ' + + console.log('Webdriver Manager: using ' + chalk.magenta('project version ' + require(path.resolve('package.json')).version)); require(path.resolve('built/lib/webdriver.js')); } // Local version. else if (fs.statSync(path.resolve(localInstall)).isDirectory()) { var chalk = require(path.resolve(localInstall, 'node_modules/chalk/index.js')); - console.log('Webdriver Tool: using ' + chalk.cyan('local installed version ' + + console.log('Webdriver Manager: using ' + chalk.cyan('local installed version ' + require(path.resolve(localInstall, 'package.json')).version)); require(path.resolve(localInstall, 'built/lib/webdriver.js')); } // Global version. else { var chalk = require(path.resolve(dir, '../node_modules/chalk/index.js')); - console.log('Webdriver Tool: using ' + chalk.green('global installed version ', + console.log('Webdriver Manager: using ' + chalk.green('global installed version ', require(path.resolve(dir, '../package.json')).version)); require(path.resolve(dir, '../built/lib/webdriver.js')); } diff --git a/lib/cli/util.ts b/lib/cli/util.ts index e42af5b8..5fcba16d 100644 --- a/lib/cli/util.ts +++ b/lib/cli/util.ts @@ -5,28 +5,31 @@ export enum LOG_LEVEL { DEBUG, WARN, INFO } export class Logger { static info(message: string, opt_noTimestamp?: boolean): void { if (!opt_noTimestamp) { - message = Logger.timestamp() + ' ' + message; + message = Logger.timestamp() + message; } console.log(message); } static warn(message: string, opt_noTimestamp?: boolean): void { if (!opt_noTimestamp) { - message = Logger.timestamp() + ' ' + chalk.yellow(message); + message = Logger.timestamp() + chalk.yellow(message); } console.log(message); } static error(message: string, opt_noTimestamp?: boolean): void { if (!opt_noTimestamp) { - message = Logger.timestamp() + ' ' + chalk.red(message); + message = Logger.timestamp() + chalk.red(message); } console.log(message); } static timestamp(): string { let d = new Date(); - return '[' + chalk.gray(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()) + ']'; + let ts = '[' + chalk.gray(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()) + ']'; + let spaces = 22 - ts.length; + ts += Array(spaces).join(' '); + return ts; } } diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 3f897eb6..73afa0cb 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -86,7 +86,7 @@ function update(options: Options): void { if (value) { Downloader.downloadBinary(binary, outputDir); } else { - Logger.info(binary.name + ' - file exists: ' + binary.filename(os.type(), os.arch())); + Logger.info(binary.name + ' - file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); Logger.info(binary.name + ' - v' + binary.versionCustom + ' up to date'); } }); @@ -112,7 +112,7 @@ function updateBinary(binary: Binary, outputDir: string, proxy: string, ignoreSS if (value) { Downloader.downloadBinary(binary, outputDir, proxy, ignoreSSL, unzip); } else { - Logger.info(binary.name + ' - file exists: ' + binary.filename(os.type(), os.arch())); + Logger.info(binary.name + ' - file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); let fileName = binary.filename(os.type(), os.arch()); unzip(binary, outputDir, fileName); Logger.info(binary.name + ' - v' + binary.versionCustom + ' up to date'); diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index da60e978..95f3467a 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -25,7 +25,7 @@ export class Downloader { static downloadBinary( binary: Binary, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, opt_callback?: Function): void { - Logger.info('downloading ' + binary.name + ' to version ' + binary.version()); + Logger.info(binary.name + ' - downloading version ' + binary.version()); var url = binary.url(os.type(), os.arch()); if (!url) { Logger.error(binary.name + ' v' + binary.version() + ' is not available for your system.'); @@ -117,7 +117,7 @@ export class Downloader { let contentLength = 0; if (opt_ignoreSSL) { - Logger.info('Ignoring SSL certificate'); + Logger.info('ignoring SSL certificate'); } let options = { @@ -156,7 +156,6 @@ export class Downloader { fs.unlink(filePath); return; } - Logger.info(fileName + ' downloaded to ' + filePath); if (callback) { callback(filePath); } diff --git a/lib/webdriver.ts b/lib/webdriver.ts index 80aae2fd..eac90079 100644 --- a/lib/webdriver.ts +++ b/lib/webdriver.ts @@ -10,7 +10,7 @@ import {Cli} from './cli'; import {Config} from './config'; let commandline = new Cli() - .usage('webdriver-tool [options]') + .usage('webdriver-manager [options]') .program(clean.program) .program(start.program) .program(status.program) diff --git a/package.json b/package.json index 161ccbc4..92fd8de9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "webdriver-tool", + "name": "webdriver-manager", "version": "0.1.0", - "description": "Webdriver tool to manage selenium standalone server", + "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", "test": "gulp test" @@ -17,12 +17,12 @@ ], "repository": { "type": "git", - "url": "git://github.com/angular/webdriver-tool.git" + "url": "git://github.com/angular/webdriver-manager.git" }, "bin": { - "webdriver-tool": "bin/webdriver-tool" + "webdriver-manager": "bin/webdriver-manager" }, - "main": "bin/webdriver-tool", + "main": "bin/webdriver-manager", "author": "Craig Nishina ", "license": "MIT", "dependencies": { From 7c7fe172ff8555524c7dd1fbe2b3b7576827a8c0 Mon Sep 17 00:00:00 2001 From: Craig Date: Thu, 21 Apr 2016 15:40:47 -0700 Subject: [PATCH 22/79] chore(test): add a travis yml file (#7) closes #6 --- .travis.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..08ae0d13 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: node_js +sudo: false +node_js: + - "4" + - "5" + +env: + global: + - CXX=g++-4.8 + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 + +before_install: + - g++-4.8 --version + +script: + - npm test From 3b303129040b17292028452f13c73d62736f1216 Mon Sep 17 00:00:00 2001 From: Craig Date: Fri, 22 Apr 2016 14:22:31 -0700 Subject: [PATCH 23/79] feat(logger): update logging methods (#5) --- bin/webdriver-manager | 2 +- lib/cli/cli.ts | 1 - lib/cli/index.ts | 2 +- lib/cli/logger.ts | 258 ++++++++++++++++++++++++++++++++++++++ lib/cli/util.ts | 36 ------ lib/cmds/start.ts | 13 +- lib/cmds/status.ts | 5 +- lib/cmds/update.ts | 15 +-- lib/config.ts | 2 +- lib/files/downloader.ts | 20 +-- lib/files/file_manager.ts | 12 +- package.json | 2 +- typings.json | 6 +- 13 files changed, 301 insertions(+), 73 deletions(-) create mode 100644 lib/cli/logger.ts delete mode 100644 lib/cli/util.ts diff --git a/bin/webdriver-manager b/bin/webdriver-manager index 13a51455..9218f8ff 100755 --- a/bin/webdriver-manager +++ b/bin/webdriver-manager @@ -5,7 +5,7 @@ var fs = require('fs'); var cwd = process.cwd(); var dir = __dirname; -var localInstall = path.resolve(cwd, 'node_modules/webdriver-tool/'); +var localInstall = path.resolve(cwd, 'node_modules/webdriver-manager/'); var parentPath = path.resolve(cwd, '..'); var folder = cwd.replace(parentPath, '').substring(1); diff --git a/lib/cli/cli.ts b/lib/cli/cli.ts index e3a8940e..3b8e9817 100644 --- a/lib/cli/cli.ts +++ b/lib/cli/cli.ts @@ -2,7 +2,6 @@ import * as chalk from 'chalk'; import * as path from 'path'; import {Config} from '../config'; -import {Logger} from './util'; import {Programs, Program} from './programs'; import {MinimistArgs, Options} from './options'; diff --git a/lib/cli/index.ts b/lib/cli/index.ts index 0032c817..3b0816a3 100644 --- a/lib/cli/index.ts +++ b/lib/cli/index.ts @@ -1,4 +1,4 @@ export * from './cli'; export * from './options'; export * from './programs'; -export * from './util'; +export * from './logger'; diff --git a/lib/cli/logger.ts b/lib/cli/logger.ts new file mode 100644 index 00000000..3fe93c51 --- /dev/null +++ b/lib/cli/logger.ts @@ -0,0 +1,258 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +// Will use chalk if chalk is available to add color to console logging +let chalk: any; +let printRed: Function; +let printYellow: Function; +let printGray: Function; + +try { + chalk = require('chalk'); + printRed = chalk.red; + printYellow = chalk.yellow; + printGray = chalk.gray; +} catch (e) { + printRed = printYellow = printGray = (msg: any) => { return msg; }; +} + +export enum LogLevel { + ERROR, + WARN, + INFO, + DEBUG +} + +export enum WriteTo { + CONSOLE, + FILE, + BOTH, + NONE +} + +let logFile = 'webdriver.log'; // the default log file name + +/** + * Logger class adds timestamp output, log levels, and identifiers to help + * when debugging. Also could write to console, file, both, or none. + */ +export class Logger { + static logLevel: LogLevel = LogLevel.INFO; + static showTimestamp: boolean = true; + static showId: boolean = true; + static writeTo: WriteTo = WriteTo.CONSOLE; + static fd: any; + static firstWrite: boolean = false; + + /** + * Set up the write location. If writing to a file, get the file descriptor. + * @param writeTo The enum for where to write the logs. + * @param opt_logFile An optional parameter to override the log file location. + */ + static setWrite(writeTo: WriteTo, opt_logFile?: string): void { + if (opt_logFile) { + logFile = opt_logFile; + } + Logger.writeTo = writeTo; + if (Logger.writeTo == WriteTo.FILE || Logger.writeTo == WriteTo.BOTH) { + Logger.fd = fs.openSync(path.resolve(logFile), 'a'); + Logger.firstWrite = false; + } + } + + /** + * Creates a logger instance with an ID for the logger. + * @constructor + */ + constructor(private id: string) {} + + /** + * Log INFO + * @param ...msgs multiple arguments to be logged. + */ + info(...msgs: any[]): void { this.log_(LogLevel.INFO, msgs); } + + /** + * Log DEBUG + * @param ...msgs multiple arguments to be logged. + */ + debug(...msgs: any[]): void { this.log_(LogLevel.DEBUG, msgs); } + + /** + * Log WARN + * @param ...msgs multiple arguments to be logged. + */ + warn(...msgs: any[]): void { this.log_(LogLevel.WARN, msgs); } + + /** + * Log ERROR + * @param ...msgs multiple arguments to be logged. + */ + error(...msgs: any[]): void { this.log_(LogLevel.ERROR, msgs); } + + /** + * For the log level set, check to see if the messages should be logged. + * @param logLevel The log level of the message. + * @param msgs The messages to be logged + */ + log_(logLevel: LogLevel, msgs: any[]): void { + switch (Logger.logLevel) { + case LogLevel.ERROR: + if (logLevel <= LogLevel.ERROR) { + this.print_(logLevel, msgs); + } + break; + case LogLevel.WARN: + if (logLevel <= LogLevel.WARN) { + this.print_(logLevel, msgs); + } + break; + case LogLevel.INFO: + if (logLevel <= LogLevel.INFO) { + this.print_(logLevel, msgs); + } + break; + case LogLevel.DEBUG: + if (logLevel <= LogLevel.DEBUG) { + this.print_(logLevel, msgs); + } + break; + default: + throw new Error('Log level undefined'); + } + } + + /** + * Format with timestamp, log level, identifier, and message and log to + * specified medium (console, file, both, none). + * @param logLevel The log level of the message. + * @param msgs The messages to be logged. + */ + print_(logLevel: LogLevel, msgs: any[]): void { + let consoleLog: string = ''; + let fileLog: string = ''; + + if (Logger.showTimestamp) { + consoleLog += Logger.timestamp_(WriteTo.CONSOLE); + fileLog += Logger.timestamp_(WriteTo.FILE); + } + consoleLog += Logger.level_(logLevel, this.id, WriteTo.CONSOLE); + fileLog += Logger.level_(logLevel, this.id, WriteTo.FILE); + if (Logger.showId) { + consoleLog += Logger.id_(logLevel, this.id, WriteTo.CONSOLE); + fileLog += Logger.id_(logLevel, this.id, WriteTo.FILE); + } + consoleLog += ' -'; + fileLog += ' - '; + + switch (Logger.writeTo) { + case WriteTo.CONSOLE: + msgs.unshift(consoleLog); + console.log.apply(console, msgs); + break; + case WriteTo.FILE: + // for the first line written to the file, add a space + if (!Logger.firstWrite) { + fs.writeSync(Logger.fd, '\n'); + Logger.firstWrite = true; + } + fileLog += ' ' + Logger.msgToFile_(msgs); + fs.writeSync(Logger.fd, fileLog + '\n'); + break; + case WriteTo.BOTH: + // for the first line written to the file, add a space + if (!Logger.firstWrite) { + fs.writeSync(Logger.fd, '\n'); + Logger.firstWrite = true; + } + fileLog += ' ' + Logger.msgToFile_(msgs); + fs.writeSync(Logger.fd, fileLog + '\n'); + msgs.unshift(consoleLog); + console.log.apply(console, msgs); + break; + case WriteTo.NONE: + break; + } + } + + /** + * Get a timestamp formatted with [hh:mm:ss] + * @param writeTo The enum for where to write the logs. + * @return The string of the formatted timestamp + */ + static timestamp_(writeTo: WriteTo): string { + let d = new Date(); + let ts = '['; + let hours = d.getHours() < 10 ? '0' + d.getHours() : d.getHours(); + let minutes = d.getMinutes() < 10 ? '0' + d.getMinutes() : d.getMinutes(); + let seconds = d.getSeconds() < 10 ? '0' + d.getSeconds() : d.getSeconds(); + if (writeTo == WriteTo.CONSOLE) { + ts += printGray(hours + ':' + minutes + ':' + seconds) + ']'; + } else { + ts += hours + ':' + minutes + ':' + seconds + ']'; + } + ts += ' '; + return ts; + } + + /** + * Get the identifier of the logger as '/' + * @param logLevel The log level of the message. + * @param writeTo The enum for where to write the logs. + * @return The string of the formatted id + */ + static id_(logLevel: LogLevel, id: string, writeTo: WriteTo): string { + let level = LogLevel[logLevel].toString(); + if (writeTo === WriteTo.FILE) { + return '/' + id; + } else if (logLevel === LogLevel.ERROR) { + return printRed('/' + id); + } else if (logLevel === LogLevel.WARN) { + return printYellow('/' + id); + } else { + return '/' + id; + } + } + + /** + * Get the log level formatted with the first letter. For info, it is I. + * @param logLevel The log level of the message. + * @param writeTo The enum for where to write the logs. + * @return The string of the formatted log level + */ + static level_(logLevel: LogLevel, id: string, writeTo: WriteTo): string { + let level = LogLevel[logLevel].toString(); + if (writeTo === WriteTo.FILE) { + return level[0]; + } else if (logLevel === LogLevel.ERROR) { + return printRed(level[0]); + } else if (logLevel === LogLevel.WARN) { + return printYellow(level[0]); + } else { + return level[0]; + } + } + + /** + * Convert the list of messages to a single string message. + * @param msgs The list of messages. + * @return The string of the formatted messages + */ + static msgToFile_(msgs: any[]): string { + let log = ''; + for (let pos = 0; pos < msgs.length; pos++) { + let msg = msgs[pos]; + let ret: any; + if (typeof msg === 'object') { + ret = JSON.stringify(msg); + } else { + ret = msg; + } + if (pos !== msgs.length - 1) { + ret += ' '; + } + log += ret; + } + return log; + } +} diff --git a/lib/cli/util.ts b/lib/cli/util.ts deleted file mode 100644 index 5fcba16d..00000000 --- a/lib/cli/util.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as chalk from 'chalk'; - -export enum LOG_LEVEL { DEBUG, WARN, INFO } - -export class Logger { - static info(message: string, opt_noTimestamp?: boolean): void { - if (!opt_noTimestamp) { - message = Logger.timestamp() + message; - } - console.log(message); - } - - static warn(message: string, opt_noTimestamp?: boolean): void { - if (!opt_noTimestamp) { - message = Logger.timestamp() + chalk.yellow(message); - } - console.log(message); - } - - static error(message: string, opt_noTimestamp?: boolean): void { - if (!opt_noTimestamp) { - message = Logger.timestamp() + chalk.red(message); - } - console.log(message); - } - - static timestamp(): string { - let d = new Date(); - let ts = '[' + chalk.gray(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()) + ']'; - let spaces = 22 - ts.length; - ts += Array(spaces).join(' '); - return ts; - } -} - -export class Req {} diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 47f67a3f..b75fb5ff 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -11,6 +11,7 @@ import {FileManager} from '../files'; import {Logger, Options, Program} from '../cli'; import {BinaryMap, ChromeDriver, IEDriver, StandAlone} from '../binaries'; +let logger = new Logger('start'); let prog = new Program() .command('start', 'start up the selenium server') .action(start) @@ -66,7 +67,7 @@ function start(options: Options) { let downloadedBinaries = FileManager.downloadedBinaries(outputDir); if (downloadedBinaries[StandAlone.id] == null) { - Logger.error( + logger.error( 'Selenium Standalone is not present. Install with ' + 'webdriver-manager update --standalone'); process.exit(1); @@ -94,21 +95,21 @@ function start(options: Options) { for (let arg in args) { argsToString += ' ' + args[arg]; } - Logger.info('java' + argsToString); + logger.info('java' + argsToString); let seleniumProcess = spawnCommand('java', args); - Logger.info('seleniumProcess.pid: ' + seleniumProcess.pid); + logger.info('seleniumProcess.pid: ' + seleniumProcess.pid); seleniumProcess.on('exit', (code: number) => { - Logger.info('Selenium Standalone has exited with code ' + code); + logger.info('Selenium Standalone has exited with code ' + code); process.exit(code); }); process.stdin.resume(); process.stdin.on('data', (chunk: Buffer) => { - Logger.info('Attempting to shut down selenium nicely'); + logger.info('Attempting to shut down selenium nicely'); http.get('http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer'); }); process.on('SIGINT', () => { - Logger.info('Staying alive until the Selenium Standalone process exits'); + logger.info('Staying alive until the Selenium Standalone process exits'); }); } diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts index db087d36..8e8c356e 100644 --- a/lib/cmds/status.ts +++ b/lib/cmds/status.ts @@ -7,6 +7,7 @@ import {Config} from '../config'; import {FileManager} from '../files'; import {Logger, Options, Program} from '../cli'; +let logger = new Logger('status'); let prog = new Program() .command('status', 'list the current available drivers') .addOption(Opts[Opt.OUT_DIR]) @@ -52,12 +53,12 @@ function status(options: Options) { log += ', '; } } - Logger.info(log); + logger.info(log); } // for binaries that are available for the operating system, show them here for (let bin in binaries) { if (downloadedBinaries[bin] == null) { - Logger.info(binaries[bin].name + ' is not present'); + logger.info(binaries[bin].name + ' is not present'); } } } diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 73afa0cb..4ee16409 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -11,7 +11,8 @@ import {Binary, ChromeDriver, IEDriver, StandAlone} from '../binaries'; import {FileManager, Downloader} from '../files'; import {Logger, Options, Program} from '../cli'; -var prog = new Program() +let logger = new Logger('update'); +let prog = new Program() .command('update', 'install or update selected binaries') .action(update) .addOption(Opts[Opt.OUT_DIR]) @@ -86,8 +87,8 @@ function update(options: Options): void { if (value) { Downloader.downloadBinary(binary, outputDir); } else { - Logger.info(binary.name + ' - file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); - Logger.info(binary.name + ' - v' + binary.versionCustom + ' up to date'); + logger.info(binary.name + ': file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); + logger.info(binary.name + ': v' + binary.versionCustom + ' up to date'); } }); } @@ -112,10 +113,10 @@ function updateBinary(binary: Binary, outputDir: string, proxy: string, ignoreSS if (value) { Downloader.downloadBinary(binary, outputDir, proxy, ignoreSSL, unzip); } else { - Logger.info(binary.name + ' - file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); + logger.info(binary.name + ': file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); let fileName = binary.filename(os.type(), os.arch()); unzip(binary, outputDir, fileName); - Logger.info(binary.name + ' - v' + binary.versionCustom + ' up to date'); + logger.info(binary.name + ': v' + binary.versionCustom + ' up to date'); } }); } @@ -129,7 +130,7 @@ function unzip(binary: T, outputDir: string, fileName: string) } catch(err) {} // unzip the file - Logger.info(binary.name + ' - unzipping ' + fileName); + logger.info(binary.name + ': unzipping ' + fileName); let zip = new AdmZip(path.resolve(outputDir, fileName)); zip.extractAllTo(outputDir, true); @@ -138,7 +139,7 @@ function unzip(binary: T, outputDir: string, fileName: string) // set permissions if (osType !== 'Windows_NT') { - Logger.info(binary.name + ' - setting permissions to 0755 for ' + mv); + logger.info(binary.name + ': setting permissions to 0755 for ' + mv); fs.chmodSync(mv, '0755'); } } diff --git a/lib/config.ts b/lib/config.ts index d481fe0c..e9f1e317 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -50,7 +50,7 @@ export class Config { */ static localVersion(): string { var cwd = process.cwd(); - var localInstall = path.resolve(cwd, 'node_modules/webdriver-tool/'); + var localInstall = path.resolve(cwd, 'node_modules/webdriver-manager/'); try { if (fs.statSync(localInstall).isDirectory()) { return require(path.resolve(localInstall, 'package.json')).version; diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index 95f3467a..82c848a6 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -9,6 +9,8 @@ import * as http from 'http'; import {Binary} from '../binaries/binary'; import {Logger} from '../cli'; +let logger = new Logger('downloader'); + /** * The file downloader. */ @@ -25,10 +27,10 @@ export class Downloader { static downloadBinary( binary: Binary, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, opt_callback?: Function): void { - Logger.info(binary.name + ' - downloading version ' + binary.version()); + logger.info(binary.name + ': downloading version ' + binary.version()); var url = binary.url(os.type(), os.arch()); if (!url) { - Logger.error(binary.name + ' v' + binary.version() + ' is not available for your system.'); + logger.error(binary.name + ' v' + binary.version() + ' is not available for your system.'); return; } Downloader.httpGetFile_( @@ -81,7 +83,7 @@ export class Downloader { static httpHeadContentLength(fileUrl: string, opt_proxy?: string, opt_ignoreSSL?: boolean): q.Promise { let deferred = q.defer(); if (opt_ignoreSSL) { - Logger.info('ignoring SSL certificate'); + logger.info('ignoring SSL certificate'); } let options = { @@ -111,13 +113,13 @@ export class Downloader { static httpGetFile_( fileUrl: string, fileName: string, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, callback?: Function): void { - Logger.info('curl -o ' + outputDir + '/' + fileName + ' ' + fileUrl); + logger.info('curl -o ' + outputDir + '/' + fileName + ' ' + fileUrl); let filePath = path.join(outputDir, fileName); let file = fs.createWriteStream(filePath); let contentLength = 0; if (opt_ignoreSSL) { - Logger.info('ignoring SSL certificate'); + logger.info('ignoring SSL certificate'); } let options = { @@ -132,13 +134,13 @@ export class Downloader { (response) => { if (response.statusCode !== 200) { fs.unlink(filePath); - Logger.error('Error: Got code ' + response.statusCode + ' from ' + fileUrl); + logger.error('Error: Got code ' + response.statusCode + ' from ' + fileUrl); } contentLength = response.headers['content-length']; }) .on('error', (error) => { - Logger.error('Error: Got error ' + error + ' from ' + fileUrl); + logger.error('Error: Got error ' + error + ' from ' + fileUrl); fs.unlink(filePath); }) .pipe(file); @@ -146,11 +148,11 @@ export class Downloader { file.on('close', function() { fs.stat(filePath, function(err, stats) { if (err) { - Logger.error('Error: Got error ' + err + ' from ' + fileUrl); + logger.error('Error: Got error ' + err + ' from ' + fileUrl); return; } if (stats.size != contentLength) { - Logger.error( + logger.error( 'Error: corrupt download for ' + fileName + '. Please re-run webdriver-manager update'); fs.unlink(filePath); diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index dc048ae7..037c2422 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -8,6 +8,8 @@ import {DownloadedBinary} from './downloaded_binary'; import {Downloader} from './downloader'; import {Logger} from '../cli'; +let logger = new Logger('file_manager'); + /** * The File Manager class is where the webdriver manager will compile a list of * binaries that could be downloaded and get a list of previously downloaded @@ -160,9 +162,9 @@ export class FileManager { if (value == readData.length) { return false; } else { - Logger.warn(path.basename(filePath) + ' expected length ' + value + + logger.warn(path.basename(filePath) + ' expected length ' + value + ', found ' + readData.length); - Logger.warn('removing file: ' + filePath); + logger.warn('removing file: ' + filePath); return true; } }); @@ -181,12 +183,12 @@ export class FileManager { static removeExistingFiles(outputDir: string): void { // folder exists if (!fs.existsSync(outputDir)) { - Logger.warn('the out_dir path ' + outputDir + ' does not exist'); + logger.warn('the out_dir path ' + outputDir + ' does not exist'); return; } let existingFiles = FileManager.getExistngFiles(outputDir); if (existingFiles.length === 0) { - Logger.warn('no files found in out_dir: ' + outputDir); + logger.warn('no files found in out_dir: ' + outputDir); return; } @@ -196,7 +198,7 @@ export class FileManager { let bin: Binary = binaries[binPos]; if (file.indexOf(bin.prefix()) !== -1) { fs.unlinkSync(path.join(outputDir, file)); - Logger.info('removed ' + file); + logger.info('removed ' + file); } } }) diff --git a/package.json b/package.json index 92fd8de9..747c6588 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,6 @@ "jasmine": "^2.4.1", "run-sequence": "^1.1.5", "typescript": "^1.8.7", - "typings": "^0.7.7" + "typings": "^0.7.12" } } diff --git a/typings.json b/typings.json index a1607003..3b95a676 100644 --- a/typings.json +++ b/typings.json @@ -3,10 +3,10 @@ "adm-zip": "registry:dt/adm-zip#0.0.0+20160317120654", "chalk": "registry:dt/chalk#0.4.0+20160317120654", "form-data": "registry:dt/form-data#0.0.0+20160316155526", - "jasmine": "registry:dt/jasmine#2.2.0+20160317120654", + "jasmine": "registry:dt/jasmine#2.2.0+20160412134438", "minimist": "registry:dt/minimist#1.1.3+20160317120654", - "node": "registry:dt/node#4.0.0+20160330064709", - "q": "registry:dt/q#0.0.0+20160323171452", + "node": "registry:dt/node#4.0.0+20160412142033", + "q": "registry:dt/q#0.0.0+20160417152954", "request": "registry:dt/request#0.0.0+20160316155526" } } From 7bede7fe853c7dcbfe2bddb3721aff244ce49bac Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 25 Apr 2016 22:42:03 -0700 Subject: [PATCH 24/79] chore(config): update default selenium standalone and ie driver (#9) --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 0c377acf..1eac9b51 100644 --- a/config.json +++ b/config.json @@ -1,8 +1,8 @@ { "webdriverVersions": { - "selenium": "2.52.0", + "selenium": "2.53.0", "chromedriver": "2.21", - "iedriver": "2.52.0" + "iedriver": "2.52.1" }, "cdnUrls": { "selenium": "https://selenium-release.storage.googleapis.com/", From d6c0b4f9a025eed30a3935ac77121e58d7edc9ea Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 25 Apr 2016 22:42:13 -0700 Subject: [PATCH 25/79] chore(npm): update global, local and project launching (#8) --- bin/webdriver-manager | 44 ++++++++++------- lib/cmds/clean.ts | 4 +- lib/cmds/opts.ts | 2 +- lib/cmds/start.ts | 6 +-- lib/cmds/status.ts | 4 +- lib/cmds/update.ts | 4 +- lib/config.ts | 107 ++++++++++++++++++++++++++++-------------- typings.json | 2 +- 8 files changed, 109 insertions(+), 64 deletions(-) diff --git a/bin/webdriver-manager b/bin/webdriver-manager index 9218f8ff..103567a4 100755 --- a/bin/webdriver-manager +++ b/bin/webdriver-manager @@ -2,31 +2,41 @@ var path = require('path'); var fs = require('fs'); - var cwd = process.cwd(); -var dir = __dirname; -var localInstall = path.resolve(cwd, 'node_modules/webdriver-manager/'); + +var nodeModuleName = 'webdriver-manager'; +var localInstall = path.resolve(cwd, 'node_modules', nodeModuleName); var parentPath = path.resolve(cwd, '..'); +var dir = __dirname; var folder = cwd.replace(parentPath, '').substring(1); -// Project version. -if (folder === 'webdriver-manager') { +var isProjectVersion = folder === nodeModuleName; +var isLocalVersion = false; +try { + isLocalVersion = fs.statSync(localInstall).isDirectory(); +} catch(e) { +} + +// project version +if (folder === nodeModuleName) { var chalk = require('chalk'); - console.log('Webdriver Manager: using ' + chalk.magenta('project version ' + - require(path.resolve('package.json')).version)); - require(path.resolve('built/lib/webdriver.js')); + console.log(nodeModuleName + ': using ' + chalk.magenta('project version ' + + require(path.resolve('package.json')).version)); + require(path.resolve('built/lib/webdriver')); } -// Local version. -else if (fs.statSync(path.resolve(localInstall)).isDirectory()) { + +// local version +else if (isLocalVersion) { var chalk = require(path.resolve(localInstall, 'node_modules/chalk/index.js')); - console.log('Webdriver Manager: using ' + chalk.cyan('local installed version ' + - require(path.resolve(localInstall, 'package.json')).version)); - require(path.resolve(localInstall, 'built/lib/webdriver.js')); + console.log(nodeModuleName + ': using ' + chalk.cyan('local installed version ' + + require(path.resolve(localInstall, 'package.json')).version)); + require(path.resolve(localInstall, 'built/lib/webdriver')); } -// Global version. + +// global version else { var chalk = require(path.resolve(dir, '../node_modules/chalk/index.js')); - console.log('Webdriver Manager: using ' + chalk.green('global installed version ', - require(path.resolve(dir, '../package.json')).version)); - require(path.resolve(dir, '../built/lib/webdriver.js')); + console.log(nodeModuleName + ': using ' + chalk.green('global installed version ' + + require(path.resolve(dir, '../package.json')).version)); + require(path.resolve(dir, '../built/lib/webdriver')); } diff --git a/lib/cmds/clean.ts b/lib/cmds/clean.ts index db0858d1..4a5b8d99 100644 --- a/lib/cmds/clean.ts +++ b/lib/cmds/clean.ts @@ -27,12 +27,12 @@ if (argv._[0] === 'clean-run') { * @param: options */ function clean(options: Options): void { - let outputDir = Config.seleniumDir; + let outputDir = Config.getSeleniumDir(); if (options[Opt.OUT_DIR].getString()) { if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { outputDir = options[Opt.OUT_DIR].getString(); } else { - outputDir = path.resolve(Config.baseDir, options[Opt.OUT_DIR].getString()); + outputDir = path.resolve(Config.getBaseDir(), options[Opt.OUT_DIR].getString()); } } FileManager.removeExistingFiles(outputDir); diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index 4bc64667..c2b186cb 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -21,7 +21,7 @@ export const CHROME_LOGS = 'chrome_logs'; * The options used by the commands. */ var opts: Options = {}; -opts[OUT_DIR] = new Option(OUT_DIR, 'Location to output/expect', 'string', Config.seleniumDir); +opts[OUT_DIR] = new Option(OUT_DIR, 'Location to output/expect', 'string', Config.getSeleniumDir()); opts[SELENIUM_PORT] = new Option(SELENIUM_PORT, 'Optional port for the selenium standalone server', 'string'); opts[IGNORE_SSL] = new Option(IGNORE_SSL, 'Ignore SSL certificates', 'boolean', false); opts[PROXY] = new Option(PROXY, 'Proxy to use for the install or update command', 'string'); diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index b75fb5ff..071614fd 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -43,12 +43,12 @@ function start(options: Options) { let osType = os.type(); let binaries = FileManager.setupBinaries(); let seleniumPort = options[Opt.SELENIUM_PORT].getString(); - let outputDir = Config.seleniumDir; + let outputDir = Config.getSeleniumDir(); if (options[Opt.OUT_DIR].getString()) { if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { outputDir = options[Opt.OUT_DIR].getString(); } else { - outputDir = path.resolve(Config.baseDir, options[Opt.OUT_DIR].getString()); + outputDir = path.resolve(Config.getBaseDir(), options[Opt.OUT_DIR].getString()); } } let chromeLogs: string = null; @@ -56,7 +56,7 @@ function start(options: Options) { if (path.isAbsolute(options[Opt.CHROME_LOGS].getString())) { chromeLogs = options[Opt.CHROME_LOGS].getString(); } else { - chromeLogs = path.resolve(Config.baseDir, options[Opt.CHROME_LOGS].getString()); + chromeLogs = path.resolve(Config.getBaseDir(), options[Opt.CHROME_LOGS].getString()); } } binaries[StandAlone.id].versionCustom = options[Opt.VERSIONS_STANDALONE].getString(); diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts index 8e8c356e..620a12b8 100644 --- a/lib/cmds/status.ts +++ b/lib/cmds/status.ts @@ -29,12 +29,12 @@ if (argv._[0] === 'status-run') { */ function status(options: Options) { let binaries = FileManager.setupBinaries(); - let outputDir = Config.seleniumDir; + let outputDir = Config.getSeleniumDir(); if (options[Opt.OUT_DIR].value) { if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { outputDir = options[Opt.OUT_DIR].getString(); } else { - outputDir = path.resolve(Config.baseDir, options[Opt.OUT_DIR].getString()); + outputDir = path.resolve(Config.getBaseDir(), options[Opt.OUT_DIR].getString()); } } let downloadedBinaries = FileManager.downloadedBinaries(outputDir); diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 4ee16409..8b056abb 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -59,12 +59,12 @@ function update(options: Options): void { if (options[Opt.IE32]) { ie32 = options[Opt.IE32].getBoolean(); } - let outputDir = Config.seleniumDir; + let outputDir = Config.getSeleniumDir(); if (options[Opt.OUT_DIR].getString()) { if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { outputDir = options[Opt.OUT_DIR].getString(); } else { - outputDir = path.resolve(Config.baseDir, options[Opt.OUT_DIR].getString()); + outputDir = path.resolve(Config.getBaseDir(), options[Opt.OUT_DIR].getString()); } FileManager.makeOutputDirectory(outputDir); } diff --git a/lib/config.ts b/lib/config.ts index e9f1e317..86f5ee37 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -14,17 +14,78 @@ export interface ConfigFile { * The configuration for webdriver-manager. */ export class Config { - static configPath: string = '../config.json'; - static packagePath: string = '../package.json'; - static baseDir: string = path.resolve(__dirname, '../../'); - static seleniumDir: string = path.resolve(Config.baseDir, 'selenium'); + static configFile: string = 'config.json'; + static packageFile: string = 'package.json'; + + static cwd = process.cwd(); + + static nodeModuleName = 'webdriver-manager'; + static localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); + static parentPath = path.resolve(Config.cwd, '..'); + static dir = __dirname; + static folder = Config.cwd.replace(Config.parentPath, '').substring(1); + + static isProjectVersion = Config.folder === Config.nodeModuleName; + static isLocalVersion = false; + + + static getFile(jsonFile: string): string { + try { + Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); + } catch(e) { + } + + // project version + if (Config.folder === Config.nodeModuleName) { + return path.resolve('built', jsonFile); + } + + // local version + else if (Config.isLocalVersion) { + return path.resolve(Config.localInstall, 'built', jsonFile); + } + + // global version + else { + return path.resolve(Config.dir, '../built', jsonFile); + } + } + + static getFolder(folder: string): string { + try { + Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); + } catch(e) { + } + + // project version + if (Config.folder === Config.nodeModuleName) { + return path.resolve(folder); + } + + // local version + else if (Config.isLocalVersion) { + return path.resolve(Config.localInstall, folder); + } + + // global version + else { + return path.resolve(Config.dir, folder); + } + } + + static getSeleniumDir(): string { + return Config.getFolder('selenium/'); + } + static getBaseDir(): string { + return Config.getFolder('/'); + } /** * Get the binary versions from the configuration file. * @returns A map of the versions defined in the configuration file. */ static binaryVersions(): ConfigFile { - let configFile = require(Config.configPath); + let configFile = require(Config.getFile(Config.configFile)); let configVersions: ConfigFile = {}; configVersions.selenium = configFile.webdriverVersions.selenium; configVersions.chrome = configFile.webdriverVersions.chromedriver; @@ -37,7 +98,7 @@ export class Config { * @returns A map of the CDN versions defined in the configuration file. */ static cdnUrls(): ConfigFile { - let configFile = require(Config.configPath); + let configFile = require(Config.getFile(Config.configFile)); let configCdnUrls: ConfigFile = {}; configCdnUrls.selenium = configFile.cdnUrls.selenium; configCdnUrls.chrome = configFile.cdnUrls.chromedriver; @@ -46,36 +107,10 @@ export class Config { } /** - * If installed as a node module, return the local version. + * Get the package version. */ - static localVersion(): string { - var cwd = process.cwd(); - var localInstall = path.resolve(cwd, 'node_modules/webdriver-manager/'); - try { - if (fs.statSync(localInstall).isDirectory()) { - return require(path.resolve(localInstall, 'package.json')).version; - } else { - return null; - } - } catch(err) { - return null; - } - } - - /** - * If installed, returns the globally installed webdriver-tool. - */ - static globalVersion(): string { - var dir = __dirname; - let globalPackageJson = path.resolve(dir, '../package.json'); - try { - if (fs.statSync(globalPackageJson).isFile()) { - return require(globalPackageJson).version; - } else { - return null; - } - } catch(err) { - return null; - } + static getVersion(): string { + let packageFile = require(Config.getFile(Config.packageFile)); + return packageFile.version; } } diff --git a/typings.json b/typings.json index 3b95a676..ba9cecb5 100644 --- a/typings.json +++ b/typings.json @@ -5,7 +5,7 @@ "form-data": "registry:dt/form-data#0.0.0+20160316155526", "jasmine": "registry:dt/jasmine#2.2.0+20160412134438", "minimist": "registry:dt/minimist#1.1.3+20160317120654", - "node": "registry:dt/node#4.0.0+20160412142033", + "node": "registry:dt/node#4.0.0+20160423143914", "q": "registry:dt/q#0.0.0+20160417152954", "request": "registry:dt/request#0.0.0+20160316155526" } From 2fe8032b3949a274704312cc7532163b16658431 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 25 Apr 2016 22:53:23 -0700 Subject: [PATCH 26/79] chore(travis): add travis status to readme (#10) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index af828f6a..5165d609 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -Webdriver Manager + +Webdriver Manager [![Build Status](https://travis-ci.org/angular/webdriver-manager.png?branch=master)](https://travis-ci.org/angular/webdriver-manager) ================= A selenium server and browser driver manager for your end to end tests. This is the same tool as `webdriver-manager` from the [Protractor](https://github.com/angular/protractor) repository. From 8d6e459174f374cb138326c4dc6374812334e172 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 26 Apr 2016 12:06:46 -0700 Subject: [PATCH 27/79] chore(readme): reference pose/webdriver-manager for previous versions (#14) closes #12 and closes #13 --- .npmignore | 1 + README.md | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index cacc253e..792eda85 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,7 @@ lib/ spec/ typings/ +selenium/ .clang-format .gitignore diff --git a/README.md b/README.md index 5165d609..38447d71 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -Webdriver Manager [![Build Status](https://travis-ci.org/angular/webdriver-manager.png?branch=master)](https://travis-ci.org/angular/webdriver-manager) +Webdriver Manager [![Build Status](https://travis-ci.org/angular/webdriver-manager.png?branch=master)](https://travis-ci.org/angular/webdriver-manager) [![Join the chat at https://gitter.im/angular/webdriver-manager](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/webdriver-manager?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ================= A selenium server and browser driver manager for your end to end tests. This is the same tool as `webdriver-manager` from the [Protractor](https://github.com/angular/protractor) repository. +**Note:** Version 9 and lower please reference [pose/webdriver-manager](https://github.com/pose/webdriver-manager). If there are features that existed in version 9 and lower, please open up an issue with the missing feature or a create a pull request. + Getting Started --------------- From 70d32df659f19510c25e97ea9a42c7f93813d448 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 26 Apr 2016 12:06:57 -0700 Subject: [PATCH 28/79] fix(dir): check selenium dir and warn user that the folder does not exist (#17) closes #11 --- lib/cmds/start.ts | 11 +++++++++++ lib/cmds/status.ts | 12 ++++++++++++ lib/files/file_manager.ts | 8 +++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 071614fd..ad333d03 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -1,3 +1,4 @@ +import * as fs from 'fs'; import * as minimist from 'minimist'; import * as path from 'path'; import * as os from 'os'; @@ -51,6 +52,16 @@ function start(options: Options) { outputDir = path.resolve(Config.getBaseDir(), options[Opt.OUT_DIR].getString()); } } + + try { + // check if folder exists + fs.statSync(outputDir).isDirectory(); + } catch (e) { + // if the folder does not exist, quit early. + logger.warn('the out_dir path ' + outputDir + ' does not exist, run webdriver-manager update'); + return; + } + let chromeLogs: string = null; if (options[Opt.CHROME_LOGS].getString()) { if (path.isAbsolute(options[Opt.CHROME_LOGS].getString())) { diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts index 620a12b8..29d8f6b7 100644 --- a/lib/cmds/status.ts +++ b/lib/cmds/status.ts @@ -1,3 +1,4 @@ +import * as fs from 'fs'; import * as minimist from 'minimist'; import * as path from 'path'; @@ -37,6 +38,17 @@ function status(options: Options) { outputDir = path.resolve(Config.getBaseDir(), options[Opt.OUT_DIR].getString()); } } + + try { + // check if folder exists + fs.statSync(outputDir).isDirectory(); + } catch (e) { + // if the folder does not exist, quit early. + logger.warn('the out_dir path ' + outputDir + ' does not exist'); + return; + } + + let downloadedBinaries = FileManager.downloadedBinaries(outputDir); // log which binaries have been downloaded for (let bin in downloadedBinaries) { diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 037c2422..04168645 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -70,7 +70,13 @@ export class FileManager { * @param outputDir The directory where binaries are saved * @returns A list of existing files. */ - static getExistngFiles(outputDir: string): string[] { return fs.readdirSync(outputDir); } + static getExistngFiles(outputDir: string): string[] { + try { + return fs.readdirSync(outputDir); + } catch (e) { + return []; + } + } /** * For the binary, operating system, and system architecture, look through From 4bb0116f9c760dc904b518eb150e8a1beae52ab5 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 26 Apr 2016 16:17:59 -0700 Subject: [PATCH 29/79] chore(release): bump version for release (#20) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 747c6588..c91e4b95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "0.1.0", + "version": "10.0.0", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From cfe7bc4eafcf1e90390884cf4cd27a2ea3c878b7 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 26 Apr 2016 16:21:57 -0700 Subject: [PATCH 30/79] chore(docs): add contributing file (#21) closes #19 --- CONTRIBUTING.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..d50963ad --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +Contributing +============ + +Questions +--------- + +Please ask support questions on [StackOverflow](http://stackoverflow.com/questions/tagged/webdriver-manager) or [Gitter](https://gitter.im/angular/webdriver-manager). + +Any questions posted to webdriver-manager's Github Issues will be closed with this note: + +Please direct general support questions like this one to an appropriate support channel, see https://github.com/angular/webdriver-manager/blob/master/CONTRIBUTING.md#questions. Thank you! + +Issues +====== + +If you have a bug or feature request, please file an issue. When submitting an issue, please include a reproducible case that we can actually run. + +Please format code and markup in your issue using [github markdown](https://help.github.com/articles/github-flavored-markdown). + + +Contributing to Source Code (Pull Requests) +=========================================== + +Loosely, follow the [Angular contribution rules](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md). + + * If your PR changes any behavior or fixes an issue, it should have an associated test. + * New features should be general and as simple as possible. + * Breaking changes should be avoided if possible. + * All pull requests require review. No PR will be submitted without a comment from a team member stating LGTM (Looks good to me). + +Webdriver-manager specific rules +------------------------- + + * JavaScript style should generally follow the [Google JS style guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml). + * Wrap code at 100 chars. + * Document public methods with jsdoc. + * Be consistent with the code around you! + +Commit Messages +--------------- + +Please write meaningful commit messages - they are used to generate the changelog, so the commit message should tell a user everything they need to know about a commit. Webdriver-manager follows AngularJS's [commit message format](https://docs.google.com/a/google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.z8a3t6ehl060). + +In summary, this style is + + (): + + + +Where `` is one of [feat, fix, docs, refactor, test, chore, deps] and +`` is a quick descriptor of the location of the change, such as cli, clientSideScripts, element. + +Testing your changes +-------------------- + +Test your changes on your machine by running `npm test` to run the test suite. + +When you submit a PR, tests will also be run on the Continuous Integration environment +through Travis. If your tests fail on Travis, take a look at the logs - if the failures +are known flakes in Internet Explorer or Safari you can ignore them, but otherwise +Travis should pass. From 03ff37b1f94a896511edd7e144ad7cd339ee1f2f Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 26 Apr 2016 16:31:06 -0700 Subject: [PATCH 31/79] chore(package): fix main in package.json and release v10.0.0! (#22) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c91e4b95..a8d91633 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "bin": { "webdriver-manager": "bin/webdriver-manager" }, - "main": "bin/webdriver-manager", + "main": "built/lib/webdriver.js", "author": "Craig Nishina ", "license": "MIT", "dependencies": { From a6f1edd782251c96d35e79a3bb78b70c2b137aa9 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 26 Apr 2016 16:44:26 -0700 Subject: [PATCH 32/79] fix(global): fix finding config.json for global installs and release 10.0.1 (#23) --- lib/config.ts | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index 86f5ee37..18f3fac4 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -47,7 +47,7 @@ export class Config { // global version else { - return path.resolve(Config.dir, '../built', jsonFile); + return path.resolve(Config.dir, '..', jsonFile); } } diff --git a/package.json b/package.json index a8d91633..c7471ce7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.0.0", + "version": "10.0.1", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From 9248ed8926d69dc1e8afabc1b198292ac7380019 Mon Sep 17 00:00:00 2001 From: Craig Date: Wed, 27 Apr 2016 14:12:07 -0700 Subject: [PATCH 33/79] chore(config): fix when installed as dependency (#26) closes #24 and closes #25 --- bin/webdriver-manager | 24 ++++++++++++++++++------ lib/config.ts | 31 ++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/bin/webdriver-manager b/bin/webdriver-manager index 103567a4..fcf50b13 100755 --- a/bin/webdriver-manager +++ b/bin/webdriver-manager @@ -12,6 +12,21 @@ var folder = cwd.replace(parentPath, '').substring(1); var isProjectVersion = folder === nodeModuleName; var isLocalVersion = false; + +var printCyan, printMagenta, printGreen; +try { + var chalk = require('chalk'); + printMagenta = chalk.magenta; + printCyan = chalk.cyan; + printGreen = chalk.green; +} +catch (e) { + var logThis = function(message) { + return message; + } + printMagenta = pringCyan = printGreen = logThis; +} + try { isLocalVersion = fs.statSync(localInstall).isDirectory(); } catch(e) { @@ -19,24 +34,21 @@ try { // project version if (folder === nodeModuleName) { - var chalk = require('chalk'); - console.log(nodeModuleName + ': using ' + chalk.magenta('project version ' + + console.log(nodeModuleName + ': using ' + printMagenta('project version ' + require(path.resolve('package.json')).version)); require(path.resolve('built/lib/webdriver')); } // local version else if (isLocalVersion) { - var chalk = require(path.resolve(localInstall, 'node_modules/chalk/index.js')); - console.log(nodeModuleName + ': using ' + chalk.cyan('local installed version ' + + console.log(nodeModuleName + ': using ' + printCyan('local installed version ' + require(path.resolve(localInstall, 'package.json')).version)); require(path.resolve(localInstall, 'built/lib/webdriver')); } // global version else { - var chalk = require(path.resolve(dir, '../node_modules/chalk/index.js')); - console.log(nodeModuleName + ': using ' + chalk.green('global installed version ' + + console.log(nodeModuleName + ': using ' + printGreen('global installed version ' + require(path.resolve(dir, '../package.json')).version)); require(path.resolve(dir, '../built/lib/webdriver')); } diff --git a/lib/config.ts b/lib/config.ts index 18f3fac4..72aa4cc0 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -28,8 +28,7 @@ export class Config { static isProjectVersion = Config.folder === Config.nodeModuleName; static isLocalVersion = false; - - static getFile(jsonFile: string): string { + static getFile_(jsonFile: string): string { try { Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); } catch(e) { @@ -51,7 +50,21 @@ export class Config { } } - static getFolder(folder: string): string { + static getConfigFile_(): string { + try { + Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); + return path.resolve(Config.localInstall, '../..', Config.configFile); + + } catch(e) { + return Config.getFile_(Config.configFile); + } + } + + static getPackageFile_(): string { + return Config.getFile_(Config.packageFile) + } + + static getFolder_(folder: string): string { try { Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); } catch(e) { @@ -64,7 +77,7 @@ export class Config { // local version else if (Config.isLocalVersion) { - return path.resolve(Config.localInstall, folder); + return path.resolve(Config.localInstall, '../..', folder); } // global version @@ -74,10 +87,10 @@ export class Config { } static getSeleniumDir(): string { - return Config.getFolder('selenium/'); + return Config.getFolder_('selenium/'); } static getBaseDir(): string { - return Config.getFolder('/'); + return Config.getFolder_('/'); } /** @@ -85,7 +98,7 @@ export class Config { * @returns A map of the versions defined in the configuration file. */ static binaryVersions(): ConfigFile { - let configFile = require(Config.getFile(Config.configFile)); + let configFile = require(Config.getConfigFile_()); let configVersions: ConfigFile = {}; configVersions.selenium = configFile.webdriverVersions.selenium; configVersions.chrome = configFile.webdriverVersions.chromedriver; @@ -98,7 +111,7 @@ export class Config { * @returns A map of the CDN versions defined in the configuration file. */ static cdnUrls(): ConfigFile { - let configFile = require(Config.getFile(Config.configFile)); + let configFile = require(Config.getConfigFile_()); let configCdnUrls: ConfigFile = {}; configCdnUrls.selenium = configFile.cdnUrls.selenium; configCdnUrls.chrome = configFile.cdnUrls.chromedriver; @@ -110,7 +123,7 @@ export class Config { * Get the package version. */ static getVersion(): string { - let packageFile = require(Config.getFile(Config.packageFile)); + let packageFile = require(Config.getPackageFile_()); return packageFile.version; } } From 38eaefae709affe85254119a61e041948982ec0b Mon Sep 17 00:00:00 2001 From: Craig Date: Wed, 27 Apr 2016 14:29:36 -0700 Subject: [PATCH 34/79] chore(config): use the correct iedriver version (#27) --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 1eac9b51..84f602b5 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "webdriverVersions": { "selenium": "2.53.0", "chromedriver": "2.21", - "iedriver": "2.52.1" + "iedriver": "2.53.1" }, "cdnUrls": { "selenium": "https://selenium-release.storage.googleapis.com/", From 0cfcc88f1383c400f72ea5e49f9600ff652f8214 Mon Sep 17 00:00:00 2001 From: Michael Giambalvo Date: Mon, 9 May 2016 13:43:41 -0700 Subject: [PATCH 35/79] fix(binary): Fix typo in fallback case when chalk isn't available. --- bin/webdriver-manager | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bin/webdriver-manager b/bin/webdriver-manager index fcf50b13..c1600c3f 100755 --- a/bin/webdriver-manager +++ b/bin/webdriver-manager @@ -13,7 +13,12 @@ var folder = cwd.replace(parentPath, '').substring(1); var isProjectVersion = folder === nodeModuleName; var isLocalVersion = false; -var printCyan, printMagenta, printGreen; +var printCyan, printMagent, printGreen; + +printCyan = printMagenta = printGreen = function(message) { + return message; +} + try { var chalk = require('chalk'); printMagenta = chalk.magenta; @@ -21,10 +26,7 @@ try { printGreen = chalk.green; } catch (e) { - var logThis = function(message) { - return message; - } - printMagenta = pringCyan = printGreen = logThis; + console.log("Error loading chalk, output will be boring."); } try { From 5bca0266118dcabf2e2782820e5c9095f6d16ed4 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 16 May 2016 16:14:02 -0700 Subject: [PATCH 36/79] fix(config): configuration file local look up when used as a dependency (#33) closes #32 --- lib/config.ts | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index 72aa4cc0..a83f491f 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,6 +1,10 @@ import * as fs from 'fs'; import * as path from 'path'; +import {Logger} from './cli'; + + +let logger = new Logger('config'); /** * Dictionary map of the different binaries. */ @@ -20,7 +24,7 @@ export class Config { static cwd = process.cwd(); static nodeModuleName = 'webdriver-manager'; - static localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); + static localInstall: string; static parentPath = path.resolve(Config.cwd, '..'); static dir = __dirname; static folder = Config.cwd.replace(Config.parentPath, '').substring(1); @@ -30,6 +34,7 @@ export class Config { static getFile_(jsonFile: string): string { try { + Config.localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); } catch(e) { } @@ -52,12 +57,21 @@ export class Config { static getConfigFile_(): string { try { + Config.localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); - return path.resolve(Config.localInstall, '../..', Config.configFile); - - } catch(e) { - return Config.getFile_(Config.configFile); + let pathConfig = path.resolve(Config.localInstall, '../..', Config.configFile); + fs.statSync(pathConfig).isFile(); + return pathConfig; + } catch (e) { + try { + fs.statSync(path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName, Config.configFile)).isFile(); + return Config.getFile_(path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName, Config.configFile)); + } catch (e1) { + logger.error('nothing to return for a config file'); + return null; + } } + } static getPackageFile_(): string { @@ -66,6 +80,7 @@ export class Config { static getFolder_(folder: string): string { try { + Config.localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); } catch(e) { } @@ -77,6 +92,7 @@ export class Config { // local version else if (Config.isLocalVersion) { + Config.localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); return path.resolve(Config.localInstall, '../..', folder); } From a5b9fe04c59e3aed1212b3718016e99404cd4070 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Tue, 24 May 2016 10:25:56 -0700 Subject: [PATCH 37/79] chore(release): version bump to 10.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c7471ce7..cf2c61b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.0.1", + "version": "10.0.2", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From d3724fbd9f6b0ceb481538f7f8f0088c8b004959 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 7 Jun 2016 18:50:50 -0700 Subject: [PATCH 38/79] fix(config): simplify locating configuration file, selenium folder (#41) * Let the bin file decide which webdriver-manager to use * Use the configuration file / package from the default position * Selenium folder will always be located to webdriver-manager/selenium/ --- lib/config.ts | 83 ++++++++------------------------------------------- 1 file changed, 12 insertions(+), 71 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index a83f491f..390a8603 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -5,9 +5,7 @@ import {Logger} from './cli'; let logger = new Logger('config'); -/** - * Dictionary map of the different binaries. - */ + export interface ConfigFile { selenium?: string; chrome?: string; @@ -15,15 +13,20 @@ export interface ConfigFile { } /** - * The configuration for webdriver-manager. + * The configuration for webdriver-manager + * + * The config.json, package.json, and selenium directory are found in the + * same location at the root directory in webdriver-manager. + * */ export class Config { static configFile: string = 'config.json'; static packageFile: string = 'package.json'; + static nodeModuleName = 'webdriver-manager'; static cwd = process.cwd(); - static nodeModuleName = 'webdriver-manager'; + static localInstall: string; static parentPath = path.resolve(Config.cwd, '..'); static dir = __dirname; @@ -32,81 +35,19 @@ export class Config { static isProjectVersion = Config.folder === Config.nodeModuleName; static isLocalVersion = false; - static getFile_(jsonFile: string): string { - try { - Config.localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); - Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); - } catch(e) { - } - - // project version - if (Config.folder === Config.nodeModuleName) { - return path.resolve('built', jsonFile); - } - - // local version - else if (Config.isLocalVersion) { - return path.resolve(Config.localInstall, 'built', jsonFile); - } - - // global version - else { - return path.resolve(Config.dir, '..', jsonFile); - } - } - static getConfigFile_(): string { - try { - Config.localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); - Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); - let pathConfig = path.resolve(Config.localInstall, '../..', Config.configFile); - fs.statSync(pathConfig).isFile(); - return pathConfig; - } catch (e) { - try { - fs.statSync(path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName, Config.configFile)).isFile(); - return Config.getFile_(path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName, Config.configFile)); - } catch (e1) { - logger.error('nothing to return for a config file'); - return null; - } - } - + return path.resolve(Config.dir, '..', Config.configFile); } static getPackageFile_(): string { - return Config.getFile_(Config.packageFile) - } - - static getFolder_(folder: string): string { - try { - Config.localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); - Config.isLocalVersion = fs.statSync(Config.localInstall).isDirectory(); - } catch(e) { - } - - // project version - if (Config.folder === Config.nodeModuleName) { - return path.resolve(folder); - } - - // local version - else if (Config.isLocalVersion) { - Config.localInstall = path.resolve(Config.cwd, 'node_modules', Config.nodeModuleName); - return path.resolve(Config.localInstall, '../..', folder); - } - - // global version - else { - return path.resolve(Config.dir, folder); - } + return path.resolve(Config.dir, '..', Config.packageFile) } static getSeleniumDir(): string { - return Config.getFolder_('selenium/'); + return path.resolve(Config.dir, '..', 'selenium/'); } static getBaseDir(): string { - return Config.getFolder_('/'); + return path.resolve(Config.dir, '..'); } /** From 3460ceb0b91268dd54b5564a798deb17c8bf102e Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Tue, 7 Jun 2016 18:59:21 -0700 Subject: [PATCH 39/79] chore(release): version bump to 10.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf2c61b2..0c10289a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.0.2", + "version": "10.0.3", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From 970167a1b2db24fc8ca34db2994507ef0187ee7e Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 7 Jun 2016 23:31:33 -0700 Subject: [PATCH 40/79] dep(typings): update typings (#42) --- package.json | 2 +- spec/files/downloader_spec.ts | 2 +- spec/files/fileManager_spec.ts | 2 +- spec/webdriver_spec.ts | 2 +- tsconfig.json | 4 +--- typings.json | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 0c10289a..91be1226 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,6 @@ "jasmine": "^2.4.1", "run-sequence": "^1.1.5", "typescript": "^1.8.7", - "typings": "^0.7.12" + "typings": "^1.0.4" } } diff --git a/spec/files/downloader_spec.ts b/spec/files/downloader_spec.ts index 8d74c525..5221d122 100644 --- a/spec/files/downloader_spec.ts +++ b/spec/files/downloader_spec.ts @@ -1,4 +1,4 @@ -/// +/// import {Downloader} from '../../lib/files'; diff --git a/spec/files/fileManager_spec.ts b/spec/files/fileManager_spec.ts index 9201a7b8..6e1696d6 100644 --- a/spec/files/fileManager_spec.ts +++ b/spec/files/fileManager_spec.ts @@ -1,4 +1,4 @@ -/// +/// import * as fs from 'fs'; import * as path from 'path'; diff --git a/spec/webdriver_spec.ts b/spec/webdriver_spec.ts index fbfb19e3..60cc2851 100644 --- a/spec/webdriver_spec.ts +++ b/spec/webdriver_spec.ts @@ -1,4 +1,4 @@ -/// +/// import * as child_process from 'child_process'; import * as path from 'path'; diff --git a/tsconfig.json b/tsconfig.json index cb334d95..d45f25c9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,8 +12,6 @@ "exclude": [ "built", "node_modules", - "typings/browser", - "typings/browser.d.ts", - "typings/main" + "typings/globals" ] } diff --git a/typings.json b/typings.json index ba9cecb5..00f0e570 100644 --- a/typings.json +++ b/typings.json @@ -1,5 +1,5 @@ { - "ambientDependencies": { + "globalDependencies": { "adm-zip": "registry:dt/adm-zip#0.0.0+20160317120654", "chalk": "registry:dt/chalk#0.4.0+20160317120654", "form-data": "registry:dt/form-data#0.0.0+20160316155526", From 5073e230574237047dd593a702f08f84907871bd Mon Sep 17 00:00:00 2001 From: Craig Date: Wed, 8 Jun 2016 10:12:51 -0700 Subject: [PATCH 41/79] chore(folder): fix selenium folder location (#43) --- lib/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index 390a8603..977cfd1e 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -44,10 +44,10 @@ export class Config { } static getSeleniumDir(): string { - return path.resolve(Config.dir, '..', 'selenium/'); + return path.resolve(Config.dir, '..', '..', 'selenium/'); } static getBaseDir(): string { - return path.resolve(Config.dir, '..'); + return path.resolve(Config.dir, '..', '..'); } /** From 134e66d88e4c31c8c1588e902fe98f20f2457a23 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Wed, 8 Jun 2016 10:16:44 -0700 Subject: [PATCH 42/79] chore(release): version bump to 10.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91be1226..5a8f6bd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.0.3", + "version": "10.0.4", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From 4e15c3d512f39ae931c2dd437ebe15458ea913d8 Mon Sep 17 00:00:00 2001 From: Craig Date: Wed, 8 Jun 2016 18:53:30 -0700 Subject: [PATCH 43/79] chore(release): add changelog and release.md (#44) --- CHANGELOG.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ release.md | 37 ++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 release.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d479f6f5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,96 @@ +# 10.0.4 + +## Dependency Upgrades + +- ([970167a](https://github.com/angular/webdriver-manager/commit/970167a1b2db24fc8ca34db2994507ef0187ee7e)) + dep(typings): update typings (#42) + +## Bug Fixes + +- ([5073e23](https://github.com/angular/webdriver-manager/commit/5073e230574237047dd593a702f08f84907871bd)) + fix(folder): fix selenium folder location (#43) + +# 10.0.3 + +## Bug Fixes + +- ([d3724fb](https://github.com/angular/webdriver-manager/commit/d3724fbd9f6b0ceb481538f7f8f0088c8b004959)) + fix(config): simplify locating configuration file, selenium folder (#41) + + * Let the bin file decide which webdriver-manager to use + * Use the configuration file / package from the default position + * Selenium folder will always be located to webdriver-manager/selenium/ + +# 10.0.2 + +## Bug Fixes + +- ([5bca026](https://github.com/angular/webdriver-manager/commit/5bca0266118dcabf2e2782820e5c9095f6d16ed4)) + fix(config): configuration file local look up when used as a dependency (#33) + + closes #32 +- ([0cfcc88](https://github.com/angular/webdriver-manager/commit/0cfcc88f1383c400f72ea5e49f9600ff652f8214)) + fix(binary): Fix typo in fallback case when chalk isn't available. + + +# 10.0.1 + +## Bug Fixes + +- ([a6f1edd](https://github.com/angular/webdriver-manager/commit/a6f1edd782251c96d35e79a3bb78b70c2b137aa9)) + fix(global): fix finding config.json for global installs and release 10.0.1 (#23) + +# 10.0.0 + + +## Bug Fixes + +- ([70d32df](https://github.com/angular/webdriver-manager/commit/70d32df659f19510c25e97ea9a42c7f93813d448)) + fix(dir): check selenium dir and warn user that the folder does not exist (#17) + +- ([0ec1443](https://github.com/angular/webdriver-manager/commit/0ec14435379161259435edc7c766388941f1a846)) + fix(binary): file type, unzipping, and permissions + + closes #7, #16 + +- ([a073fd0](https://github.com/angular/webdriver-manager/commit/a073fd0e9d0290e52ac3a808643b069c71b196c3)) + fix(npm): use global, local, and project without env + + closes #20 + + +- ([6ccb9d8](https://github.com/angular/webdriver-manager/commit/6ccb9d8b9ac6daf79388c44e6d53f1d3d71fd3f8)) + fix(versions): versions option should stay consistent with existing webdriver-manager + + closes #6 + +- ([c34b05c](https://github.com/angular/webdriver-manager/commit/c34b05cc66849708a2fc515bc455a6a661c867d6)) + fix(bin): local, project, and global usage + +- ([4a0caf5](https://github.com/angular/webdriver-manager/commit/4a0caf5a69cacda01df87d4b4cc35092e519d267)) + fix(dep): fix dependency for chalk + +- ([15ae0e8](https://github.com/angular/webdriver-manager/commit/15ae0e815270c8af2441002492e3165edd3140df)) + fix(chmod): set permissions to 755 + +([1820fbc](https://github.com/angular/webdriver-manager/commit/1820fbc46ddc45b70911fb1678f1d99247ec7028)) + Initial commit + +## Features + +- ([374c3e7](https://github.com/angular/webdriver-manager/commit/374c3e719fce18a2f0a1b751b19bffb7d266cc69)) + feat(length): on update, check to see the file is the correct length + + closes #8 +- ([8c47291](https://github.com/angular/webdriver-manager/commit/8c472918ac73390890bbc39fcc4c7a2e86d3b262)) + feat(local): use the local version of webdriver-tool if it is installed + + closes #5 + +- ([f0622d2](https://github.com/angular/webdriver-manager/commit/f0622d2e173b68e4afcd409f9c0356c8a1c2652a)) + feat(logs): add chrome logs command line option + + + closes #11 +- ([3b30312](https://github.com/angular/webdriver-manager/commit/3b303129040b17292028452f13c73d62736f1216)) + feat(logger): update logging methods (#5) diff --git a/release.md b/release.md new file mode 100644 index 00000000..89e4b8b8 --- /dev/null +++ b/release.md @@ -0,0 +1,37 @@ +Webdriver Manager Release Checklist +----------------------------------- +Say the previous release was 0.0.J, the current release is 0.0.K, and the next release will be 0.0.L. + +- Make sure [Travis](https://travis-ci.org/angular/webdriver-manager/builds) is passing. + +- Make sure .gitignore and .npmignore are updated with any new files that need to be ignored. + +- Update package.json with a version bump. If the changes are only bug fixes, increment the patch (e.g. 0.0.5 -> 0.0.6), otherwise increment the minor version. + +- Update CHANGELOG.md + + - You can get a list of changes in the correct format by running + + ``` + git log 0.0.J..HEAD --format="- ([%h](https://github.com/angular/webdriver-manager/commit/%H)) %n%w(100,2,2)%B" > /tmp/changes.txt + ``` + - Create a new section in CHANGELOG.md and copy in features (`feat`), big dependency version updates (`deps`), bug fixes (`fix`), and breaking changes. No need to note chores or stylistic changes - the changelog should be primarily useful to someone using Protractor, not developing on it. + + - Breaking changes should be in their own section and include before/after examples of how to fix code that needs to change. + + - Make a commit with the API and package.json changes titled chore(release): version bump and changelog for 0.0.K. + + - Tag the release with `git tag 0.0.K` + + - Push to github + + - Push tags to github (`git push --tags`) + + - Verify that the changelog and tags look sane on github + + - NPM publish + + - Let people know + - Have @ProtractorTest tweet about it + + - Close the 0.0.K milestone From d9372459c51a1aec553a79edaa32e497608a65de Mon Sep 17 00:00:00 2001 From: Sammy Jelin Date: Mon, 16 May 2016 18:02:40 -0700 Subject: [PATCH 44/79] feat(android): support android --- README.md | 5 + config.json | 7 +- lib/binaries/android_sdk.ts | 71 ++++++++++ lib/binaries/binary.ts | 15 +++ lib/binaries/index.ts | 1 + lib/cmds/initialize.ts | 232 +++++++++++++++++++++++++++++++++ lib/cmds/opts.ts | 21 ++- lib/cmds/start.ts | 81 +++++++++++- lib/cmds/update.ts | 85 ++++++++++-- lib/config.ts | 3 + lib/files/downloader.ts | 4 +- lib/files/file_manager.ts | 12 +- mobile.md | 46 +++++++ package.json | 6 +- spec/files/fileManager_spec.ts | 45 ++++++- typings.json | 6 +- 16 files changed, 610 insertions(+), 30 deletions(-) create mode 100644 lib/binaries/android_sdk.ts create mode 100644 lib/cmds/initialize.ts create mode 100644 mobile.md diff --git a/README.md b/README.md index 38447d71..c895e084 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,8 @@ Clear out the server and driver files. If `webdriver-manager start` does not wor ``` webdriver-manager clean ``` + +Mobile Support +-------------- + +See [`mobile.md`](mobile.md). diff --git a/config.json b/config.json index 84f602b5..0b175b1b 100644 --- a/config.json +++ b/config.json @@ -2,12 +2,13 @@ "webdriverVersions": { "selenium": "2.53.0", "chromedriver": "2.21", - "iedriver": "2.53.1" + "iedriver": "2.53.1", + "androidsdk": "24.4.1" }, "cdnUrls": { "selenium": "https://selenium-release.storage.googleapis.com/", "chromedriver": "https://chromedriver.storage.googleapis.com/", - "iedriver": "https://selenium-release.storage.googleapis.com/" + "iedriver": "https://selenium-release.storage.googleapis.com/", + "androidsdk": "http://dl.google.com/android/" } - } diff --git a/lib/binaries/android_sdk.ts b/lib/binaries/android_sdk.ts new file mode 100644 index 00000000..df82ad9f --- /dev/null +++ b/lib/binaries/android_sdk.ts @@ -0,0 +1,71 @@ +import * as child_process from 'child_process'; +import * as rimraf from 'rimraf'; +import * as path from 'path'; +import {arch, type} from 'os'; + +import {Binary, OS} from './binary'; +import {Config} from '../config'; + +/** + * The android sdk binary. + */ +export class AndroidSDK extends Binary { + static os = [OS.Windows_NT, OS.Linux, OS.Darwin]; + static id = 'android'; + static versionDefault = Config.binaryVersions().android; + static isDefault = false; + static shortName = ['android']; + static DEFAULT_API_LEVELS = '24'; + static DEFAULT_ABIS = 'x86_64'; + + constructor() { + super(); + this.name = 'android-sdk'; + this.versionCustom = AndroidSDK.versionDefault; + this.prefixDefault = 'android-sdk_r'; + this.suffixDefault = '.zip'; + this.cdn = Config.cdnUrls().android; + } + + id(): string { return AndroidSDK.id; } + + versionDefault(): string { return AndroidSDK.versionDefault; } + + suffix(ostype: string): string { + if (ostype === 'Darwin') { + return '-macosx' + this.suffixDefault; + } else if (ostype === 'Linux') { + return '-linux.tgz'; + } else if (ostype === 'Windows_NT') { + return '-windows' + this.suffixDefault; + } + } + + url(ostype: string): string { + return this.cdn + this.filename(ostype); + } + + zipContentName(ostype: string): string { + if (ostype === 'Darwin') { + return this.name + '-macosx'; + } else if (ostype === 'Linux') { + return this.name + '-linux'; + } else if (ostype === 'Windows_NT') { + return this.name + '-windows'; + } + } + + executableSuffix(): string { return ''; } + + remove(sdkPath: string): void { + try { + let avds = require(path.join(sdkPath, 'available_avds.json')); + let version = path.basename(sdkPath).slice(this.prefixDefault.length); + avds.forEach((avd: string) => { + child_process.spawnSync(path.join(sdkPath, 'tools', 'android'), ['delete', + 'avd', '-n', avd + '-v' + version + '-wd-manager']); + }); + } catch (e) {} + rimraf.sync(sdkPath); + } +} diff --git a/lib/binaries/binary.ts b/lib/binaries/binary.ts index 5445fdf4..5d3e5d94 100644 --- a/lib/binaries/binary.ts +++ b/lib/binaries/binary.ts @@ -1,3 +1,5 @@ +import * as fs from 'fs'; + /** * operating system enum */ @@ -57,6 +59,14 @@ export class Binary { filename(ostype?: string, arch?: string): string { return this.prefix() + this.version() + this.suffix(ostype, arch); } + /** + * @param ostype The operating system. + * @returns The file name for the file inside the downloaded zip file + */ + zipContentName(ostype: string): string { + return this.name + this.executableSuffix(ostype); + } + shortVersion(version: string): string { return version.slice(0, version.lastIndexOf('.')); } /** @@ -73,4 +83,9 @@ export class Binary { * A base class method that should be overridden. */ url(ostype?: string, arch?: string): string { return 'not implemented'; } + + /** + * Delete an instance of this binary from the file system + */ + remove(filename: string): void { fs.unlinkSync(filename); } } diff --git a/lib/binaries/index.ts b/lib/binaries/index.ts index 1d45e1c6..2632bda3 100644 --- a/lib/binaries/index.ts +++ b/lib/binaries/index.ts @@ -1,4 +1,5 @@ export * from './binary'; export * from './chrome_driver'; export * from './ie_driver'; +export * from './android_sdk'; export * from './stand_alone'; diff --git a/lib/cmds/initialize.ts b/lib/cmds/initialize.ts new file mode 100644 index 00000000..9bafa1a8 --- /dev/null +++ b/lib/cmds/initialize.ts @@ -0,0 +1,232 @@ +import * as os from 'os'; +import * as path from 'path'; +import * as q from 'q'; +import * as glob from 'glob'; +import * as ini from 'ini'; +import * as fs from 'fs'; +import * as child_process from 'child_process'; +import {Logger} from '../cli'; + +const noop = () => {}; + +// Make a function which configures a child process to automatically respond +// to a certain question +function respondFactory(question: string, answer: string): Function { + return (child: child_process.ChildProcess) => { + (child.stdin).setDefaultEncoding('utf-8'); + child.stdout.on('data', (data: Buffer|String) => { + if (data != null) { + if (data.toString().indexOf(question) != -1) { + child.stdin.write(answer + '\n'); + } + } + }); + }; +} + +// Run a command on the android SDK +function runAndroidSDKCommand(sdkPath: string, cmd: string, args: string[], + spawnOptions: Object, config_fun?: Function): q.Promise { + + let child = child_process.spawn(path.join(sdkPath, 'tools', 'android'), + [cmd].concat(args), spawnOptions); + + if(config_fun) { + config_fun(child); + }; + + let deferred = q.defer() + child.on('exit', (code: number) => { + if (deferred != null) { + if (code) { + deferred.reject(code); + } else { + deferred.resolve(); + } + deferred = null; + } + }); + child.on('error', (err: Error) => { + if (deferred != null) { + deferred.reject(err); + deferred = null; + } + }); + return deferred.promise; +} + +// Download updates via the android SDK +function downloadAndroidUpdates(sdkPath: string, targets: string[], + search_all: boolean, auto_accept: boolean): q.Promise { + + return runAndroidSDKCommand(sdkPath, 'update', ['sdk', '-u'].concat(search_all + ? ['-a'] : []).concat(['-t', targets.join(',')]), + {stdio: auto_accept ? 'pipe' : 'inherit'}, + auto_accept ? respondFactory('Do you accept the license', 'y') : noop); +} + +// Setup hardware acceleration for x86-64 emulation +function setupHardwareAcceleration(sdkPath: string) { + //TODO(sjelin): check that the BIOS option is set properly on linux + if (os.type() == 'Darwin') { + console.log('Enabling hardware acceleration (requires root access)'); + child_process.spawnSync('sudo', [path.join(sdkPath, 'extras', 'intel', + 'Hardware_Accelerated_Execution_Manager', 'silent_install.sh')], + {stdio: 'inherit'}); + } else if (os.type() == 'Windows_NT') { + console.log('Enabling hardware acceleration (requires admin access)'); + child_process.spawnSync('runas', ['/noprofile', '/user:Administrator', + path.join(sdkPath, 'extras', 'intel', + 'Hardware_Accelerated_Execution_Manager', 'silent_install.bat')], + {stdio: 'inherit'}); + } +} + +// Get a list of all the SDK download targets for a given set of APIs and ABIs +function getAndroidSDKTargets(apiLevels: string[], abis: string[]): string[] { + return apiLevels.map((level) => { + return 'android-' + level; + }).concat(abis.reduce((targets, abi) => { + let abiParts: string[] = abi.split('/'); + let deviceType: string = 'default'; + let architecture: string; + if (abiParts.length == 1) { + architecture = abiParts[0]; + } else { + deviceType = abiParts[0]; + architecture = abiParts[1]; + } + if (deviceType.toUpperCase() == 'DEFAULT') { + deviceType = 'android'; + } + return targets.concat(apiLevels.map((level) => { + return 'sys-img-' + architecture + '-' + deviceType + '-' + level; + })); + }, [])); +} + +// All the information about an android virtual device +class AVDDescriptor { + api: string; + deviceType: string; + architecture: string; + abi: string; + name: string; + + constructor(api: string, deviceType: string, architecture: string) { + this.api = api; + this.deviceType = deviceType; + this.architecture = architecture; + this.abi = (deviceType.toUpperCase() == 'DEFAULT' ? '' : deviceType + '/') + + architecture; + this.name = [api, deviceType, architecture].join('-'); + } + + avdName(version: string): string { + return this.name + '-v' + version + '-wd-manager'; + } +} + +// Gets the descriptors for all AVDs which are possible to make given the +// SDKs which were downloaded +function getAVDDescriptors(sdkPath: string): q.Promise { + let deferred = q.defer(); + glob(path.join(sdkPath, 'system-images', '*', '*', '*'), + (err: Error, files: string[]) => { + if (err) { + deferred.reject(err); + } else { + deferred.resolve(files.map((file: string) => { + let info = file.split(path.sep).slice(-3); + return new AVDDescriptor(info[0], info[1], info[2]); + })); + } + } + ); + return deferred.promise; +} + +function sequentialForEach(array: T[], func: (x: T) => q.Promise): + q.Promise { + + let ret = q(null); + + array.forEach((x: T) => { + ret = ret.then(() => { return func(x); }); + }); + + return ret; +} + +// Configures the hardware.ini file for a system image of a new AVD +function configureAVDHardware(sdkPath: string, desc: AVDDescriptor): + q.Promise { + let file = path.join(sdkPath, 'system-images', desc.api, desc.deviceType, + desc.architecture, 'hardware.ini'); + return q.nfcall(fs.stat, file).then((stats: fs.Stats) => { + return q.nfcall(fs.readFile, file); + }, (err: Error) => { + return q(''); + }).then((contents: string|Buffer) => { + let config: any = ini.parse(contents.toString()); + config['hw.keyboard'] = 'yes'; + config['hw.battery'] = 'yes'; + config['hw.ramSize'] = 1024; + return q.nfcall(fs.writeFile, file, ini.stringify(config)); + }); +} + +// Make an android virtual device +function makeAVD(sdkPath: string, desc: AVDDescriptor, version: string): + q.Promise { + return runAndroidSDKCommand(sdkPath, 'delete', + ['avd', '--name', desc.avdName(version)], {}) + .then(noop, noop).then(() => { + return runAndroidSDKCommand(sdkPath, 'create', + ['avd', '--name', desc.avdName(version), '--target', desc.api, '--abi', + desc.abi], {stdio: 'pipe'}, + respondFactory('Do you wish to create a custom hardware profile', 'no')); + }); +} + +// Initialize the android SDK +export function android(sdkPath: string, apiLevels: string[], abis: string[], + acceptLicenses: boolean, version: string, logger: Logger): void { + + let avdDescriptors: AVDDescriptor[]; + let tools = ['platform-tool', 'tool']; + if ((os.type() == 'Darwin') || (os.type() == 'Windows_NT')) { + tools.push('extra-intel-Hardware_Accelerated_Execution_Manager'); + } + + logger.info('android-sdk: Downloading additional SDK updates'); + downloadAndroidUpdates(sdkPath, tools, false, acceptLicenses).then(() => { + return setupHardwareAcceleration(sdkPath); + }).then(() => { + logger.info('android-sdk: Downloading more additional SDK updates ' + + '(this may take a while)'); + return downloadAndroidUpdates(sdkPath, ['build-tools-24.0.0']. + concat(getAndroidSDKTargets(apiLevels, abis)), true, acceptLicenses); + }).then(() => { + return getAVDDescriptors(sdkPath); + }).then((descriptors: AVDDescriptor[]) => { + avdDescriptors = descriptors; + logger.info('android-sdk: Configuring virtual device hardware'); + return sequentialForEach(avdDescriptors, (descriptor: AVDDescriptor) => { + return configureAVDHardware(sdkPath, descriptor); + }); + }).then(() => { + return sequentialForEach(avdDescriptors, (descriptor: AVDDescriptor) => { + logger.info('android-sdk: Setting up virtual device "' + descriptor.name + + '"'); + return makeAVD(sdkPath, descriptor, version); + }); + }).then(() => { + return q.nfcall(fs.writeFile, path.join(sdkPath, 'available_avds.json'), + JSON.stringify(avdDescriptors.map((descriptor: AVDDescriptor) => { + return descriptor.name; + }))); + }).then(() => { + logger.info('android-sdk: Initialization complete'); + }).done(); +}; diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index c2b186cb..890e7698 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -1,9 +1,11 @@ import {Config} from '../config'; import {Cli, Option, Options} from '../cli'; -import {ChromeDriver, IEDriver, StandAlone} from '../binaries'; +import {ChromeDriver, IEDriver, AndroidSDK, StandAlone} from '../binaries'; export const OUT_DIR = 'out_dir'; export const SELENIUM_PORT = 'seleniumPort'; +export const APPIUM_PORT = 'appium-port'; +export const AVD_PORT = 'avd-port'; export const IGNORE_SSL = 'ignore_ssl'; export const PROXY = 'proxy'; export const ALTERNATE_CDN = 'alternate_cdn'; @@ -11,11 +13,17 @@ export const STANDALONE = 'standalone'; export const CHROME = 'chrome'; export const IE = 'ie'; export const IE32 = 'ie32'; +export const ANDROID = 'android'; export const VERSIONS_CHROME = 'versions.chrome'; export const VERSIONS_STANDALONE = 'versions.standalone'; export const VERSIONS_IE = 'versions.ie'; +export const VERSIONS_ANDROID = 'versions.android'; export const CHROME_LOGS = 'chrome_logs'; - +export const ANDROID_API_LEVELS = 'android-api-levels'; +export const ANDROID_ABIS = 'android-abis'; +export const ANDROID_ACCEPT_LICENSES = 'android-accept-licenses'; +export const AVDS = 'avds'; +export const AVD_USE_SNAPSHOTS = 'avd-use-snapshots'; /** * The options used by the commands. @@ -23,6 +31,8 @@ export const CHROME_LOGS = 'chrome_logs'; var opts: Options = {}; opts[OUT_DIR] = new Option(OUT_DIR, 'Location to output/expect', 'string', Config.getSeleniumDir()); opts[SELENIUM_PORT] = new Option(SELENIUM_PORT, 'Optional port for the selenium standalone server', 'string'); +opts[APPIUM_PORT] = new Option(APPIUM_PORT, 'Optional port for the appium server', 'string'); +opts[AVD_PORT] = new Option(AVD_PORT, 'Optional port for android virtual devices. See mobile.md for details', 'string'); opts[IGNORE_SSL] = new Option(IGNORE_SSL, 'Ignore SSL certificates', 'boolean', false); opts[PROXY] = new Option(PROXY, 'Proxy to use for the install or update command', 'string'); opts[ALTERNATE_CDN] = new Option(ALTERNATE_CDN, 'Alternate CDN to binaries', 'string'); @@ -30,9 +40,16 @@ opts[STANDALONE] = new Option(STANDALONE, 'Install or update selenium standalone opts[CHROME] = new Option(CHROME, 'Install or update chromedriver', 'boolean', ChromeDriver.isDefault); opts[IE] = new Option(IE, 'Install or update ie driver', 'boolean', IEDriver.isDefault); opts[IE32] = new Option(IE32, 'Install or update 32-bit ie driver', 'boolean', IEDriver.isDefault); +opts[ANDROID] = new Option(ANDROID, 'Update/use the android sdk', 'boolean', AndroidSDK.isDefault); opts[VERSIONS_CHROME] = new Option(VERSIONS_CHROME, 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); +opts[VERSIONS_ANDROID] = new Option(VERSIONS_ANDROID, 'Optional android sdk version', 'string', AndroidSDK.versionDefault); opts[VERSIONS_STANDALONE] = new Option(VERSIONS_STANDALONE, 'Optional seleniuim standalone server version', 'string', StandAlone.versionDefault); opts[VERSIONS_IE] = new Option(VERSIONS_IE, 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); opts[CHROME_LOGS] = new Option(CHROME_LOGS, 'File path to chrome logs', 'string', undefined); +opts[ANDROID_API_LEVELS] = new Option(ANDROID_API_LEVELS, 'Which versions of the android API you want to emulate', 'string', AndroidSDK.DEFAULT_API_LEVELS); +opts[ANDROID_ABIS] = new Option(ANDROID_ABIS, 'Which ABIs you want to use in android emulation', 'string', AndroidSDK.DEFAULT_ABIS); +opts[ANDROID_ACCEPT_LICENSES] = new Option(ANDROID_ACCEPT_LICENSES, 'Automatically accept android licenses', 'boolean', false); +opts[AVDS] = new Option(AVDS, 'Android virtual devices to emulate. Use "all" for emulating all possible devices, and "none" for no devices', 'string', 'all'); +opts[AVD_USE_SNAPSHOTS] = new Option(AVD_USE_SNAPSHOTS, 'Rather than booting a new AVD every time, save/load snapshots of the last time it was used', 'boolean', true); export var Opts = opts; diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index ad333d03..9523f7ef 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -10,7 +10,7 @@ import * as Opt from './'; import {Config} from '../config'; import {FileManager} from '../files'; import {Logger, Options, Program} from '../cli'; -import {BinaryMap, ChromeDriver, IEDriver, StandAlone} from '../binaries'; +import {BinaryMap, Binary, ChromeDriver, IEDriver, AndroidSDK, StandAlone} from '../binaries'; let logger = new Logger('start'); let prog = new Program() @@ -18,9 +18,15 @@ let prog = new Program() .action(start) .addOption(Opts[Opt.OUT_DIR]) .addOption(Opts[Opt.SELENIUM_PORT]) + .addOption(Opts[Opt.APPIUM_PORT]) + .addOption(Opts[Opt.AVD_PORT]) .addOption(Opts[Opt.VERSIONS_STANDALONE]) .addOption(Opts[Opt.VERSIONS_CHROME]) - .addOption(Opts[Opt.CHROME_LOGS]); + .addOption(Opts[Opt.VERSIONS_ANDROID]) + .addOption(Opts[Opt.CHROME_LOGS]) + .addOption(Opts[Opt.ANDROID]) + .addOption(Opts[Opt.AVDS]) + .addOption(Opts[Opt.AVD_USE_SNAPSHOTS]); if (os.type() === 'Windows_NT') { prog.addOption(Opts[Opt.VERSIONS_IE]); @@ -75,6 +81,7 @@ function start(options: Options) { if (options[Opt.VERSIONS_IE]) { binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString(); } + binaries[AndroidSDK.id].versionCustom = options[Opt.VERSIONS_ANDROID].getString(); let downloadedBinaries = FileManager.downloadedBinaries(outputDir); if (downloadedBinaries[StandAlone.id] == null) { @@ -100,6 +107,16 @@ function start(options: Options) { '-Dwebdriver.ie.driver=' + path.join(outputDir, binaries[IEDriver.id].executableFilename(osType))); } + if (options[Opt.ANDROID].getBoolean()) { + if (downloadedBinaries[AndroidSDK.id] != null) { + let avds = options[Opt.AVDS].getString(); + startAndroid(outputDir, binaries[AndroidSDK.id], avds.split(','), + options[Opt.AVD_USE_SNAPSHOTS].getBoolean(), + options[Opt.APPIUM_PORT].getString()); + startAppium(outputDir, options[Opt.APPIUM_PORT].getString()); + } else { + } + } // log the command to launch selenium server let argsToString = ''; @@ -112,12 +129,16 @@ function start(options: Options) { logger.info('seleniumProcess.pid: ' + seleniumProcess.pid); seleniumProcess.on('exit', (code: number) => { logger.info('Selenium Standalone has exited with code ' + code); + killAndroid(); + killAppium(); process.exit(code); }); process.stdin.resume(); process.stdin.on('data', (chunk: Buffer) => { logger.info('Attempting to shut down selenium nicely'); http.get('http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer'); + killAndroid(); + killAppium(); }); process.on('SIGINT', () => { logger.info('Staying alive until the Selenium Standalone process exits'); @@ -132,3 +153,59 @@ function spawnCommand(command: string, args?: string[]) { return childProcess.spawn(winCommand, finalArgs, {stdio: 'inherit'}); } + +// Manage processes used in android emulation +let androidProcesses: childProcess.ChildProcess[] = []; + +function startAndroid(outputDir: string, sdk: Binary, avds: string[], + useSnapshots: boolean, port: string): void { + let sdkPath = path.join(outputDir, sdk.executableFilename(os.type())); + if (avds[0] == 'all') { + avds = require(path.join(sdkPath, 'available_avds.json')); + } else if (avds[0] == 'none') { + avds.length = 0; + } + avds.forEach((avd: string, i: number) => { + logger.info('Booting up AVD ' + avd); + // Credit to appium-ci, which this code was adapted from + let emuBin = 'emulator'; //TODO(sjelin): get the 64bit linux version working + let emuArgs = [ + '-avd', avd + '-v' + sdk.versionCustom + '-wd-manager', + '-netfast', + ]; + if (!useSnapshots) { + emuArgs = emuArgs.concat(['-no-snapshot-load', '-no-snapshot-save']); + } + if (port) { + emuArgs = emuArgs.concat(['-ports', (port+2*i) + ',' + (port+2*i+1)]); + } + if (emuBin !== 'emulator') { + emuArgs = emuArgs.concat(['-qemu', '-enable-kvm']); + } + androidProcesses.push(childProcess.spawn(path.join(sdkPath, 'tools', + emuBin), emuArgs, {stdio: 'inherit'})); + }); +} + +function killAndroid() { + androidProcesses.forEach((androidProcess: childProcess.ChildProcess) => { + androidProcess.kill(); + }); + androidProcesses.length = 0; +} + +// Manage appium process +let appiumProcess: childProcess.ChildProcess; + +function startAppium(outputDir: string, port: string) { + logger.info('Starting appium server'); + appiumProcess = childProcess.spawn(path.join(outputDir, 'appium', + 'node_modules', '.bin', 'appium'), port ? ['--port', port] : []); +} + +function killAppium() { + if (appiumProcess != null) { + appiumProcess.kill(); + appiumProcess = null; + } +} diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 8b056abb..47e48c5c 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -1,15 +1,19 @@ import * as AdmZip from 'adm-zip'; +import * as child_process from 'child_process'; import * as fs from 'fs'; import * as minimist from 'minimist'; import * as os from 'os'; import * as path from 'path'; +import * as q from 'q'; +import * as rimraf from 'rimraf'; import {Opts} from './opts'; import * as Opt from './'; import {Config} from '../config'; -import {Binary, ChromeDriver, IEDriver, StandAlone} from '../binaries'; +import {Binary, ChromeDriver, IEDriver, AndroidSDK, StandAlone} from '../binaries'; import {FileManager, Downloader} from '../files'; import {Logger, Options, Program} from '../cli'; +import {android as initializeAndroid} from './initialize'; let logger = new Logger('update'); let prog = new Program() @@ -20,7 +24,11 @@ let prog = new Program() .addOption(Opts[Opt.PROXY]) .addOption(Opts[Opt.ALTERNATE_CDN]) .addOption(Opts[Opt.STANDALONE]) - .addOption(Opts[Opt.CHROME]); + .addOption(Opts[Opt.CHROME]) + .addOption(Opts[Opt.ANDROID]) + .addOption(Opts[Opt.ANDROID_API_LEVELS]) + .addOption(Opts[Opt.ANDROID_ABIS]) + .addOption(Opts[Opt.ANDROID_ACCEPT_LICENSES]); if (os.type() === 'Windows_NT') { prog.addOption(Opts[Opt.IE]).addOption(Opts[Opt.IE32]); @@ -28,12 +36,13 @@ if (os.type() === 'Windows_NT') { prog .addOption(Opts[Opt.VERSIONS_STANDALONE]) - .addOption(Opts[Opt.VERSIONS_CHROME]); + .addOption(Opts[Opt.VERSIONS_CHROME]) + .addOption(Opts[Opt.VERSIONS_ANDROID]); if (os.type() === 'Windows_NT') { prog.addOption(Opts[Opt.VERSIONS_IE]); } -export var program = prog; +export let program = prog; // stand alone runner let argv = minimist(process.argv.slice(2), prog.getMinimistOptions()); @@ -59,7 +68,11 @@ function update(options: Options): void { if (options[Opt.IE32]) { ie32 = options[Opt.IE32].getBoolean(); } + let android: boolean = options[Opt.ANDROID].getBoolean(); let outputDir = Config.getSeleniumDir(); + let android_api_levels: string[] = options[Opt.ANDROID_API_LEVELS].getString().split(','); + let android_abis: string[] = options[Opt.ANDROID_ABIS].getString().split(','); + let android_accept_licenses: boolean = options[Opt.ANDROID_ACCEPT_LICENSES].getBoolean(); if (options[Opt.OUT_DIR].getString()) { if (path.isAbsolute(options[Opt.OUT_DIR].getString())) { outputDir = options[Opt.OUT_DIR].getString(); @@ -78,6 +91,7 @@ function update(options: Options): void { if (options[Opt.VERSIONS_IE]) { binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString(); } + binaries[AndroidSDK.id].versionCustom = options[Opt.VERSIONS_ANDROID].getString(); // if the file has not been completely downloaded, download it // else if the file has already been downloaded, unzip the file, rename it, and give it permissions @@ -106,12 +120,30 @@ function update(options: Options): void { binary.arch = 'Win32'; updateBinary(binary, outputDir, proxy, ignoreSSL); } + if (android) { + let binary = binaries[AndroidSDK.id]; + let sdk_path = path.join(outputDir, binary.executableFilename(os.type())); + + updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => { + initializeAndroid(path.join(outputDir, binary.executableFilename(os.type( + ))), android_api_levels, android_abis, android_accept_licenses, + binaries[AndroidSDK.id].versionCustom, logger); + }); + installAppium(outputDir); + } } -function updateBinary(binary: Binary, outputDir: string, proxy: string, ignoreSSL: boolean) { - FileManager.toDownload(binary, outputDir).then((value: boolean) => { +function updateBinary(binary: Binary, outputDir: string, proxy: string, ignoreSSL: boolean): q.Promise { + return FileManager.toDownload(binary, outputDir).then((value: boolean) => { if (value) { - Downloader.downloadBinary(binary, outputDir, proxy, ignoreSSL, unzip); + let deferred = q.defer(); + Downloader.downloadBinary(binary, outputDir, proxy, ignoreSSL, + (binary: Binary, outputDir: string, fileName: string) => { + unzip(binary, outputDir, fileName); + deferred.resolve(); + } + ); + return deferred.promise; } else { logger.info(binary.name + ': file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); let fileName = binary.filename(os.type(), os.arch()); @@ -127,19 +159,48 @@ function unzip(binary: T, outputDir: string, fileName: string) let mv = path.join(outputDir, binary.executableFilename(osType)); try { fs.unlinkSync(mv); - } catch(err) {} + } catch(err) { + try { + rimraf.sync(mv); + } catch (err2) {} + } // unzip the file logger.info(binary.name + ': unzipping ' + fileName); - let zip = new AdmZip(path.resolve(outputDir, fileName)); - zip.extractAllTo(outputDir, true); + if (fileName.slice(-4) == '.zip') { + let zip = new AdmZip(path.resolve(outputDir, fileName)); + zip.extractAllTo(outputDir, true); + } else { + // We will only ever get .tar files on linux + child_process.spawnSync('tar', ['zxvf', path.resolve(outputDir, fileName), + '-C', outputDir]); + } // rename - fs.renameSync(path.join(outputDir, binary.name + binary.executableSuffix(osType)), mv); + fs.renameSync(path.join(outputDir, binary.zipContentName(osType)), mv); // set permissions if (osType !== 'Windows_NT') { logger.info(binary.name + ': setting permissions to 0755 for ' + mv); - fs.chmodSync(mv, '0755'); + if (binary.id() !== AndroidSDK.id) { + fs.chmodSync(mv, '0755'); + } else { + fs.chmodSync(path.join(mv, 'tools', 'android'), '0755'); + fs.chmodSync(path.join(mv, 'tools', 'emulator'), '0755'); + //TODO(sjelin): get 64 bit versions working + } } } + +function installAppium(outputDir: string): void { + logger.info('appium: installing appium'); + + let folder = path.join(outputDir, 'appium'); + try { + rimraf.sync(folder); + } catch(err) {} + + fs.mkdirSync(folder); + fs.writeFileSync(path.join(folder, 'package.json'), '{}'); + child_process.spawn('npm', ['install', 'appium'], {cwd: folder}); +} diff --git a/lib/config.ts b/lib/config.ts index 977cfd1e..88dffc1c 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -10,6 +10,7 @@ export interface ConfigFile { selenium?: string; chrome?: string; ie?: string; + android?: string; } /** @@ -60,6 +61,7 @@ export class Config { configVersions.selenium = configFile.webdriverVersions.selenium; configVersions.chrome = configFile.webdriverVersions.chromedriver; configVersions.ie = configFile.webdriverVersions.iedriver; + configVersions.android = configFile.webdriverVersions.androidsdk; return configVersions; } @@ -73,6 +75,7 @@ export class Config { configCdnUrls.selenium = configFile.cdnUrls.selenium; configCdnUrls.chrome = configFile.cdnUrls.chromedriver; configCdnUrls.ie = configFile.cdnUrls.iedriver; + configCdnUrls.android = configFile.cdnUrls.androidsdk; return configCdnUrls; } diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index 82c848a6..7456f9a1 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -34,9 +34,9 @@ export class Downloader { return; } Downloader.httpGetFile_( - url, binary.filename(os.type(), os.arch()), outputDir, opt_proxy, opt_ignoreSSL, (downloaded: boolean) => { + url, binary.filename(os.type(), os.arch()), outputDir, opt_proxy, opt_ignoreSSL, (filePath: string) => { if (opt_callback) { - opt_callback(binary, outputDir, downloaded); + opt_callback(binary, outputDir, filePath); } }); } diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 04168645..1a327e43 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -2,8 +2,9 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; import * as q from 'q'; +import * as rimraf from 'rimraf'; -import {Binary, BinaryMap, ChromeDriver, IEDriver, StandAlone, OS} from '../binaries'; +import {Binary, BinaryMap, ChromeDriver, IEDriver, AndroidSDK, StandAlone, OS} from '../binaries'; import {DownloadedBinary} from './downloaded_binary'; import {Downloader} from './downloader'; import {Logger} from '../cli'; @@ -55,6 +56,9 @@ export class FileManager { if (FileManager.checkOS_(osType, IEDriver)) { binaries[IEDriver.id] = new IEDriver(); } + if (FileManager.checkOS_(osType, AndroidSDK)) { + binaries[AndroidSDK.id] = new AndroidSDK(); + } return binaries; } @@ -203,10 +207,14 @@ export class FileManager { for (let binPos in binaries) { let bin: Binary = binaries[binPos]; if (file.indexOf(bin.prefix()) !== -1) { - fs.unlinkSync(path.join(outputDir, file)); + + bin.remove(path.join(outputDir, file)); logger.info('removed ' + file); } } }) + try { + rimraf.sync(path.join(outputDir, 'appium')); + } catch (e) {} } } diff --git a/mobile.md b/mobile.md new file mode 100644 index 00000000..e368dc88 --- /dev/null +++ b/mobile.md @@ -0,0 +1,46 @@ +Mobile Browser Support +====================== + +Support for mobile browsers is provided via [appium](https://github.com/appium/appium). If you +have the Android SDK installed, when you run `webdriver-manager start`, appium will automatically +start on the port specified by `--appium-port`. + + +Android SDK +----------- + +`webdriver-manager` will not install the android SDK by default. If you want to test on android, +run `webdriver-manager update --android`. This will download the android SDK, appium, and set up +some virtual android devices for you to run tests against. By default, this will create only an +android device running version 24 on x86-64. If you need a different device, you must use the +`--android-api-levels` and `--android-abis` flags. So you might run a command like this: + +``` +webdriver-manager update --android --android-api-levels 23 --android-abis armeabi-v7a +``` + +Valid values for the `--android-api-levels` flag are: `2`, `3`, `4`, `5`, ..., `24` + +Valid values for the `--android-abi` flag are: `x86_64`, `armeabi-v7a`, `x86`, `mips`, + `android-wear/x86_64`, `android-wear/armeabi-v7a`, `android-wear/x86`, `android-wear/mips`, + `android-tv/x86_64`, `android-tv/armeabi-v7a`, `android-tv/x86`, `android-tv/mips`, + `google_apis/x86_64`, `google_apis/armeabi-v7a`, `google_apis/x86`, `google_apis/mips` + + +As a practical matter, if you don't want to manually accept the license agreements, you can use +`--android-accept-licenses`, which will accept them on your behalf. + +Once you have installed the Android SDK with the virtual devices you need, use +`webdriver-manager start --android` to boot up appium and begin emulating your android device(s). +By default `webdriver-manager` will emulate all available android devices. If you would rather +emulate a specific device, use `--avds`. So you might use: + +``` +webdriver-manager start --android --avds android-23-default-x86_64 +``` + +If you would prefer not to emulate any android virtual devices, use `--avds none`. + +If you need to specify the ports used by the android virtual devices, use `--avd_port`. The port +you specify will be used for the console of the first device, and the port one higher will be used +for its ADB. The second device will use the next two ports, and so on. diff --git a/package.json b/package.json index 5a8f6bd3..e695c1b4 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,13 @@ "dependencies": { "adm-zip": "^0.4.7", "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", "minimist": "^1.2.0", "q": "^1.4.1", - "request": "^2.69.0" + "request": "^2.69.0", + "rimraf": "^2.5.2" }, "devDependencies": { "clang-format": "^1.0.35", diff --git a/spec/files/fileManager_spec.ts b/spec/files/fileManager_spec.ts index 6e1696d6..5360ce09 100644 --- a/spec/files/fileManager_spec.ts +++ b/spec/files/fileManager_spec.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import {Binary, ChromeDriver, IEDriver, StandAlone} from '../../lib/binaries'; +import {Binary, AndroidSDK, ChromeDriver, IEDriver, StandAlone} from '../../lib/binaries'; import {DownloadedBinary, FileManager} from '../../lib/files'; @@ -15,6 +15,7 @@ describe('file manager', () => { expect(FileManager.checkOS_(osType, ChromeDriver)).toBe(true); expect(FileManager.checkOS_(osType, IEDriver)).toBe(true); expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); + expect(FileManager.checkOS_(osType, AndroidSDK)).toBe(true); }); it('should return the binary array', () => { @@ -22,6 +23,7 @@ describe('file manager', () => { expect(binaries[StandAlone.id].name).toBe((new StandAlone()).name); expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); expect(binaries[IEDriver.id].name).toBe((new IEDriver()).name); + expect(binaries[AndroidSDK.id].name).toBe((new AndroidSDK()).name); }); }); @@ -32,12 +34,14 @@ describe('file manager', () => { expect(FileManager.checkOS_(osType, ChromeDriver)).toBe(true); expect(FileManager.checkOS_(osType, IEDriver)).toBe(false); expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); + expect(FileManager.checkOS_(osType, AndroidSDK)).toBe(true); }); it('should return the binary array', () => { let binaries = FileManager.compileBinaries_(osType); expect(binaries[StandAlone.id].name).toBe((new StandAlone()).name); expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); + expect(binaries[AndroidSDK.id].name).toBe((new AndroidSDK()).name); expect(binaries[IEDriver.id]).toBeUndefined(); }); }); @@ -49,6 +53,7 @@ describe('file manager', () => { expect(FileManager.checkOS_(osType, ChromeDriver)).toBe(true); expect(FileManager.checkOS_(osType, IEDriver)).toBe(false); expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); + expect(FileManager.checkOS_(osType, AndroidSDK)).toBe(true); }); it('should return the binary array', () => { @@ -56,6 +61,7 @@ describe('file manager', () => { expect(binaries[StandAlone.id].name).toBe((new StandAlone()).name); expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); expect(binaries[IEDriver.id]).toBeUndefined(); + expect(binaries[AndroidSDK.id].name).toBe((new AndroidSDK()).name); }); }); @@ -63,6 +69,7 @@ describe('file manager', () => { let existingFiles: string[]; let selenium = new StandAlone(); let chrome = new ChromeDriver(); + let android = new AndroidSDK(); let ie = new IEDriver(); let ostype: string; let arch: string; @@ -73,10 +80,14 @@ describe('file manager', () => { existingFiles = [ selenium.prefix() + '2.51.0' + selenium.executableSuffix(), selenium.prefix() + '2.52.0' + selenium.executableSuffix()]; - existingFiles.push(chrome.prefix() + '2.20' + chrome.suffix(ostype, arch)); - existingFiles.push(chrome.prefix() + '2.20' + chrome.executableSuffix(ostype)); - existingFiles.push(chrome.prefix() + '2.21' + chrome.suffix(ostype, arch)); - existingFiles.push(chrome.prefix() + '2.21' + chrome.executableSuffix(ostype)); + existingFiles.push(chrome.prefix() + '2.20' + chrome.suffix(ostype, arch)); + existingFiles.push(chrome.prefix() + '2.20' + chrome.executableSuffix(ostype)); + existingFiles.push(chrome.prefix() + '2.21' + chrome.suffix(ostype, arch)); + existingFiles.push(chrome.prefix() + '2.21' + chrome.executableSuffix(ostype)); + existingFiles.push(android.prefix() + '24.1.0' + android.suffix(ostype)); + existingFiles.push(android.prefix() + '24.1.0' + android.executableSuffix()); + existingFiles.push(android.prefix() + '24.1.1' + android.suffix(ostype)); + existingFiles.push(android.prefix() + '24.1.1' + android.executableSuffix()); if (ostype == 'Windows_NT') { existingFiles.push(ie.prefix() + '_Win32_2.51.0' + ie.suffix()); existingFiles.push(ie.prefix() + '_Win32_2.51.0' + ie.executableSuffix(ostype)); @@ -137,6 +148,30 @@ describe('file manager', () => { }); }); + describe('versions for android', () => { + it('should find the correct version for windows', () => { + setup('Windows_NT'); + let downloaded = FileManager.downloadedVersions_(android, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('24.1.0'); + expect(downloaded.versions[1]).toBe('24.1.1'); + }); + it('should find the correct version for mac', () => { + setup('Darwin'); + let downloaded = FileManager.downloadedVersions_(android, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('24.1.0'); + expect(downloaded.versions[1]).toBe('24.1.1'); + }); + it('should find the correct version for linux', () => { + setup('Linux'); + let downloaded = FileManager.downloadedVersions_(android, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(2); + expect(downloaded.versions[0]).toBe('24.1.0'); + expect(downloaded.versions[1]).toBe('24.1.1'); + }); + }); + describe('versions for ie on windows', () => { it('should find the correct version for windows', () => { setup('Windows_NT'); diff --git a/typings.json b/typings.json index 00f0e570..8c3b9be5 100644 --- a/typings.json +++ b/typings.json @@ -3,10 +3,14 @@ "adm-zip": "registry:dt/adm-zip#0.0.0+20160317120654", "chalk": "registry:dt/chalk#0.4.0+20160317120654", "form-data": "registry:dt/form-data#0.0.0+20160316155526", + "glob": "registry:dt/glob#5.0.10+20160317120654", + "ini": "registry:dt/ini#1.3.3+20160316155526", "jasmine": "registry:dt/jasmine#2.2.0+20160412134438", + "minimatch": "registry:dt/minimatch#2.0.8+20160317120654", "minimist": "registry:dt/minimist#1.1.3+20160317120654", "node": "registry:dt/node#4.0.0+20160423143914", "q": "registry:dt/q#0.0.0+20160417152954", - "request": "registry:dt/request#0.0.0+20160316155526" + "request": "registry:dt/request#0.0.0+20160316155526", + "rimraf": "registry:dt/rimraf#0.0.0+20160317120654" } } From cd60fc47f2220b10d61a56ecb1860816db85f3fa Mon Sep 17 00:00:00 2001 From: Denis Date: Fri, 17 Jun 2016 10:57:12 -0400 Subject: [PATCH 45/79] chore(start): send a shutdown command on the same port for the selenium server. --- lib/cmds/start.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 9523f7ef..602f499b 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -136,7 +136,8 @@ function start(options: Options) { process.stdin.resume(); process.stdin.on('data', (chunk: Buffer) => { logger.info('Attempting to shut down selenium nicely'); - http.get('http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer'); + let port = seleniumPort || '4444'; + http.get('http://localhost:' + port + '/selenium-server/driver/?cmd=shutDownSeleniumServer'); killAndroid(); killAppium(); }); From 81c2aa3ea6435934797b4d10c6734945484a641d Mon Sep 17 00:00:00 2001 From: Craig Date: Thu, 23 Jun 2016 13:04:40 -0700 Subject: [PATCH 46/79] fix(iedriver): download url fix for iedriver (#54) closes #53 --- lib/binaries/ie_driver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/binaries/ie_driver.ts b/lib/binaries/ie_driver.ts index 06bef433..0df19cda 100644 --- a/lib/binaries/ie_driver.ts +++ b/lib/binaries/ie_driver.ts @@ -15,7 +15,7 @@ export class IEDriver extends Binary { constructor() { super(); - this.name = 'IEDriver'; + this.name = 'IEDriverServer'; this.versionCustom = IEDriver.versionDefault; this.prefixDefault = 'IEDriverServer'; this.suffixDefault = '.zip'; @@ -40,7 +40,7 @@ export class IEDriver extends Binary { } url(): string { - let urlBase = this.cdn + this.shortVersion(this.version()) + '/'; + let urlBase = this.cdn + this.shortVersion(this.versionCustom) + '/'; let filename = this.prefix() + this.version() + this.suffix(); return urlBase + filename; } From 57372ebd076f6b1ccaf41d920601e867b7b3084c Mon Sep 17 00:00:00 2001 From: Craig Date: Thu, 23 Jun 2016 15:34:16 -0700 Subject: [PATCH 47/79] feat(edge): add Microsoft Edge support in CLI (#56) closes #55 --- lib/cmds/opts.ts | 2 ++ lib/cmds/start.ts | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index 890e7698..64668e00 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -13,6 +13,7 @@ export const STANDALONE = 'standalone'; export const CHROME = 'chrome'; export const IE = 'ie'; export const IE32 = 'ie32'; +export const EDGE = 'edge'; export const ANDROID = 'android'; export const VERSIONS_CHROME = 'versions.chrome'; export const VERSIONS_STANDALONE = 'versions.standalone'; @@ -40,6 +41,7 @@ opts[STANDALONE] = new Option(STANDALONE, 'Install or update selenium standalone opts[CHROME] = new Option(CHROME, 'Install or update chromedriver', 'boolean', ChromeDriver.isDefault); opts[IE] = new Option(IE, 'Install or update ie driver', 'boolean', IEDriver.isDefault); opts[IE32] = new Option(IE32, 'Install or update 32-bit ie driver', 'boolean', IEDriver.isDefault); +opts[EDGE] = new Option(EDGE, 'Use installed Microsoft Edge driver', 'string', 'C:\\Program Files (x86)\\Microsoft Web Driver\\MicrosoftWebDriver.exe'); opts[ANDROID] = new Option(ANDROID, 'Update/use the android sdk', 'boolean', AndroidSDK.isDefault); opts[VERSIONS_CHROME] = new Option(VERSIONS_CHROME, 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); opts[VERSIONS_ANDROID] = new Option(VERSIONS_ANDROID, 'Optional android sdk version', 'string', AndroidSDK.versionDefault); diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 602f499b..c7ebe455 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -30,6 +30,7 @@ let prog = new Program() if (os.type() === 'Windows_NT') { prog.addOption(Opts[Opt.VERSIONS_IE]); + prog.addOption(Opts[Opt.EDGE]); } export var program = prog; @@ -107,6 +108,11 @@ function start(options: Options) { '-Dwebdriver.ie.driver=' + path.join(outputDir, binaries[IEDriver.id].executableFilename(osType))); } + if (options[Opt.EDGE]) { + args.push( + '-Dwebdriver.edge.driver=' + + options[Opt.EDGE].getString()); + } if (options[Opt.ANDROID].getBoolean()) { if (downloadedBinaries[AndroidSDK.id] != null) { let avds = options[Opt.AVDS].getString(); From 88354caef072b2637068f16fb7703a8032ba4e08 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Mon, 27 Jun 2016 14:16:24 -0700 Subject: [PATCH 48/79] chore(release): version bump to 10.1.0 --- CHANGELOG.md | 20 +++++++++++++++++++- package.json | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d479f6f5..880d5f60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,28 @@ +# 10.1.0 + +## Bug Fixes + +- ([81c2aa3](https://github.com/angular/webdriver-manager/commit/81c2aa3ea6435934797b4d10c6734945484a641d)) + fix(iedriver): download url fix for iedriver (#54) + + closes #53 + +## Features + +- ([57372eb](https://github.com/angular/webdriver-manager/commit/57372ebd076f6b1ccaf41d920601e867b7b3084c)) + feat(edge): add Microsoft Edge support in CLI (#56) + + closes #55 +- ([d937245](https://github.com/angular/webdriver-manager/commit/d9372459c51a1aec553a79edaa32e497608a65de)) + feat(android): support android + # 10.0.4 ## Dependency Upgrades - ([970167a](https://github.com/angular/webdriver-manager/commit/970167a1b2db24fc8ca34db2994507ef0187ee7e)) dep(typings): update typings (#42) - + ## Bug Fixes - ([5073e23](https://github.com/angular/webdriver-manager/commit/5073e230574237047dd593a702f08f84907871bd)) diff --git a/package.json b/package.json index e695c1b4..61bbeb8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.0.4", + "version": "10.1.0", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From aa1b8b7cd9295f02b9bf69274e21eef1a7f3b7f0 Mon Sep 17 00:00:00 2001 From: Sammy Jelin Date: Thu, 30 Jun 2016 17:37:40 -0700 Subject: [PATCH 49/79] feat(ios): iOS support (#57) --- config.json | 3 ++- lib/binaries/appium.ts | 36 ++++++++++++++++++++++++++++++++++ lib/binaries/index.ts | 1 + lib/cmds/initialize.ts | 11 +++++++++++ lib/cmds/opts.ts | 6 +++++- lib/cmds/start.ts | 20 ++++++++++++++----- lib/cmds/update.ts | 28 ++++++++++++++++++++------ lib/config.ts | 2 ++ lib/files/file_manager.ts | 9 +++++---- mobile.md | 22 +++++++++++++++++---- spec/files/fileManager_spec.ts | 31 ++++++++++++++++++++++++++++- 11 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 lib/binaries/appium.ts diff --git a/config.json b/config.json index 0b175b1b..5d140093 100644 --- a/config.json +++ b/config.json @@ -3,7 +3,8 @@ "selenium": "2.53.0", "chromedriver": "2.21", "iedriver": "2.53.1", - "androidsdk": "24.4.1" + "androidsdk": "24.4.1", + "appium": "1.5.3" }, "cdnUrls": { "selenium": "https://selenium-release.storage.googleapis.com/", diff --git a/lib/binaries/appium.ts b/lib/binaries/appium.ts new file mode 100644 index 00000000..810a3d38 --- /dev/null +++ b/lib/binaries/appium.ts @@ -0,0 +1,36 @@ +import * as child_process from 'child_process'; +import * as rimraf from 'rimraf'; +import * as path from 'path'; +import {arch, type} from 'os'; + +import {Binary, OS} from './binary'; +import {Config} from '../config'; + +/** + * The appium binary. + */ +export class Appium extends Binary { + static os = [OS.Windows_NT, OS.Linux, OS.Darwin]; + static id = 'appium'; + static versionDefault = Config.binaryVersions().appium; + static isDefault = false; + static shortName = ['appium']; + + constructor() { + super(); + this.name = 'appium'; + this.versionCustom = Appium.versionDefault; + this.prefixDefault = 'appium-'; + this.suffixDefault = ''; + } + + id(): string { return Appium.id; } + + versionDefault(): string { return Appium.versionDefault; } + + executableSuffix(): string { return ''; } + + remove(sdkPath: string): void { + rimraf.sync(sdkPath); + } +} diff --git a/lib/binaries/index.ts b/lib/binaries/index.ts index 2632bda3..3dbfb4a6 100644 --- a/lib/binaries/index.ts +++ b/lib/binaries/index.ts @@ -2,4 +2,5 @@ export * from './binary'; export * from './chrome_driver'; export * from './ie_driver'; export * from './android_sdk'; +export * from './appium'; export * from './stand_alone'; diff --git a/lib/cmds/initialize.ts b/lib/cmds/initialize.ts index 9bafa1a8..5c38e22f 100644 --- a/lib/cmds/initialize.ts +++ b/lib/cmds/initialize.ts @@ -230,3 +230,14 @@ export function android(sdkPath: string, apiLevels: string[], abis: string[], logger.info('android-sdk: Initialization complete'); }).done(); }; + +export function iOS(logger: Logger) { + if (os.type() != 'Darwin') { + throw new Error('Must be on a Mac to simulate iOS devices.'); + } + try { + fs.statSync('/Applications/Xcode.app'); + } catch(e) { + logger.warn('You must install the xcode commandline tools!'); + } +} diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index 64668e00..f7c1855d 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -1,6 +1,6 @@ import {Config} from '../config'; import {Cli, Option, Options} from '../cli'; -import {ChromeDriver, IEDriver, AndroidSDK, StandAlone} from '../binaries'; +import {ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlone} from '../binaries'; export const OUT_DIR = 'out_dir'; export const SELENIUM_PORT = 'seleniumPort'; @@ -15,10 +15,12 @@ export const IE = 'ie'; export const IE32 = 'ie32'; export const EDGE = 'edge'; export const ANDROID = 'android'; +export const IOS = 'ios'; export const VERSIONS_CHROME = 'versions.chrome'; export const VERSIONS_STANDALONE = 'versions.standalone'; export const VERSIONS_IE = 'versions.ie'; export const VERSIONS_ANDROID = 'versions.android'; +export const VERSIONS_APPIUM = 'versions.appium'; export const CHROME_LOGS = 'chrome_logs'; export const ANDROID_API_LEVELS = 'android-api-levels'; export const ANDROID_ABIS = 'android-abis'; @@ -43,9 +45,11 @@ opts[IE] = new Option(IE, 'Install or update ie driver', 'boolean', IEDriver.isD opts[IE32] = new Option(IE32, 'Install or update 32-bit ie driver', 'boolean', IEDriver.isDefault); opts[EDGE] = new Option(EDGE, 'Use installed Microsoft Edge driver', 'string', 'C:\\Program Files (x86)\\Microsoft Web Driver\\MicrosoftWebDriver.exe'); opts[ANDROID] = new Option(ANDROID, 'Update/use the android sdk', 'boolean', AndroidSDK.isDefault); +opts[IOS] = new Option(IOS, 'Update the iOS sdk', 'boolean', false); opts[VERSIONS_CHROME] = new Option(VERSIONS_CHROME, 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); opts[VERSIONS_ANDROID] = new Option(VERSIONS_ANDROID, 'Optional android sdk version', 'string', AndroidSDK.versionDefault); opts[VERSIONS_STANDALONE] = new Option(VERSIONS_STANDALONE, 'Optional seleniuim standalone server version', 'string', StandAlone.versionDefault); +opts[VERSIONS_APPIUM] = new Option(VERSIONS_APPIUM, 'Optional appium version', 'string', Appium.versionDefault); opts[VERSIONS_IE] = new Option(VERSIONS_IE, 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); opts[CHROME_LOGS] = new Option(CHROME_LOGS, 'File path to chrome logs', 'string', undefined); opts[ANDROID_API_LEVELS] = new Option(ANDROID_API_LEVELS, 'Which versions of the android API you want to emulate', 'string', AndroidSDK.DEFAULT_API_LEVELS); diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index c7ebe455..518f0983 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -10,7 +10,8 @@ import * as Opt from './'; import {Config} from '../config'; import {FileManager} from '../files'; import {Logger, Options, Program} from '../cli'; -import {BinaryMap, Binary, ChromeDriver, IEDriver, AndroidSDK, StandAlone} from '../binaries'; +import {BinaryMap, Binary, ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlone} from + '../binaries'; let logger = new Logger('start'); let prog = new Program() @@ -23,11 +24,16 @@ let prog = new Program() .addOption(Opts[Opt.VERSIONS_STANDALONE]) .addOption(Opts[Opt.VERSIONS_CHROME]) .addOption(Opts[Opt.VERSIONS_ANDROID]) + .addOption(Opts[Opt.VERSIONS_APPIUM]) .addOption(Opts[Opt.CHROME_LOGS]) .addOption(Opts[Opt.ANDROID]) .addOption(Opts[Opt.AVDS]) .addOption(Opts[Opt.AVD_USE_SNAPSHOTS]); +if (os.type() === 'Darwin') { + prog.addOption(Opts[Opt.IOS]); +} + if (os.type() === 'Windows_NT') { prog.addOption(Opts[Opt.VERSIONS_IE]); prog.addOption(Opts[Opt.EDGE]); @@ -83,6 +89,7 @@ function start(options: Options) { binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString(); } binaries[AndroidSDK.id].versionCustom = options[Opt.VERSIONS_ANDROID].getString(); + binaries[Appium.id].versionCustom = options[Opt.VERSIONS_APPIUM].getString(); let downloadedBinaries = FileManager.downloadedBinaries(outputDir); if (downloadedBinaries[StandAlone.id] == null) { @@ -118,11 +125,14 @@ function start(options: Options) { let avds = options[Opt.AVDS].getString(); startAndroid(outputDir, binaries[AndroidSDK.id], avds.split(','), options[Opt.AVD_USE_SNAPSHOTS].getBoolean(), - options[Opt.APPIUM_PORT].getString()); - startAppium(outputDir, options[Opt.APPIUM_PORT].getString()); + options[Opt.AVD_PORT].getString()); } else { + logger.warn('Not starting android because it is not installed'); } } + if (downloadedBinaries[Appium.id] != null) { + startAppium(outputDir, binaries[Appium.id], options[Opt.APPIUM_PORT].getString()); + } // log the command to launch selenium server let argsToString = ''; @@ -204,9 +214,9 @@ function killAndroid() { // Manage appium process let appiumProcess: childProcess.ChildProcess; -function startAppium(outputDir: string, port: string) { +function startAppium(outputDir: string, binary: Binary, port: string) { logger.info('Starting appium server'); - appiumProcess = childProcess.spawn(path.join(outputDir, 'appium', + appiumProcess = childProcess.spawn(path.join(outputDir, binary.filename(), 'node_modules', '.bin', 'appium'), port ? ['--port', port] : []); } diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 47e48c5c..6eeb7244 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -10,10 +10,10 @@ import * as rimraf from 'rimraf'; import {Opts} from './opts'; import * as Opt from './'; import {Config} from '../config'; -import {Binary, ChromeDriver, IEDriver, AndroidSDK, StandAlone} from '../binaries'; +import {Binary, ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlone} from '../binaries'; import {FileManager, Downloader} from '../files'; import {Logger, Options, Program} from '../cli'; -import {android as initializeAndroid} from './initialize'; +import {android as initializeAndroid, iOS as checkIOS} from './initialize'; let logger = new Logger('update'); let prog = new Program() @@ -30,6 +30,10 @@ let prog = new Program() .addOption(Opts[Opt.ANDROID_ABIS]) .addOption(Opts[Opt.ANDROID_ACCEPT_LICENSES]); +if (os.type() === 'Darwin') { + prog.addOption(Opts[Opt.IOS]); +} + if (os.type() === 'Windows_NT') { prog.addOption(Opts[Opt.IE]).addOption(Opts[Opt.IE32]); } @@ -37,6 +41,7 @@ if (os.type() === 'Windows_NT') { prog .addOption(Opts[Opt.VERSIONS_STANDALONE]) .addOption(Opts[Opt.VERSIONS_CHROME]) + .addOption(Opts[Opt.VERSIONS_APPIUM]) .addOption(Opts[Opt.VERSIONS_ANDROID]); if (os.type() === 'Windows_NT') { @@ -69,6 +74,10 @@ function update(options: Options): void { ie32 = options[Opt.IE32].getBoolean(); } let android: boolean = options[Opt.ANDROID].getBoolean(); + let ios: boolean = false; + if (options[Opt.IOS]) { + ios = options[Opt.IOS].getBoolean(); + } let outputDir = Config.getSeleniumDir(); let android_api_levels: string[] = options[Opt.ANDROID_API_LEVELS].getString().split(','); let android_abis: string[] = options[Opt.ANDROID_ABIS].getString().split(','); @@ -92,6 +101,7 @@ function update(options: Options): void { binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString(); } binaries[AndroidSDK.id].versionCustom = options[Opt.VERSIONS_ANDROID].getString(); + binaries[Appium.id].versionCustom = options[Opt.VERSIONS_APPIUM].getString(); // if the file has not been completely downloaded, download it // else if the file has already been downloaded, unzip the file, rename it, and give it permissions @@ -129,7 +139,12 @@ function update(options: Options): void { ))), android_api_levels, android_abis, android_accept_licenses, binaries[AndroidSDK.id].versionCustom, logger); }); - installAppium(outputDir); + } + if (ios) { + checkIOS(logger); + } + if (android || ios) { + installAppium(binaries[Appium.id], outputDir); } } @@ -192,15 +207,16 @@ function unzip(binary: T, outputDir: string, fileName: string) } } -function installAppium(outputDir: string): void { +function installAppium(binary: Binary, outputDir: string): void { logger.info('appium: installing appium'); - let folder = path.join(outputDir, 'appium'); + let folder = path.join(outputDir, binary.filename()); try { rimraf.sync(folder); } catch(err) {} fs.mkdirSync(folder); fs.writeFileSync(path.join(folder, 'package.json'), '{}'); - child_process.spawn('npm', ['install', 'appium'], {cwd: folder}); + child_process.spawn('npm', ['install', 'appium@' + binary.version()], {cwd: + folder}); } diff --git a/lib/config.ts b/lib/config.ts index 88dffc1c..68a4ecfc 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -11,6 +11,7 @@ export interface ConfigFile { chrome?: string; ie?: string; android?: string; + appium?: string; } /** @@ -62,6 +63,7 @@ export class Config { configVersions.chrome = configFile.webdriverVersions.chromedriver; configVersions.ie = configFile.webdriverVersions.iedriver; configVersions.android = configFile.webdriverVersions.androidsdk; + configVersions.appium = configFile.webdriverVersions.appium; return configVersions; } diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 1a327e43..24b82694 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -4,7 +4,8 @@ import * as fs from 'fs'; import * as q from 'q'; import * as rimraf from 'rimraf'; -import {Binary, BinaryMap, ChromeDriver, IEDriver, AndroidSDK, StandAlone, OS} from '../binaries'; +import {Binary, BinaryMap, ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlone, OS} from + '../binaries'; import {DownloadedBinary} from './downloaded_binary'; import {Downloader} from './downloader'; import {Logger} from '../cli'; @@ -59,6 +60,9 @@ export class FileManager { if (FileManager.checkOS_(osType, AndroidSDK)) { binaries[AndroidSDK.id] = new AndroidSDK(); } + if (FileManager.checkOS_(osType, Appium)) { + binaries[Appium.id] = new Appium(); + } return binaries; } @@ -213,8 +217,5 @@ export class FileManager { } } }) - try { - rimraf.sync(path.join(outputDir, 'appium')); - } catch (e) {} } } diff --git a/mobile.md b/mobile.md index e368dc88..294c3e41 100644 --- a/mobile.md +++ b/mobile.md @@ -2,15 +2,15 @@ Mobile Browser Support ====================== Support for mobile browsers is provided via [appium](https://github.com/appium/appium). If you -have the Android SDK installed, when you run `webdriver-manager start`, appium will automatically -start on the port specified by `--appium-port`. +have used `webdriver-manager update --android` or `webdriver-manager update --ios`, when you run +`webdriver-manager start`, Appium will automatically start on the port specified by `--appium-port`. Android SDK ----------- `webdriver-manager` will not install the android SDK by default. If you want to test on android, -run `webdriver-manager update --android`. This will download the android SDK, appium, and set up +run `webdriver-manager update --android`. This will download the android SDK, Appium, and set up some virtual android devices for you to run tests against. By default, this will create only an android device running version 24 on x86-64. If you need a different device, you must use the `--android-api-levels` and `--android-abis` flags. So you might run a command like this: @@ -31,7 +31,7 @@ As a practical matter, if you don't want to manually accept the license agreemen `--android-accept-licenses`, which will accept them on your behalf. Once you have installed the Android SDK with the virtual devices you need, use -`webdriver-manager start --android` to boot up appium and begin emulating your android device(s). +`webdriver-manager start --android` to boot up Appium and begin emulating your android device(s). By default `webdriver-manager` will emulate all available android devices. If you would rather emulate a specific device, use `--avds`. So you might use: @@ -44,3 +44,17 @@ If you would prefer not to emulate any android virtual devices, use `--avds none If you need to specify the ports used by the android virtual devices, use `--avd_port`. The port you specify will be used for the console of the first device, and the port one higher will be used for its ADB. The second device will use the next two ports, and so on. + + +iOS +--------- + +When you run `webdriver-manager update --ios`, `webdriver-manager` will install Appium and check +your computer for iOS simulation capabilities. `webdriver-manager` cannot download the xcode +commandline tools for you however, nor can it agree to Apple's user agreement. The xcode +commandline tools come with several virtual devices pre-setup. If you need more, run +`xcrun simctl` for help doing that. + +Once you have installed Appium, `webdriver-manager` will launch it automatically when you run +`webdriver-manager start`. Appium will automatically handle starting iOS device emulation as +needed. diff --git a/spec/files/fileManager_spec.ts b/spec/files/fileManager_spec.ts index 5360ce09..70c319a2 100644 --- a/spec/files/fileManager_spec.ts +++ b/spec/files/fileManager_spec.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import {Binary, AndroidSDK, ChromeDriver, IEDriver, StandAlone} from '../../lib/binaries'; +import {Binary, AndroidSDK, ChromeDriver, IEDriver, Appium, StandAlone} from '../../lib/binaries'; import {DownloadedBinary, FileManager} from '../../lib/files'; @@ -16,6 +16,7 @@ describe('file manager', () => { expect(FileManager.checkOS_(osType, IEDriver)).toBe(true); expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); expect(FileManager.checkOS_(osType, AndroidSDK)).toBe(true); + expect(FileManager.checkOS_(osType, Appium)).toBe(true); }); it('should return the binary array', () => { @@ -24,6 +25,7 @@ describe('file manager', () => { expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); expect(binaries[IEDriver.id].name).toBe((new IEDriver()).name); expect(binaries[AndroidSDK.id].name).toBe((new AndroidSDK()).name); + expect(binaries[Appium.id].name).toBe((new Appium()).name); }); }); @@ -35,6 +37,7 @@ describe('file manager', () => { expect(FileManager.checkOS_(osType, IEDriver)).toBe(false); expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); expect(FileManager.checkOS_(osType, AndroidSDK)).toBe(true); + expect(FileManager.checkOS_(osType, Appium)).toBe(true); }); it('should return the binary array', () => { @@ -42,6 +45,7 @@ describe('file manager', () => { expect(binaries[StandAlone.id].name).toBe((new StandAlone()).name); expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); expect(binaries[AndroidSDK.id].name).toBe((new AndroidSDK()).name); + expect(binaries[Appium.id].name).toBe((new Appium()).name); expect(binaries[IEDriver.id]).toBeUndefined(); }); }); @@ -54,6 +58,7 @@ describe('file manager', () => { expect(FileManager.checkOS_(osType, IEDriver)).toBe(false); expect(FileManager.checkOS_(osType, StandAlone)).toBe(true); expect(FileManager.checkOS_(osType, AndroidSDK)).toBe(true); + expect(FileManager.checkOS_(osType, Appium)).toBe(true); }); it('should return the binary array', () => { @@ -62,6 +67,7 @@ describe('file manager', () => { expect(binaries[ChromeDriver.id].name).toBe((new ChromeDriver()).name); expect(binaries[IEDriver.id]).toBeUndefined(); expect(binaries[AndroidSDK.id].name).toBe((new AndroidSDK()).name); + expect(binaries[Appium.id].name).toBe((new Appium()).name); }); }); @@ -70,6 +76,7 @@ describe('file manager', () => { let selenium = new StandAlone(); let chrome = new ChromeDriver(); let android = new AndroidSDK(); + let appium = new Appium(); let ie = new IEDriver(); let ostype: string; let arch: string; @@ -88,6 +95,7 @@ describe('file manager', () => { existingFiles.push(android.prefix() + '24.1.0' + android.executableSuffix()); existingFiles.push(android.prefix() + '24.1.1' + android.suffix(ostype)); existingFiles.push(android.prefix() + '24.1.1' + android.executableSuffix()); + existingFiles.push(appium.prefix() + '1.5.3' + appium.suffix(ostype)); if (ostype == 'Windows_NT') { existingFiles.push(ie.prefix() + '_Win32_2.51.0' + ie.suffix()); existingFiles.push(ie.prefix() + '_Win32_2.51.0' + ie.executableSuffix(ostype)); @@ -172,6 +180,27 @@ describe('file manager', () => { }); }); + describe('versions for appium', () => { + it('should find the correct version for windows', () => { + setup('Windows_NT'); + let downloaded = FileManager.downloadedVersions_(appium, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(1); + expect(downloaded.versions[0]).toBe('1.5.3'); + }); + it('should find the correct version for mac', () => { + setup('Darwin'); + let downloaded = FileManager.downloadedVersions_(appium, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(1); + expect(downloaded.versions[0]).toBe('1.5.3'); + }); + it('should find the correct version for linux', () => { + setup('Linux'); + let downloaded = FileManager.downloadedVersions_(appium, ostype, arch, existingFiles); + expect(downloaded.versions.length).toBe(1); + expect(downloaded.versions[0]).toBe('1.5.3'); + }); + }); + describe('versions for ie on windows', () => { it('should find the correct version for windows', () => { setup('Windows_NT'); From eb00fb455d40e38fbb16f1bde29b55246acce6d4 Mon Sep 17 00:00:00 2001 From: Sammy Jelin Date: Thu, 30 Jun 2016 17:55:33 -0700 Subject: [PATCH 50/79] chore(release): version bump to 10.2.0 (#58) --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 880d5f60..80eecbe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 10.2.0 + +- ([aa1b8b7](https://github.com/angular/webdriver-manager/commit/aa1b8b7cd9295f02b9bf69274e21eef1a7f3b7f0)) + feat(ios): iOS support (#57) + # 10.1.0 ## Bug Fixes diff --git a/package.json b/package.json index 61bbeb8e..4a7de08e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.1.0", + "version": "10.2.0", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From d2e83e4fb5b249006f6a0a1b635d83fedbf0cdf6 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 11 Jul 2016 14:11:02 -0700 Subject: [PATCH 51/79] chore(config): upgrade to latest driver versions (#59) --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 5d140093..dfd43a5f 100644 --- a/config.json +++ b/config.json @@ -1,7 +1,7 @@ { "webdriverVersions": { - "selenium": "2.53.0", - "chromedriver": "2.21", + "selenium": "2.53.1", + "chromedriver": "2.22", "iedriver": "2.53.1", "androidsdk": "24.4.1", "appium": "1.5.3" From 4fe8f56ea58e0ba435fb260002f231c10849a548 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Mon, 11 Jul 2016 14:22:47 -0700 Subject: [PATCH 52/79] chore(release): version bump to 10.2.1 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80eecbe4..bf5dc01b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 10.2.1 + +upgrade to latest chrome driver and selenium standalone server versions + # 10.2.0 - ([aa1b8b7](https://github.com/angular/webdriver-manager/commit/aa1b8b7cd9295f02b9bf69274e21eef1a7f3b7f0)) diff --git a/package.json b/package.json index 4a7de08e..443753cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.2.0", + "version": "10.2.1", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From 009f68294aa483b69272d8abb12eefc298c67128 Mon Sep 17 00:00:00 2001 From: Craig Date: Thu, 4 Aug 2016 15:15:02 -0700 Subject: [PATCH 53/79] chore(typings): update ambient typings to current versions (#71) --- typings.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/typings.json b/typings.json index 8c3b9be5..f8e0a02d 100644 --- a/typings.json +++ b/typings.json @@ -2,15 +2,15 @@ "globalDependencies": { "adm-zip": "registry:dt/adm-zip#0.0.0+20160317120654", "chalk": "registry:dt/chalk#0.4.0+20160317120654", - "form-data": "registry:dt/form-data#0.0.0+20160316155526", + "form-data": "registry:dt/form-data#0.0.0+20160724024111", "glob": "registry:dt/glob#5.0.10+20160317120654", "ini": "registry:dt/ini#1.3.3+20160316155526", - "jasmine": "registry:dt/jasmine#2.2.0+20160412134438", + "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", "minimatch": "registry:dt/minimatch#2.0.8+20160317120654", "minimist": "registry:dt/minimist#1.1.3+20160317120654", - "node": "registry:dt/node#4.0.0+20160423143914", - "q": "registry:dt/q#0.0.0+20160417152954", - "request": "registry:dt/request#0.0.0+20160316155526", + "node": "registry:dt/node#6.0.0+20160802155038", + "q": "registry:dt/q#0.0.0+20160613154756", + "request": "registry:dt/request#0.0.0+20160726020908", "rimraf": "registry:dt/rimraf#0.0.0+20160317120654" } } From 18f9f1dfea02cd8f5c5a2cd5f09130f0ca24f68a Mon Sep 17 00:00:00 2001 From: James Martin Date: Wed, 3 Aug 2016 11:32:58 -0400 Subject: [PATCH 54/79] chore(selenium): add dev/urandom to selenium start args to prevent startup delays in linux --- lib/cmds/start.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 518f0983..b004a18f 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -99,6 +99,13 @@ function start(options: Options) { process.exit(1); } let args: string[] = ['-jar', path.join(outputDir, binaries[StandAlone.id].filename())]; + if (osType === 'Linux') { + // selenium server may take a long time to start because /dev/random is BLOCKING if there is not enough entropy + // the solution is to use /dev/urandom, which is NON-BLOCKING (use /dev/./urandom because of a java bug) + // https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/1301 + // https://bugs.openjdk.java.net/browse/JDK-6202721 + args.push('-Djava.security.egd=file:///dev/./urandom'); + } if (seleniumPort) { args.push('-port', seleniumPort); } From 95a03fb2a451b4c1c8398afa45436fa76b8b5b1c Mon Sep 17 00:00:00 2001 From: Craig Date: Thu, 4 Aug 2016 15:28:07 -0700 Subject: [PATCH 55/79] chore(gulp): formatting from new gulp task to format via clang (#70) --- gulpfile.js | 22 +-- lib/binaries/android_sdk.ts | 20 +-- lib/binaries/appium.ts | 12 +- lib/binaries/binary.ts | 28 ++-- lib/binaries/chrome_driver.ts | 4 +- lib/binaries/ie_driver.ts | 4 +- lib/binaries/stand_alone.ts | 4 +- lib/cli/cli.ts | 8 +- lib/cli/options.ts | 3 +- lib/cli/programs.ts | 13 +- lib/cmds/clean.ts | 11 +- lib/cmds/initialize.ts | 243 +++++++++++++++++----------------- lib/cmds/opts.ts | 57 +++++--- lib/cmds/start.ts | 81 ++++++------ lib/cmds/status.ts | 13 +- lib/cmds/update.ts | 94 +++++++------ lib/config.ts | 16 +-- lib/files/downloader.ts | 33 ++--- lib/files/file_manager.ts | 17 ++- lib/webdriver.ts | 13 +- 20 files changed, 367 insertions(+), 329 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index bfd92519..7677a2aa 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,8 +1,6 @@ 'use strict'; var gulp = require('gulp'); -var clangFormat = require('clang-format'); -var gulpFormat = require('gulp-clang-format'); var runSequence = require('run-sequence'); var spawn = require('child_process').spawn; @@ -30,12 +28,18 @@ gulp.task('copy', function() { .pipe(gulp.dest('built/')); }); -gulp.task('clang', function() { - return gulp.src(['src/**/*.ts']) - .pipe(gulpFormat.checkFormat('file', clangFormat)) - .on('warning', function(e) { - console.log(e); - }); +gulp.task('format:enforce', () => { + const format = require('gulp-clang-format'); + const clangFormat = require('clang-format'); + return gulp.src(['lib/**/*.ts']).pipe( + format.checkFormat('file', clangFormat, {verbose: true, fail: true})); +}); + +gulp.task('format', () => { + const format = require('gulp-clang-format'); + const clangFormat = require('clang-format'); + return gulp.src(['lib/**/*.ts'], { base: '.' }).pipe( + format.format('file', clangFormat)).pipe(gulp.dest('.')); }); gulp.task('typings', function(done) { @@ -47,7 +51,7 @@ gulp.task('tsc', function(done) { }); gulp.task('prepublish', function(done) { - runSequence(['typings', 'clang'], 'tsc', 'copy', done); + runSequence(['typings', 'format'], 'tsc', 'copy', done); }); gulp.task('default',['prepublish']); diff --git a/lib/binaries/android_sdk.ts b/lib/binaries/android_sdk.ts index df82ad9f..02802432 100644 --- a/lib/binaries/android_sdk.ts +++ b/lib/binaries/android_sdk.ts @@ -1,11 +1,13 @@ import * as child_process from 'child_process'; -import * as rimraf from 'rimraf'; -import * as path from 'path'; import {arch, type} from 'os'; +import * as path from 'path'; +import * as rimraf from 'rimraf'; -import {Binary, OS} from './binary'; import {Config} from '../config'; +import {Binary, OS} from './binary'; + + /** * The android sdk binary. */ @@ -41,9 +43,7 @@ export class AndroidSDK extends Binary { } } - url(ostype: string): string { - return this.cdn + this.filename(ostype); - } + url(ostype: string): string { return this.cdn + this.filename(ostype); } zipContentName(ostype: string): string { if (ostype === 'Darwin') { @@ -62,10 +62,12 @@ export class AndroidSDK extends Binary { let avds = require(path.join(sdkPath, 'available_avds.json')); let version = path.basename(sdkPath).slice(this.prefixDefault.length); avds.forEach((avd: string) => { - child_process.spawnSync(path.join(sdkPath, 'tools', 'android'), ['delete', - 'avd', '-n', avd + '-v' + version + '-wd-manager']); + child_process.spawnSync( + path.join(sdkPath, 'tools', 'android'), + ['delete', 'avd', '-n', avd + '-v' + version + '-wd-manager']); }); - } catch (e) {} + } catch (e) { + } rimraf.sync(sdkPath); } } diff --git a/lib/binaries/appium.ts b/lib/binaries/appium.ts index 810a3d38..ffd36e74 100644 --- a/lib/binaries/appium.ts +++ b/lib/binaries/appium.ts @@ -1,11 +1,13 @@ import * as child_process from 'child_process'; -import * as rimraf from 'rimraf'; -import * as path from 'path'; import {arch, type} from 'os'; +import * as path from 'path'; +import * as rimraf from 'rimraf'; -import {Binary, OS} from './binary'; import {Config} from '../config'; +import {Binary, OS} from './binary'; + + /** * The appium binary. */ @@ -30,7 +32,5 @@ export class Appium extends Binary { executableSuffix(): string { return ''; } - remove(sdkPath: string): void { - rimraf.sync(sdkPath); - } + remove(sdkPath: string): void { rimraf.sync(sdkPath); } } diff --git a/lib/binaries/binary.ts b/lib/binaries/binary.ts index 5d3e5d94..d27c4be8 100644 --- a/lib/binaries/binary.ts +++ b/lib/binaries/binary.ts @@ -19,16 +19,16 @@ export interface BinaryMap { [id: string]: T; } * The binary object base class */ export class Binary { - static os: OS[]; // the operating systems, the binary can run on - static id: string; // the binaries key identifier - static isDefault: boolean; // to download by default - static versionDefault: string; // a static default version variable - static shortName: string[]; // the names used for a binary download - name: string; // used for logging to console - prefixDefault: string; // start of the file name - versionCustom: string; // version of file - suffixDefault: string; // file type for downloading - cdn: string; // url protocol and host + static os: OS[]; // the operating systems, the binary can run on + static id: string; // the binaries key identifier + static isDefault: boolean; // to download by default + static versionDefault: string; // a static default version variable + static shortName: string[]; // the names used for a binary download + name: string; // used for logging to console + prefixDefault: string; // start of the file name + versionCustom: string; // version of file + suffixDefault: string; // file type for downloading + cdn: string; // url protocol and host arch: string; /** @@ -57,15 +57,15 @@ export class Binary { suffix(ostype?: string, arch?: string): string { return this.suffixDefault; } - filename(ostype?: string, arch?: string): string { return this.prefix() + this.version() + this.suffix(ostype, arch); } + filename(ostype?: string, arch?: string): string { + return this.prefix() + this.version() + this.suffix(ostype, arch); + } /** * @param ostype The operating system. * @returns The file name for the file inside the downloaded zip file */ - zipContentName(ostype: string): string { - return this.name + this.executableSuffix(ostype); - } + zipContentName(ostype: string): string { return this.name + this.executableSuffix(ostype); } shortVersion(version: string): string { return version.slice(0, version.lastIndexOf('.')); } diff --git a/lib/binaries/chrome_driver.ts b/lib/binaries/chrome_driver.ts index 538ea29a..39613a49 100644 --- a/lib/binaries/chrome_driver.ts +++ b/lib/binaries/chrome_driver.ts @@ -1,8 +1,10 @@ import {arch, type} from 'os'; -import {Binary, OS} from './binary'; import {Config} from '../config'; +import {Binary, OS} from './binary'; + + /** * The chrome driver binary. */ diff --git a/lib/binaries/ie_driver.ts b/lib/binaries/ie_driver.ts index 0df19cda..c0ac9ff5 100644 --- a/lib/binaries/ie_driver.ts +++ b/lib/binaries/ie_driver.ts @@ -1,8 +1,10 @@ import * as os from 'os'; -import {Binary, OS} from './binary'; import {Config} from '../config'; +import {Binary, OS} from './binary'; + + /** * The internet explorer binary. */ diff --git a/lib/binaries/stand_alone.ts b/lib/binaries/stand_alone.ts index 06566743..db793b34 100644 --- a/lib/binaries/stand_alone.ts +++ b/lib/binaries/stand_alone.ts @@ -1,6 +1,8 @@ -import {Binary, OS} from './binary'; import {Config} from '../config'; +import {Binary, OS} from './binary'; + + /** * The selenium server jar. */ diff --git a/lib/cli/cli.ts b/lib/cli/cli.ts index 3b8e9817..e9971b61 100644 --- a/lib/cli/cli.ts +++ b/lib/cli/cli.ts @@ -2,8 +2,10 @@ import * as chalk from 'chalk'; import * as path from 'path'; import {Config} from '../config'; -import {Programs, Program} from './programs'; + import {MinimistArgs, Options} from './options'; +import {Program, Programs} from './programs'; + /** @@ -119,7 +121,7 @@ export class Cli { */ getMinimistOptions(): Object { let allOptions = this.getOptions(); - let minimistOptions: MinimistArgs = {} + let minimistOptions: MinimistArgs = {}; let minimistBoolean: string[] = []; let minimistString: string[] = []; let minimistNumber: string[] = []; @@ -135,7 +137,7 @@ export class Cli { } minimistOptions['boolean'] = minimistBoolean; minimistOptions['string'] = minimistString; - minimistOptions['number']= minimistNumber; + minimistOptions['number'] = minimistNumber; return minimistOptions; } } diff --git a/lib/cli/options.ts b/lib/cli/options.ts index 187c4286..85915042 100644 --- a/lib/cli/options.ts +++ b/lib/cli/options.ts @@ -11,7 +11,8 @@ export class Option { defaultValue: number|string|boolean; value: number|string|boolean; - constructor(opt: string, description: string, type: string, defaultValue?: number|string|boolean) { + constructor( + opt: string, description: string, type: string, defaultValue?: number|string|boolean) { this.opt = opt; this.description = description; this.type = type; diff --git a/lib/cli/programs.ts b/lib/cli/programs.ts index 3327f289..81922003 100644 --- a/lib/cli/programs.ts +++ b/lib/cli/programs.ts @@ -1,5 +1,7 @@ import * as minimist from 'minimist'; -import {Args, MinimistArgs, Options, Option} from './options'; + +import {Args, MinimistArgs, Option, Options} from './options'; + /** * Dictionary that maps the command and the program. @@ -38,7 +40,8 @@ export class Program { * @param defaultValue The option's default value. * @returns The program for method chaining. */ - option(opt: string, description: string, type: string, opt_defaultValue?: number|string|boolean): Program { + option(opt: string, description: string, type: string, opt_defaultValue?: number|string|boolean): + Program { this.options[opt] = new Option(opt, description, type, opt_defaultValue); return this; } @@ -77,7 +80,7 @@ export class Program { private getValue_(key: string, json: JSON): string { let keyList: string[] = key.split('.'); - let tempJson:any = json; + let tempJson: any = json; while (keyList.length > 0) { let keyItem = keyList[0]; if (tempJson[keyItem]) { @@ -216,7 +219,7 @@ export class Program { getMinimistOptions() { let allOptions: Options = {}; allOptions = this.getOptions_(allOptions); - let minimistOptions: MinimistArgs = {} + let minimistOptions: MinimistArgs = {}; let minimistBoolean: string[] = []; let minimistString: string[] = []; let minimistNumber: string[] = []; @@ -232,7 +235,7 @@ export class Program { } minimistOptions['boolean'] = minimistBoolean; minimistOptions['string'] = minimistString; - minimistOptions['number']= minimistNumber; + minimistOptions['number'] = minimistNumber; return minimistOptions; } } diff --git a/lib/cmds/clean.ts b/lib/cmds/clean.ts index 4a5b8d99..39d5961d 100644 --- a/lib/cmds/clean.ts +++ b/lib/cmds/clean.ts @@ -1,16 +1,17 @@ import * as minimist from 'minimist'; import * as path from 'path'; +import {Options, Program} from '../cli'; import {Config} from '../config'; import {FileManager} from '../files'; -import {Options, Program} from '../cli'; -import {Opts} from './opts'; + import * as Opt from './'; +import {Opts} from './opts'; let prog = new Program() - .command('clean', 'removes all downloaded driver files from the out_dir') - .action(clean) - .addOption(Opts[Opt.OUT_DIR]); + .command('clean', 'removes all downloaded driver files from the out_dir') + .action(clean) + .addOption(Opts[Opt.OUT_DIR]); export var program = prog; diff --git a/lib/cmds/initialize.ts b/lib/cmds/initialize.ts index 5c38e22f..17bb14db 100644 --- a/lib/cmds/initialize.ts +++ b/lib/cmds/initialize.ts @@ -1,10 +1,11 @@ +import * as child_process from 'child_process'; +import * as fs from 'fs'; +import * as glob from 'glob'; +import * as ini from 'ini'; import * as os from 'os'; import * as path from 'path'; import * as q from 'q'; -import * as glob from 'glob'; -import * as ini from 'ini'; -import * as fs from 'fs'; -import * as child_process from 'child_process'; + import {Logger} from '../cli'; const noop = () => {}; @@ -14,7 +15,7 @@ const noop = () => {}; function respondFactory(question: string, answer: string): Function { return (child: child_process.ChildProcess) => { (child.stdin).setDefaultEncoding('utf-8'); - child.stdout.on('data', (data: Buffer|String) => { + child.stdout.on('data', (data: Buffer | String) => { if (data != null) { if (data.toString().indexOf(question) != -1) { child.stdin.write(answer + '\n'); @@ -25,13 +26,13 @@ function respondFactory(question: string, answer: string): Function { } // Run a command on the android SDK -function runAndroidSDKCommand(sdkPath: string, cmd: string, args: string[], - spawnOptions: Object, config_fun?: Function): q.Promise { - - let child = child_process.spawn(path.join(sdkPath, 'tools', 'android'), - [cmd].concat(args), spawnOptions); +function runAndroidSDKCommand( + sdkPath: string, cmd: string, args: string[], spawnOptions: Object, + config_fun?: Function): q.Promise { + let child = + child_process.spawn(path.join(sdkPath, 'tools', 'android'), [cmd].concat(args), spawnOptions); - if(config_fun) { + if (config_fun) { config_fun(child); }; @@ -56,53 +57,58 @@ function runAndroidSDKCommand(sdkPath: string, cmd: string, args: string[], } // Download updates via the android SDK -function downloadAndroidUpdates(sdkPath: string, targets: string[], - search_all: boolean, auto_accept: boolean): q.Promise { - - return runAndroidSDKCommand(sdkPath, 'update', ['sdk', '-u'].concat(search_all - ? ['-a'] : []).concat(['-t', targets.join(',')]), +function downloadAndroidUpdates( + sdkPath: string, targets: string[], search_all: boolean, auto_accept: boolean): q.Promise { + return runAndroidSDKCommand( + sdkPath, 'update', + ['sdk', '-u'].concat(search_all ? ['-a'] : []).concat(['-t', targets.join(',')]), {stdio: auto_accept ? 'pipe' : 'inherit'}, auto_accept ? respondFactory('Do you accept the license', 'y') : noop); } // Setup hardware acceleration for x86-64 emulation function setupHardwareAcceleration(sdkPath: string) { - //TODO(sjelin): check that the BIOS option is set properly on linux + // TODO(sjelin): check that the BIOS option is set properly on linux if (os.type() == 'Darwin') { console.log('Enabling hardware acceleration (requires root access)'); - child_process.spawnSync('sudo', [path.join(sdkPath, 'extras', 'intel', - 'Hardware_Accelerated_Execution_Manager', 'silent_install.sh')], + child_process.spawnSync( + 'sudo', [path.join( + sdkPath, 'extras', 'intel', 'Hardware_Accelerated_Execution_Manager', + 'silent_install.sh')], {stdio: 'inherit'}); } else if (os.type() == 'Windows_NT') { console.log('Enabling hardware acceleration (requires admin access)'); - child_process.spawnSync('runas', ['/noprofile', '/user:Administrator', - path.join(sdkPath, 'extras', 'intel', - 'Hardware_Accelerated_Execution_Manager', 'silent_install.bat')], + child_process.spawnSync( + 'runas', + [ + '/noprofile', '/user:Administrator', + path.join( + sdkPath, 'extras', 'intel', 'Hardware_Accelerated_Execution_Manager', + 'silent_install.bat') + ], {stdio: 'inherit'}); } } // Get a list of all the SDK download targets for a given set of APIs and ABIs function getAndroidSDKTargets(apiLevels: string[], abis: string[]): string[] { - return apiLevels.map((level) => { - return 'android-' + level; - }).concat(abis.reduce((targets, abi) => { - let abiParts: string[] = abi.split('/'); - let deviceType: string = 'default'; - let architecture: string; - if (abiParts.length == 1) { - architecture = abiParts[0]; - } else { - deviceType = abiParts[0]; - architecture = abiParts[1]; - } - if (deviceType.toUpperCase() == 'DEFAULT') { - deviceType = 'android'; - } - return targets.concat(apiLevels.map((level) => { - return 'sys-img-' + architecture + '-' + deviceType + '-' + level; - })); - }, [])); + return apiLevels.map((level) => { return 'android-' + level; }) + .concat(abis.reduce((targets, abi) => { + let abiParts: string[] = abi.split('/'); + let deviceType: string = 'default'; + let architecture: string; + if (abiParts.length == 1) { + architecture = abiParts[0]; + } else { + deviceType = abiParts[0]; + architecture = abiParts[1]; + } + if (deviceType.toUpperCase() == 'DEFAULT') { + deviceType = 'android'; + } + return targets.concat(apiLevels.map( + (level) => { return 'sys-img-' + architecture + '-' + deviceType + '-' + level; })); + }, [])); } // All the information about an android virtual device @@ -117,82 +123,72 @@ class AVDDescriptor { this.api = api; this.deviceType = deviceType; this.architecture = architecture; - this.abi = (deviceType.toUpperCase() == 'DEFAULT' ? '' : deviceType + '/') + - architecture; + this.abi = (deviceType.toUpperCase() == 'DEFAULT' ? '' : deviceType + '/') + architecture; this.name = [api, deviceType, architecture].join('-'); } - avdName(version: string): string { - return this.name + '-v' + version + '-wd-manager'; - } + avdName(version: string): string { return this.name + '-v' + version + '-wd-manager'; } } // Gets the descriptors for all AVDs which are possible to make given the // SDKs which were downloaded function getAVDDescriptors(sdkPath: string): q.Promise { let deferred = q.defer(); - glob(path.join(sdkPath, 'system-images', '*', '*', '*'), - (err: Error, files: string[]) => { - if (err) { - deferred.reject(err); - } else { - deferred.resolve(files.map((file: string) => { - let info = file.split(path.sep).slice(-3); - return new AVDDescriptor(info[0], info[1], info[2]); - })); - } - } - ); + glob(path.join(sdkPath, 'system-images', '*', '*', '*'), (err: Error, files: string[]) => { + if (err) { + deferred.reject(err); + } else { + deferred.resolve(files.map((file: string) => { + let info = file.split(path.sep).slice(-3); + return new AVDDescriptor(info[0], info[1], info[2]); + })); + } + }); return deferred.promise; } -function sequentialForEach(array: T[], func: (x: T) => q.Promise): - q.Promise { - +function sequentialForEach(array: T[], func: (x: T) => q.Promise): q.Promise { let ret = q(null); - array.forEach((x: T) => { - ret = ret.then(() => { return func(x); }); - }); + array.forEach((x: T) => { ret = ret.then(() => { return func(x); }); }); return ret; } // Configures the hardware.ini file for a system image of a new AVD -function configureAVDHardware(sdkPath: string, desc: AVDDescriptor): - q.Promise { - let file = path.join(sdkPath, 'system-images', desc.api, desc.deviceType, - desc.architecture, 'hardware.ini'); - return q.nfcall(fs.stat, file).then((stats: fs.Stats) => { - return q.nfcall(fs.readFile, file); - }, (err: Error) => { - return q(''); - }).then((contents: string|Buffer) => { - let config: any = ini.parse(contents.toString()); - config['hw.keyboard'] = 'yes'; - config['hw.battery'] = 'yes'; - config['hw.ramSize'] = 1024; - return q.nfcall(fs.writeFile, file, ini.stringify(config)); - }); +function configureAVDHardware(sdkPath: string, desc: AVDDescriptor): q.Promise { + let file = path.join( + sdkPath, 'system-images', desc.api, desc.deviceType, desc.architecture, 'hardware.ini'); + return q.nfcall(fs.stat, file) + .then( + (stats: fs.Stats) => { return q.nfcall(fs.readFile, file); }, + (err: Error) => { return q(''); }) + .then((contents: string | Buffer) => { + let config: any = ini.parse(contents.toString()); + config['hw.keyboard'] = 'yes'; + config['hw.battery'] = 'yes'; + config['hw.ramSize'] = 1024; + return q.nfcall(fs.writeFile, file, ini.stringify(config)); + }); } // Make an android virtual device -function makeAVD(sdkPath: string, desc: AVDDescriptor, version: string): - q.Promise { - return runAndroidSDKCommand(sdkPath, 'delete', - ['avd', '--name', desc.avdName(version)], {}) - .then(noop, noop).then(() => { - return runAndroidSDKCommand(sdkPath, 'create', - ['avd', '--name', desc.avdName(version), '--target', desc.api, '--abi', - desc.abi], {stdio: 'pipe'}, - respondFactory('Do you wish to create a custom hardware profile', 'no')); - }); +function makeAVD(sdkPath: string, desc: AVDDescriptor, version: string): q.Promise { + return runAndroidSDKCommand(sdkPath, 'delete', ['avd', '--name', desc.avdName(version)], {}) + .then(noop, noop) + .then(() => { + return runAndroidSDKCommand( + sdkPath, 'create', + ['avd', '--name', desc.avdName(version), '--target', desc.api, '--abi', desc.abi], + {stdio: 'pipe'}, + respondFactory('Do you wish to create a custom hardware profile', 'no')); + }); } // Initialize the android SDK -export function android(sdkPath: string, apiLevels: string[], abis: string[], - acceptLicenses: boolean, version: string, logger: Logger): void { - +export function android( + sdkPath: string, apiLevels: string[], abis: string[], acceptLicenses: boolean, version: string, + logger: Logger): void { let avdDescriptors: AVDDescriptor[]; let tools = ['platform-tool', 'tool']; if ((os.type() == 'Darwin') || (os.type() == 'Windows_NT')) { @@ -200,35 +196,38 @@ export function android(sdkPath: string, apiLevels: string[], abis: string[], } logger.info('android-sdk: Downloading additional SDK updates'); - downloadAndroidUpdates(sdkPath, tools, false, acceptLicenses).then(() => { - return setupHardwareAcceleration(sdkPath); - }).then(() => { - logger.info('android-sdk: Downloading more additional SDK updates ' + - '(this may take a while)'); - return downloadAndroidUpdates(sdkPath, ['build-tools-24.0.0']. - concat(getAndroidSDKTargets(apiLevels, abis)), true, acceptLicenses); - }).then(() => { - return getAVDDescriptors(sdkPath); - }).then((descriptors: AVDDescriptor[]) => { - avdDescriptors = descriptors; - logger.info('android-sdk: Configuring virtual device hardware'); - return sequentialForEach(avdDescriptors, (descriptor: AVDDescriptor) => { - return configureAVDHardware(sdkPath, descriptor); - }); - }).then(() => { - return sequentialForEach(avdDescriptors, (descriptor: AVDDescriptor) => { - logger.info('android-sdk: Setting up virtual device "' + descriptor.name + - '"'); - return makeAVD(sdkPath, descriptor, version); - }); - }).then(() => { - return q.nfcall(fs.writeFile, path.join(sdkPath, 'available_avds.json'), - JSON.stringify(avdDescriptors.map((descriptor: AVDDescriptor) => { - return descriptor.name; - }))); - }).then(() => { - logger.info('android-sdk: Initialization complete'); - }).done(); + downloadAndroidUpdates(sdkPath, tools, false, acceptLicenses) + .then(() => { return setupHardwareAcceleration(sdkPath); }) + .then(() => { + logger.info( + 'android-sdk: Downloading more additional SDK updates ' + + '(this may take a while)'); + return downloadAndroidUpdates( + sdkPath, ['build-tools-24.0.0'].concat(getAndroidSDKTargets(apiLevels, abis)), true, + acceptLicenses); + }) + .then(() => { return getAVDDescriptors(sdkPath); }) + .then((descriptors: AVDDescriptor[]) => { + avdDescriptors = descriptors; + logger.info('android-sdk: Configuring virtual device hardware'); + return sequentialForEach(avdDescriptors, (descriptor: AVDDescriptor) => { + return configureAVDHardware(sdkPath, descriptor); + }); + }) + .then(() => { + return sequentialForEach(avdDescriptors, (descriptor: AVDDescriptor) => { + logger.info('android-sdk: Setting up virtual device "' + descriptor.name + '"'); + return makeAVD(sdkPath, descriptor, version); + }); + }) + .then(() => { + return q.nfcall( + fs.writeFile, path.join(sdkPath, 'available_avds.json'), + JSON.stringify( + avdDescriptors.map((descriptor: AVDDescriptor) => { return descriptor.name; }))); + }) + .then(() => { logger.info('android-sdk: Initialization complete'); }) + .done(); }; export function iOS(logger: Logger) { @@ -237,7 +236,7 @@ export function iOS(logger: Logger) { } try { fs.statSync('/Applications/Xcode.app'); - } catch(e) { + } catch (e) { logger.warn('You must install the xcode commandline tools!'); } } diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index f7c1855d..bfec122a 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -1,6 +1,6 @@ -import {Config} from '../config'; +import {AndroidSDK, Appium, ChromeDriver, IEDriver, StandAlone} from '../binaries'; import {Cli, Option, Options} from '../cli'; -import {ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlone} from '../binaries'; +import {Config} from '../config'; export const OUT_DIR = 'out_dir'; export const SELENIUM_PORT = 'seleniumPort'; @@ -33,29 +33,52 @@ export const AVD_USE_SNAPSHOTS = 'avd-use-snapshots'; */ var opts: Options = {}; opts[OUT_DIR] = new Option(OUT_DIR, 'Location to output/expect', 'string', Config.getSeleniumDir()); -opts[SELENIUM_PORT] = new Option(SELENIUM_PORT, 'Optional port for the selenium standalone server', 'string'); +opts[SELENIUM_PORT] = + new Option(SELENIUM_PORT, 'Optional port for the selenium standalone server', 'string'); opts[APPIUM_PORT] = new Option(APPIUM_PORT, 'Optional port for the appium server', 'string'); -opts[AVD_PORT] = new Option(AVD_PORT, 'Optional port for android virtual devices. See mobile.md for details', 'string'); +opts[AVD_PORT] = new Option( + AVD_PORT, 'Optional port for android virtual devices. See mobile.md for details', 'string'); opts[IGNORE_SSL] = new Option(IGNORE_SSL, 'Ignore SSL certificates', 'boolean', false); opts[PROXY] = new Option(PROXY, 'Proxy to use for the install or update command', 'string'); opts[ALTERNATE_CDN] = new Option(ALTERNATE_CDN, 'Alternate CDN to binaries', 'string'); -opts[STANDALONE] = new Option(STANDALONE, 'Install or update selenium standalone', 'boolean', StandAlone.isDefault); -opts[CHROME] = new Option(CHROME, 'Install or update chromedriver', 'boolean', ChromeDriver.isDefault); +opts[STANDALONE] = new Option( + STANDALONE, 'Install or update selenium standalone', 'boolean', StandAlone.isDefault); +opts[CHROME] = + new Option(CHROME, 'Install or update chromedriver', 'boolean', ChromeDriver.isDefault); opts[IE] = new Option(IE, 'Install or update ie driver', 'boolean', IEDriver.isDefault); opts[IE32] = new Option(IE32, 'Install or update 32-bit ie driver', 'boolean', IEDriver.isDefault); -opts[EDGE] = new Option(EDGE, 'Use installed Microsoft Edge driver', 'string', 'C:\\Program Files (x86)\\Microsoft Web Driver\\MicrosoftWebDriver.exe'); +opts[EDGE] = new Option( + EDGE, 'Use installed Microsoft Edge driver', 'string', + 'C:\\Program Files (x86)\\Microsoft Web Driver\\MicrosoftWebDriver.exe'); opts[ANDROID] = new Option(ANDROID, 'Update/use the android sdk', 'boolean', AndroidSDK.isDefault); opts[IOS] = new Option(IOS, 'Update the iOS sdk', 'boolean', false); -opts[VERSIONS_CHROME] = new Option(VERSIONS_CHROME, 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); -opts[VERSIONS_ANDROID] = new Option(VERSIONS_ANDROID, 'Optional android sdk version', 'string', AndroidSDK.versionDefault); -opts[VERSIONS_STANDALONE] = new Option(VERSIONS_STANDALONE, 'Optional seleniuim standalone server version', 'string', StandAlone.versionDefault); -opts[VERSIONS_APPIUM] = new Option(VERSIONS_APPIUM, 'Optional appium version', 'string', Appium.versionDefault); -opts[VERSIONS_IE] = new Option(VERSIONS_IE, 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); +opts[VERSIONS_CHROME] = new Option( + VERSIONS_CHROME, 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); +opts[VERSIONS_ANDROID] = new Option( + VERSIONS_ANDROID, 'Optional android sdk version', 'string', AndroidSDK.versionDefault); +opts[VERSIONS_STANDALONE] = new Option( + VERSIONS_STANDALONE, 'Optional seleniuim standalone server version', 'string', + StandAlone.versionDefault); +opts[VERSIONS_APPIUM] = + new Option(VERSIONS_APPIUM, 'Optional appium version', 'string', Appium.versionDefault); +opts[VERSIONS_IE] = new Option( + VERSIONS_IE, 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); opts[CHROME_LOGS] = new Option(CHROME_LOGS, 'File path to chrome logs', 'string', undefined); -opts[ANDROID_API_LEVELS] = new Option(ANDROID_API_LEVELS, 'Which versions of the android API you want to emulate', 'string', AndroidSDK.DEFAULT_API_LEVELS); -opts[ANDROID_ABIS] = new Option(ANDROID_ABIS, 'Which ABIs you want to use in android emulation', 'string', AndroidSDK.DEFAULT_ABIS); -opts[ANDROID_ACCEPT_LICENSES] = new Option(ANDROID_ACCEPT_LICENSES, 'Automatically accept android licenses', 'boolean', false); -opts[AVDS] = new Option(AVDS, 'Android virtual devices to emulate. Use "all" for emulating all possible devices, and "none" for no devices', 'string', 'all'); -opts[AVD_USE_SNAPSHOTS] = new Option(AVD_USE_SNAPSHOTS, 'Rather than booting a new AVD every time, save/load snapshots of the last time it was used', 'boolean', true); +opts[ANDROID_API_LEVELS] = new Option( + ANDROID_API_LEVELS, 'Which versions of the android API you want to emulate', 'string', + AndroidSDK.DEFAULT_API_LEVELS); +opts[ANDROID_ABIS] = new Option( + ANDROID_ABIS, 'Which ABIs you want to use in android emulation', 'string', + AndroidSDK.DEFAULT_ABIS); +opts[ANDROID_ACCEPT_LICENSES] = + new Option(ANDROID_ACCEPT_LICENSES, 'Automatically accept android licenses', 'boolean', false); +opts[AVDS] = new Option( + AVDS, + 'Android virtual devices to emulate. Use "all" for emulating all possible devices, and "none" for no devices', + 'string', 'all'); +opts[AVD_USE_SNAPSHOTS] = new Option( + AVD_USE_SNAPSHOTS, + 'Rather than booting a new AVD every time, save/load snapshots of the last time it was used', + 'boolean', true); export var Opts = opts; diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index b004a18f..bc18783a 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -1,34 +1,34 @@ +import * as childProcess from 'child_process'; import * as fs from 'fs'; +import * as http from 'http'; import * as minimist from 'minimist'; -import * as path from 'path'; import * as os from 'os'; -import * as childProcess from 'child_process'; -import * as http from 'http'; +import * as path from 'path'; -import {Opts} from './opts'; -import * as Opt from './'; +import {AndroidSDK, Appium, Binary, BinaryMap, ChromeDriver, IEDriver, StandAlone} from '../binaries'; +import {Logger, Options, Program} from '../cli'; import {Config} from '../config'; import {FileManager} from '../files'; -import {Logger, Options, Program} from '../cli'; -import {BinaryMap, Binary, ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlone} from - '../binaries'; + +import * as Opt from './'; +import {Opts} from './opts'; let logger = new Logger('start'); let prog = new Program() - .command('start', 'start up the selenium server') - .action(start) - .addOption(Opts[Opt.OUT_DIR]) - .addOption(Opts[Opt.SELENIUM_PORT]) - .addOption(Opts[Opt.APPIUM_PORT]) - .addOption(Opts[Opt.AVD_PORT]) - .addOption(Opts[Opt.VERSIONS_STANDALONE]) - .addOption(Opts[Opt.VERSIONS_CHROME]) - .addOption(Opts[Opt.VERSIONS_ANDROID]) - .addOption(Opts[Opt.VERSIONS_APPIUM]) - .addOption(Opts[Opt.CHROME_LOGS]) - .addOption(Opts[Opt.ANDROID]) - .addOption(Opts[Opt.AVDS]) - .addOption(Opts[Opt.AVD_USE_SNAPSHOTS]); + .command('start', 'start up the selenium server') + .action(start) + .addOption(Opts[Opt.OUT_DIR]) + .addOption(Opts[Opt.SELENIUM_PORT]) + .addOption(Opts[Opt.APPIUM_PORT]) + .addOption(Opts[Opt.AVD_PORT]) + .addOption(Opts[Opt.VERSIONS_STANDALONE]) + .addOption(Opts[Opt.VERSIONS_CHROME]) + .addOption(Opts[Opt.VERSIONS_ANDROID]) + .addOption(Opts[Opt.VERSIONS_APPIUM]) + .addOption(Opts[Opt.CHROME_LOGS]) + .addOption(Opts[Opt.ANDROID]) + .addOption(Opts[Opt.AVDS]) + .addOption(Opts[Opt.AVD_USE_SNAPSHOTS]); if (os.type() === 'Darwin') { prog.addOption(Opts[Opt.IOS]); @@ -123,16 +123,14 @@ function start(options: Options) { path.join(outputDir, binaries[IEDriver.id].executableFilename(osType))); } if (options[Opt.EDGE]) { - args.push( - '-Dwebdriver.edge.driver=' + - options[Opt.EDGE].getString()); + args.push('-Dwebdriver.edge.driver=' + options[Opt.EDGE].getString()); } if (options[Opt.ANDROID].getBoolean()) { if (downloadedBinaries[AndroidSDK.id] != null) { let avds = options[Opt.AVDS].getString(); - startAndroid(outputDir, binaries[AndroidSDK.id], avds.split(','), - options[Opt.AVD_USE_SNAPSHOTS].getBoolean(), - options[Opt.AVD_PORT].getString()); + startAndroid( + outputDir, binaries[AndroidSDK.id], avds.split(','), + options[Opt.AVD_USE_SNAPSHOTS].getBoolean(), options[Opt.AVD_PORT].getString()); } else { logger.warn('Not starting android because it is not installed'); } @@ -173,7 +171,7 @@ function spawnCommand(command: string, args?: string[]) { let osType = os.type(); let windows: boolean = osType === 'Windows_NT'; let winCommand = windows ? 'cmd' : command; - let finalArgs: string[] = windows ? ['/c'].concat([command],args) : args; + let finalArgs: string[] = windows ? ['/c'].concat([command], args) : args; return childProcess.spawn(winCommand, finalArgs, {stdio: 'inherit'}); } @@ -181,8 +179,8 @@ function spawnCommand(command: string, args?: string[]) { // Manage processes used in android emulation let androidProcesses: childProcess.ChildProcess[] = []; -function startAndroid(outputDir: string, sdk: Binary, avds: string[], - useSnapshots: boolean, port: string): void { +function startAndroid( + outputDir: string, sdk: Binary, avds: string[], useSnapshots: boolean, port: string): void { let sdkPath = path.join(outputDir, sdk.executableFilename(os.type())); if (avds[0] == 'all') { avds = require(path.join(sdkPath, 'available_avds.json')); @@ -192,29 +190,29 @@ function startAndroid(outputDir: string, sdk: Binary, avds: string[], avds.forEach((avd: string, i: number) => { logger.info('Booting up AVD ' + avd); // Credit to appium-ci, which this code was adapted from - let emuBin = 'emulator'; //TODO(sjelin): get the 64bit linux version working + let emuBin = 'emulator'; // TODO(sjelin): get the 64bit linux version working let emuArgs = [ - '-avd', avd + '-v' + sdk.versionCustom + '-wd-manager', + '-avd', + avd + '-v' + sdk.versionCustom + '-wd-manager', '-netfast', ]; if (!useSnapshots) { emuArgs = emuArgs.concat(['-no-snapshot-load', '-no-snapshot-save']); } if (port) { - emuArgs = emuArgs.concat(['-ports', (port+2*i) + ',' + (port+2*i+1)]); + emuArgs = emuArgs.concat(['-ports', (port + 2 * i) + ',' + (port + 2 * i + 1)]); } if (emuBin !== 'emulator') { emuArgs = emuArgs.concat(['-qemu', '-enable-kvm']); } - androidProcesses.push(childProcess.spawn(path.join(sdkPath, 'tools', - emuBin), emuArgs, {stdio: 'inherit'})); + androidProcesses.push( + childProcess.spawn(path.join(sdkPath, 'tools', emuBin), emuArgs, {stdio: 'inherit'})); }); } function killAndroid() { - androidProcesses.forEach((androidProcess: childProcess.ChildProcess) => { - androidProcess.kill(); - }); + androidProcesses.forEach( + (androidProcess: childProcess.ChildProcess) => { androidProcess.kill(); }); androidProcesses.length = 0; } @@ -223,8 +221,9 @@ let appiumProcess: childProcess.ChildProcess; function startAppium(outputDir: string, binary: Binary, port: string) { logger.info('Starting appium server'); - appiumProcess = childProcess.spawn(path.join(outputDir, binary.filename(), - 'node_modules', '.bin', 'appium'), port ? ['--port', port] : []); + appiumProcess = childProcess.spawn( + path.join(outputDir, binary.filename(), 'node_modules', '.bin', 'appium'), + port ? ['--port', port] : []); } function killAppium() { diff --git a/lib/cmds/status.ts b/lib/cmds/status.ts index 29d8f6b7..385abc08 100644 --- a/lib/cmds/status.ts +++ b/lib/cmds/status.ts @@ -2,17 +2,18 @@ import * as fs from 'fs'; import * as minimist from 'minimist'; import * as path from 'path'; -import {Opts} from './opts'; -import * as Opt from './'; +import {Logger, Options, Program} from '../cli'; import {Config} from '../config'; import {FileManager} from '../files'; -import {Logger, Options, Program} from '../cli'; + +import * as Opt from './'; +import {Opts} from './opts'; let logger = new Logger('status'); let prog = new Program() - .command('status', 'list the current available drivers') - .addOption(Opts[Opt.OUT_DIR]) - .action(status); + .command('status', 'list the current available drivers') + .addOption(Opts[Opt.OUT_DIR]) + .action(status); export var program = prog; diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 6eeb7244..9f599a1c 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -7,28 +7,29 @@ import * as path from 'path'; import * as q from 'q'; import * as rimraf from 'rimraf'; -import {Opts} from './opts'; -import * as Opt from './'; -import {Config} from '../config'; -import {Binary, ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlone} from '../binaries'; -import {FileManager, Downloader} from '../files'; +import {AndroidSDK, Appium, Binary, ChromeDriver, IEDriver, StandAlone} from '../binaries'; import {Logger, Options, Program} from '../cli'; +import {Config} from '../config'; +import {Downloader, FileManager} from '../files'; + +import * as Opt from './'; import {android as initializeAndroid, iOS as checkIOS} from './initialize'; +import {Opts} from './opts'; let logger = new Logger('update'); let prog = new Program() - .command('update', 'install or update selected binaries') - .action(update) - .addOption(Opts[Opt.OUT_DIR]) - .addOption(Opts[Opt.IGNORE_SSL]) - .addOption(Opts[Opt.PROXY]) - .addOption(Opts[Opt.ALTERNATE_CDN]) - .addOption(Opts[Opt.STANDALONE]) - .addOption(Opts[Opt.CHROME]) - .addOption(Opts[Opt.ANDROID]) - .addOption(Opts[Opt.ANDROID_API_LEVELS]) - .addOption(Opts[Opt.ANDROID_ABIS]) - .addOption(Opts[Opt.ANDROID_ACCEPT_LICENSES]); + .command('update', 'install or update selected binaries') + .action(update) + .addOption(Opts[Opt.OUT_DIR]) + .addOption(Opts[Opt.IGNORE_SSL]) + .addOption(Opts[Opt.PROXY]) + .addOption(Opts[Opt.ALTERNATE_CDN]) + .addOption(Opts[Opt.STANDALONE]) + .addOption(Opts[Opt.CHROME]) + .addOption(Opts[Opt.ANDROID]) + .addOption(Opts[Opt.ANDROID_API_LEVELS]) + .addOption(Opts[Opt.ANDROID_ABIS]) + .addOption(Opts[Opt.ANDROID_ACCEPT_LICENSES]); if (os.type() === 'Darwin') { prog.addOption(Opts[Opt.IOS]); @@ -38,11 +39,10 @@ if (os.type() === 'Windows_NT') { prog.addOption(Opts[Opt.IE]).addOption(Opts[Opt.IE32]); } -prog - .addOption(Opts[Opt.VERSIONS_STANDALONE]) - .addOption(Opts[Opt.VERSIONS_CHROME]) - .addOption(Opts[Opt.VERSIONS_APPIUM]) - .addOption(Opts[Opt.VERSIONS_ANDROID]); +prog.addOption(Opts[Opt.VERSIONS_STANDALONE]) + .addOption(Opts[Opt.VERSIONS_CHROME]) + .addOption(Opts[Opt.VERSIONS_APPIUM]) + .addOption(Opts[Opt.VERSIONS_ANDROID]); if (os.type() === 'Windows_NT') { prog.addOption(Opts[Opt.VERSIONS_IE]); @@ -104,14 +104,17 @@ function update(options: Options): void { binaries[Appium.id].versionCustom = options[Opt.VERSIONS_APPIUM].getString(); // if the file has not been completely downloaded, download it - // else if the file has already been downloaded, unzip the file, rename it, and give it permissions + // else if the file has already been downloaded, unzip the file, rename it, and give it + // permissions if (standalone) { let binary = binaries[StandAlone.id]; FileManager.toDownload(binary, outputDir).then((value: boolean) => { if (value) { Downloader.downloadBinary(binary, outputDir); } else { - logger.info(binary.name + ': file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); + logger.info( + binary.name + ': file exists ' + + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); logger.info(binary.name + ': v' + binary.versionCustom + ' up to date'); } }); @@ -122,7 +125,7 @@ function update(options: Options): void { } if (ie) { let binary = binaries[IEDriver.id]; - binary.arch = os.arch(); // Win32 or x64 + binary.arch = os.arch(); // Win32 or x64 updateBinary(binary, outputDir, proxy, ignoreSSL); } if (ie32) { @@ -135,9 +138,9 @@ function update(options: Options): void { let sdk_path = path.join(outputDir, binary.executableFilename(os.type())); updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => { - initializeAndroid(path.join(outputDir, binary.executableFilename(os.type( - ))), android_api_levels, android_abis, android_accept_licenses, - binaries[AndroidSDK.id].versionCustom, logger); + initializeAndroid( + path.join(outputDir, binary.executableFilename(os.type())), android_api_levels, + android_abis, android_accept_licenses, binaries[AndroidSDK.id].versionCustom, logger); }); } if (ios) { @@ -148,19 +151,22 @@ function update(options: Options): void { } } -function updateBinary(binary: Binary, outputDir: string, proxy: string, ignoreSSL: boolean): q.Promise { +function updateBinary( + binary: Binary, outputDir: string, proxy: string, ignoreSSL: boolean): q.Promise { return FileManager.toDownload(binary, outputDir).then((value: boolean) => { if (value) { let deferred = q.defer(); - Downloader.downloadBinary(binary, outputDir, proxy, ignoreSSL, - (binary: Binary, outputDir: string, fileName: string) => { - unzip(binary, outputDir, fileName); - deferred.resolve(); - } - ); + Downloader.downloadBinary( + binary, outputDir, proxy, ignoreSSL, + (binary: Binary, outputDir: string, fileName: string) => { + unzip(binary, outputDir, fileName); + deferred.resolve(); + }); return deferred.promise; } else { - logger.info(binary.name + ': file exists ' + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); + logger.info( + binary.name + ': file exists ' + + path.resolve(outputDir, binary.filename(os.type(), os.arch()))); let fileName = binary.filename(os.type(), os.arch()); unzip(binary, outputDir, fileName); logger.info(binary.name + ': v' + binary.versionCustom + ' up to date'); @@ -174,10 +180,11 @@ function unzip(binary: T, outputDir: string, fileName: string) let mv = path.join(outputDir, binary.executableFilename(osType)); try { fs.unlinkSync(mv); - } catch(err) { + } catch (err) { try { rimraf.sync(mv); - } catch (err2) {} + } catch (err2) { + } } // unzip the file @@ -187,8 +194,7 @@ function unzip(binary: T, outputDir: string, fileName: string) zip.extractAllTo(outputDir, true); } else { // We will only ever get .tar files on linux - child_process.spawnSync('tar', ['zxvf', path.resolve(outputDir, fileName), - '-C', outputDir]); + child_process.spawnSync('tar', ['zxvf', path.resolve(outputDir, fileName), '-C', outputDir]); } // rename @@ -202,7 +208,7 @@ function unzip(binary: T, outputDir: string, fileName: string) } else { fs.chmodSync(path.join(mv, 'tools', 'android'), '0755'); fs.chmodSync(path.join(mv, 'tools', 'emulator'), '0755'); - //TODO(sjelin): get 64 bit versions working + // TODO(sjelin): get 64 bit versions working } } } @@ -213,10 +219,10 @@ function installAppium(binary: Binary, outputDir: string): void { let folder = path.join(outputDir, binary.filename()); try { rimraf.sync(folder); - } catch(err) {} + } catch (err) { + } fs.mkdirSync(folder); fs.writeFileSync(path.join(folder, 'package.json'), '{}'); - child_process.spawn('npm', ['install', 'appium@' + binary.version()], {cwd: - folder}); + child_process.spawn('npm', ['install', 'appium@' + binary.version()], {cwd: folder}); } diff --git a/lib/config.ts b/lib/config.ts index 68a4ecfc..3c61ef8d 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -37,20 +37,12 @@ export class Config { static isProjectVersion = Config.folder === Config.nodeModuleName; static isLocalVersion = false; - static getConfigFile_(): string { - return path.resolve(Config.dir, '..', Config.configFile); - } + static getConfigFile_(): string { return path.resolve(Config.dir, '..', Config.configFile); } - static getPackageFile_(): string { - return path.resolve(Config.dir, '..', Config.packageFile) - } + static getPackageFile_(): string { return path.resolve(Config.dir, '..', Config.packageFile) } - static getSeleniumDir(): string { - return path.resolve(Config.dir, '..', '..', 'selenium/'); - } - static getBaseDir(): string { - return path.resolve(Config.dir, '..', '..'); - } + static getSeleniumDir(): string { return path.resolve(Config.dir, '..', '..', 'selenium/'); } + static getBaseDir(): string { return path.resolve(Config.dir, '..', '..'); } /** * Get the binary versions from the configuration file. diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index 7456f9a1..91ee8f06 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -1,10 +1,10 @@ +import * as fs from 'fs'; +import * as http from 'http'; import * as os from 'os'; import * as path from 'path'; -import * as fs from 'fs'; +import * as q from 'q'; import * as request from 'request'; import * as url from 'url'; -import * as q from 'q'; -import * as http from 'http'; import {Binary} from '../binaries/binary'; import {Logger} from '../cli'; @@ -15,7 +15,6 @@ let logger = new Logger('downloader'); * The file downloader. */ export class Downloader { - /** * Download the binary file. * @param binary The binary of interest. @@ -25,8 +24,8 @@ export class Downloader { * @param opt_callback Callback method to be executed after the file is downloaded. */ static downloadBinary( - binary: Binary, outputDir: string, opt_proxy?: string, - opt_ignoreSSL?: boolean, opt_callback?: Function): void { + binary: Binary, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, + opt_callback?: Function): void { logger.info(binary.name + ': downloading version ' + binary.version()); var url = binary.url(os.type(), os.arch()); if (!url) { @@ -34,7 +33,8 @@ export class Downloader { return; } Downloader.httpGetFile_( - url, binary.filename(os.type(), os.arch()), outputDir, opt_proxy, opt_ignoreSSL, (filePath: string) => { + url, binary.filename(os.type(), os.arch()), outputDir, opt_proxy, opt_ignoreSSL, + (filePath: string) => { if (opt_callback) { opt_callback(binary, outputDir, filePath); } @@ -72,7 +72,8 @@ export class Downloader { // If the HTTPS_PROXY and HTTP_PROXY environment variable is set, use that as the proxy if (protocol === 'https:') { - return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy; + return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || + process.env.http_proxy; } else if (protocol === 'http:') { return process.env.HTTP_PROXY || process.env.http_proxy; } @@ -80,7 +81,8 @@ export class Downloader { return null; } - static httpHeadContentLength(fileUrl: string, opt_proxy?: string, opt_ignoreSSL?: boolean): q.Promise { + static httpHeadContentLength(fileUrl: string, opt_proxy?: string, opt_ignoreSSL?: boolean): + q.Promise { let deferred = q.defer(); if (opt_ignoreSSL) { logger.info('ignoring SSL certificate'); @@ -95,11 +97,10 @@ export class Downloader { }; let contentLength = 0; - request(options) - .on('response', (response) => { - contentLength = response.headers['content-length']; - deferred.resolve(contentLength); - }); + request(options).on('response', (response) => { + contentLength = response.headers['content-length']; + deferred.resolve(contentLength); + }); return deferred.promise; } @@ -111,8 +112,8 @@ export class Downloader { * @param opt_ignoreSSL To ignore SSL. */ static httpGetFile_( - fileUrl: string, fileName: string, outputDir: string, opt_proxy?: string, opt_ignoreSSL?: boolean, - callback?: Function): void { + fileUrl: string, fileName: string, outputDir: string, opt_proxy?: string, + opt_ignoreSSL?: boolean, callback?: Function): void { logger.info('curl -o ' + outputDir + '/' + fileName + ' ' + fileUrl); let filePath = path.join(outputDir, fileName); let file = fs.createWriteStream(filePath); diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 24b82694..9100668f 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -1,11 +1,11 @@ +import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import * as fs from 'fs'; import * as q from 'q'; import * as rimraf from 'rimraf'; import {Binary, BinaryMap, ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlone, OS} from - '../binaries'; +'../binaries'; import {DownloadedBinary} from './downloaded_binary'; import {Downloader} from './downloader'; import {Logger} from '../cli'; @@ -84,7 +84,7 @@ export class FileManager { } catch (e) { return []; } - } + } /** * For the binary, operating system, and system architecture, look through @@ -94,9 +94,8 @@ export class FileManager { * @param existingFiles A list of existing files. * @returns The downloaded binary with all the versions found. */ - static downloadedVersions_( - binary: Binary, osType: string, arch: string, - existingFiles: string[]): DownloadedBinary { + static downloadedVersions_(binary: Binary, osType: string, arch: string, existingFiles: string[]): + DownloadedBinary { let versions: string[] = []; for (let existPos in existingFiles) { let existFile: string = existingFiles[existPos]; @@ -176,8 +175,9 @@ export class FileManager { if (value == readData.length) { return false; } else { - logger.warn(path.basename(filePath) + ' expected length ' + value + - ', found ' + readData.length); + logger.warn( + path.basename(filePath) + ' expected length ' + value + ', found ' + + readData.length); logger.warn('removing file: ' + filePath); return true; } @@ -211,7 +211,6 @@ export class FileManager { for (let binPos in binaries) { let bin: Binary = binaries[binPos]; if (file.indexOf(bin.prefix()) !== -1) { - bin.remove(path.join(outputDir, file)); logger.info('removed ' + file); } diff --git a/lib/webdriver.ts b/lib/webdriver.ts index eac90079..4568aebe 100644 --- a/lib/webdriver.ts +++ b/lib/webdriver.ts @@ -10,11 +10,11 @@ import {Cli} from './cli'; import {Config} from './config'; let commandline = new Cli() - .usage('webdriver-manager [options]') - .program(clean.program) - .program(start.program) - .program(status.program) - .program(update.program); + .usage('webdriver-manager [options]') + .program(clean.program) + .program(start.program) + .program(status.program) + .program(update.program); let minimistOptions = commandline.getMinimistOptions(); let argv = minimist(process.argv.slice(2), minimistOptions); @@ -22,8 +22,7 @@ let cmd = argv._; if (commandline.programs[cmd[0]]) { if (cmd[0] === 'help') { commandline.printHelp(); - } - else if (cmd[1] === 'help' || argv['help'] || argv['h']) { + } else if (cmd[1] === 'help' || argv['help'] || argv['h']) { commandline.programs[cmd[0]].printHelp(); } else { commandline.programs[cmd[0]].run(JSON.parse(JSON.stringify(argv))); From 00740238dc31b71d5dfd0db6c929b47b43c7b0d8 Mon Sep 17 00:00:00 2001 From: James Martin Date: Wed, 3 Aug 2016 11:32:58 -0400 Subject: [PATCH 56/79] chore(cleanup): fix typo in bin/webdriver-manager --- bin/webdriver-manager | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/webdriver-manager b/bin/webdriver-manager index c1600c3f..db376115 100755 --- a/bin/webdriver-manager +++ b/bin/webdriver-manager @@ -13,11 +13,11 @@ var folder = cwd.replace(parentPath, '').substring(1); var isProjectVersion = folder === nodeModuleName; var isLocalVersion = false; -var printCyan, printMagent, printGreen; +var printCyan, printMagenta, printGreen; printCyan = printMagenta = printGreen = function(message) { - return message; -} + return message; +}; try { var chalk = require('chalk'); From 018261410207c3fd276da0608bb291ba8746ffbf Mon Sep 17 00:00:00 2001 From: Craig Date: Fri, 5 Aug 2016 16:05:18 -0700 Subject: [PATCH 57/79] chore(cleanup): format comments (#74) --- lib/cmds/start.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index bc18783a..5d5dc32a 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -100,8 +100,9 @@ function start(options: Options) { } let args: string[] = ['-jar', path.join(outputDir, binaries[StandAlone.id].filename())]; if (osType === 'Linux') { - // selenium server may take a long time to start because /dev/random is BLOCKING if there is not enough entropy - // the solution is to use /dev/urandom, which is NON-BLOCKING (use /dev/./urandom because of a java bug) + // selenium server may take a long time to start because /dev/random is BLOCKING if there is not + // enough entropy the solution is to use /dev/urandom, which is NON-BLOCKING (use /dev/./urandom + // because of a java bug) // https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/1301 // https://bugs.openjdk.java.net/browse/JDK-6202721 args.push('-Djava.security.egd=file:///dev/./urandom'); From 8b61b71410dbca6e205fbc599b954fe61a8ee937 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 8 Aug 2016 12:07:56 -0700 Subject: [PATCH 58/79] fix(start): use ie32 if specified via command line (#72) closes #68 --- lib/cmds/start.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 5d5dc32a..e63dbd1f 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -119,6 +119,9 @@ function start(options: Options) { } } if (downloadedBinaries[IEDriver.id] != null) { + if (options[Opt.IE32]) { + binaries[IEDriver.id].arch = 'Win32'; + } args.push( '-Dwebdriver.ie.driver=' + path.join(outputDir, binaries[IEDriver.id].executableFilename(osType))); From fa20ca82e191b122ed49b144b8ebc53ee3b92a9d Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 8 Aug 2016 12:27:57 -0700 Subject: [PATCH 59/79] fix(start): check if edge driver exists before adding to args (#73) closes #60 --- lib/cmds/start.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index e63dbd1f..818f71a7 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -127,7 +127,16 @@ function start(options: Options) { path.join(outputDir, binaries[IEDriver.id].executableFilename(osType))); } if (options[Opt.EDGE]) { - args.push('-Dwebdriver.edge.driver=' + options[Opt.EDGE].getString()); + // validate that the file exists prior to adding it to args + try { + let edgeFile = options[Opt.EDGE].getString(); + if (fs.statSync(edgeFile).isFile()) { + args.push('-Dwebdriver.edge.driver=' + options[Opt.EDGE].getString()); + } + } catch (err) { + // Either the default file or user specified location of the edge + // driver does not exist. + } } if (options[Opt.ANDROID].getBoolean()) { if (downloadedBinaries[AndroidSDK.id] != null) { From 236a8ec901133cb21247fc452d7ef7c9d5fed172 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 8 Aug 2016 12:30:31 -0700 Subject: [PATCH 60/79] fix(downloader): increase timeouts and unlink sync on download errors (#75) closes #62 and #63 --- lib/files/downloader.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index 91ee8f06..73b69661 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -127,22 +127,32 @@ export class Downloader { url: fileUrl, strictSSL: !opt_ignoreSSL, rejectUnauthorized: !opt_ignoreSSL, - proxy: Downloader.resolveProxy_(fileUrl, opt_proxy) + proxy: Downloader.resolveProxy_(fileUrl, opt_proxy), + // default Linux can be anywhere from 20-120 seconds + // increasing this arbitrarily to 4 minutes + timeout: 240000 }; request(options) .on('response', (response) => { if (response.statusCode !== 200) { - fs.unlink(filePath); + fs.unlinkSync(filePath); logger.error('Error: Got code ' + response.statusCode + ' from ' + fileUrl); } contentLength = response.headers['content-length']; }) .on('error', (error) => { - logger.error('Error: Got error ' + error + ' from ' + fileUrl); - fs.unlink(filePath); + if (error.code === 'ETIMEDOUT') { + logger.error('Connection timeout downloading: ' + fileUrl); + logger.error('Default timeout is 4 minutes.'); + + } else if (error.connect){ + logger.error('Could not connect to the server to download: ' + fileUrl); + } + logger.error(error); + fs.unlinkSync(filePath); }) .pipe(file); @@ -156,7 +166,7 @@ export class Downloader { logger.error( 'Error: corrupt download for ' + fileName + '. Please re-run webdriver-manager update'); - fs.unlink(filePath); + fs.unlinkSync(filePath); return; } if (callback) { From 21ef0bb56e9357d394e3a921ef6d699a9f957355 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 8 Aug 2016 13:50:20 -0700 Subject: [PATCH 61/79] chore(downloader): set error type to any (#77) - 'code' and 'connect' is not defined as part of the 'error' type declaration - setting error to any prevents this transpile error --- lib/files/downloader.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index 73b69661..6ef57e61 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -144,11 +144,11 @@ export class Downloader { }) .on('error', (error) => { - if (error.code === 'ETIMEDOUT') { + if ((error as any).code === 'ETIMEDOUT') { logger.error('Connection timeout downloading: ' + fileUrl); logger.error('Default timeout is 4 minutes.'); - } else if (error.connect){ + } else if ((error as any).connect) { logger.error('Could not connect to the server to download: ' + fileUrl); } logger.error(error); From 83468588fc21f7584b76a8c55afe659db045a4c9 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 8 Aug 2016 13:50:43 -0700 Subject: [PATCH 62/79] feat(logging): add logging property to selenium standalone (#76) closes #61 --- lib/cmds/opts.ts | 2 ++ lib/cmds/start.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index bfec122a..63ca7b22 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -22,6 +22,7 @@ export const VERSIONS_IE = 'versions.ie'; export const VERSIONS_ANDROID = 'versions.android'; export const VERSIONS_APPIUM = 'versions.appium'; export const CHROME_LOGS = 'chrome_logs'; +export const LOGGING = 'logging'; export const ANDROID_API_LEVELS = 'android-api-levels'; export const ANDROID_ABIS = 'android-abis'; export const ANDROID_ACCEPT_LICENSES = 'android-accept-licenses'; @@ -64,6 +65,7 @@ opts[VERSIONS_APPIUM] = opts[VERSIONS_IE] = new Option( VERSIONS_IE, 'Optional internet explorer driver version', 'string', IEDriver.versionDefault); opts[CHROME_LOGS] = new Option(CHROME_LOGS, 'File path to chrome logs', 'string', undefined); +opts[LOGGING] = new Option(LOGGING, 'File path to logging properties file', 'string', undefined); opts[ANDROID_API_LEVELS] = new Option( ANDROID_API_LEVELS, 'Which versions of the android API you want to emulate', 'string', AndroidSDK.DEFAULT_API_LEVELS); diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 818f71a7..89791273 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -26,6 +26,7 @@ let prog = new Program() .addOption(Opts[Opt.VERSIONS_ANDROID]) .addOption(Opts[Opt.VERSIONS_APPIUM]) .addOption(Opts[Opt.CHROME_LOGS]) + .addOption(Opts[Opt.LOGGING]) .addOption(Opts[Opt.ANDROID]) .addOption(Opts[Opt.AVDS]) .addOption(Opts[Opt.AVD_USE_SNAPSHOTS]); @@ -76,6 +77,7 @@ function start(options: Options) { } let chromeLogs: string = null; + let loggingFile: string = null; if (options[Opt.CHROME_LOGS].getString()) { if (path.isAbsolute(options[Opt.CHROME_LOGS].getString())) { chromeLogs = options[Opt.CHROME_LOGS].getString(); @@ -107,6 +109,16 @@ function start(options: Options) { // https://bugs.openjdk.java.net/browse/JDK-6202721 args.push('-Djava.security.egd=file:///dev/./urandom'); } + + if (options[Opt.LOGGING].getString()) { + if (path.isAbsolute(options[Opt.LOGGING].getString())) { + loggingFile = options[Opt.LOGGING].getString(); + } else { + loggingFile = path.resolve(Config.getBaseDir(), options[Opt.LOGGING].getString()); + } + args.push('-Djava.util.logging.config.file=' + loggingFile); + } + if (seleniumPort) { args.push('-port', seleniumPort); } From 58c76ffa8307b29fd003cc3197c0ac9414402580 Mon Sep 17 00:00:00 2001 From: James Martin Date: Mon, 8 Aug 2016 16:07:46 -0400 Subject: [PATCH 63/79] chore(filemanager): replace deprecated fs.existsSync() with fs.statSync() --- lib/files/file_manager.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 9100668f..5b29aff5 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -18,8 +18,15 @@ let logger = new Logger('file_manager'); * file versions. */ export class FileManager { + /** + * Create a directory if it does not exist. + * @param outputDir The directory to create. + */ static makeOutputDirectory(outputDir: string) { - if (!fs.existsSync(outputDir) || !fs.statSync(outputDir).isDirectory()) { + try { + fs.statSync(outputDir); + } catch (e) { + logger.info('creating folder ' + outputDir); fs.mkdirSync(outputDir); } } @@ -78,7 +85,7 @@ export class FileManager { * @param outputDir The directory where binaries are saved * @returns A list of existing files. */ - static getExistngFiles(outputDir: string): string[] { + static getExistingFiles(outputDir: string): string[] { try { return fs.readdirSync(outputDir); } catch (e) { @@ -134,7 +141,7 @@ export class FileManager { let ostype = os.type(); let arch = os.arch(); let binaries = FileManager.setupBinaries(); - let existingFiles = FileManager.getExistngFiles(outputDir); + let existingFiles = FileManager.getExistingFiles(outputDir); let downloaded: BinaryMap = {}; for (let bin in binaries) { let binary = FileManager.downloadedVersions_(binaries[bin], ostype, arch, existingFiles); @@ -195,14 +202,15 @@ export class FileManager { * @param outputDir The directory where files are downloaded and stored. */ static removeExistingFiles(outputDir: string): void { - // folder exists - if (!fs.existsSync(outputDir)) { - logger.warn('the out_dir path ' + outputDir + ' does not exist'); + try { + fs.statSync(outputDir); + } catch (e) { + logger.warn('path does not exist ' + outputDir); return; } - let existingFiles = FileManager.getExistngFiles(outputDir); + let existingFiles = FileManager.getExistingFiles(outputDir); if (existingFiles.length === 0) { - logger.warn('no files found in out_dir: ' + outputDir); + logger.warn('no files found in path ' + outputDir); return; } From ffb05817f6ae28eea03156ccca23335a9d23de89 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Mon, 8 Aug 2016 14:20:05 -0700 Subject: [PATCH 64/79] chore(release): version bump to 10.2.2 - version bump on package.json - changelog updated - typings dependency updated for node --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ package.json | 2 +- typings.json | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf5dc01b..cc8cb64f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +# 10.2.2 + +## Bug Fixes +- ([236a8ec](https://github.com/angular/webdriver-manager/commit/236a8ec901133cb21247fc452d7ef7c9d5fed172)) + fix(downloader): increase timeouts and unlink sync on download errors (#75) + + closes #62 and #63 +- ([fa20ca8](https://github.com/angular/webdriver-manager/commit/fa20ca82e191b122ed49b144b8ebc53ee3b92a9d)) + fix(start): check if edge driver exists before adding to args (#73) + + closes #60 +- ([8b61b71](https://github.com/angular/webdriver-manager/commit/8b61b71410dbca6e205fbc599b954fe61a8ee937)) + fix(start): use ie32 if specified via command line (#72) + + closes #68 + +## Features + +- ([8346858](https://github.com/angular/webdriver-manager/commit/83468588fc21f7584b76a8c55afe659db045a4c9)) + feat(logging): add logging property to selenium standalone (#76) + + closes #61 +- ([18f9f1d](https://github.com/angular/webdriver-manager/commit/18f9f1dfea02cd8f5c5a2cd5f09130f0ca24f68a)) + chore(selenium): add dev/urandom to selenium start args to prevent startup delays in linux + + # 10.2.1 upgrade to latest chrome driver and selenium standalone server versions diff --git a/package.json b/package.json index 443753cd..36811b31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.2.1", + "version": "10.2.2", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", diff --git a/typings.json b/typings.json index f8e0a02d..6aa7d02c 100644 --- a/typings.json +++ b/typings.json @@ -8,7 +8,7 @@ "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", "minimatch": "registry:dt/minimatch#2.0.8+20160317120654", "minimist": "registry:dt/minimist#1.1.3+20160317120654", - "node": "registry:dt/node#6.0.0+20160802155038", + "node": "registry:dt/node#6.0.0+20160805072842", "q": "registry:dt/q#0.0.0+20160613154756", "request": "registry:dt/request#0.0.0+20160726020908", "rimraf": "registry:dt/rimraf#0.0.0+20160317120654" From c96090c0f7cc24209b34f9634699e68669650070 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 22 Aug 2016 17:31:14 -0700 Subject: [PATCH 65/79] fix(update): download standalone with proxy and ignore ssl (#81) closes #79 --- lib/cmds/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 9f599a1c..c26db912 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -110,7 +110,7 @@ function update(options: Options): void { let binary = binaries[StandAlone.id]; FileManager.toDownload(binary, outputDir).then((value: boolean) => { if (value) { - Downloader.downloadBinary(binary, outputDir); + Downloader.downloadBinary(binary, outputDir, proxy, ignoreSSL); } else { logger.info( binary.name + ': file exists ' + From d6597e8a06004888371cca12b8e803c7d44eaf8d Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 23 Aug 2016 12:11:37 -0700 Subject: [PATCH 66/79] fix(start): add the correct flags for windows (#83) closes #68 --- lib/cmds/start.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 89791273..94c2dfdc 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -36,8 +36,10 @@ if (os.type() === 'Darwin') { } if (os.type() === 'Windows_NT') { - prog.addOption(Opts[Opt.VERSIONS_IE]); - prog.addOption(Opts[Opt.EDGE]); + prog.addOption(Opts[Opt.VERSIONS_IE]) + .addOption(Opts[Opt.IE32]) + .addOption(Opts[Opt.IE]) + .addOption(Opts[Opt.EDGE]); } export var program = prog; From fe85c94e8db0680be25461cd3ea1ef59fc4d8fa4 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 23 Aug 2016 12:11:55 -0700 Subject: [PATCH 67/79] dep(types): update typescript, remove typings in favor of @types (#84) --- gulpfile.js | 6 +----- package.json | 18 +++++++++++++++--- spec/files/downloader_spec.ts | 2 -- spec/files/fileManager_spec.ts | 2 -- spec/webdriver_spec.ts | 2 -- tsconfig.json | 3 +-- typings.json | 16 ---------------- 7 files changed, 17 insertions(+), 32 deletions(-) delete mode 100644 typings.json diff --git a/gulpfile.js b/gulpfile.js index 7677a2aa..95f83d5a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -42,16 +42,12 @@ gulp.task('format', () => { format.format('file', clangFormat)).pipe(gulp.dest('.')); }); -gulp.task('typings', function(done) { - runSpawn(done, 'node', ['node_modules/typings/dist/bin.js', 'install']); -}); - gulp.task('tsc', function(done) { runSpawn(done, 'node', ['node_modules/typescript/bin/tsc']); }); gulp.task('prepublish', function(done) { - runSequence(['typings', 'format'], 'tsc', 'copy', done); + runSequence('format', 'tsc', 'copy', done); }); gulp.task('default',['prepublish']); diff --git a/package.json b/package.json index 36811b31..aa43fcb5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "protractor", "webdriver", "webdriverjs", - "selenium" + "selenium", + "selenium-webdriver" ], "repository": { "type": "git", @@ -37,12 +38,23 @@ "rimraf": "^2.5.2" }, "devDependencies": { + "@types/adm-zip": "^0.4.29", + "@types/chalk": "^0.4.28", + "@types/form-data": "0.0.30", + "@types/glob": "^5.0.29", + "@types/ini": "^1.3.28", + "@types/jasmine": "^2.2.32", + "@types/minimatch": "^2.0.28", + "@types/minimist": "^1.1.28", + "@types/node": "^6.0.37", + "@types/q": "0.0.29", + "@types/request": "0.0.29", + "@types/rimraf": "0.0.27", "clang-format": "^1.0.35", "gulp": "^3.9.1", "gulp-clang-format": "^1.0.23", "jasmine": "^2.4.1", "run-sequence": "^1.1.5", - "typescript": "^1.8.7", - "typings": "^1.0.4" + "typescript": "^2.0.0" } } diff --git a/spec/files/downloader_spec.ts b/spec/files/downloader_spec.ts index 5221d122..572adf5b 100644 --- a/spec/files/downloader_spec.ts +++ b/spec/files/downloader_spec.ts @@ -1,5 +1,3 @@ -/// - import {Downloader} from '../../lib/files'; describe('downloader', () => { diff --git a/spec/files/fileManager_spec.ts b/spec/files/fileManager_spec.ts index 70c319a2..adcf9fab 100644 --- a/spec/files/fileManager_spec.ts +++ b/spec/files/fileManager_spec.ts @@ -1,5 +1,3 @@ -/// - import * as fs from 'fs'; import * as path from 'path'; import {Binary, AndroidSDK, ChromeDriver, IEDriver, Appium, StandAlone} from '../../lib/binaries'; diff --git a/spec/webdriver_spec.ts b/spec/webdriver_spec.ts index 60cc2851..1d13fffd 100644 --- a/spec/webdriver_spec.ts +++ b/spec/webdriver_spec.ts @@ -1,5 +1,3 @@ -/// - import * as child_process from 'child_process'; import * as path from 'path'; import {cli} from '../lib/webdriver'; diff --git a/tsconfig.json b/tsconfig.json index d45f25c9..78b35f6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,6 @@ }, "exclude": [ "built", - "node_modules", - "typings/globals" + "node_modules" ] } diff --git a/typings.json b/typings.json deleted file mode 100644 index 6aa7d02c..00000000 --- a/typings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "globalDependencies": { - "adm-zip": "registry:dt/adm-zip#0.0.0+20160317120654", - "chalk": "registry:dt/chalk#0.4.0+20160317120654", - "form-data": "registry:dt/form-data#0.0.0+20160724024111", - "glob": "registry:dt/glob#5.0.10+20160317120654", - "ini": "registry:dt/ini#1.3.3+20160316155526", - "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", - "minimatch": "registry:dt/minimatch#2.0.8+20160317120654", - "minimist": "registry:dt/minimist#1.1.3+20160317120654", - "node": "registry:dt/node#6.0.0+20160805072842", - "q": "registry:dt/q#0.0.0+20160613154756", - "request": "registry:dt/request#0.0.0+20160726020908", - "rimraf": "registry:dt/rimraf#0.0.0+20160317120654" - } -} From 752a5274bee7e1272644398adc169d710f988a14 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 23 Aug 2016 12:12:44 -0700 Subject: [PATCH 68/79] chore(cleanup): clean up publish and clang formatting (#85) --- .npmignore | 10 +++++++--- lib/webdriver.ts | 6 ++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.npmignore b/.npmignore index 792eda85..2a15ffa4 100644 --- a/.npmignore +++ b/.npmignore @@ -1,9 +1,13 @@ lib/ -spec/ -typings/ selenium/ +spec/ .clang-format +.gitattributes +.github/ .gitignore .npmignore - +.travis.yml +npm-debug.log +release.md +tsconfig.json diff --git a/lib/webdriver.ts b/lib/webdriver.ts index 4568aebe..4dd6acb8 100644 --- a/lib/webdriver.ts +++ b/lib/webdriver.ts @@ -1,12 +1,10 @@ -#!/usr/bin/env node - import * as minimist from 'minimist'; + +import {Cli} from './cli'; import * as clean from './cmds/clean'; import * as start from './cmds/start'; import * as status from './cmds/status'; import * as update from './cmds/update'; - -import {Cli} from './cli'; import {Config} from './config'; let commandline = new Cli() From 7ec082a1bcc7f262237a616ec96592c36c28b89a Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 23 Aug 2016 12:13:29 -0700 Subject: [PATCH 69/79] feat(start): add a gecko driver path to the start command (#86) --- lib/cmds/opts.ts | 2 ++ lib/cmds/start.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index 63ca7b22..04fd257e 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -14,6 +14,7 @@ export const CHROME = 'chrome'; export const IE = 'ie'; export const IE32 = 'ie32'; export const EDGE = 'edge'; +export const GECKO = 'gecko'; export const ANDROID = 'android'; export const IOS = 'ios'; export const VERSIONS_CHROME = 'versions.chrome'; @@ -51,6 +52,7 @@ opts[IE32] = new Option(IE32, 'Install or update 32-bit ie driver', 'boolean', I opts[EDGE] = new Option( EDGE, 'Use installed Microsoft Edge driver', 'string', 'C:\\Program Files (x86)\\Microsoft Web Driver\\MicrosoftWebDriver.exe'); +opts[GECKO] = new Option(GECKO, 'Use path for gecko driver', 'string'); opts[ANDROID] = new Option(ANDROID, 'Update/use the android sdk', 'boolean', AndroidSDK.isDefault); opts[IOS] = new Option(IOS, 'Update the iOS sdk', 'boolean', false); opts[VERSIONS_CHROME] = new Option( diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 94c2dfdc..461166e6 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -26,6 +26,7 @@ let prog = new Program() .addOption(Opts[Opt.VERSIONS_ANDROID]) .addOption(Opts[Opt.VERSIONS_APPIUM]) .addOption(Opts[Opt.CHROME_LOGS]) + .addOption(Opts[Opt.GECKO]) .addOption(Opts[Opt.LOGGING]) .addOption(Opts[Opt.ANDROID]) .addOption(Opts[Opt.AVDS]) @@ -152,6 +153,17 @@ function start(options: Options) { // driver does not exist. } } + if (options[Opt.GECKO].getString()) { + let gecko = options[Opt.GECKO].getString(); + try { + if (fs.statSync(gecko).isFile()) { + args.push('-Dwebdriver.edge.driver=' + gecko); + } + } catch (err) { + // The file does not exist. + logger.warn('The absolute path provided for gecko (' + gecko + ') does not exist'); + } + } if (options[Opt.ANDROID].getBoolean()) { if (downloadedBinaries[AndroidSDK.id] != null) { let avds = options[Opt.AVDS].getString(); From fa4835453385d4c79fcbba7bb6d408557c870bae Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 23 Aug 2016 17:22:16 -0700 Subject: [PATCH 70/79] fix(downloader): fix against working proxy (#87) --- lib/files/downloader.ts | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/files/downloader.ts b/lib/files/downloader.ts index 6ef57e61..a667fda0 100644 --- a/lib/files/downloader.ts +++ b/lib/files/downloader.ts @@ -119,19 +119,35 @@ export class Downloader { let file = fs.createWriteStream(filePath); let contentLength = 0; - if (opt_ignoreSSL) { - logger.info('ignoring SSL certificate'); + interface Options { + url: string; + timeout: number; + strictSSL?: boolean; + rejectUnauthorized?: boolean; + proxy?: string; + headers?: {[key: string]: any}; + [key: string]: any; } - let options = { + let options: Options = { url: fileUrl, - strictSSL: !opt_ignoreSSL, - rejectUnauthorized: !opt_ignoreSSL, - proxy: Downloader.resolveProxy_(fileUrl, opt_proxy), // default Linux can be anywhere from 20-120 seconds // increasing this arbitrarily to 4 minutes timeout: 240000 - }; + } + + if (opt_ignoreSSL) { + logger.info('ignoring SSL certificate'); + options.strictSSL = !opt_ignoreSSL; + options.rejectUnauthorized = !opt_ignoreSSL; + } + + if (opt_proxy) { + options.proxy = Downloader.resolveProxy_(fileUrl, opt_proxy); + if (options.url.indexOf('https://') === 0) { + options.url = options.url.replace('https://', 'http://'); + } + } request(options) .on('response', From fc74fa39e7dd3ba15dac3de1e7f01c38993dfaa7 Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Wed, 24 Aug 2016 11:31:27 -0700 Subject: [PATCH 71/79] chore(release): version bump to 10.2.3 --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc8cb64f..e81caa6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,33 @@ +# 10.2.3 + +## Bug Fixes + +- ([fa48354](https://github.com/angular/webdriver-manager/commit/fa4835453385d4c79fcbba7bb6d408557c870bae)) + fix(downloader): fix against working proxy (#87) + +- ([d6597e8](https://github.com/angular/webdriver-manager/commit/d6597e8a06004888371cca12b8e803c7d44eaf8d)) + fix(start): add the correct flags for windows (#83) + + closes #68 +- ([c96090c](https://github.com/angular/webdriver-manager/commit/c96090c0f7cc24209b34f9634699e68669650070)) + fix(update): download standalone with proxy and ignore ssl (#81) + + closes #79 + +## Features + +- ([7ec082a](https://github.com/angular/webdriver-manager/commit/7ec082a1bcc7f262237a616ec96592c36c28b89a)) + feat(start): add a gecko driver path to the start command (#86) + +## Dependencies + +- ([fe85c94](https://github.com/angular/webdriver-manager/commit/fe85c94e8db0680be25461cd3ea1ef59fc4d8fa4)) + dep(types): update typescript, remove typings in favor of @types (#84) + # 10.2.2 ## Bug Fixes + - ([236a8ec](https://github.com/angular/webdriver-manager/commit/236a8ec901133cb21247fc452d7ef7c9d5fed172)) fix(downloader): increase timeouts and unlink sync on download errors (#75) diff --git a/package.json b/package.json index aa43fcb5..75093850 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.2.2", + "version": "10.2.3", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", From 9a4a58e6dfd711e734fa34e94122bf03d7fd85c4 Mon Sep 17 00:00:00 2001 From: Craig Date: Tue, 30 Aug 2016 13:03:13 -0700 Subject: [PATCH 72/79] chore(chromedriver): update chromedriver version to 2.23 (#91) closes #90 --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index dfd43a5f..f3d14f3c 100644 --- a/config.json +++ b/config.json @@ -1,7 +1,7 @@ { "webdriverVersions": { "selenium": "2.53.1", - "chromedriver": "2.22", + "chromedriver": "2.23", "iedriver": "2.53.1", "androidsdk": "24.4.1", "appium": "1.5.3" From 61af7be4edbaf070bdcc35dc85f11fb46ab9577e Mon Sep 17 00:00:00 2001 From: mgiambalvo Date: Fri, 2 Sep 2016 10:46:19 -0700 Subject: [PATCH 73/79] feat(gecko): Add geckodriver, related config, and flags Users will still need 'marionette': true in their capabilities in order to use gecko driver. --- .gitignore | 1 + config.json | 2 ++ lib/binaries/gecko_driver.ts | 52 ++++++++++++++++++++++++++++++++++++ lib/binaries/ie_driver.ts | 1 - lib/binaries/index.ts | 1 + lib/cmds/opts.ts | 7 +++-- lib/cmds/start.ts | 18 +++++-------- lib/cmds/update.ts | 14 +++++++++- lib/config.ts | 3 +++ lib/files/file_manager.ts | 4 +++ tsconfig.json | 2 +- 11 files changed, 88 insertions(+), 17 deletions(-) create mode 100644 lib/binaries/gecko_driver.ts diff --git a/.gitignore b/.gitignore index 2e7e84c0..683ccbc2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ built/ node_modules/ selenium/ typings/ +.idea/ diff --git a/config.json b/config.json index f3d14f3c..514c4cc2 100644 --- a/config.json +++ b/config.json @@ -2,6 +2,7 @@ "webdriverVersions": { "selenium": "2.53.1", "chromedriver": "2.23", + "geckodriver": "v0.9.0", "iedriver": "2.53.1", "androidsdk": "24.4.1", "appium": "1.5.3" @@ -9,6 +10,7 @@ "cdnUrls": { "selenium": "https://selenium-release.storage.googleapis.com/", "chromedriver": "https://chromedriver.storage.googleapis.com/", + "geckodriver": "https://github.com/mozilla/geckodriver/releases/download/", "iedriver": "https://selenium-release.storage.googleapis.com/", "androidsdk": "http://dl.google.com/android/" } diff --git a/lib/binaries/gecko_driver.ts b/lib/binaries/gecko_driver.ts new file mode 100644 index 00000000..20bdf48c --- /dev/null +++ b/lib/binaries/gecko_driver.ts @@ -0,0 +1,52 @@ +import * as path from 'path'; + +import {Config} from '../config'; + +import {Binary, OS} from './binary'; + +/** + * The gecko driver binary. + */ +export class GeckoDriver extends Binary { + static os = [OS.Windows_NT, OS.Linux, OS.Darwin]; + static id = 'gecko'; + static versionDefault = Config.binaryVersions().gecko; + static isDefault = true; + static shortName = ['gecko']; + + static suffixes: {[key: string]: string} = { + 'Darwin': '-mac.tar.gz', + 'Linux': '-linux64.tar.gz', + 'Windows_NT': '-win64.zip' + }; + + constructor() { + super(); + this.name = 'geckodriver'; + this.versionCustom = GeckoDriver.versionDefault; + this.prefixDefault = 'geckodriver-'; + this.cdn = Config.cdnUrls().gecko; + } + + id(): string { return GeckoDriver.id; } + + versionDefault(): string { return GeckoDriver.versionDefault; } + + suffix(ostype: string, arch: string): string { + if (!GeckoDriver.supports(ostype, arch)) { + throw new Error('GeckoDriver doesn\'t support ${ostype} ${arch}!'); + } + + return GeckoDriver.suffixes[ostype]; + } + + static supports(ostype: string, arch: string): boolean { + return arch == 'x64' && (ostype in GeckoDriver.suffixes); + } + + url(ostype: string, arch: string): string { + let urlBase = this.cdn + this.version() + '/'; + let filename = this.prefix() + this.version() + this.suffix(ostype, arch); + return urlBase + filename; + } +} diff --git a/lib/binaries/ie_driver.ts b/lib/binaries/ie_driver.ts index c0ac9ff5..4b1f5f88 100644 --- a/lib/binaries/ie_driver.ts +++ b/lib/binaries/ie_driver.ts @@ -35,7 +35,6 @@ export class IEDriver extends Binary { return '_x64_' + this.versionCustom; } else { return '_Win32_' + this.versionCustom; - ; } } return ''; diff --git a/lib/binaries/index.ts b/lib/binaries/index.ts index 3dbfb4a6..986505ba 100644 --- a/lib/binaries/index.ts +++ b/lib/binaries/index.ts @@ -1,5 +1,6 @@ export * from './binary'; export * from './chrome_driver'; +export * from './gecko_driver'; export * from './ie_driver'; export * from './android_sdk'; export * from './appium'; diff --git a/lib/cmds/opts.ts b/lib/cmds/opts.ts index 04fd257e..bc2ef130 100644 --- a/lib/cmds/opts.ts +++ b/lib/cmds/opts.ts @@ -1,4 +1,4 @@ -import {AndroidSDK, Appium, ChromeDriver, IEDriver, StandAlone} from '../binaries'; +import {AndroidSDK, Appium, ChromeDriver, GeckoDriver, IEDriver, StandAlone} from '../binaries'; import {Cli, Option, Options} from '../cli'; import {Config} from '../config'; @@ -18,6 +18,7 @@ export const GECKO = 'gecko'; export const ANDROID = 'android'; export const IOS = 'ios'; export const VERSIONS_CHROME = 'versions.chrome'; +export const VERSIONS_GECKO = 'versions.gecko'; export const VERSIONS_STANDALONE = 'versions.standalone'; export const VERSIONS_IE = 'versions.ie'; export const VERSIONS_ANDROID = 'versions.android'; @@ -47,16 +48,18 @@ opts[STANDALONE] = new Option( STANDALONE, 'Install or update selenium standalone', 'boolean', StandAlone.isDefault); opts[CHROME] = new Option(CHROME, 'Install or update chromedriver', 'boolean', ChromeDriver.isDefault); +opts[GECKO] = new Option(GECKO, 'Install or update geckodriver', 'boolean', GeckoDriver.isDefault); opts[IE] = new Option(IE, 'Install or update ie driver', 'boolean', IEDriver.isDefault); opts[IE32] = new Option(IE32, 'Install or update 32-bit ie driver', 'boolean', IEDriver.isDefault); opts[EDGE] = new Option( EDGE, 'Use installed Microsoft Edge driver', 'string', 'C:\\Program Files (x86)\\Microsoft Web Driver\\MicrosoftWebDriver.exe'); -opts[GECKO] = new Option(GECKO, 'Use path for gecko driver', 'string'); opts[ANDROID] = new Option(ANDROID, 'Update/use the android sdk', 'boolean', AndroidSDK.isDefault); opts[IOS] = new Option(IOS, 'Update the iOS sdk', 'boolean', false); opts[VERSIONS_CHROME] = new Option( VERSIONS_CHROME, 'Optional chrome driver version', 'string', ChromeDriver.versionDefault); +opts[VERSIONS_GECKO] = new Option( + VERSIONS_GECKO, 'Optional gecko driver version', 'string', GeckoDriver.versionDefault); opts[VERSIONS_ANDROID] = new Option( VERSIONS_ANDROID, 'Optional android sdk version', 'string', AndroidSDK.versionDefault); opts[VERSIONS_STANDALONE] = new Option( diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 461166e6..9b8a5a05 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -6,6 +6,7 @@ import * as os from 'os'; import * as path from 'path'; import {AndroidSDK, Appium, Binary, BinaryMap, ChromeDriver, IEDriver, StandAlone} from '../binaries'; +import {GeckoDriver} from '../binaries/gecko_driver'; import {Logger, Options, Program} from '../cli'; import {Config} from '../config'; import {FileManager} from '../files'; @@ -26,7 +27,6 @@ let prog = new Program() .addOption(Opts[Opt.VERSIONS_ANDROID]) .addOption(Opts[Opt.VERSIONS_APPIUM]) .addOption(Opts[Opt.CHROME_LOGS]) - .addOption(Opts[Opt.GECKO]) .addOption(Opts[Opt.LOGGING]) .addOption(Opts[Opt.ANDROID]) .addOption(Opts[Opt.AVDS]) @@ -133,6 +133,11 @@ function start(options: Options) { args.push('-Dwebdriver.chrome.logfile=' + chromeLogs); } } + if (downloadedBinaries[GeckoDriver.id] != null) { + args.push( + '-Dwebdriver.gecko.driver=' + + path.join(outputDir, binaries[GeckoDriver.id].executableFilename(osType))); + } if (downloadedBinaries[IEDriver.id] != null) { if (options[Opt.IE32]) { binaries[IEDriver.id].arch = 'Win32'; @@ -153,17 +158,6 @@ function start(options: Options) { // driver does not exist. } } - if (options[Opt.GECKO].getString()) { - let gecko = options[Opt.GECKO].getString(); - try { - if (fs.statSync(gecko).isFile()) { - args.push('-Dwebdriver.edge.driver=' + gecko); - } - } catch (err) { - // The file does not exist. - logger.warn('The absolute path provided for gecko (' + gecko + ') does not exist'); - } - } if (options[Opt.ANDROID].getBoolean()) { if (downloadedBinaries[AndroidSDK.id] != null) { let avds = options[Opt.AVDS].getString(); diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index c26db912..74c3ca69 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import * as q from 'q'; import * as rimraf from 'rimraf'; -import {AndroidSDK, Appium, Binary, ChromeDriver, IEDriver, StandAlone} from '../binaries'; +import {AndroidSDK, Appium, Binary, ChromeDriver, GeckoDriver, IEDriver, StandAlone} from '../binaries'; import {Logger, Options, Program} from '../cli'; import {Config} from '../config'; import {Downloader, FileManager} from '../files'; @@ -31,6 +31,10 @@ let prog = new Program() .addOption(Opts[Opt.ANDROID_ABIS]) .addOption(Opts[Opt.ANDROID_ACCEPT_LICENSES]); +if (GeckoDriver.supports(os.type(), os.arch())) { + prog.addOption(Opts[Opt.VERSIONS_GECKO]).addOption(Opts[Opt.GECKO]); +} + if (os.type() === 'Darwin') { prog.addOption(Opts[Opt.IOS]); } @@ -65,6 +69,7 @@ if (argv._[0] === 'update-run') { function update(options: Options): void { let standalone = options[Opt.STANDALONE].getBoolean(); let chrome = options[Opt.CHROME].getBoolean(); + let gecko = options[Opt.GECKO].getBoolean(); let ie: boolean = false; let ie32: boolean = false; if (options[Opt.IE]) { @@ -100,6 +105,9 @@ function update(options: Options): void { if (options[Opt.VERSIONS_IE]) { binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString(); } + if (options[Opt.VERSIONS_GECKO]) { + binaries[GeckoDriver.id].versionCustom = options[Opt.VERSIONS_GECKO].getString(); + } binaries[AndroidSDK.id].versionCustom = options[Opt.VERSIONS_ANDROID].getString(); binaries[Appium.id].versionCustom = options[Opt.VERSIONS_APPIUM].getString(); @@ -123,6 +131,10 @@ function update(options: Options): void { let binary = binaries[ChromeDriver.id]; updateBinary(binary, outputDir, proxy, ignoreSSL); } + if (gecko) { + let binary = binaries[GeckoDriver.id]; + updateBinary(binary, outputDir, proxy, ignoreSSL); + } if (ie) { let binary = binaries[IEDriver.id]; binary.arch = os.arch(); // Win32 or x64 diff --git a/lib/config.ts b/lib/config.ts index 3c61ef8d..9da62d72 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -9,6 +9,7 @@ let logger = new Logger('config'); export interface ConfigFile { selenium?: string; chrome?: string; + gecko?: string; ie?: string; android?: string; appium?: string; @@ -53,6 +54,7 @@ export class Config { let configVersions: ConfigFile = {}; configVersions.selenium = configFile.webdriverVersions.selenium; configVersions.chrome = configFile.webdriverVersions.chromedriver; + configVersions.gecko = configFile.webdriverVersions.geckodriver; configVersions.ie = configFile.webdriverVersions.iedriver; configVersions.android = configFile.webdriverVersions.androidsdk; configVersions.appium = configFile.webdriverVersions.appium; @@ -68,6 +70,7 @@ export class Config { let configCdnUrls: ConfigFile = {}; configCdnUrls.selenium = configFile.cdnUrls.selenium; configCdnUrls.chrome = configFile.cdnUrls.chromedriver; + configCdnUrls.gecko = configFile.cdnUrls.geckodriver; configCdnUrls.ie = configFile.cdnUrls.iedriver; configCdnUrls.android = configFile.cdnUrls.androidsdk; return configCdnUrls; diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 5b29aff5..47102f01 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -9,6 +9,7 @@ import {Binary, BinaryMap, ChromeDriver, IEDriver, AndroidSDK, Appium, StandAlon import {DownloadedBinary} from './downloaded_binary'; import {Downloader} from './downloader'; import {Logger} from '../cli'; +import {GeckoDriver} from '../binaries/gecko_driver'; let logger = new Logger('file_manager'); @@ -61,6 +62,9 @@ export class FileManager { if (FileManager.checkOS_(osType, ChromeDriver)) { binaries[ChromeDriver.id] = new ChromeDriver(); } + if (FileManager.checkOS_(osType, GeckoDriver)) { + binaries[GeckoDriver.id] = new GeckoDriver(); + } if (FileManager.checkOS_(osType, IEDriver)) { binaries[IEDriver.id] = new IEDriver(); } diff --git a/tsconfig.json b/tsconfig.json index 78b35f6c..d32da744 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "target": "es5", "module": "commonjs", "moduleResolution": "node", - "sourceMap": false, + "sourceMap": true, "declaration": true, "removeComments": false, "noImplicitAny": true, From 5241fc14eaf2b5cdf4b35362f260f6973cea0b1e Mon Sep 17 00:00:00 2001 From: Scott O'Hara Date: Tue, 13 Sep 2016 03:38:53 +1000 Subject: [PATCH 74/79] chore(chromedriver): update chromedriver version to 2.24 (#92) Chromedriver < 2.24 has issues with Chrome 54+ (https://bugs.chromium.org/p/chromedriver/issues/detail?id=1451). --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 514c4cc2..5eccc6d9 100644 --- a/config.json +++ b/config.json @@ -1,7 +1,7 @@ { "webdriverVersions": { "selenium": "2.53.1", - "chromedriver": "2.23", + "chromedriver": "2.24", "geckodriver": "v0.9.0", "iedriver": "2.53.1", "androidsdk": "24.4.1", From b183fadd4ae0b47b0773d6979d090c74419ee327 Mon Sep 17 00:00:00 2001 From: Jan Molak Date: Wed, 21 Sep 2016 10:01:23 +0100 Subject: [PATCH 75/79] fix(filemanager): Binaries can be downloaded from a custom CDN with alternate_cdn(#97) closes #96 --- lib/binaries/android_sdk.ts | 6 ++-- lib/binaries/appium.ts | 5 ++-- lib/binaries/binary.ts | 2 ++ lib/binaries/chrome_driver.ts | 6 ++-- lib/binaries/gecko_driver.ts | 6 ++-- lib/binaries/ie_driver.ts | 6 ++-- lib/binaries/stand_alone.ts | 6 ++-- lib/cmds/update.ts | 2 +- lib/files/file_manager.ts | 20 +++++++------ spec/files/fileManager_spec.ts | 52 ++++++++++++++++++++++++++++++++++ 10 files changed, 85 insertions(+), 26 deletions(-) diff --git a/lib/binaries/android_sdk.ts b/lib/binaries/android_sdk.ts index 02802432..f091647d 100644 --- a/lib/binaries/android_sdk.ts +++ b/lib/binaries/android_sdk.ts @@ -20,13 +20,13 @@ export class AndroidSDK extends Binary { static DEFAULT_API_LEVELS = '24'; static DEFAULT_ABIS = 'x86_64'; - constructor() { - super(); + constructor(alternateCDN?: string) { + super(alternateCDN || Config.cdnUrls().android); + this.name = 'android-sdk'; this.versionCustom = AndroidSDK.versionDefault; this.prefixDefault = 'android-sdk_r'; this.suffixDefault = '.zip'; - this.cdn = Config.cdnUrls().android; } id(): string { return AndroidSDK.id; } diff --git a/lib/binaries/appium.ts b/lib/binaries/appium.ts index ffd36e74..29853b0c 100644 --- a/lib/binaries/appium.ts +++ b/lib/binaries/appium.ts @@ -18,8 +18,9 @@ export class Appium extends Binary { static isDefault = false; static shortName = ['appium']; - constructor() { - super(); + constructor(alternateCDN?: string) { + super(alternateCDN || Config.cdnUrls().appium); + this.name = 'appium'; this.versionCustom = Appium.versionDefault; this.prefixDefault = 'appium-'; diff --git a/lib/binaries/binary.ts b/lib/binaries/binary.ts index d27c4be8..f3f1a476 100644 --- a/lib/binaries/binary.ts +++ b/lib/binaries/binary.ts @@ -31,6 +31,8 @@ export class Binary { cdn: string; // url protocol and host arch: string; + constructor(public cdn: string) {} + /** * @param ostype The operating system. * @returns The executable file type. diff --git a/lib/binaries/chrome_driver.ts b/lib/binaries/chrome_driver.ts index 39613a49..8507029c 100644 --- a/lib/binaries/chrome_driver.ts +++ b/lib/binaries/chrome_driver.ts @@ -15,13 +15,13 @@ export class ChromeDriver extends Binary { static isDefault = true; static shortName = ['chrome']; - constructor() { - super(); + constructor(alternateCDN?: string) { + super(alternateCDN || Config.cdnUrls().chrome); + this.name = 'chromedriver'; this.versionCustom = ChromeDriver.versionDefault; this.prefixDefault = 'chromedriver_'; this.suffixDefault = '.zip'; - this.cdn = Config.cdnUrls().chrome; } id(): string { return ChromeDriver.id; } diff --git a/lib/binaries/gecko_driver.ts b/lib/binaries/gecko_driver.ts index 20bdf48c..90061cba 100644 --- a/lib/binaries/gecko_driver.ts +++ b/lib/binaries/gecko_driver.ts @@ -20,12 +20,12 @@ export class GeckoDriver extends Binary { 'Windows_NT': '-win64.zip' }; - constructor() { - super(); + constructor(alternateCDN?: string) { + super(alternateCDN || Config.cdnUrls().gecko); + this.name = 'geckodriver'; this.versionCustom = GeckoDriver.versionDefault; this.prefixDefault = 'geckodriver-'; - this.cdn = Config.cdnUrls().gecko; } id(): string { return GeckoDriver.id; } diff --git a/lib/binaries/ie_driver.ts b/lib/binaries/ie_driver.ts index 4b1f5f88..8ed96f1a 100644 --- a/lib/binaries/ie_driver.ts +++ b/lib/binaries/ie_driver.ts @@ -15,13 +15,13 @@ export class IEDriver extends Binary { static isDefault = false; static shortName = ['ie', 'ie32']; - constructor() { - super(); + constructor(alternateCDN?: string) { + super(alternateCDN || Config.cdnUrls().ie); + this.name = 'IEDriverServer'; this.versionCustom = IEDriver.versionDefault; this.prefixDefault = 'IEDriverServer'; this.suffixDefault = '.zip'; - this.cdn = Config.cdnUrls().ie; this.arch = os.arch(); } diff --git a/lib/binaries/stand_alone.ts b/lib/binaries/stand_alone.ts index db793b34..fdf2c6b1 100644 --- a/lib/binaries/stand_alone.ts +++ b/lib/binaries/stand_alone.ts @@ -13,13 +13,13 @@ export class StandAlone extends Binary { static isDefault = true; static shortName = ['standalone']; - constructor() { - super(); + constructor(alternateCDN?: string) { + super(alternateCDN || Config.cdnUrls().selenium); + this.name = 'selenium standalone'; this.versionCustom = StandAlone.versionDefault; this.prefixDefault = 'selenium-server-standalone-'; this.suffixDefault = '.jar'; - this.cdn = Config.cdnUrls().selenium; } id(): string { return StandAlone.id; } diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index 74c3ca69..b602d521 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -99,7 +99,7 @@ function update(options: Options): void { let proxy = options[Opt.PROXY].getString(); // setup versions for binaries - let binaries = FileManager.setupBinaries(); + let binaries = FileManager.setupBinaries(options[Opt.ALTERNATE_CDN].getString()); binaries[StandAlone.id].versionCustom = options[Opt.VERSIONS_STANDALONE].getString(); binaries[ChromeDriver.id].versionCustom = options[Opt.VERSIONS_CHROME].getString(); if (options[Opt.VERSIONS_IE]) { diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index 47102f01..f77cb398 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -52,27 +52,28 @@ export class FileManager { * For the operating system, create a list that includes the binaries * for selenium standalone, chrome, and internet explorer. * @param osType The operating system. + * @param alternateCDN URL of the alternative CDN to be used instead of the default ones. * @returns A binary map that are available for the operating system. */ - static compileBinaries_(osType: string): BinaryMap { + static compileBinaries_(osType: string, alternateCDN?: string): BinaryMap { let binaries: BinaryMap = {}; if (FileManager.checkOS_(osType, StandAlone)) { - binaries[StandAlone.id] = new StandAlone(); + binaries[StandAlone.id] = new StandAlone(alternateCDN); } if (FileManager.checkOS_(osType, ChromeDriver)) { - binaries[ChromeDriver.id] = new ChromeDriver(); + binaries[ChromeDriver.id] = new ChromeDriver(alternateCDN); } if (FileManager.checkOS_(osType, GeckoDriver)) { - binaries[GeckoDriver.id] = new GeckoDriver(); + binaries[GeckoDriver.id] = new GeckoDriver(alternateCDN); } if (FileManager.checkOS_(osType, IEDriver)) { - binaries[IEDriver.id] = new IEDriver(); + binaries[IEDriver.id] = new IEDriver(alternateCDN); } if (FileManager.checkOS_(osType, AndroidSDK)) { - binaries[AndroidSDK.id] = new AndroidSDK(); + binaries[AndroidSDK.id] = new AndroidSDK(alternateCDN); } if (FileManager.checkOS_(osType, Appium)) { - binaries[Appium.id] = new Appium(); + binaries[Appium.id] = new Appium(alternateCDN); } return binaries; } @@ -80,9 +81,12 @@ export class FileManager { /** * Look up the operating system and compile a list of binaries that are available * for the system. + * @param alternateCDN URL of the alternative CDN to be used instead of the default ones. * @returns A binary map that is available for the operating system. */ - static setupBinaries(): BinaryMap { return FileManager.compileBinaries_(os.type()); } + static setupBinaries(alternateCDN?: string): BinaryMap { + return FileManager.compileBinaries_(os.type(), alternateCDN); + } /** * Get the list of existing files from the output directory diff --git a/spec/files/fileManager_spec.ts b/spec/files/fileManager_spec.ts index adcf9fab..c794d340 100644 --- a/spec/files/fileManager_spec.ts +++ b/spec/files/fileManager_spec.ts @@ -2,6 +2,9 @@ import * as fs from 'fs'; import * as path from 'path'; import {Binary, AndroidSDK, ChromeDriver, IEDriver, Appium, StandAlone} from '../../lib/binaries'; import {DownloadedBinary, FileManager} from '../../lib/files'; +import { BinaryMap } from '../../built/lib/binaries/binary'; +import { Config } from '../../lib/config'; +import { GeckoDriver } from '../../lib/binaries/gecko_driver'; describe('file manager', () => { @@ -211,4 +214,53 @@ describe('file manager', () => { }); }); }); + + describe('configuring the CDN location', () => { + + describe('when no custom CDN is specified', () => { + + let defaults = Config.cdnUrls(); + let binaries = FileManager.compileBinaries_('Windows_NT'); + + it('should use the default configuration for Android SDK', () => { + expect(binaries[AndroidSDK.id].cdn).toEqual(defaults[AndroidSDK.id]); + }); + + it('should use the default configuration for Appium', () => { + expect(binaries[Appium.id].cdn).toEqual(defaults[Appium.id]); + }); + + it('should use the default configuration for Chrome Driver', () => { + expect(binaries[ChromeDriver.id].cdn).toEqual(defaults[ChromeDriver.id]); + }); + + it('should use the default configuration for Gecko Driver', () => { + expect(binaries[GeckoDriver.id].cdn).toEqual(defaults[GeckoDriver.id]); + }); + + it('should use the default configuration for IE Driver', () => { + expect(binaries[IEDriver.id].cdn).toEqual(defaults[IEDriver.id]); + }); + + it('should use the default configuration for Selenium Standalone', () => { + expect(binaries[StandAlone.id].cdn).toEqual(defaults['selenium']); + }); + }); + + describe('when custom CDN is specified', () => { + + it('should configure the CDN for each binary', () => { + let customCDN = 'https://my.corporate.cdn/'; + let binaries = FileManager.compileBinaries_('Windows_NT', customCDN); + + forEachOf(binaries, binary => expect(binary.cdn).toEqual(customCDN, binary.name)); + }); + }); + + function forEachOf(binaries: BinaryMap, fn: (binary: T) => void) { + for (var key in binaries) { + fn(binaries[key]); + } + } + }); }); From 946ee005f7d316fd2d404c4bdbeae9a3802051af Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 5 Oct 2016 22:08:49 -0400 Subject: [PATCH 76/79] fix(chrome_driver): use the x64 binary if chrome driver version is greater than 2.23 (#95) * fix(chrome_driver): use the x64 binary if chrome driver version is greater than 2.23 Closes #93 Signed-off-by: Will Soto * fix(chrome_driver): add semver to better determine version number Signed-off-by: Will Soto * refactor(chrome_driver): check first to see if we have valid semver or not Signed-off-by: Will Soto --- lib/binaries/chrome_driver.ts | 16 +++++++++++++++- package.json | 4 +++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/binaries/chrome_driver.ts b/lib/binaries/chrome_driver.ts index 8507029c..b6c62e7c 100644 --- a/lib/binaries/chrome_driver.ts +++ b/lib/binaries/chrome_driver.ts @@ -1,4 +1,5 @@ import {arch, type} from 'os'; +import * as semver from 'semver'; import {Config} from '../config'; @@ -30,7 +31,20 @@ export class ChromeDriver extends Binary { suffix(ostype: string, arch: string): string { if (ostype === 'Darwin') { - return 'mac32' + this.suffixDefault; + let version: string = this.version(); + + if (version.split('.').length === 2) { + // we need to make the version valid semver since there is only a major and a minor + version = `${version}.0`; + } + + if (semver.gt(version, '2.23.0')) { + // after chromedriver version 2.23, the name of the binary changed + // They no longer provide a 32 bit binary + return 'mac64' + this.suffixDefault; + } else { + return 'mac32' + this.suffixDefault; + } } else if (ostype === 'Linux') { if (arch === 'x64') { return 'linux64' + this.suffixDefault; diff --git a/package.json b/package.json index 75093850..1601522f 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "minimist": "^1.2.0", "q": "^1.4.1", "request": "^2.69.0", - "rimraf": "^2.5.2" + "rimraf": "^2.5.2", + "semver": "^5.3.0" }, "devDependencies": { "@types/adm-zip": "^0.4.29", @@ -50,6 +51,7 @@ "@types/q": "0.0.29", "@types/request": "0.0.29", "@types/rimraf": "0.0.27", + "@types/semver": "^5.3.30", "clang-format": "^1.0.35", "gulp": "^3.9.1", "gulp-clang-format": "^1.0.23", From 3984ea4e5cfd2edf0401a5e5310aecaaecb63555 Mon Sep 17 00:00:00 2001 From: Roy Ling Date: Fri, 14 Oct 2016 04:28:55 +0800 Subject: [PATCH 77/79] fix(filemanager): respect proxy/ignoreSSL options in contentLength HEAD request (#101) --- lib/cmds/update.ts | 4 ++-- lib/files/file_manager.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cmds/update.ts b/lib/cmds/update.ts index b602d521..5c2afdcc 100644 --- a/lib/cmds/update.ts +++ b/lib/cmds/update.ts @@ -116,7 +116,7 @@ function update(options: Options): void { // permissions if (standalone) { let binary = binaries[StandAlone.id]; - FileManager.toDownload(binary, outputDir).then((value: boolean) => { + FileManager.toDownload(binary, outputDir, proxy, ignoreSSL).then((value: boolean) => { if (value) { Downloader.downloadBinary(binary, outputDir, proxy, ignoreSSL); } else { @@ -165,7 +165,7 @@ function update(options: Options): void { function updateBinary( binary: Binary, outputDir: string, proxy: string, ignoreSSL: boolean): q.Promise { - return FileManager.toDownload(binary, outputDir).then((value: boolean) => { + return FileManager.toDownload(binary, outputDir, proxy, ignoreSSL).then((value: boolean) => { if (value) { let deferred = q.defer(); Downloader.downloadBinary( diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index f77cb398..f9a1138a 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -166,7 +166,7 @@ export class FileManager { * @param outputDir The directory where files are downloaded and stored. * @returns If the file should be downloaded. */ - static toDownload(binary: T, outputDir: string): q.Promise { + static toDownload(binary: T, outputDir: string, proxy: string, ignoreSSL: boolean): q.Promise { let osType = os.type(); let osArch = os.arch(); let filePath: string; @@ -185,7 +185,7 @@ export class FileManager { readData = fs.readFileSync(filePath); // we have the version, verify it is the correct file size - let contentLength = Downloader.httpHeadContentLength(binary.url(osType, osArch)); + let contentLength = Downloader.httpHeadContentLength(binary.url(osType, osArch), proxy, ignoreSSL); return contentLength.then((value: any): boolean => { if (value == readData.length) { return false; From 301f01821a607b434de633462d38d315272c4241 Mon Sep 17 00:00:00 2001 From: Craig Date: Thu, 13 Oct 2016 14:17:06 -0700 Subject: [PATCH 78/79] chore(types): clang format updates and typings fixes (#103) - clang format updates - fix typings and transpile issues --- lib/binaries/android_sdk.ts | 16 +++++++++--- lib/binaries/appium.ts | 16 +++++++++--- lib/binaries/binary.ts | 40 ++++++++++++++++++++++-------- lib/binaries/chrome_driver.ts | 8 ++++-- lib/binaries/gecko_driver.ts | 8 ++++-- lib/binaries/ie_driver.ts | 8 ++++-- lib/binaries/stand_alone.ts | 12 ++++++--- lib/cli/logger.ts | 20 +++++++++++---- lib/cli/programs.ts | 4 ++- lib/cmds/initialize.ts | 45 +++++++++++++++++++++++++--------- lib/cmds/start.ts | 5 ++-- lib/config.ts | 17 ++++++++++--- lib/files/downloaded_binary.ts | 4 ++- lib/files/file_manager.ts | 6 +++-- spec/files/fileManager_spec.ts | 6 ++--- 15 files changed, 158 insertions(+), 57 deletions(-) diff --git a/lib/binaries/android_sdk.ts b/lib/binaries/android_sdk.ts index f091647d..00b899c5 100644 --- a/lib/binaries/android_sdk.ts +++ b/lib/binaries/android_sdk.ts @@ -29,9 +29,13 @@ export class AndroidSDK extends Binary { this.suffixDefault = '.zip'; } - id(): string { return AndroidSDK.id; } + id(): string { + return AndroidSDK.id; + } - versionDefault(): string { return AndroidSDK.versionDefault; } + versionDefault(): string { + return AndroidSDK.versionDefault; + } suffix(ostype: string): string { if (ostype === 'Darwin') { @@ -43,7 +47,9 @@ export class AndroidSDK extends Binary { } } - url(ostype: string): string { return this.cdn + this.filename(ostype); } + url(ostype: string): string { + return this.cdn + this.filename(ostype); + } zipContentName(ostype: string): string { if (ostype === 'Darwin') { @@ -55,7 +61,9 @@ export class AndroidSDK extends Binary { } } - executableSuffix(): string { return ''; } + executableSuffix(): string { + return ''; + } remove(sdkPath: string): void { try { diff --git a/lib/binaries/appium.ts b/lib/binaries/appium.ts index 29853b0c..07ec8ab3 100644 --- a/lib/binaries/appium.ts +++ b/lib/binaries/appium.ts @@ -27,11 +27,19 @@ export class Appium extends Binary { this.suffixDefault = ''; } - id(): string { return Appium.id; } + id(): string { + return Appium.id; + } - versionDefault(): string { return Appium.versionDefault; } + versionDefault(): string { + return Appium.versionDefault; + } - executableSuffix(): string { return ''; } + executableSuffix(): string { + return ''; + } - remove(sdkPath: string): void { rimraf.sync(sdkPath); } + remove(sdkPath: string): void { + rimraf.sync(sdkPath); + } } diff --git a/lib/binaries/binary.ts b/lib/binaries/binary.ts index f3f1a476..e93e83a8 100644 --- a/lib/binaries/binary.ts +++ b/lib/binaries/binary.ts @@ -31,7 +31,9 @@ export class Binary { cdn: string; // url protocol and host arch: string; - constructor(public cdn: string) {} + constructor(cdn?: string) { + this.cdn = cdn; + } /** * @param ostype The operating system. @@ -53,11 +55,17 @@ export class Binary { return this.prefix() + this.version() + this.executableSuffix(ostype); } - prefix(): string { return this.prefixDefault; } + prefix(): string { + return this.prefixDefault; + } - version(): string { return this.versionCustom; } + version(): string { + return this.versionCustom; + } - suffix(ostype?: string, arch?: string): string { return this.suffixDefault; } + suffix(ostype?: string, arch?: string): string { + return this.suffixDefault; + } filename(ostype?: string, arch?: string): string { return this.prefix() + this.version() + this.suffix(ostype, arch); @@ -67,27 +75,39 @@ export class Binary { * @param ostype The operating system. * @returns The file name for the file inside the downloaded zip file */ - zipContentName(ostype: string): string { return this.name + this.executableSuffix(ostype); } + zipContentName(ostype: string): string { + return this.name + this.executableSuffix(ostype); + } - shortVersion(version: string): string { return version.slice(0, version.lastIndexOf('.')); } + shortVersion(version: string): string { + return version.slice(0, version.lastIndexOf('.')); + } /** * A base class method that should be overridden. */ - id(): string { return 'not implemented'; } + id(): string { + return 'not implemented'; + } /** * A base class method that should be overridden. */ - versionDefault(): string { return 'not implemented'; } + versionDefault(): string { + return 'not implemented'; + } /** * A base class method that should be overridden. */ - url(ostype?: string, arch?: string): string { return 'not implemented'; } + url(ostype?: string, arch?: string): string { + return 'not implemented'; + } /** * Delete an instance of this binary from the file system */ - remove(filename: string): void { fs.unlinkSync(filename); } + remove(filename: string): void { + fs.unlinkSync(filename); + } } diff --git a/lib/binaries/chrome_driver.ts b/lib/binaries/chrome_driver.ts index b6c62e7c..645ed880 100644 --- a/lib/binaries/chrome_driver.ts +++ b/lib/binaries/chrome_driver.ts @@ -25,9 +25,13 @@ export class ChromeDriver extends Binary { this.suffixDefault = '.zip'; } - id(): string { return ChromeDriver.id; } + id(): string { + return ChromeDriver.id; + } - versionDefault(): string { return ChromeDriver.versionDefault; } + versionDefault(): string { + return ChromeDriver.versionDefault; + } suffix(ostype: string, arch: string): string { if (ostype === 'Darwin') { diff --git a/lib/binaries/gecko_driver.ts b/lib/binaries/gecko_driver.ts index 90061cba..ccad396f 100644 --- a/lib/binaries/gecko_driver.ts +++ b/lib/binaries/gecko_driver.ts @@ -28,9 +28,13 @@ export class GeckoDriver extends Binary { this.prefixDefault = 'geckodriver-'; } - id(): string { return GeckoDriver.id; } + id(): string { + return GeckoDriver.id; + } - versionDefault(): string { return GeckoDriver.versionDefault; } + versionDefault(): string { + return GeckoDriver.versionDefault; + } suffix(ostype: string, arch: string): string { if (!GeckoDriver.supports(ostype, arch)) { diff --git a/lib/binaries/ie_driver.ts b/lib/binaries/ie_driver.ts index 8ed96f1a..587c8527 100644 --- a/lib/binaries/ie_driver.ts +++ b/lib/binaries/ie_driver.ts @@ -25,9 +25,13 @@ export class IEDriver extends Binary { this.arch = os.arch(); } - id(): string { return IEDriver.id; } + id(): string { + return IEDriver.id; + } - versionDefault(): string { return IEDriver.versionDefault; } + versionDefault(): string { + return IEDriver.versionDefault; + } version(): string { if (os.type() == 'Windows_NT') { diff --git a/lib/binaries/stand_alone.ts b/lib/binaries/stand_alone.ts index fdf2c6b1..d915d181 100644 --- a/lib/binaries/stand_alone.ts +++ b/lib/binaries/stand_alone.ts @@ -22,9 +22,13 @@ export class StandAlone extends Binary { this.suffixDefault = '.jar'; } - id(): string { return StandAlone.id; } + id(): string { + return StandAlone.id; + } - versionDefault(): string { return StandAlone.versionDefault; } + versionDefault(): string { + return StandAlone.versionDefault; + } url(): string { let urlBase = this.cdn + this.shortVersion(this.version()) + '/'; @@ -32,5 +36,7 @@ export class StandAlone extends Binary { return urlBase + filename; } - executableSuffix(ostype?: string): string { return '.jar'; } + executableSuffix(ostype?: string): string { + return '.jar'; + } } diff --git a/lib/cli/logger.ts b/lib/cli/logger.ts index 3fe93c51..771e425e 100644 --- a/lib/cli/logger.ts +++ b/lib/cli/logger.ts @@ -13,7 +13,9 @@ try { printYellow = chalk.yellow; printGray = chalk.gray; } catch (e) { - printRed = printYellow = printGray = (msg: any) => { return msg; }; + printRed = printYellow = printGray = (msg: any) => { + return msg; + }; } export enum LogLevel { @@ -70,25 +72,33 @@ export class Logger { * Log INFO * @param ...msgs multiple arguments to be logged. */ - info(...msgs: any[]): void { this.log_(LogLevel.INFO, msgs); } + info(...msgs: any[]): void { + this.log_(LogLevel.INFO, msgs); + } /** * Log DEBUG * @param ...msgs multiple arguments to be logged. */ - debug(...msgs: any[]): void { this.log_(LogLevel.DEBUG, msgs); } + debug(...msgs: any[]): void { + this.log_(LogLevel.DEBUG, msgs); + } /** * Log WARN * @param ...msgs multiple arguments to be logged. */ - warn(...msgs: any[]): void { this.log_(LogLevel.WARN, msgs); } + warn(...msgs: any[]): void { + this.log_(LogLevel.WARN, msgs); + } /** * Log ERROR * @param ...msgs multiple arguments to be logged. */ - error(...msgs: any[]): void { this.log_(LogLevel.ERROR, msgs); } + error(...msgs: any[]): void { + this.log_(LogLevel.ERROR, msgs); + } /** * For the log level set, check to see if the messages should be logged. diff --git a/lib/cli/programs.ts b/lib/cli/programs.ts index 81922003..76a5d8cd 100644 --- a/lib/cli/programs.ts +++ b/lib/cli/programs.ts @@ -181,7 +181,9 @@ export class Program { this.printOptions(this.posDescription(), this.posDefault()); } - posDescription(): number { return this.lengthOf_('opt') + 2 * Program.MIN_SPACING; } + posDescription(): number { + return this.lengthOf_('opt') + 2 * Program.MIN_SPACING; + } posDefault(): number { return this.posDescription() + this.lengthOf_('description') + Program.MIN_SPACING; diff --git a/lib/cmds/initialize.ts b/lib/cmds/initialize.ts index 17bb14db..30371819 100644 --- a/lib/cmds/initialize.ts +++ b/lib/cmds/initialize.ts @@ -92,7 +92,10 @@ function setupHardwareAcceleration(sdkPath: string) { // Get a list of all the SDK download targets for a given set of APIs and ABIs function getAndroidSDKTargets(apiLevels: string[], abis: string[]): string[] { - return apiLevels.map((level) => { return 'android-' + level; }) + return apiLevels + .map((level) => { + return 'android-' + level; + }) .concat(abis.reduce((targets, abi) => { let abiParts: string[] = abi.split('/'); let deviceType: string = 'default'; @@ -106,8 +109,9 @@ function getAndroidSDKTargets(apiLevels: string[], abis: string[]): string[] { if (deviceType.toUpperCase() == 'DEFAULT') { deviceType = 'android'; } - return targets.concat(apiLevels.map( - (level) => { return 'sys-img-' + architecture + '-' + deviceType + '-' + level; })); + return targets.concat(apiLevels.map((level) => { + return 'sys-img-' + architecture + '-' + deviceType + '-' + level; + })); }, [])); } @@ -127,7 +131,9 @@ class AVDDescriptor { this.name = [api, deviceType, architecture].join('-'); } - avdName(version: string): string { return this.name + '-v' + version + '-wd-manager'; } + avdName(version: string): string { + return this.name + '-v' + version + '-wd-manager'; + } } // Gets the descriptors for all AVDs which are possible to make given the @@ -150,7 +156,11 @@ function getAVDDescriptors(sdkPath: string): q.Promise { function sequentialForEach(array: T[], func: (x: T) => q.Promise): q.Promise { let ret = q(null); - array.forEach((x: T) => { ret = ret.then(() => { return func(x); }); }); + array.forEach((x: T) => { + ret = ret.then(() => { + return func(x); + }); + }); return ret; } @@ -161,8 +171,12 @@ function configureAVDHardware(sdkPath: string, desc: AVDDescriptor): q.Promise { return q.nfcall(fs.readFile, file); }, - (err: Error) => { return q(''); }) + (stats: fs.Stats) => { + return q.nfcall(fs.readFile, file); + }, + (err: Error) => { + return q(''); + }) .then((contents: string | Buffer) => { let config: any = ini.parse(contents.toString()); config['hw.keyboard'] = 'yes'; @@ -197,7 +211,9 @@ export function android( logger.info('android-sdk: Downloading additional SDK updates'); downloadAndroidUpdates(sdkPath, tools, false, acceptLicenses) - .then(() => { return setupHardwareAcceleration(sdkPath); }) + .then(() => { + return setupHardwareAcceleration(sdkPath); + }) .then(() => { logger.info( 'android-sdk: Downloading more additional SDK updates ' + @@ -206,7 +222,9 @@ export function android( sdkPath, ['build-tools-24.0.0'].concat(getAndroidSDKTargets(apiLevels, abis)), true, acceptLicenses); }) - .then(() => { return getAVDDescriptors(sdkPath); }) + .then(() => { + return getAVDDescriptors(sdkPath); + }) .then((descriptors: AVDDescriptor[]) => { avdDescriptors = descriptors; logger.info('android-sdk: Configuring virtual device hardware'); @@ -223,10 +241,13 @@ export function android( .then(() => { return q.nfcall( fs.writeFile, path.join(sdkPath, 'available_avds.json'), - JSON.stringify( - avdDescriptors.map((descriptor: AVDDescriptor) => { return descriptor.name; }))); + JSON.stringify(avdDescriptors.map((descriptor: AVDDescriptor) => { + return descriptor.name; + }))); + }) + .then(() => { + logger.info('android-sdk: Initialization complete'); }) - .then(() => { logger.info('android-sdk: Initialization complete'); }) .done(); }; diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index 9b8a5a05..d716b957 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -244,8 +244,9 @@ function startAndroid( } function killAndroid() { - androidProcesses.forEach( - (androidProcess: childProcess.ChildProcess) => { androidProcess.kill(); }); + androidProcesses.forEach((androidProcess: childProcess.ChildProcess) => { + androidProcess.kill(); + }); androidProcesses.length = 0; } diff --git a/lib/config.ts b/lib/config.ts index 9da62d72..dcba0a9f 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -7,6 +7,7 @@ import {Logger} from './cli'; let logger = new Logger('config'); export interface ConfigFile { + [key: string]: string; selenium?: string; chrome?: string; gecko?: string; @@ -38,12 +39,20 @@ export class Config { static isProjectVersion = Config.folder === Config.nodeModuleName; static isLocalVersion = false; - static getConfigFile_(): string { return path.resolve(Config.dir, '..', Config.configFile); } + static getConfigFile_(): string { + return path.resolve(Config.dir, '..', Config.configFile); + } - static getPackageFile_(): string { return path.resolve(Config.dir, '..', Config.packageFile) } + static getPackageFile_(): string { + return path.resolve(Config.dir, '..', Config.packageFile) + } - static getSeleniumDir(): string { return path.resolve(Config.dir, '..', '..', 'selenium/'); } - static getBaseDir(): string { return path.resolve(Config.dir, '..', '..'); } + static getSeleniumDir(): string { + return path.resolve(Config.dir, '..', '..', 'selenium/'); + } + static getBaseDir(): string { + return path.resolve(Config.dir, '..', '..'); + } /** * Get the binary versions from the configuration file. diff --git a/lib/files/downloaded_binary.ts b/lib/files/downloaded_binary.ts index ff295b95..662300c6 100644 --- a/lib/files/downloaded_binary.ts +++ b/lib/files/downloaded_binary.ts @@ -14,5 +14,7 @@ export class DownloadedBinary extends Binary { this.versionCustom = binary.versionCustom; } - id(): string { return this.binary.id(); } + id(): string { + return this.binary.id(); + } } diff --git a/lib/files/file_manager.ts b/lib/files/file_manager.ts index f9a1138a..d6c65fb7 100644 --- a/lib/files/file_manager.ts +++ b/lib/files/file_manager.ts @@ -166,7 +166,8 @@ export class FileManager { * @param outputDir The directory where files are downloaded and stored. * @returns If the file should be downloaded. */ - static toDownload(binary: T, outputDir: string, proxy: string, ignoreSSL: boolean): q.Promise { + static toDownload( + binary: T, outputDir: string, proxy: string, ignoreSSL: boolean): q.Promise { let osType = os.type(); let osArch = os.arch(); let filePath: string; @@ -185,7 +186,8 @@ export class FileManager { readData = fs.readFileSync(filePath); // we have the version, verify it is the correct file size - let contentLength = Downloader.httpHeadContentLength(binary.url(osType, osArch), proxy, ignoreSSL); + let contentLength = + Downloader.httpHeadContentLength(binary.url(osType, osArch), proxy, ignoreSSL); return contentLength.then((value: any): boolean => { if (value == readData.length) { return false; diff --git a/spec/files/fileManager_spec.ts b/spec/files/fileManager_spec.ts index c794d340..9cc72cd4 100644 --- a/spec/files/fileManager_spec.ts +++ b/spec/files/fileManager_spec.ts @@ -1,8 +1,8 @@ import * as fs from 'fs'; import * as path from 'path'; -import {Binary, AndroidSDK, ChromeDriver, IEDriver, Appium, StandAlone} from '../../lib/binaries'; -import {DownloadedBinary, FileManager} from '../../lib/files'; -import { BinaryMap } from '../../built/lib/binaries/binary'; +import { Binary, AndroidSDK, ChromeDriver, IEDriver, Appium, StandAlone } from '../../lib/binaries'; +import { DownloadedBinary, FileManager } from '../../lib/files'; +import { BinaryMap } from '../../lib/binaries/binary'; import { Config } from '../../lib/config'; import { GeckoDriver } from '../../lib/binaries/gecko_driver'; From 4b6f4457838e08009271b859e357dc0c491f37ec Mon Sep 17 00:00:00 2001 From: Craig Date: Fri, 14 Oct 2016 13:09:39 -0700 Subject: [PATCH 79/79] chore(release): version bump and update changelog (#104) - supported browsers markdown - update jar file command order - typing dependency outdated bump --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ browsers.md | 20 ++++++++++++++++++++ lib/cmds/start.ts | 5 ++++- package.json | 10 +++++----- 4 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 browsers.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e81caa6d..6cc0daa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,37 @@ +# 10.2.4 + +## Bug Fixes + +- ([3984ea4](https://github.com/angular/webdriver-manager/commit/3984ea4e5cfd2edf0401a5e5310aecaaecb63555)) + fix(filemanager): respect proxy/ignoreSSL options in contentLength HEAD request (#101) + + +- ([946ee00](https://github.com/angular/webdriver-manager/commit/946ee005f7d316fd2d404c4bdbeae9a3802051af)) + fix(chrome_driver): use the x64 binary if chrome driver version is greater than 2.23 (#95) + + * fix(chrome_driver): use the x64 binary if chrome driver version is greater than 2.23 + * fix(chrome_driver): add semver to better determine version number + * refactor(chrome_driver): check first to see if we have valid semver or not + + closes #93 +- ([b183fad](https://github.com/angular/webdriver-manager/commit/b183fadd4ae0b47b0773d6979d090c74419ee327)) + fix(filemanager): Binaries can be downloaded from a custom CDN with alternate_cdn(#97) + + closes #96 + +## Features + +- ([5241fc1](https://github.com/angular/webdriver-manager/commit/5241fc14eaf2b5cdf4b35362f260f6973cea0b1e)) + chore(chromedriver): update chromedriver version to 2.24 (#92) + + Chromedriver < 2.24 has issues with Chrome 54+ + (https://bugs.chromium.org/p/chromedriver/issues/detail?id=1451). + +- ([61af7be](https://github.com/angular/webdriver-manager/commit/61af7be4edbaf070bdcc35dc85f11fb46ab9577e)) + feat(gecko): Add geckodriver, related config, and flags + + Users will still need 'marionette': true in their capabilities in order to use gecko driver. + # 10.2.3 ## Bug Fixes diff --git a/browsers.md b/browsers.md new file mode 100644 index 00000000..9b83daf5 --- /dev/null +++ b/browsers.md @@ -0,0 +1,20 @@ +### Tests with Protractor test suite (v 4.0.9) + +After running the Protractor test suite, the following are the supported +browsers / drivers. We support other drivers and browsers; however, since +the test suite checks only firefox and chrome, these are the only browsers +reported. + +### Current supported browsers / drivers + +| selenium standalone | firefox | chromedriver | chrome | +| ------------------- | ------- | ------------ | ------ | +| 2.53.1 | 47.0.1 | 2.24 | 53 | + + +### Investigated + +| selenium standalone | firefox 47.0.1 | firefox 49.0.1 | +| ------------------- | -------------- | -------------- | +| 2.53.1 | pass | fail | +| 3.0.0 | fail | fail | diff --git a/lib/cmds/start.ts b/lib/cmds/start.ts index d716b957..d30fae5d 100644 --- a/lib/cmds/start.ts +++ b/lib/cmds/start.ts @@ -103,7 +103,7 @@ function start(options: Options) { 'webdriver-manager update --standalone'); process.exit(1); } - let args: string[] = ['-jar', path.join(outputDir, binaries[StandAlone.id].filename())]; + let args: string[] = []; if (osType === 'Linux') { // selenium server may take a long time to start because /dev/random is BLOCKING if there is not // enough entropy the solution is to use /dev/urandom, which is NON-BLOCKING (use /dev/./urandom @@ -173,6 +173,9 @@ function start(options: Options) { } // log the command to launch selenium server + args.push('-jar'); + args.push(path.join(outputDir, binaries[StandAlone.id].filename())); + let argsToString = ''; for (let arg in args) { argsToString += ' ' + args[arg]; diff --git a/package.json b/package.json index 1601522f..44504f16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdriver-manager", - "version": "10.2.3", + "version": "10.2.4", "description": "A selenium server and browser driver manager for your end to end tests.", "scripts": { "prepublish": "gulp prepublish", @@ -41,16 +41,16 @@ "devDependencies": { "@types/adm-zip": "^0.4.29", "@types/chalk": "^0.4.28", - "@types/form-data": "0.0.30", + "@types/form-data": "^0.0.32", "@types/glob": "^5.0.29", "@types/ini": "^1.3.28", "@types/jasmine": "^2.2.32", "@types/minimatch": "^2.0.28", "@types/minimist": "^1.1.28", "@types/node": "^6.0.37", - "@types/q": "0.0.29", - "@types/request": "0.0.29", - "@types/rimraf": "0.0.27", + "@types/q": "^0.0.32", + "@types/request": "^0.0.31", + "@types/rimraf": "^0.0.28", "@types/semver": "^5.3.30", "clang-format": "^1.0.35", "gulp": "^3.9.1",