Skip to content

Commit 1f9f612

Browse files
committed
Implement WebDriver tunnel.
1 parent aa20012 commit 1f9f612

File tree

7 files changed

+1704
-102
lines changed

7 files changed

+1704
-102
lines changed

src/SeleniumTunnel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export default class SeleniumTunnel extends Tunnel
184184
});
185185
});
186186

187-
resolve(Task.all(tasks).then(() => {}));
187+
Task.all(tasks).then(() => resolve());
188188
},
189189
() => {
190190
tasks &&

src/Tunnel.ts

Lines changed: 23 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ import {
88
request,
99
Response
1010
} from '@theintern/common';
11-
import { spawn, ChildProcess } from 'child_process';
11+
import { ChildProcess } from 'child_process';
1212
import { join } from 'path';
1313
import { format as formatUrl, Url } from 'url';
14-
import { fileExists, kill, on } from './lib/util';
14+
import { fileExists, on } from './lib/util';
1515
import { JobState } from './interfaces';
1616
import * as decompress from 'decompress';
17+
import {
18+
makeChildWithCommand,
19+
stopChildProcess
20+
} from './lib/tunnelChildProcesses';
1721

1822
/**
1923
* A Tunnel is a mechanism for connecting to a WebDriver service provider that
@@ -121,6 +125,12 @@ export default class Tunnel extends Evented<TunnelEvents, string>
121125
/** Whether or not to tell the tunnel to provide verbose logging output. */
122126
verbose!: boolean;
123127

128+
/**
129+
* The directory in which the tunnel implementations will download
130+
* any necessary applications.
131+
*/
132+
basePath: string = __dirname;
133+
124134
protected _startTask: CancellablePromise<any> | undefined;
125135
protected _stopTask: Promise<number | void> | undefined;
126136
protected _handle: Handle | undefined;
@@ -259,8 +269,8 @@ export default class Tunnel extends Evented<TunnelEvents, string>
259269
/**
260270
* Sends information about a job to the tunnel provider.
261271
*
262-
* @param jobId The job to send data about. This is usually a session ID.
263-
* @param data Data to send to the tunnel provider about the job.
272+
* @param _jobId The job to send data about. This is usually a session ID.
273+
* @param _data Data to send to the tunnel provider about the job.
264274
* @returns A promise that resolves once the job state request is complete.
265275
*/
266276
sendJobState(_jobId: string, _data: JobState): CancellablePromise<void> {
@@ -446,89 +456,12 @@ export default class Tunnel extends Evented<TunnelEvents, string>
446456
executor: ChildExecutor,
447457
...values: string[]
448458
): CancellablePromise {
449-
const command = this.executable;
450-
const args = this._makeArgs(...values);
451-
const options = this._makeOptions(...values);
452-
const child = spawn(command, args, options);
453-
454-
child.stdout.setEncoding('utf8');
455-
child.stderr.setEncoding('utf8');
456-
457-
let handle: Handle;
458-
let canceled = false;
459-
460-
// Ensure child process is killed when parent exits
461-
process.on('exit', () => kill(child.pid));
462-
process.on('SIGINT', () => kill(child.pid));
463-
464-
const task = new Task(
465-
(resolve, reject) => {
466-
let errorMessage = '';
467-
let exitCode: number | undefined;
468-
let stderrClosed = false;
469-
let exitted = false;
470-
471-
function handleChildExit() {
472-
reject(
473-
new Error(
474-
`Tunnel failed to start: ${errorMessage ||
475-
`Exit code: ${exitCode}`}`
476-
)
477-
);
478-
}
479-
480-
handle = createCompositeHandle(
481-
on(child, 'error', reject),
482-
483-
on(child.stderr, 'data', (data: string) => {
484-
errorMessage += data;
485-
}),
486-
487-
on(child, 'exit', () => {
488-
exitted = true;
489-
if (stderrClosed) {
490-
handleChildExit();
491-
}
492-
}),
493-
494-
// stderr might still have data in buffer at the time the
495-
// exit event is sent, so we have to store data from stderr
496-
// and the exit code and reject only once stderr closes
497-
on(child.stderr, 'close', () => {
498-
stderrClosed = true;
499-
if (exitted) {
500-
handleChildExit();
501-
}
502-
})
503-
);
504-
505-
const result = executor(child, resolve, reject);
506-
if (result) {
507-
handle = createCompositeHandle(handle, result);
508-
}
509-
},
510-
() => {
511-
canceled = true;
512-
513-
// Make a best effort to kill the process, but don't throw
514-
// exceptions
515-
try {
516-
kill(child.pid);
517-
} catch (error) {}
518-
}
459+
return makeChildWithCommand(
460+
this.executable,
461+
executor,
462+
this._makeArgs(...values),
463+
this._makeOptions(...values)
519464
);
520-
521-
return task.finally(() => {
522-
handle.destroy();
523-
if (canceled) {
524-
// We only want this to run when cancelation has occurred
525-
return new Promise(resolve => {
526-
child.once('exit', () => {
527-
resolve();
528-
});
529-
});
530-
}
531-
});
532465
}
533466

534467
/**
@@ -620,21 +553,7 @@ export default class Tunnel extends Evented<TunnelEvents, string>
620553
*/
621554
protected _stop() {
622555
return new Promise<number | void>((resolve, reject) => {
623-
const childProcess = this._process;
624-
if (!childProcess) {
625-
resolve();
626-
return;
627-
}
628-
629-
childProcess.once('exit', code => {
630-
resolve(code == null ? undefined : code);
631-
});
632-
633-
try {
634-
kill(childProcess.pid);
635-
} catch (error) {
636-
reject(error);
637-
}
556+
stopChildProcess(this._process, resolve, reject);
638557
});
639558
}
640559
}
@@ -768,6 +687,9 @@ export interface TunnelProperties extends DownloadProperties {
768687

769688
/** [[Tunnel.Tunnel.verbose|More info]] */
770689
verbose: boolean;
690+
691+
/** [[Tunnel.Tunnel.basePath|More info]] */
692+
basePath: string | undefined;
771693
}
772694

773695
export type TunnelOptions = Partial<TunnelProperties>;

0 commit comments

Comments
 (0)