text: import { execFile } from 'child_process';
  import Client from '../../client.js';
  import configuredClients from "./clients.js";
  import { enterJob } from '../../../utils/buildmessage.js';
  import { getUrlWithResuming } from '../../../utils/http-helpers.js';
  import { execFileSync } from '../../../utils/processes';
  import { ensureDependencies } from '../../../cli/dev-bundle-helpers.js';
  import {
    mkdtemp,
    pathJoin,
    chmod,
    statOrNull,
    readFile,
    createWriteStream,
    getDevBundle,
  } from '../../../fs/files';
  
  const NPM_DEPENDENCIES = {
    'browserstack-webdriver': '2.41.1',
    'browserstack-local': '1.3.0',
  };
  
  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('browserstack-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 key = BrowserStackClient._getBrowserStackKey();
      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 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,
      };
  
      const triggerRequest = () => {
        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(triggerRequest)
        .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;
    }
  
    static _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 {
        execFileSync("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);
    }
  
    _launchBrowserStackTunnel() {
      this.tunnelProcess = new (require('browserstack-local')).Local();
  
      const options = {
        key: this.constructor._getBrowserStackKey(),
        onlyAutomate: true,
        verbose: true,
        // The ",0" means "SSL off".  It's localhost, after all.
        only: `${this.host},${this.port},0`,
      }
  
      return new Promise((resolve, reject) => {
        this.tunnelProcess.start(options, (err) => {
          if (err) {
            reject(err);
          } else {
            resolve();
          }
        });
      });
    }
  
    static prerequisitesMet() {
      return !! this._getBrowserStackKey();
    }
  
    static pushClients(clients, appConfig) {
      configuredClients.forEach((client) => {
        clients.push(new BrowserStackClient(
          {
            ...appConfig,
            config: {
              seleniumOptions: client.selenium,
              browserStackOptions: client.browserstack,
            },
          },
        ));
      });
    }
  }
  