From 78b2aea90d0bda1b49953c1c90373d30bee4a447 Mon Sep 17 00:00:00 2001 From: Dan Slov Date: Wed, 21 Aug 2024 13:52:54 -0700 Subject: [PATCH] sc: use sc api to retrieve SC download url --- src/cli.js | 1 + src/commands/sc.js | 10 +- src/constants.js | 226 +++++++++++++------------------------- src/index.js | 85 ++++++++------ src/sauceConnectLoader.js | 31 +++--- src/utils.js | 18 ++- tests/index.test.js | 19 ++-- tests/typings/test.ts | 4 +- tests/utils.test.js | 5 + 9 files changed, 185 insertions(+), 214 deletions(-) diff --git a/src/cli.js b/src/cli.js index 0f667502..99feb290 100644 --- a/src/cli.js +++ b/src/cli.js @@ -16,6 +16,7 @@ export const run = () => { .epilog(EPILOG) .demandCommand() .commandDir('commands') + .wrap(yargs.terminalWidth()) .help() .version(SAUCE_VERSION_NOTE); diff --git a/src/commands/sc.js b/src/commands/sc.js index 0a8751bf..27bb791a 100644 --- a/src/commands/sc.js +++ b/src/commands/sc.js @@ -2,7 +2,15 @@ import SauceLabs from './..'; import {DEFAULT_OPTIONS, SAUCE_CONNECT_CLI_PARAMS} from '../constants'; export const command = 'sc [flags]'; -export const describe = 'Sauce Connect interface'; +export const describe = `Sauce Connect Proxy interface. + - Only the 'sc run' command is currently supported + - See https://docs.saucelabs.com/dev/cli/sauce-connect-5/run/ for detailed CLI documentation. + - Sauce Connect Proxy 4.x.x cannot be used with the library version 9.0.0 and newer + - Some Sauce Connect CLI option aliases differ from the 'sc' binary + - Some CLI options differ from the 'sc' binary: + - '--proxy' matches '--proxy-sauce', see https://docs.saucelabs.com/dev/cli/sauce-connect-5/run/#proxy-sauce + - '--sc-upstream-proxy' matches '--proxy-sauce', see https://docs.saucelabs.com/dev/cli/sauce-connect-5/run/#proxy +`; export const builder = (yargs) => { for (const option of SAUCE_CONNECT_CLI_PARAMS) { yargs.option(option.name, option); diff --git a/src/constants.js b/src/constants.js index ce640c16..e57bb9ef 100644 --- a/src/constants.js +++ b/src/constants.js @@ -3,25 +3,7 @@ import os from 'os'; import {version} from '../package.json'; -export const DEFAULT_SAUCE_CONNECT_VERSION = '4.9.1'; -export const SAUCE_CONNECT_BASE = 'https://saucelabs.com/downloads'; -export const SAUCE_CONNECT_VERSIONS_ENDPOINT = - 'https://saucelabs.com/versions.json'; -export const SAUCE_CONNECT_PLATFORM_DATA = { - darwin: { - url: `${SAUCE_CONNECT_BASE}/sc-%s-osx.zip`, - use: 'bin/sc', - }, - linux: { - url: `${SAUCE_CONNECT_BASE}/sc-%s-linux.tar.gz`, - use: 'bin/sc', - }, - win32: { - url: `${SAUCE_CONNECT_BASE}/sc-%s-win32.zip`, - use: 'bin/sc', - }, -}; - +export const DEFAULT_SAUCE_CONNECT_VERSION = '5.1.3'; export const SAUCE_VERSION_NOTE = `node-saucelabs v${version}\nSauce Connect v${DEFAULT_SAUCE_CONNECT_VERSION}`; const protocols = [ @@ -156,218 +138,162 @@ export const SAUCE_CONNECT_CLI_PARAMS = [ description: 'Specify the Sauce Connect version you want to use.', default: DEFAULT_SAUCE_CONNECT_VERSION, }, - { - /** - * Sauce Connect parameter - */ - alias: 'a', - name: 'auth', - description: - 'Perform basic authentication when an URL on asks for a username and password.', - }, - { - name: 'cainfo', - description: - 'CA certificate bundle to use for verifying REST connections. (default "/usr/local/etc/openssl/cert.pem")', - }, - { - name: 'capath', - description: - 'Directory of CA certs to use for verifying REST connections. (default "/etc/ssl/certs")', - deprecated: true, - }, { alias: 'c', name: 'config-file', description: 'Path to YAML config file.', }, { - alias: 'D', - name: 'direct-domains', + alias: 'i', + name: 'tunnel-name', description: - 'Comma-separated list of domains. Requests whose host matches one of these will be relayed directly through the internet, instead of through the tunnel.', + 'Tunnel name used for this tunnel or the tunnels in the same HA pool.', }, { - name: 'dns', + alias: 'M', + name: 'metadata', description: - 'Use specified name server(s). Example: --dns 8.8.8.8,8.8.4.4:53', + 'Custom metadata key-value pairs. This flag is, primarily, used by Sauce Labs to assign custom properties to the tunnel for reporting purposes.', }, { - name: 'doctor', + alias: 's', + name: 'shared', description: - 'Perform checks to detect possible misconfiguration or problems.', - type: 'boolean', - deprecated: true, + "Share the tunnel within the same org unit. Only the 'all' option is currently supported. See here: https://docs.saucelabs.com/basics/acct-team-mgmt/sauce-connect-proxy-tunnels/.", }, { - alias: 'F', - name: 'fast-fail-regexps', + alias: 't', + name: 'tunnel-pool', + type: 'boolean', description: - 'Comma-separated list of regular expressions. Requests matching one of these will get dropped instantly and will not go through the tunnel.', - }, - { - alias: 'z', - name: 'log-stats', - description: 'Log statistics about HTTP traffic every .', - type: 'number', - deprecated: true, + 'Denotes a tunnel as part of a high availability tunnel pool. See here: https://docs.saucelabs.com/secure-connections/sauce-connect/setup-configuration/high-availability/.', }, { - alias: 'l', - name: 'logfile', - description: 'Specify custom logfile.', + alias: 'F', + name: 'deny-domains', + description: + "Deny requests to the matching domains. Prefix domains with '-' to exclude requests from being denied. Special keyword 'all' matches all domains.", }, { - name: 'max-logsize', + alias: 'D', + name: 'direct-domains', description: - 'Rotate logfile after reaching size. Disabled by default.', - type: 'number', + "Forward matching requests to their origin server over the public internet. Requests that don't match \"direct domains\" will be forwarded to customer-side over the Sauce Connect Proxy connection. You can specify --direct-domains or --tunnel-domains, but not both. Prefix domains with '-' to exclude requests from being forwarded directly.", }, { - alias: 'M', - name: 'max-missed-acks', + alias: 'B', + name: 'tls-passthrough-domains', description: - 'The max number of keepalive acks that can be missed before triggering reconnect.', - type: 'number', - deprecated: true, + "Pass matching requests to their origin server without SSL/TLS re-encryption. Requests that don't match will be re-encrypted. You can specify --tls-passthrough-domains or --tls-resign-domains, but not both. Prefix domains with '-' to exclude requests from being passed through.", }, { - name: 'metrics-address', - description: 'host:port server used to expose client-side metrics.', - deprecated: true, + alias: 'b', + name: 'tls-resign-domains', + description: + "Resign SSL/TLS certificates for matching requests. You can specify --tls-resign-domains or --tls-passthrough-domains, but not both. Prefix domains with '-' to exclude requests from being resigned.", }, { - name: 'status-address', - description: 'host:port server used to expose client status.', + alias: 'T', + name: 'tunnel-domains', + description: + "Forward matching requests over the Sauce Connect Proxy connection. Requests not matching \"tunnel domains\" will be forwarded to their origin server over the public internet. You can specify --tunnel-domains or --direct-domains, but not both. Prefix domains with '-' to exclude requests from being forwarded over the SC Proxy connection. Special keyword 'all' matches all domains.", }, { - name: 'no-autodetect', - description: 'Disable the autodetection of proxy settings.', - type: 'boolean', + alias: 'a', + name: 'auth', + description: 'Site or upstream proxy basic authentication credentials.', }, { - alias: 'N', - name: 'no-proxy-caching', + name: 'pac', description: - 'Disable caching in Sauce Connect. All requests will be sent through the tunnel.', - type: 'boolean', - deprecated: true, + 'Proxy Auto-Configuration file to use for upstream proxy selection.', }, { - name: 'no-remove-colliding-tunnels', + name: 'sc-upstream-proxy', description: - "Don't remove identified tunnels with the same name, or any other default tunnels if this is a default tunnel. Jobs will be distributed between these tunnels, enabling load balancing and high availability. By default, colliding tunnels will be removed when Sauce Connect is starting up.", - type: 'boolean', - deprecated: true, + 'Upstream proxy for test sessions . It is used for requests received from the Sauce Connect Server only.', }, { - alias: 'B', - name: 'no-ssl-bump-domains', + name: 'proxy-localhost', description: - 'Comma-separated list of domains. Requests whose host matches one of these will not be SSL re-encrypted.', + 'Setting this to "allow" enables sending requests to localhost through the upstream proxy. Setting this to "direct" sends requests to localhost directly without using the upstream proxy. By default, requests to localhost are denied.', }, { - name: 'pac', + name: 'proxy-sauce', description: - 'Proxy autoconfiguration. Can be an http(s) or local file:// (absolute path only) URI.', - }, - { - alias: 'd', - name: 'pidfile', - description: 'File that will be created with the pid of the process.', - }, - { - alias: 'T', - name: 'proxy-tunnel', - description: 'Use the proxy configured with -p for the tunnel connection.', - type: 'boolean', + 'Establish a tunnel through an upstream proxy. Proxy for requests to Sauce Labs REST API and Sauce Connect servers only. See the -x, --proxy flag for more details on the format.', }, { - alias: 'w', - name: 'proxy-userpwd', + name: 'dns-round-robin', description: - 'Username and password required to access the proxy configured with -p.', + 'If more than one DNS server is specified with the --dns-server flag, passing this flag will enable round-robin selection.', }, { - alias: 'f', - name: 'readyfile', - description: 'File that will be touched to signal when tunnel is ready.', + alias: 'n', + name: 'dns-server', + description: + 'DNS server(s) to use instead of system default. There are two execution policies, when more then one server is specified. Fallback: the first server in a list is used as primary, the rest are used as fallbacks. Round robin: the servers are used in a round-robin fashion. The port is optional, if not specified the default port is 53.', }, { - alias: 'X', - name: 'scproxy-port', - description: 'Port on which scproxy will be listening.', + name: 'dns-timeout', + description: + 'Timeout for dialing DNS servers. Only used if DNS servers are specified.', }, { - name: 'scproxy-read-limit', + name: 'cacert-file', description: - 'Rate limit reads in scproxy to X bytes per second. This option can be used to adjust local network transfer rate in order not to overload the tunnel connection.', - deprecated: true, + 'Add your own CA certificates to verify against. The system root certificates will be used in addition to any certificates in this list. Use this flag multiple times to specify multiple CA certificate files.', }, { - name: 'scproxy-write-limit', + name: 'http-dial-timeout', description: - 'Rate limit writes in scproxy to X bytes per second. This option can be used to adjust local network transfer rate in order not to overload the tunnel connection.', - deprecated: true, + 'The maximum amount of time a dial will wait for a connect to complete. With or without a timeout, the operating system may impose its own earlier timeout. For instance, TCP timeouts are often around 3 minutes.', }, { - alias: 'P', - name: 'se-port', + name: 'http-idle-conn-timeout', description: - "Port on which Sauce Connect's Selenium relay will listen for requests. Selenium commands reaching Connect on this port will be relayed to Sauce Labs securely and reliably through Connect's tunnel (default 4445)", - type: 'number', - default: 4445, + 'The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. Zero means no limit.', }, { - alias: 's', - name: 'shared-tunnel', + name: 'http-response-header-timeout', description: - 'Let sub-accounts of the tunnel owner use the tunnel if requested.', - type: 'boolean', + "The amount of time to wait for a server's response headers after fully writing the request (including its body, if any).This time does not include the time to read the response body. Zero means no limit.", }, { - name: 'tunnel-cainfo', + name: 'http-tls-handshake-timeout', description: - 'CA certificate bundle to use for verifying tunnel connections. (default "/usr/local/etc/openssl/cert.pem")', + 'The maximum amount of time waiting to wait for a TLS handshake. Zero means no limit.', }, { - name: 'tunnel-capath', + name: 'http-tls-keylog-file', description: - 'Directory of CA certs to use for verifying tunnel connections. (default "/etc/ssl/certs")', - deprecated: true, + 'File to log TLS master secrets in NSS key log format. By default, the value is taken from the SSLKEYLOGFILE environment variable. It can be used to allow external programs such as Wireshark to decrypt TLS connections.', }, { - name: 'tunnel-cert', + name: 'api-address', description: - 'Specify certificate to use for the tunnel connection, either public or private. Default: private. (default "private")', + 'The server address to listen on. If the host is empty, the server will listen on all available interfaces.', }, { - alias: 't', - name: 'tunnel-domains', - description: - "Inverse of '--direct-domains'. Only requests for domains in this list will be sent through the tunnel. Overrides '--direct-domains'.", + name: 'api-basic-auth', + description: 'Basic authentication credentials to protect the server.', }, { - name: 'tunnel-identifier', + name: 'api-idle-timeout', description: - 'Tunnel name used for this tunnel or the tunnels in the same HA pool.', - deprecated: true, + 'The maximum amount of time to wait for the next request before closing connection.', }, { - alias: 'i', - name: 'tunnel-name', - description: - 'Tunnel name used for this tunnel or the tunnels in the same HA pool.', + name: 'log-file', + description: 'Path to the log file, if empty, logs to stdout.', }, { - name: 'tunnel-pool', - description: 'The tunnel is a part of a high availability tunnel pool.', + name: 'log-http', + description: 'HTTP request and response logging mode.', }, { - alias: 'v', - name: 'verbose', - type: 'boolean', - description: 'Enable verbose logging. Can be used up to two times.', + name: 'log-level', + description: 'Log level.', }, ]; export const SC_BOOLEAN_CLI_PARAMS = SAUCE_CONNECT_CLI_PARAMS.filter( diff --git a/src/index.js b/src/index.js index a745bb7e..7ef00c96 100644 --- a/src/index.js +++ b/src/index.js @@ -7,9 +7,9 @@ import {spawn} from 'child_process'; import got from 'got'; import FormData from 'form-data'; import {camelCase} from 'change-case'; -import queryString from 'query-string'; - import { + getCPUArch, + getPlatform, createHMAC, getAPIHost, getAssetHost, @@ -20,6 +20,8 @@ import { getStrictSsl, getRegionSubDomain, } from './utils'; +import queryString from 'query-string'; + import { PROTOCOL_MAP, DEFAULT_OPTIONS, @@ -33,7 +35,6 @@ import { SC_CLOSE_TIMEOUT, DEFAULT_SAUCE_CONNECT_VERSION, SC_FAILURE_MESSAGES, - SAUCE_CONNECT_VERSIONS_ENDPOINT, SC_WAIT_FOR_MESSAGES, SC_BOOLEAN_CLI_PARAMS, } from './constants'; @@ -58,7 +59,8 @@ export default class SauceLabs { }); if (typeof this._options.proxy === 'string') { - var proxyAgent = createProxyAgent(this._options.proxy); + this.proxy = this._options.proxy; + const proxyAgent = createProxyAgent(this.proxy); this._api = got.extend( { agent: proxyAgent, @@ -240,33 +242,27 @@ export default class SauceLabs { } } - let sauceConnectVersion = argv.scVersion; - if (!sauceConnectVersion) { - sauceConnectVersion = await this._getLatestSauceConnectVersion(); - } + const sauceConnectVersion = argv.scVersion || DEFAULT_SAUCE_CONNECT_VERSION; + const scUpstreamProxy = argv.scUpstreamProxy; const args = Object.entries(argv) /** * filter out yargs, yargs params and custom parameters */ .filter( ([k]) => - !['_', '$0', 'sc-version', 'logger', ...SC_PARAMS_TO_STRIP].includes( - k - ) + ![ + '_', + '$0', + 'sc-version', + 'sc-upstream-proxy', + 'logger', + ...SC_PARAMS_TO_STRIP, + ].includes(k) ) /** * remove duplicate params by yargs */ .filter(([k]) => !k.match(/[A-Z]/g)) - /** - * replace tunnel-identifier for tunnel-name - */ - .map(([k, v]) => [k === 'tunnel-identifier' ? 'tunnel-name' : k, v]) - /** - * SC uses `--no-XXX` params which gets parsed out by yargs - * therefor we need to re-add it here - */ - .map(([k, v]) => [typeof v === 'boolean' && !v ? `no-${k}` : k, v]) /** * SC doesn't like boolean values, so we need to make sure to * no pass it along when we deal with a boolean param @@ -274,16 +270,39 @@ export default class SauceLabs { .map(([k, v]) => SC_BOOLEAN_CLI_PARAMS.includes(k) ? `--${k}` : `--${k}=${v}` ); - args.push(`--user=${this.username}`); - args.push(`--api-key=${this._accessKey}`); + args.push(`--username=${this.username}`); + args.push(`--access-key=${this._accessKey}`); + if (scUpstreamProxy) { + // map `--sc-upstream-proxy` to sc's `--proxy`. It's done because the app CLI + // conflicts with sc's CLI, `--proxy` here is equivalent to `--proxy-sauce` in sc. + // See: https://docs.saucelabs.com/dev/cli/sauce-connect-5/run/#proxy + // See: https://docs.saucelabs.com/dev/cli/sauce-connect-5/run/#proxy and + // https://docs.saucelabs.com/dev/cli/sauce-connect-5/run/#proxy-sauce + args.push(`--proxy=${scUpstreamProxy}`); + } + if (this.proxy) { + args.push(`--proxy-sauce=${this.proxy}`); + } const region = argv.region || this.region; if (region) { const scRegion = getRegionSubDomain({region}); args.push(`--region=${scRegion}`); + } else { + // --region is required for Sauce Connect 5. + throw new Error('Missing region'); } const scLoader = new SauceConnectLoader({sauceConnectVersion}); - await scLoader.verifyAlreadyDownloaded(); + const isDownloaded = await scLoader.verifyAlreadyDownloaded(); + if (!isDownloaded) { + let sauceConnectURL = await this._getSauceConnectDownloadURL( + sauceConnectVersion + ); + await scLoader.verifyAlreadyDownloaded({url: sauceConnectURL}); + } + if (args.length == 0 || args[0] != 'run') { + args.unshift('run'); + } const cp = spawn(scLoader.path, args); return new Promise((resolve, reject) => { const close = () => @@ -353,17 +372,17 @@ export default class SauceLabs { }); } - async _getLatestSauceConnectVersion() { - try { - const {body} = await this._api.get(SAUCE_CONNECT_VERSIONS_ENDPOINT, { - responseType: 'json', - }); - const responseJson = body.data; - return responseJson['Sauce Connect']['version']; - } catch (err) { - // fallback - return DEFAULT_SAUCE_CONNECT_VERSION; + async _getSauceConnectDownloadURL(sauceConnectVersion) { + const platform = getPlatform(); + const cpuARCH = getCPUArch(); + const scVersionInfo = await this._callAPI('scVersions', { + client_host: `${platform}-${cpuARCH}`, + client_version: sauceConnectVersion, + }); + if (!scVersionInfo.download_url) { + throw new Error('Failed to retrieve Sauce Connect download URL'); } + return scVersionInfo.download_url; } async _downloadJobAsset(jobId, assetName, {filepath} = {}) { diff --git a/src/sauceConnectLoader.js b/src/sauceConnectLoader.js index debdd59c..fcf4ff85 100644 --- a/src/sauceConnectLoader.js +++ b/src/sauceConnectLoader.js @@ -20,8 +20,7 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -import {format} from 'util'; -import {SAUCE_CONNECT_PLATFORM_DATA} from './constants'; +// import SauceLabs from './'; import {getPlatform} from './utils'; import {mkdirSync, unlinkSync, createWriteStream} from 'fs'; import {basename, join} from 'path'; @@ -31,32 +30,32 @@ import compressing from 'compressing'; export default class SauceConnectLoader { constructor(options = {}) { - const platform = getPlatform(); - const platformData = SAUCE_CONNECT_PLATFORM_DATA[platform]; - if (!platformData) { - throw new ReferenceError(`Unsupported platform ${platform}`); - } - const {url, use} = platformData; - this.url = format(url, options.sauceConnectVersion); this.destDir = join(__dirname, 'sc-loader'); this.destSC = join( __dirname, 'sc-loader', `.sc-v${options.sauceConnectVersion}` ); - this.path = join(this.destSC, use); + let scBinary = 'sc'; + if (getPlatform().startsWith('win')) { + scBinary += '.exe'; + } + this.path = join(this.destSC, scBinary); } /** - * Verify if SC was already downloaded, + * Verify if SC was already downloaded. * if not then download it * * @api public */ - verifyAlreadyDownloaded() { + verifyAlreadyDownloaded(options = {}) { return fs.stat(this.path).catch((err) => { if (err?.code === 'ENOENT') { - return this._download(); + if (options.url) { + return this._download(options.url); + } + return false; } throw err; }); @@ -65,13 +64,13 @@ export default class SauceConnectLoader { /** * Download Sauce Connect */ - _download() { + _download(sauceConnectURL) { mkdirSync(this.destDir, {recursive: true}); - const compressedFilePath = join(this.destDir, basename(this.url)); + const compressedFilePath = join(this.destDir, basename(sauceConnectURL)); return new Promise((resolve, reject) => { const file = createWriteStream(compressedFilePath); https - .get(this.url, (response) => { + .get(sauceConnectURL, (response) => { response.pipe(file); file.on('finish', () => { file.close(); diff --git a/src/utils.js b/src/utils.js index 342492f8..7ba81e90 100644 --- a/src/utils.js +++ b/src/utils.js @@ -192,8 +192,24 @@ export function getStrictSsl() { } /** - * Mainly just here for testing + * Returns an OS platform + * - darwin + * - linux + * - win32 + * etc */ export function getPlatform() { return process.platform; } + +/** + * Returns CPU architecture + * - 'x32' + * - 'x64' + * - 'arm' + * - 'arm64' + * etc + */ +export function getCPUArch() { + return process.arch; +} diff --git a/tests/index.test.js b/tests/index.test.js index ae8598b4..6caabeb0 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -476,7 +476,7 @@ describe('startSauceConnect', () => { ); await api.startSauceConnect({ scVersion: '1.2.3', - tunnelIdentifier: 'my-tunnel', + tunnelName: 'my-tunnel', 'proxy-tunnel': 'abc', verbose: true, region: 'eu', @@ -510,7 +510,7 @@ describe('startSauceConnect', () => { 50 ); await api.startSauceConnect({ - tunnelIdentifier: 'my-tunnel', + tunnelName: 'my-tunnel', 'proxy-tunnel': 'abc', logger: (log) => logs.push(log), }); @@ -531,7 +531,7 @@ describe('startSauceConnect', () => { 50 ); await api.startSauceConnect({ - tunnelIdentifier: 'my-tunnel', + tunnelName: 'my-tunnel', 'proxy-tunnel': 'abc', logger: (log) => logs.push(log), }); @@ -545,7 +545,7 @@ describe('startSauceConnect', () => { const err = await api .startSauceConnect({ scVersion: '1.2.3', - tunnelIdentifier: 'my-tunnel', + tunnelName: 'my-tunnel', 'proxy-tunnel': 'abc', }) .catch((err) => err); @@ -559,7 +559,7 @@ describe('startSauceConnect', () => { const err = await api .startSauceConnect({ scVersion: '1.2.3', - tunnelIdentifier: 'my-tunnel', + tunnelName: 'my-tunnel', 'proxy-tunnel': 'abc', }) .catch((err) => err); @@ -576,10 +576,7 @@ describe('startSauceConnect', () => { ), 50 ); - const sc = await api.startSauceConnect( - {tunnelIdentifier: 'my-tunnel'}, - true - ); + const sc = await api.startSauceConnect({tunnelName: 'my-tunnel'}, true); setTimeout(() => { sc.cp.stdout.emit('data', 'Some other message'); sc.cp.stdout.emit('data', 'Goodbye'); @@ -592,7 +589,7 @@ describe('startSauceConnect', () => { const api = new SauceLabs({user: 'foo', key: 'bar'}); setTimeout(() => stderrEmitter.emit('data', 'Uuups'), 50); const res = await api - .startSauceConnect({tunnelIdentifier: 'my-tunnel'}) + .startSauceConnect({tunnelName: 'my-tunnel'}) .catch((err) => err); expect(res).toEqual(new Error('Uuups')); }); @@ -609,7 +606,7 @@ describe('startSauceConnect', () => { 150 ); const res = await api - .startSauceConnect({tunnelIdentifier: 'my-tunnel'}) + .startSauceConnect({tunnelName: 'my-tunnel'}) .catch((err) => err); expect(res instanceof Error).toBe(false); }); diff --git a/tests/typings/test.ts b/tests/typings/test.ts index 23ad5e7c..9975fdb9 100644 --- a/tests/typings/test.ts +++ b/tests/typings/test.ts @@ -12,8 +12,8 @@ async function foobar() { console.log(job.selenium_version); const sc = await api.startSauceConnect({ - scVersion: '4.5.4', - tunnelIdentifier: '1234', + scVersion: '5.1.3', + tunnelName: '1234', logger: (output: string) => console.log(output), }); sc.cp.pid; diff --git a/tests/utils.test.js b/tests/utils.test.js index 9925399b..a185c98c 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -9,6 +9,7 @@ import { getStrictSsl, getAssetHost, createProxyAgent, + getCPUArch, getPlatform, } from '../src/utils'; @@ -22,6 +23,10 @@ test('getPlatform', () => { expect(getPlatform()).toBe(process.platform); }); +test('getCPUArch', () => { + expect(getCPUArch()).toBe(process.arch); +}); + test('getAPIHost', () => { expect(getAPIHost(sauceAPI.servers, sauceAPI.basePath, {})).toBe( 'https://api.us-west-1.saucelabs.com/rest'