Skip to content

Commit 7f5c9c7

Browse files
committed
chore(cli): add cli with yargs
- Do not use yarg 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.
1 parent 8972ad2 commit 7f5c9c7

15 files changed

+579
-284
lines changed

lib/cli/index.ts

Lines changed: 120 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,121 @@
1-
import * as yargs from 'yargs';
2-
import * as clean from '../cmds/clean';
3-
import * as start from '../cmds/start';
4-
import * as status from '../cmds/status';
5-
import * as update from '../cmds/update';
6-
7-
const chrome = {
8-
describe: 'Install or update chromedriver.',
9-
default: true
10-
};
11-
const gecko = {
12-
describe: 'Install or update geckodriver.',
13-
default: true
14-
};
15-
const ie = {
16-
describe: 'Install or update ie driver.',
17-
default: false
18-
};
19-
const ignoreSSL = {
20-
describe: 'Ignore SSL certificates.'
21-
}
22-
const outDir = {
23-
describe: 'Location of output.',
24-
default: 'downloads'
25-
};
26-
const proxy = {
27-
describe: 'Use a proxy server to download files.'
28-
};
29-
const standalone = {
30-
describe: 'Install or update selenium server standalone.',
31-
default: true
32-
};
33-
const versionsChrome = {
34-
describe: 'The chromedriver version.'
35-
};
36-
const versionsGecko = {
37-
describe: 'The geckodriver version.',
38-
};
39-
const versionsIe = {
40-
describe: 'The ie driver version.'
41-
};
42-
const versionsStandalone = {
43-
describe: 'The selenium server standalone version.'
44-
};
45-
46-
yargs
47-
.command('clean', 'Removes downloaded files from the out_dir', {
48-
'out_dir': outDir
49-
}, (argv: yargs.Arguments) => {
50-
clean.handler(argv);
51-
})
52-
.command('start', 'Start up the selenium server.', {
53-
'chrome': chrome,
54-
'gecko': gecko,
55-
'ie': ie,
56-
'out_dir': outDir,
57-
'standalone': standalone,
58-
'versions_chrome': versionsChrome,
59-
'versions_gecko': versionsGecko,
60-
'versions_ie': versionsIe,
61-
'versions_standalone': versionsStandalone,
62-
}, (argv: yargs.Arguments) => {
63-
start.handler(argv);
64-
})
65-
.command('status', 'List the current available binaries.', {
66-
'out_dir': outDir
67-
}, (argv: yargs.Arguments) => {
68-
status.handler(argv);
69-
})
70-
.command('update', 'Install or update selected binaries.', {
71-
'chrome': chrome,
72-
'gecko': gecko,
73-
'ie': ie,
74-
'ignore_ssl': ignoreSSL,
75-
'out_dir': outDir,
76-
'proxy': proxy,
77-
'standalone': standalone,
78-
'versions.chrome': versionsChrome,
79-
'versions.gecko': versionsGecko,
80-
'versions.ie': versionsIe,
81-
'versions.standalone': versionsStandalone,
82-
}, (argv: yargs.Arguments) => {
83-
update.handler(argv);
84-
})
85-
.help()
1+
import * as clean from '../cmds/clean';
2+
import * as start from '../cmds/start';
3+
import * as status from '../cmds/status';
4+
import * as update from '../cmds/update';
5+
6+
// Not using yarg typings due to:
7+
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/28061#issuecomment-412365576
8+
// Although the fix is to cast all my objects into a yargs.Options
9+
// objects, the error is not obvious to debug if an error occurs.
10+
const yargs = require('yargs');
11+
12+
// The following constants should be used to match
13+
// the Option provider names.
14+
const CHROME = 'chrome';
15+
const chromeOption = {
16+
describe: 'Install or update chromedriver.',
17+
default: true,
18+
type: 'boolean'
19+
};
20+
const GECKO = 'gecko';
21+
const geckoOption = {
22+
describe: 'Install or update geckodriver.',
23+
default: true,
24+
type: 'boolean'
25+
};
26+
const IEDRIVER = 'iedriver';
27+
const ieOption = {
28+
describe: 'Install or update ie driver.',
29+
default: false,
30+
type: 'boolean'
31+
};
32+
const IGNORE_SSL = 'ignore_ssl';
33+
const ignoreSSLOption = {
34+
describe: 'Ignore SSL certificates.',
35+
type: 'boolean'
36+
};
37+
const OUT_DIR = 'out_dir';
38+
let outDirOption = {
39+
describe: 'Location of output.',
40+
default: 'downloads',
41+
type: 'string'
42+
};
43+
const PROXY = 'proxy';
44+
const proxyOption = {
45+
describe: 'Use a proxy server to download files.',
46+
type: 'string'
47+
};
48+
const STANDALONE = 'standalone';
49+
const standaloneOption = {
50+
describe: 'Install or update selenium server standalone.',
51+
default: true,
52+
type: 'boolean'
53+
};
54+
const VERSIONS_CHROME = 'versions.chrome';
55+
const versionsChromeOption = {
56+
describe: 'The chromedriver version.',
57+
type: 'string'
58+
};
59+
const VERSIONS_GECKO = 'versions.gecko';
60+
const versionsGeckoOption = {
61+
describe: 'The geckodriver version.',
62+
type: 'string'
63+
};
64+
const VERSIONS_IE = 'versions.ie';
65+
const versionsIeOption = {
66+
describe: 'The ie driver version.',
67+
type: 'string'
68+
};
69+
const VERSIONS_STANDALONE = 'versions.standalone';
70+
const versionsStandaloneOption = {
71+
describe: 'The selenium server standalone version.',
72+
type: 'string'
73+
};
74+
75+
yargs
76+
.command('clean', 'Removes downloaded files from the out_dir',
77+
(yargs: any) => {
78+
return yargs.option(OUT_DIR, outDirOption)
79+
}, (argv: any) => {
80+
clean.handler(argv);
81+
})
82+
.command('start', 'Start up the selenium server.',
83+
(yargs: any) => {
84+
return yargs
85+
.option(CHROME, chromeOption)
86+
.option(GECKO, geckoOption)
87+
.option(IEDRIVER, ieOption)
88+
.option(OUT_DIR, outDirOption)
89+
.option(STANDALONE, standaloneOption)
90+
.option(VERSIONS_CHROME, versionsChromeOption)
91+
.option(VERSIONS_GECKO, versionsGeckoOption)
92+
.option(VERSIONS_IE, versionsIeOption)
93+
.option(VERSIONS_STANDALONE, versionsStandaloneOption);
94+
}, (argv: any) => {
95+
start.handler(argv);
96+
})
97+
.command('status', 'List the current available binaries.',
98+
(yargs: any) => {
99+
return yargs.option(OUT_DIR, outDirOption)
100+
}, (argv: any) => {
101+
status.handler(argv);
102+
})
103+
.command('update', 'Install or update selected binaries.',
104+
(yargs: any) => {
105+
return yargs.option(OUT_DIR, outDirOption)
106+
.option(CHROME, chromeOption)
107+
.option(GECKO, geckoOption)
108+
.option(IEDRIVER, ieOption)
109+
.option(IGNORE_SSL, ignoreSSLOption)
110+
.option(OUT_DIR, outDirOption)
111+
.option(PROXY, proxyOption)
112+
.option(STANDALONE, standaloneOption)
113+
.option(VERSIONS_CHROME, versionsChromeOption)
114+
.option(VERSIONS_GECKO, versionsGeckoOption)
115+
.option(VERSIONS_IE, versionsIeOption)
116+
.option(VERSIONS_STANDALONE, versionsStandaloneOption);
117+
}, (argv: any) => {
118+
update.handler(argv);
119+
})
120+
.help()
86121
.argv;

lib/cmds/clean.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
import * as yargs from 'yargs';
2-
import { Options } from './options';
3-
import { constructAllProviders } from './utils';
4-
5-
export function handler(argv: yargs.Arguments) {
6-
let options = constructAllProviders(argv);
7-
console.log(clean(options));
8-
}
9-
10-
export function clean(options: Options): string {
11-
let filesCleaned: string[] = [];
12-
for (let provider of options.providers) {
13-
let cleanedFiles = provider.binary.cleanFiles();
14-
if (cleanedFiles) {
15-
filesCleaned.push(cleanedFiles);
16-
}
17-
}
18-
filesCleaned.push(options.server.binary.cleanFiles());
19-
return (filesCleaned.sort()).join();
20-
}
1+
import { Options } from './options';
2+
import { constructAllProviders } from './utils';
3+
4+
/**
5+
* Handles removing files that were downloaded and logs the files.
6+
* @param argv The argv from yargs.
7+
*/
8+
export function handler(argv: any) {
9+
let options = constructAllProviders(argv);
10+
console.log(clean(options));
11+
}
12+
13+
/**
14+
* Goes through all the providers and removes the downloaded files.
15+
* @param options The constructed options.
16+
* @returns A list of deleted files.
17+
*/
18+
export function clean(options: Options): string {
19+
let filesCleaned: string[] = [];
20+
for (let provider of options.providers) {
21+
let cleanedFiles = provider.binary.cleanFiles();
22+
if (cleanedFiles) {
23+
filesCleaned.push(cleanedFiles);
24+
}
25+
}
26+
if (options.server && options.server.binary) {
27+
filesCleaned.push(options.server.binary.cleanFiles());
28+
}
29+
return (filesCleaned.sort()).join('\n');
30+
}

lib/cmds/cmds.spec-e2e.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import * as fs from 'fs';
2+
import * as os from 'os';
3+
import * as path from 'path';
4+
import * as rimraf from 'rimraf';
5+
import {
6+
constructAllProviders,
7+
constructProviders,
8+
} from './utils';
9+
import { update } from './update';
10+
import { status } from './status';
11+
import { start } from './start';
12+
import { clean } from './clean';
13+
import { SeleniumServer } from '../provider/selenium_server';
14+
import { resolve } from 'url';
15+
16+
describe('using the cli', () => {
17+
let tmpDir = path.resolve(os.tmpdir(), 'test');
18+
let origTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
19+
20+
beforeAll(() => {
21+
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
22+
try {
23+
fs.mkdirSync(tmpDir);
24+
} catch (err) {}
25+
});
26+
27+
afterAll(() => {
28+
jasmine.DEFAULT_TIMEOUT_INTERVAL = origTimeout;
29+
try {
30+
rimraf.sync(tmpDir);
31+
} catch (err) {}
32+
});
33+
34+
describe('a user runs update', () => {
35+
it('should download the files', async() => {
36+
let argv = {
37+
_: ['foobar'],
38+
chrome: true,
39+
standalone: true,
40+
out_dir: tmpDir,
41+
'$0': 'bin\\webdriver-manager'
42+
};
43+
let options = constructProviders(argv);
44+
await update(options);
45+
const existFiles = fs.readdirSync(tmpDir);
46+
expect(existFiles.length).toBe(7);
47+
});
48+
});
49+
50+
describe('a user runs status', () => {
51+
it('should get the list of versions', () => {
52+
let argv = {
53+
_: ['foobar'],
54+
out_dir: tmpDir,
55+
'$0': 'bin\\webdriver-manager'
56+
};
57+
let options = constructAllProviders(argv);
58+
let statusLog = status(options);
59+
console.log(statusLog);
60+
let lines = statusLog.split('\n');
61+
expect(lines.length).toBe(2);
62+
});
63+
});
64+
65+
describe('a user runs start', () => {
66+
it ('should start the selenium server standalone', async() => {
67+
let argv = {
68+
_: ['foobar'],
69+
chrome: true,
70+
standalone: true,
71+
out_dir: tmpDir,
72+
'$0': 'bin\\webdriver-manager'
73+
};
74+
let options = constructProviders(argv);
75+
// Do not await this promise to start the server since the promise is
76+
// never resolved.
77+
let startProcess = start(options);
78+
79+
// Arbitrarily wait for the server to start.
80+
await new Promise((resolve, _) => {
81+
setTimeout(resolve, 3000);
82+
});
83+
let seleniumServer = (options.server.binary as SeleniumServer);
84+
expect(seleniumServer.seleniumProcess).toBeTruthy();
85+
expect(seleniumServer.seleniumProcess.pid).toBeTruthy();
86+
87+
// Kill the server with the pid.
88+
await seleniumServer.stopServer();
89+
expect(await startProcess).toBe(0);
90+
});
91+
});
92+
93+
describe('a user run clean', () => {
94+
it('should remove the files', () => {
95+
let argv = {
96+
_: ['foobar'],
97+
out_dir: tmpDir,
98+
'$0': 'bin\\webdriver-manager'
99+
};
100+
let options = constructAllProviders(argv);
101+
let cleanLogs = clean(options);
102+
console.log(cleanLogs);
103+
let lines = cleanLogs.split('\n');
104+
expect(lines.length).toBe(7);
105+
const existFiles = fs.readdirSync(tmpDir);
106+
expect(existFiles.length).toBe(0);
107+
});
108+
});
109+
});

0 commit comments

Comments
 (0)