Skip to content

Commit

Permalink
chore(cli): add cli with yargs
Browse files Browse the repository at this point in the history
- Do not use yargs typings until
DefinitelyTyped/DefinitelyTyped#28061 is
resolved.
- Created e2e test to updates, checks status, starts server, and removes
files.
- Expose seleniumProcess from the SeleniumServer class.
- Moved SIGINT event to start handler method. This will let a user stop
the selenium sever with the process pid and without existing their
JavaScript process.
  • Loading branch information
cnishina committed Aug 13, 2018
1 parent 8972ad2 commit 9bd1401
Show file tree
Hide file tree
Showing 16 changed files with 581 additions and 286 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ jobs:
- image: circleci/node:8
steps:
- checkout

- run: node --version
- run: npm --version

- run: npm install
- run: npm run test-unit
- run: npm run test-int
- run: npm run test-proxy

- run: npm run test-e2e
203 changes: 118 additions & 85 deletions lib/cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,119 @@
import * as yargs from 'yargs';
import * as clean from '../cmds/clean';
import * as start from '../cmds/start';
import * as status from '../cmds/status';
import * as update from '../cmds/update';

const chrome = {
describe: 'Install or update chromedriver.',
default: true
};
const gecko = {
describe: 'Install or update geckodriver.',
default: true
};
const ie = {
describe: 'Install or update ie driver.',
default: false
};
const ignoreSSL = {
describe: 'Ignore SSL certificates.'
}
const outDir = {
describe: 'Location of output.',
default: 'downloads'
};
const proxy = {
describe: 'Use a proxy server to download files.'
};
const standalone = {
describe: 'Install or update selenium server standalone.',
default: true
};
const versionsChrome = {
describe: 'The chromedriver version.'
};
const versionsGecko = {
describe: 'The geckodriver version.',
};
const versionsIe = {
describe: 'The ie driver version.'
};
const versionsStandalone = {
describe: 'The selenium server standalone version.'
};

yargs
.command('clean', 'Removes downloaded files from the out_dir', {
'out_dir': outDir
}, (argv: yargs.Arguments) => {
clean.handler(argv);
})
.command('start', 'Start up the selenium server.', {
'chrome': chrome,
'gecko': gecko,
'ie': ie,
'out_dir': outDir,
'standalone': standalone,
'versions_chrome': versionsChrome,
'versions_gecko': versionsGecko,
'versions_ie': versionsIe,
'versions_standalone': versionsStandalone,
}, (argv: yargs.Arguments) => {
start.handler(argv);
})
.command('status', 'List the current available binaries.', {
'out_dir': outDir
}, (argv: yargs.Arguments) => {
status.handler(argv);
})
.command('update', 'Install or update selected binaries.', {
'chrome': chrome,
'gecko': gecko,
'ie': ie,
'ignore_ssl': ignoreSSL,
'out_dir': outDir,
'proxy': proxy,
'standalone': standalone,
'versions.chrome': versionsChrome,
'versions.gecko': versionsGecko,
'versions.ie': versionsIe,
'versions.standalone': versionsStandalone,
}, (argv: yargs.Arguments) => {
update.handler(argv);
})
.help()
import * as clean from '../cmds/clean';
import * as start from '../cmds/start';
import * as status from '../cmds/status';
import * as update from '../cmds/update';

// Not using yargs type definitions due to:
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/28061#issuecomment-412365576
// Although the fix is to cast all my objects into a yargs.Options
// objects, the error is not obvious to debug if an error occurs.
const yargs = require('yargs');

const CHROME = 'chrome';
const chromeOption = {
describe: 'Install or update chromedriver.',
default: true,
type: 'boolean'
};
const GECKO = 'gecko';
const geckoOption = {
describe: 'Install or update geckodriver.',
default: true,
type: 'boolean'
};
const IEDRIVER = 'iedriver';
const ieOption = {
describe: 'Install or update ie driver.',
default: false,
type: 'boolean'
};
const IGNORE_SSL = 'ignore_ssl';
const ignoreSSLOption = {
describe: 'Ignore SSL certificates.',
type: 'boolean'
};
const OUT_DIR = 'out_dir';
let outDirOption = {
describe: 'Location of output.',
default: 'downloads',
type: 'string'
};
const PROXY = 'proxy';
const proxyOption = {
describe: 'Use a proxy server to download files.',
type: 'string'
};
const STANDALONE = 'standalone';
const standaloneOption = {
describe: 'Install or update selenium server standalone.',
default: true,
type: 'boolean'
};
const VERSIONS_CHROME = 'versions.chrome';
const versionsChromeOption = {
describe: 'The chromedriver version.',
type: 'string'
};
const VERSIONS_GECKO = 'versions.gecko';
const versionsGeckoOption = {
describe: 'The geckodriver version.',
type: 'string'
};
const VERSIONS_IE = 'versions.ie';
const versionsIeOption = {
describe: 'The ie driver version.',
type: 'string'
};
const VERSIONS_STANDALONE = 'versions.standalone';
const versionsStandaloneOption = {
describe: 'The selenium server standalone version.',
type: 'string'
};

yargs
.command('clean', 'Removes downloaded files from the out_dir.',
(yargs: any) => {
return yargs.option(OUT_DIR, outDirOption)
}, (argv: any) => {
clean.handler(argv);
})
.command('start', 'Start up the selenium server.',
(yargs: any) => {
return yargs
.option(CHROME, chromeOption)
.option(GECKO, geckoOption)
.option(IEDRIVER, ieOption)
.option(OUT_DIR, outDirOption)
.option(STANDALONE, standaloneOption)
.option(VERSIONS_CHROME, versionsChromeOption)
.option(VERSIONS_GECKO, versionsGeckoOption)
.option(VERSIONS_IE, versionsIeOption)
.option(VERSIONS_STANDALONE, versionsStandaloneOption);
}, (argv: any) => {
start.handler(argv);
})
.command('status', 'List the current available binaries.',
(yargs: any) => {
return yargs.option(OUT_DIR, outDirOption)
}, (argv: any) => {
status.handler(argv);
})
.command('update', 'Install or update selected binaries.',
(yargs: any) => {
return yargs.option(OUT_DIR, outDirOption)
.option(CHROME, chromeOption)
.option(GECKO, geckoOption)
.option(IEDRIVER, ieOption)
.option(IGNORE_SSL, ignoreSSLOption)
.option(OUT_DIR, outDirOption)
.option(PROXY, proxyOption)
.option(STANDALONE, standaloneOption)
.option(VERSIONS_CHROME, versionsChromeOption)
.option(VERSIONS_GECKO, versionsGeckoOption)
.option(VERSIONS_IE, versionsIeOption)
.option(VERSIONS_STANDALONE, versionsStandaloneOption);
}, (argv: any) => {
update.handler(argv);
})
.help()
.argv;
50 changes: 30 additions & 20 deletions lib/cmds/clean.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import * as yargs from 'yargs';
import { Options } from './options';
import { constructAllProviders } from './utils';

export function handler(argv: yargs.Arguments) {
let options = constructAllProviders(argv);
console.log(clean(options));
}

export function clean(options: Options): string {
let filesCleaned: string[] = [];
for (let provider of options.providers) {
let cleanedFiles = provider.binary.cleanFiles();
if (cleanedFiles) {
filesCleaned.push(cleanedFiles);
}
}
filesCleaned.push(options.server.binary.cleanFiles());
return (filesCleaned.sort()).join();
}
import { Options } from './options';
import { constructAllProviders } from './utils';

/**
* Handles removing files that were downloaded and logs the files.
* @param argv The argv from yargs.
*/
export function handler(argv: any) {
let options = constructAllProviders(argv);
console.log(clean(options));
}

/**
* Goes through all the providers and removes the downloaded files.
* @param options The constructed options.
* @returns A list of deleted files.
*/
export function clean(options: Options): string {
let filesCleaned: string[] = [];
for (let provider of options.providers) {
let cleanedFiles = provider.binary.cleanFiles();
if (cleanedFiles) {
filesCleaned.push(cleanedFiles);
}
}
if (options.server && options.server.binary) {
filesCleaned.push(options.server.binary.cleanFiles());
}
return (filesCleaned.sort()).join('\n');
}
111 changes: 111 additions & 0 deletions lib/cmds/cmds.spec-e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as rimraf from 'rimraf';
import {
constructAllProviders,
constructProviders,
} from './utils';
import { update } from './update';
import { status } from './status';
import { start } from './start';
import { clean } from './clean';
import { SeleniumServer } from '../provider/selenium_server';
import { resolve } from 'url';

describe('using the cli', () => {
let tmpDir = path.resolve(os.tmpdir(), 'test');
let origTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;

beforeAll(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
try {
fs.mkdirSync(tmpDir);
} catch (err) {}
});

afterAll(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = origTimeout;
try {
rimraf.sync(tmpDir);
} catch (err) {}
});

describe('a user runs update', () => {
it('should download the files', async() => {
let argv = {
_: ['foobar'],
chrome: true,
standalone: true,
out_dir: tmpDir,
'$0': 'bin\\webdriver-manager'
};
let options = constructProviders(argv);
await update(options);
const existFiles = fs.readdirSync(tmpDir);
expect(existFiles.length).toBe(7);
});
});

describe('a user runs status', () => {
it('should get the list of versions', () => {
let argv = {
_: ['foobar'],
out_dir: tmpDir,
'$0': 'bin\\webdriver-manager'
};
let options = constructAllProviders(argv);
let statusLog = status(options);
console.log(statusLog);
let lines = statusLog.split('\n');
expect(lines.length).toBe(2);
});
});

describe('a user runs start', () => {
it ('should start the selenium server standalone', async() => {
let argv = {
_: ['foobar'],
chrome: true,
standalone: true,
out_dir: tmpDir,
'$0': 'bin\\webdriver-manager'
};
let options = constructProviders(argv);
// Do not await this promise to start the server since the promise is
// never resolved by waiting, it is either killed by pid or get request.
let startProcess = start(options);

// Arbitrarily wait for the server to start.
await new Promise((resolve, _) => {
setTimeout(resolve, 3000);
});
let seleniumServer = (options.server.binary as SeleniumServer);
expect(seleniumServer.seleniumProcess).toBeTruthy();
expect(seleniumServer.seleniumProcess.pid).toBeTruthy();

// Stop the server using the get request.
await seleniumServer.stopServer();

// Check to see that the exit code is 0.
expect(await startProcess).toBe(0);
});
});

describe('a user runs clean', () => {
it('should remove the files', () => {
let argv = {
_: ['foobar'],
out_dir: tmpDir,
'$0': 'bin\\webdriver-manager'
};
let options = constructAllProviders(argv);
let cleanLogs = clean(options);
console.log(cleanLogs);
let lines = cleanLogs.split('\n');
expect(lines.length).toBe(7);
const existFiles = fs.readdirSync(tmpDir);
expect(existFiles.length).toBe(0);
});
});
});
Loading

0 comments on commit 9bd1401

Please sign in to comment.