mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
195 lines
5.2 KiB
JavaScript
195 lines
5.2 KiB
JavaScript
import Client from '../../client.js';
|
|
import configuredClients from "./clients.js";
|
|
import { enterJob } from '../../../utils/buildmessage.js';
|
|
import { execFileAsync, execFileSync } from '../../../utils/processes';
|
|
import { ensureDependencies } from '../../../cli/dev-bundle-helpers.js';
|
|
import {
|
|
mkdtemp,
|
|
pathJoin,
|
|
readFile,
|
|
} from '../../../fs/files';
|
|
|
|
const NPM_DEPENDENCIES = {
|
|
'selenium-webdriver': '4.1.1',
|
|
'browserstack-local': '1.4.8',
|
|
};
|
|
|
|
const USER = 'dev1141';
|
|
|
|
// A memoized key from BrowserStackClient._getBrowserStackKey.
|
|
let browserStackKey;
|
|
|
|
export default class BrowserStackClient extends Client {
|
|
constructor(options) {
|
|
super(options);
|
|
|
|
enterJob(
|
|
{
|
|
title: "Installing BrowserStack WebDriver in Meteor tool",
|
|
},
|
|
() => {
|
|
ensureDependencies(NPM_DEPENDENCIES);
|
|
}
|
|
);
|
|
|
|
this.npmPackageExports = require("selenium-webdriver");
|
|
|
|
// Capabilities which are allowed by selenium.
|
|
this.config.seleniumOptions = this.config.seleniumOptions || {};
|
|
|
|
// Additional capabilities which are unique to BrowserStack.
|
|
this.config.browserStackOptions = this.config.browserStackOptions || {};
|
|
|
|
this._setName();
|
|
}
|
|
|
|
_setName() {
|
|
const name = this.config.seleniumOptions.browserName || "default";
|
|
const version = this.config.seleniumOptions.version || "";
|
|
const device =
|
|
(this.config.browserStackOptions.realMobile &&
|
|
this.config.browserStackOptions.device) ||
|
|
"";
|
|
|
|
this.name =
|
|
"BrowserStack: " +
|
|
name +
|
|
(version && ` Version ${version}`) +
|
|
(device && ` (Device: ${device})`);
|
|
}
|
|
|
|
connect() {
|
|
|
|
const triggerRequest = (key) => {
|
|
const capabilities = {
|
|
// Authentication
|
|
"browserstack.user": USER,
|
|
"browserstack.key": key,
|
|
// Use the BrowserStackLocal tunnel, to allow BrowserStack to
|
|
// tunnel to the machine this server is running on.
|
|
"browserstack.local": true,
|
|
|
|
// Enabled the capturing of "Visual Logs" (i.e. Screenshots).
|
|
"browserstack.debug": true,
|
|
|
|
// On browsers that support it, capture the console
|
|
"browserstack.console": "errors",
|
|
|
|
...this.config.seleniumOptions,
|
|
...this.config.browserStackOptions,
|
|
};
|
|
this.driver = new this.npmPackageExports.Builder()
|
|
.usingServer("https://hub-cloud.browserstack.com/wd/hub")
|
|
.withCapabilities(capabilities)
|
|
.build();
|
|
|
|
this.driver.get(this.url);
|
|
};
|
|
|
|
this._launchBrowserStackTunnel()
|
|
.then(key => triggerRequest(key))
|
|
.catch((e) => {
|
|
// In the event of an error, shut down the daemon.
|
|
this.stop();
|
|
|
|
throw e;
|
|
});
|
|
}
|
|
|
|
stop() {
|
|
this.driver && this.driver.quit();
|
|
this.driver = null;
|
|
|
|
this.tunnelProcess && this.tunnelProcess.stop(() => {});
|
|
this.tunnelProcess = null;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {Promise<string>} The BrowserStack key.
|
|
*/
|
|
static async _getBrowserStackKey() {
|
|
// Use the memoized version, first and foremost.
|
|
if (typeof browserStackKey !== "undefined") {
|
|
return browserStackKey;
|
|
}
|
|
|
|
if (process.env.BROWSERSTACK_ACCESS_KEY) {
|
|
return (browserStackKey = process.env.BROWSERSTACK_ACCESS_KEY);
|
|
}
|
|
|
|
// Try to get the credentials from S3 with the s3cmd tool.
|
|
const outputDir = pathJoin(mkdtemp(), "key");
|
|
const browserstackKey = "s3://meteor-browserstack-keys/browserstack-key";
|
|
try {
|
|
await execFileAsync("s3cmd", ["get", browserstackKey, outputDir]);
|
|
|
|
return (browserStackKey = readFile(outputDir, "utf8").trim());
|
|
} catch (e) {
|
|
// A failure is acceptable here; it was just a try.
|
|
console.warn(
|
|
`Failed to load browserstack key from
|
|
${browserstackKey}`,
|
|
e
|
|
);
|
|
}
|
|
|
|
return (browserStackKey = null);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {Promise<string>} The BrowserStack key.
|
|
*/
|
|
_launchBrowserStackTunnel() {
|
|
this.tunnelProcess = new (require("browserstack-local").Local)();
|
|
/**
|
|
* @returns {Promise<string>} The BrowserStack key.
|
|
*/
|
|
const getKey = () => this.constructor._getBrowserStackKey();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
getKey().then((key) => {
|
|
if (!key) {
|
|
throw new Error(
|
|
"BrowserStack key not found. Ensure that s3cmd is setup with " +
|
|
"S3 credentials, or set BROWSERSTACK_ACCESS_KEY in your environment."
|
|
);
|
|
}
|
|
const options = {
|
|
key: key,
|
|
onlyAutomate: true,
|
|
verbose: true,
|
|
// The ",0" means "SSL off". It's localhost, after all.
|
|
only: `${this.host},${this.port},0`,
|
|
};
|
|
this.tunnelProcess.start(options, (err) => {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
resolve(key);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
static async prerequisitesMet() {
|
|
return !!(await this._getBrowserStackKey());
|
|
}
|
|
|
|
static pushClients(clients, appConfig) {
|
|
configuredClients.forEach((client) => {
|
|
clients.push(
|
|
new BrowserStackClient({
|
|
...appConfig,
|
|
config: {
|
|
seleniumOptions: client.selenium,
|
|
browserStackOptions: client.browserstack,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
}
|
|
}
|