Skip to content

Create a web driver tunnel #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15,932 changes: 12,207 additions & 3,725 deletions docs/api.json

Large diffs are not rendered by default.

105 changes: 105 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
"@types/node": "~12.0.5",
"@types/semver": "~6.0.0",
"@types/shelljs": "~0.8.5",
"@types/sinon": "^7.5.0",
"concurrently": "~4.1.0",
"intern": "~4.4.3",
"lint-staged": "~8.2.0",
"pre-commit": "~1.2.2",
"prettier": "~1.17.1",
"semver": "~6.1.1",
"shelljs": "~0.8.1"
"shelljs": "~0.8.1",
"sinon": "^7.5.0"
},
"scripts": {
"build": "npm run clean && concurrently intern-dev-build intern-dev-api",
Expand Down
2 changes: 1 addition & 1 deletion src/SeleniumTunnel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export default class SeleniumTunnel extends Tunnel
});
});

resolve(Task.all(tasks).then(() => {}));
Task.all(tasks).then(() => resolve());
},
() => {
tasks &&
Expand Down
124 changes: 23 additions & 101 deletions src/Tunnel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import {
request,
Response
} from '@theintern/common';
import { spawn, ChildProcess } from 'child_process';
import { ChildProcess } from 'child_process';
import { join } from 'path';
import { format as formatUrl, Url } from 'url';
import { fileExists, kill, on } from './lib/util';
import { fileExists, on } from './lib/util';
import { JobState } from './interfaces';
import * as decompress from 'decompress';
import {
makeChildWithCommand,
stopChildProcess
} from './lib/tunnelChildProcesses';

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

/**
* The directory in which the tunnel implementations will download
* any necessary applications.
*/
basePath: string = __dirname;

protected _startTask: CancellablePromise<any> | undefined;
protected _stopTask: Promise<number | void> | undefined;
protected _handle: Handle | undefined;
Expand Down Expand Up @@ -259,8 +269,8 @@ export default class Tunnel extends Evented<TunnelEvents, string>
/**
* Sends information about a job to the tunnel provider.
*
* @param jobId The job to send data about. This is usually a session ID.
* @param data Data to send to the tunnel provider about the job.
* @param _jobId The job to send data about. This is usually a session ID.
* @param _data Data to send to the tunnel provider about the job.
* @returns A promise that resolves once the job state request is complete.
*/
sendJobState(_jobId: string, _data: JobState): CancellablePromise<void> {
Expand Down Expand Up @@ -446,89 +456,12 @@ export default class Tunnel extends Evented<TunnelEvents, string>
executor: ChildExecutor,
...values: string[]
): CancellablePromise {
const command = this.executable;
const args = this._makeArgs(...values);
const options = this._makeOptions(...values);
const child = spawn(command, args, options);

child.stdout.setEncoding('utf8');
child.stderr.setEncoding('utf8');

let handle: Handle;
let canceled = false;

// Ensure child process is killed when parent exits
process.on('exit', () => kill(child.pid));
process.on('SIGINT', () => kill(child.pid));

const task = new Task(
(resolve, reject) => {
let errorMessage = '';
let exitCode: number | undefined;
let stderrClosed = false;
let exitted = false;

function handleChildExit() {
reject(
new Error(
`Tunnel failed to start: ${errorMessage ||
`Exit code: ${exitCode}`}`
)
);
}

handle = createCompositeHandle(
on(child, 'error', reject),

on(child.stderr, 'data', (data: string) => {
errorMessage += data;
}),

on(child, 'exit', () => {
exitted = true;
if (stderrClosed) {
handleChildExit();
}
}),

// stderr might still have data in buffer at the time the
// exit event is sent, so we have to store data from stderr
// and the exit code and reject only once stderr closes
on(child.stderr, 'close', () => {
stderrClosed = true;
if (exitted) {
handleChildExit();
}
})
);

const result = executor(child, resolve, reject);
if (result) {
handle = createCompositeHandle(handle, result);
}
},
() => {
canceled = true;

// Make a best effort to kill the process, but don't throw
// exceptions
try {
kill(child.pid);
} catch (error) {}
}
return makeChildWithCommand(
this.executable,
executor,
this._makeArgs(...values),
this._makeOptions(...values)
);

return task.finally(() => {
handle.destroy();
if (canceled) {
// We only want this to run when cancelation has occurred
return new Promise(resolve => {
child.once('exit', () => {
resolve();
});
});
}
});
}

/**
Expand Down Expand Up @@ -620,21 +553,7 @@ export default class Tunnel extends Evented<TunnelEvents, string>
*/
protected _stop() {
return new Promise<number | void>((resolve, reject) => {
const childProcess = this._process;
if (!childProcess) {
resolve();
return;
}

childProcess.once('exit', code => {
resolve(code == null ? undefined : code);
});

try {
kill(childProcess.pid);
} catch (error) {
reject(error);
}
stopChildProcess(this._process, resolve, reject);
});
}
}
Expand Down Expand Up @@ -768,6 +687,9 @@ export interface TunnelProperties extends DownloadProperties {

/** [[Tunnel.Tunnel.verbose|More info]] */
verbose: boolean;

/** [[Tunnel.Tunnel.basePath|More info]] */
basePath: string | undefined;
}

export type TunnelOptions = Partial<TunnelProperties>;
Expand Down
Loading