@web/test-runner-saucelabs
Version:
Saucelabs launcher for Web Test Runner
91 lines (76 loc) • 2.6 kB
text/typescript
import { BrowserLauncher } from '@web/test-runner-core';
import SaucelabsAPI, {
SauceLabsOptions,
SauceConnectOptions,
SauceConnectInstance,
} from 'saucelabs';
import internalIp from 'internal-ip';
/**
* Wraps a Promise with a timeout, rejecing the promise with the timeout.
*/
export function withTimeout<T>(promise: Promise<T>, message: string): Promise<T> {
return new Promise<T>((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error(message));
}, 5 * 60 * 1000);
promise
.then(val => resolve(val))
.catch(err => reject(err))
.finally(() => clearTimeout(timeoutId));
});
}
export class SauceLabsLauncherManager {
private api: SaucelabsAPI;
private launchers = new Set<BrowserLauncher>();
private connectionPromise?: Promise<SauceConnectInstance>;
private connection?: SauceConnectInstance;
private options: SauceLabsOptions;
private connectOptions?: SauceConnectOptions;
constructor(options: SauceLabsOptions, connectOptions?: SauceConnectOptions) {
this.options = options;
this.connectOptions = connectOptions;
this.api = new SaucelabsAPI(this.options);
process.on('SIGINT', this.closeConnection);
process.on('SIGTERM', this.closeConnection);
process.on('beforeExit', this.closeConnection);
process.on('exit', this.closeConnection);
}
async registerLauncher(launcher: BrowserLauncher) {
this.launchers.add(launcher);
if (this.connectionPromise != null) {
await this.connectionPromise;
return;
}
console.log('[Saucelabs] Setting up Sauce Connect proxy...');
this.connectionPromise = withTimeout(
this.api.startSauceConnect({
...this.connectOptions,
noSslBumpDomains: `127.0.0.1,localhost,${internalIp.v4.sync()}`,
}),
'[Saucelabs] Timed out setting up Sauce Connect proxy after 5 minutes.',
);
this.connection = await this.connectionPromise;
}
async deregisterLauncher(launcher: BrowserLauncher) {
this.launchers.delete(launcher);
if (this.launchers.size === 0) {
return this.closeConnection();
}
}
private closeConnection = async () => {
if (this.connection == null && this.connectionPromise == null) {
// already closed
return;
}
if (this.connection == null) {
// wait for connection to finish opening
await this.connectionPromise;
}
const connection = this.connection;
this.connection = undefined;
this.connectionPromise = undefined;
if (connection != null) {
await connection.close();
}
};
}