From fe62ec02d6f5f8f5c8ed1f945f076c53a85bfc03 Mon Sep 17 00:00:00 2001 From: Alana McKenzie Date: Thu, 23 Jan 2025 14:28:25 -0800 Subject: [PATCH] update endpoint to retrive SC download Signed-off-by: Alana McKenzie --- README.md | 7 +- apis/sauce.json | 61 +++- docs/interface.md | 8 +- e2e/sc.test.js | 31 +- src/cli.js | 1 - src/constants.js | 10 +- src/index.js | 92 ++++-- src/sauceConnectLoader.js | 2 +- src/utils.js | 22 +- tests/__responses__/all_versions.json | 270 ------------------ tests/__responses__/download_error.json | 6 + tests/__responses__/download_macos.json | 12 + .../download_windows_x86_64.json | 12 + tests/__responses__/latest_versions.json | 30 -- tests/__responses__/versions.json | 26 -- tests/__snapshots__/index.test.js.snap | 67 ++--- tests/index.test.js | 156 +++++----- tests/sauceConnectLoader.test.js | 55 ++-- tests/utils.test.js | 39 ++- 19 files changed, 376 insertions(+), 531 deletions(-) delete mode 100644 tests/__responses__/all_versions.json create mode 100644 tests/__responses__/download_error.json create mode 100644 tests/__responses__/download_macos.json create mode 100644 tests/__responses__/download_windows_x86_64.json delete mode 100644 tests/__responses__/latest_versions.json delete mode 100644 tests/__responses__/versions.json diff --git a/README.md b/README.md index 4d8d3695..4669c6d0 100644 --- a/README.md +++ b/README.md @@ -104,12 +104,12 @@ or start Sauce Connect Proxy in EU datacenter: # start Sauce Connect tunnel for eu-central-1 region $ sl sc --region eu --tunnel-name "my-tunnel" # run a specific Sauce Connect version -$ sl sc --scVersion 4.9.1 +$ sl sc --scVersion 5.2.2 # see all available Sauce Connect parameters via: $ sl sc --help ``` -You can see all available Sauce Connect parameters on the [Sauce Labs Docs](https://docs.saucelabs.com/dev/cli/sauce-connect-proxy/). +You can see all available Sauce Connect parameters on the [Sauce Labs Docs](https://docs.saucelabs.com/dev/cli/sauce-connect-5/run/). ### As NPM Package @@ -179,7 +179,7 @@ import SauceLabs from 'saucelabs'; */ logger: (stdout) => console.log(stdout), /** - * see all available parameters here: https://docs.saucelabs.com/dev/cli/sauce-connect-proxy/ + * see all available parameters here: https://docs.saucelabs.com/dev/cli/sauce-connect-5/run/ * all parameters have to be applied camel cased instead of with hyphens, e.g. * to apply the `--tunnel-name` parameter, set: */ @@ -219,6 +219,7 @@ const myAccount = new SauceLabs({ user: 'YOUR-USER', key: 'YOUR-ACCESS-KEY', region: 'eu', // run in EU datacenter + tunnelName: 'my-tunnel', }); // get full webdriver url from the client depending on `region` option: diff --git a/apis/sauce.json b/apis/sauce.json index 675a3805..86a83581 100644 --- a/apis/sauce.json +++ b/apis/sauce.json @@ -458,6 +458,36 @@ }, "type": "object" }, + "SauceConnectDownload": { + "type": "object", + "properties": { + "download": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "version": { + "type": "string" + }, + "checksums": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "algorithm": { + "type": "string" + } + } + } + } + } + } + } + }, "SauceConnectDownloadInfo": { "type": "object", "properties": { @@ -827,10 +857,24 @@ "required": false, "type": "string" }, + "clientArch": { + "description": "SC client host CPU arch", + "in": "query", + "name": "arch", + "required": true, + "type": "string" + }, + "clientOS": { + "description": "SC client host OS", + "in": "query", + "name": "os", + "required": true, + "type": "string" + }, "clientVersion": { "description": "SC client version", "in": "query", - "name": "client_version", + "name": "version", "required": false, "type": "string" }, @@ -1768,25 +1812,25 @@ "tags": ["Tunnel"] } }, - "/v1/public/tunnels/info/versions": { + "/v1/public/tunnels/sauce-connect/download": { "get": { - "operationId": "sc_versions", + "operationId": "sc_download", "parameters": [ { - "$ref": "#/parameters/clientVersion" + "$ref": "#/parameters/clientArch" }, { - "$ref": "#/parameters/clientHost" + "$ref": "#/parameters/clientOS" }, { - "$ref": "#/parameters/all" + "$ref": "#/parameters/clientVersion" } ], "responses": { "200": { - "description": "Tunnels", + "description": "download", "schema": { - "$ref": "#/definitions/SauceConnectVersions" + "$ref": "#/definitions/SauceConnectDownload" } }, "default": { @@ -1796,7 +1840,6 @@ } } }, - "summary": "Get tunnels for the user or all the users in the team", "tags": ["Tunnel"] } }, diff --git a/docs/interface.md b/docs/interface.md index 48278315..d6990fa4 100644 --- a/docs/interface.md +++ b/docs/interface.md @@ -185,12 +185,12 @@ The following commands are available via package or cli tool: - GET /v1/public/tunnels/info/versions
- Get tunnels for the user or all the users in the team + GET /v1/public/tunnels/sauce-connect/download
+ Get Sauce Connect download information for the latest version

Example:

- api.scVersions({ ...options }) + api.scDownload({ ...options })

Options

- + diff --git a/e2e/sc.test.js b/e2e/sc.test.js index 351602df..0dff0934 100644 --- a/e2e/sc.test.js +++ b/e2e/sc.test.js @@ -23,27 +23,42 @@ test('should be able to get Sauce Connect versions', async () => { return; } const api = new SauceLabs(); - const scVersion = await api.scVersions({ - clientVersion: '5.1.0', - clientHost: 'darwin-arm64', + const response = await api.scDownload({ + version: '5.2.2', + arch: 'arm64', + os: 'macos', }); - expect(scVersion.status).toEqual('UPGRADE'); - expect(scVersion.latest_version).toMatch(/5\./); - console.log(scVersion.download_url); + + expect(response.download.version).toEqual('5.2.2'); + expect(response.download.url).toMatch(/5\.2\.2/); + console.log(response.download.url); }); test('should not be able to run Sauce Connect due to invalid credentials', async () => { if (SKIP_TEST) { return; } - const api = new SauceLabs({key: 'foobar'}); + const api = new SauceLabs({key: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'}); const err = await api .startSauceConnect({ logger: console.log.bind(console), tunnelName: `node-saucelabs E2E test - ${ID}`, }) .catch((err) => err); - expect(err.message).toContain('Unauthorized'); + expect(err.message).toContain('Not authorized'); +}); + +test('should not be able to run Sauce Connect due to missing tunnel-name', async () => { + if (SKIP_TEST) { + return; + } + const api = new SauceLabs(); + const err = await api + .startSauceConnect({ + logger: console.log.bind(console), + }) + .catch((err) => err); + expect(err.message).toContain('Missing tunnel-name'); }); test('should be able to run Sauce Connect', async () => { diff --git a/src/cli.js b/src/cli.js index 99feb290..0f667502 100644 --- a/src/cli.js +++ b/src/cli.js @@ -16,7 +16,6 @@ export const run = () => { .epilog(EPILOG) .demandCommand() .commandDir('commands') - .wrap(yargs.terminalWidth()) .help() .version(SAUCE_VERSION_NOTE); diff --git a/src/constants.js b/src/constants.js index f342db9b..7dc4af84 100644 --- a/src/constants.js +++ b/src/constants.js @@ -3,7 +3,7 @@ import os from 'os'; import {version} from '../package.json'; -export const DEFAULT_SAUCE_CONNECT_VERSION = '5.2.0'; +export const DEFAULT_SAUCE_CONNECT_VERSION = '5.2.2'; export const SAUCE_VERSION_NOTE = `node-saucelabs v${version}\nSauce Connect v${DEFAULT_SAUCE_CONNECT_VERSION}`; const protocols = [ @@ -313,10 +313,6 @@ export const SC_PARAMS_TO_STRIP = [ ]; export const SC_READY_MESSAGE = 'Sauce Connect is up, you may start your tests'; -export const SC_FAILURE_MESSAGES = [ - 'Sauce Connect could not establish a connection', - 'Sauce Connect failed to start', -]; -export const SC_WAIT_FOR_MESSAGES = ['\u001b[K', 'Please wait for']; // "\u001b" = Escape character -export const SC_CLOSE_MESSAGE = 'Goodbye'; +export const SC_FAILURE_MESSAGES = ['fatal error exiting']; +export const SC_CLOSE_MESSAGE = 'tunnel was shutdown'; export const SC_CLOSE_TIMEOUT = 5000; diff --git a/src/index.js b/src/index.js index 7ef00c96..10b451e1 100644 --- a/src/index.js +++ b/src/index.js @@ -35,7 +35,6 @@ import { SC_CLOSE_TIMEOUT, DEFAULT_SAUCE_CONNECT_VERSION, SC_FAILURE_MESSAGES, - SC_WAIT_FOR_MESSAGES, SC_BOOLEAN_CLI_PARAMS, } from './constants'; import SauceConnectLoader from './sauceConnectLoader'; @@ -243,6 +242,12 @@ export default class SauceLabs { } const sauceConnectVersion = argv.scVersion || DEFAULT_SAUCE_CONNECT_VERSION; + if (sauceConnectVersion.startsWith('4')) { + throw new Error( + `This Sauce Connect version (${sauceConnectVersion}) is no longer supported. Please use Sauce Connect 5.` + ); + } + const scUpstreamProxy = argv.scUpstreamProxy; const args = Object.entries(argv) /** @@ -255,6 +260,7 @@ export default class SauceLabs { '$0', 'sc-version', 'sc-upstream-proxy', + 'tunnel-name', 'logger', ...SC_PARAMS_TO_STRIP, ].includes(k) @@ -292,17 +298,34 @@ export default class SauceLabs { // --region is required for Sauce Connect 5. throw new Error('Missing region'); } - const scLoader = new SauceConnectLoader({sauceConnectVersion}); + + const tunnelName = argv.tunnelName; + if (tunnelName) { + args.push(`--tunnel-name=${tunnelName}`); + } else { + // --tunnel-name is required for Sauce Connect 5. + throw new Error('Missing tunnel-name'); + } + + // download and verify the Sauce Connect client + let scLoader = new SauceConnectLoader({version: sauceConnectVersion}); const isDownloaded = await scLoader.verifyAlreadyDownloaded(); + if (!isDownloaded) { - let sauceConnectURL = await this._getSauceConnectDownloadURL( - sauceConnectVersion - ); - await scLoader.verifyAlreadyDownloaded({url: sauceConnectURL}); + let download = await this._getSauceConnectDownload(sauceConnectVersion); + + // downloaded version may differ from the input version, eg. if a partial version is given as input + // update scLoader if necessary + if (download.version != sauceConnectVersion) { + scLoader = new SauceConnectLoader({version: download.version}); + } + await scLoader.verifyAlreadyDownloaded({url: download.url}); } + if (args.length == 0 || args[0] != 'run') { args.unshift('run'); } + const cp = spawn(scLoader.path, args); return new Promise((resolve, reject) => { const close = () => @@ -321,19 +344,6 @@ export default class SauceLabs { cp.stderr.on('data', (data) => { const output = data.toString(); - - /** - * check if error output is just an escape sequence or - * other expected data - */ - if ( - SC_WAIT_FOR_MESSAGES.find((msg) => - escape(output).includes(escape(msg)) - ) - ) { - return; - } - return reject(new Error(output)); }); cp.stdout.on('data', (data) => { @@ -372,17 +382,43 @@ export default class SauceLabs { }); } - async _getSauceConnectDownloadURL(sauceConnectVersion) { + /** + * Retrieve the download URL for the Sauce Connect client specific to this device's OS and architecture. + * Throws an exception on any error response + * @param {string} version Full or partial version for the download to match + * @returns {Object} download + * @returns {string} download.url + * @returns {string} download.version + * @returns {Object[]} download.checksums + * @returns {string} download.checksums[].value + * @returns {string} download.checksums[].algorithm + */ + async _getSauceConnectDownload(version) { 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'); + const cpuArch = getCPUArch(); + var response = {}; + try { + response = await this._callAPI('scDownload', { + os: platform, + arch: cpuArch, + version: version, + }); + } catch (err) { + // if this endpoint is down, the start tunnels endpoint is likely down as well. + throw new Error(`Failed to retrieve Sauce Connect download. ${err}`); + } + + if (response.error) { + // likely an input value error. some platform/arch combinations may not be supported. + throw new Error( + `Failed to retrieve Sauce Connect download. code: ${response.error.code} message: ${response.error.message}` + ); + } + if (!response.download) { + // unexpected, inconsistent with API definition + throw new Error(`Failed to retrieve Sauce Connect download.`); } - return scVersionInfo.download_url; + return response.download; } async _downloadJobAsset(jobId, assetName, {filepath} = {}) { diff --git a/src/sauceConnectLoader.js b/src/sauceConnectLoader.js index fcf4ff85..c8df3237 100644 --- a/src/sauceConnectLoader.js +++ b/src/sauceConnectLoader.js @@ -100,7 +100,7 @@ export default class SauceConnectLoader { strip: 1, }) .then(() => { - if (getPlatform() !== 'win32') { + if (getPlatform() !== 'windows') { // ensure the sc executable is actually executable return fs.chmod(this.path, 0o755); } diff --git a/src/utils.js b/src/utils.js index 7ba81e90..d1b020bb 100644 --- a/src/utils.js +++ b/src/utils.js @@ -192,24 +192,28 @@ export function getStrictSsl() { } /** - * Returns an OS platform - * - darwin + * Returns an OS platform compatible with scDownload endpoint + * - macos * - linux - * - win32 - * etc + * - windows */ export function getPlatform() { + if (process.platform == 'win32') { + return 'windows'; + } else if (process.platform == 'darwin') { + return 'macos'; + } return process.platform; } /** - * Returns CPU architecture - * - 'x32' - * - 'x64' - * - 'arm' + * Returns CPU architecture compatible with scDownload endpoint + * - 'x86_64' * - 'arm64' - * etc */ export function getCPUArch() { + if (process.arch == 'x64') { + return 'x86_64'; + } return process.arch; } diff --git a/tests/__responses__/all_versions.json b/tests/__responses__/all_versions.json deleted file mode 100644 index 59c2fdec..00000000 --- a/tests/__responses__/all_versions.json +++ /dev/null @@ -1,270 +0,0 @@ -{ - "downloads": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_linux.x86_64.tar.gz", - "sha256": "6247e61e39ff054cf524341a681f4045c557be79bcf63c8c501e634ef2d54a41" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_linux.aarch64.tar.gz", - "sha256": "b4951cf64a724ceb03cf0ae676a35a0ca721210c0e42426384dcec23159f1fe5" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_darwin.all.zip", - "sha256": "d0a29f2df3277e8468c55810ef99685bed632ec6d2eed9fdbd941f2f8faf2354" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_windows.x86_64.zip", - "sha256": "d3e692cca8b9311a4822e8a6f8715e787319e70dc5551cbb212f5be68d36f7bb" - }, - "windows-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_windows.aarch64.zip", - "sha256": "fbb1b15c9a5ae6435cba0dc43f195df32764e5c7b1fcc6a2e625df58ea05642d" - } - }, - "info_url": "https://docs.saucelabs.com/secure-connections/sauce-connect-5/installation/", - "latest_version": "5.1.1", - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_darwin.all.zip", - "sha256": "d0a29f2df3277e8468c55810ef99685bed632ec6d2eed9fdbd941f2f8faf2354", - "status": "LATEST", - "client_version": "5.1.1", - "all_downloads": { - "4.7.0": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.7.0-linux.tar.gz", - "sha1": "f0bf8e35894e9b35bf9fae8f4f34e83845b4bb6b" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sc-4.7.0-osx.zip", - "sha1": "8e41a471bdf4cfeed7cd06d6af9dd081b9aa028d" - } - }, - "4.7.1": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.7.1-linux.tar.gz", - "sha1": "e5d7f82ad98251a653d1b0537f1103e49eda5e11" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sc-4.7.1-linux-arm64.tar.gz", - "sha1": "d07c8f62ec64168f9cc80d73a59976764f2c62b4" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sc-4.7.1-osx.zip", - "sha1": "1f18defa14a5cc4b663bf07213411f6bdd535b6d" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sc-4.7.1-win32.zip", - "sha1": "9c91e5adbd023973efe0eb14d2d427d2c0ef3c25" - } - }, - "4.8.0": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.0-linux.tar.gz", - "sha1": "a6bcfeab41b245e503c1f2aad382bfa8956893a1" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.0-linux-arm64.tar.gz", - "sha1": "8ce9b5a740710e6eef1be70b1b1d347df938d46a" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.0-osx.zip", - "sha1": "8c4c7de20c68b704cffddcaddea44a6773b05746" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.0-win32.zip", - "sha1": "48382adec66130d96148ccaff46894088366ed90" - } - }, - "4.8.1": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.1-linux.tar.gz", - "sha1": "9c16682e4c9716734432789884f868212f95f563" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.1-linux-arm64.tar.gz", - "sha1": "2a6a5fd0ad90c1d776048e4f9fd60a1a8a26c3a2" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.1-osx.zip", - "sha1": "4c5b8b570994a76396c75858455032bfdbb83589" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.1-win32.zip", - "sha1": "f3df33f01bf8d9585cfcda084b54300089266159" - } - }, - "4.8.2": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.2-linux.tar.gz", - "sha1": "e65e77e849a80d1eb1de03ba56abf5a4d51cf1c5" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.2-linux-arm64.tar.gz", - "sha1": "fd782a658f4d28b9792edaf9df730a87ae797cba" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.2-osx.zip", - "sha1": "5c2f81f6b0f246a641384d33df5c091ca0174730" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.2-win32.zip", - "sha1": "1c81cbe9d1b25b8f8483cc1163d54d94191f7665" - } - }, - "4.8.3": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.3-linux.tar.gz", - "sha1": "1af0d45ce48e3f0707164dbf0577adad2e6b7853" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.3-linux-arm64.tar.gz", - "sha1": "de6c894d018a1dd1f38c3c90bd4b8084c2e9fd4d" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.3-osx.zip", - "sha1": "c6edb040d3c7398c0f2be41957846b85a8d1e6e6" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sc-4.8.3-win32.zip", - "sha1": "6df7d8114954b09d61e54ac8c642137fa98d342f" - } - }, - "4.9.0": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.0-linux.tar.gz", - "sha1": "f263177c700ebc29a0c5772a04e9b04bc1487c91" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.0-linux-arm64.tar.gz", - "sha1": "04f697d585bdc7d95d7663dea52f5b895628b0ba" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.0-osx.zip", - "sha1": "f3080fbd76a3847c9c19dae6131f93a1c3abb008" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.0-win32.zip", - "sha1": "fe35f66126ddd6e8d043790906206c2999d69f1a" - } - }, - "4.9.1": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.1-linux.tar.gz", - "sha1": "9310bc860f7870a1f872b11c4dc6073a1ad34e5e" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.1-linux-arm64.tar.gz", - "sha1": "535e6c9edcc0ca94cac7c9f800f910dcea808cbf" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.1-osx.zip", - "sha1": "64f9c1bac5d4f5b9acb6fbb629b6df0f5671b4c8" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.1-win32.zip", - "sha1": "63858695eb6840306921607a97af0083c0697bf3" - } - }, - "4.9.2": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.2-linux.tar.gz", - "sha1": "5589571bdc186f3f1b05fe6ce68529501a42fb43" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.2-linux-arm64.tar.gz", - "sha1": "8b02c4343b74c36c575817ea4a6eae5fb5718f6c" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sc-4.9.2-win32.zip", - "sha1": "47c19feda3fb684f88acd816e9c8f2e3d4a1e3c0" - } - }, - "5.0.0": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.0/sauce-connect-5.0.0_linux.x86_64.tar.gz", - "sha1": "1dfcae164bf28dc5071d496777f23187285f7578" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.0/sauce-connect-5.0.0_linux.aarch64.tar.gz", - "sha1": "9861f28c8703a8d4a0f7824684e467974e202350" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.0/sauce-connect-5.0.0_darwin.all.zip", - "sha1": "19d56467d90a98cafd85ed1f2b7647c95d8fe27b" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.0/sauce-connect-5.0.0_windows.x86_64.zip", - "sha1": "40f1375d0d28e8c704f8b829fb842bedd11ea6c9" - }, - "windows-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.0/sauce-connect-5.0.0_windows.aarch64.zip", - "sha1": "a7be976a39a72e0c1c931f7b981649a93c0a4b36" - } - }, - "5.0.1": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.1/sauce-connect-5.0.1_linux.x86_64.tar.gz", - "sha1": "23fe2956742c1244757e249e5e05a59e96039482" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.1/sauce-connect-5.0.1_linux.aarch64.tar.gz", - "sha1": "962c1cd7884ad703e213d86ba9df8d7b5d58dbe7" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.1/sauce-connect-5.0.1_darwin.all.zip", - "sha1": "5eaa165b5eae2c57c8fc07f7272404c09a56cb89" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.1/sauce-connect-5.0.1_windows.x86_64.zip", - "sha1": "f6afc12802581922fdff2ee7472f1b3a1d259ec9" - }, - "windows-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.0.1/sauce-connect-5.0.1_windows.aarch64.zip", - "sha1": "c92ae859e212fc83221b92486886e741bb6f336c" - } - }, - "5.1.0": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.0/sauce-connect-5.1.0_linux.x86_64.tar.gz", - "sha256": "69ef7830b8ea3fc2bd9129cbf3f5ff04a22c6a7cd8a3c9e1e1ff0ee411b0ca20" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.0/sauce-connect-5.1.0_linux.aarch64.tar.gz", - "sha256": "679b940e6e7e99ded716fd22e0a4229be012c85aaad9d0ca9242e2bbcf8f50ed" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.0/sauce-connect-5.1.0_darwin.all.zip", - "sha256": "e587d5a9b5e5c928600d9e5e35d74ae9926218c7e13ab1da90f26da411d360cd" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.0/sauce-connect-5.1.0_windows.x86_64.zip", - "sha256": "291f149685fa20015f78423a5650438978aa38ba3c72671fe834d80c706e32c4" - }, - "windows-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.0/sauce-connect-5.1.0_windows.aarch64.zip", - "sha256": "a6b1e61f93eb8afb4bf461d7df43724ae1cafdb9553ca7f7cc59905ed431d7f7" - } - }, - "5.1.1": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_linux.x86_64.tar.gz", - "sha256": "6247e61e39ff054cf524341a681f4045c557be79bcf63c8c501e634ef2d54a41" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_linux.aarch64.tar.gz", - "sha256": "b4951cf64a724ceb03cf0ae676a35a0ca721210c0e42426384dcec23159f1fe5" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_darwin.all.zip", - "sha256": "d0a29f2df3277e8468c55810ef99685bed632ec6d2eed9fdbd941f2f8faf2354" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_windows.x86_64.zip", - "sha256": "d3e692cca8b9311a4822e8a6f8715e787319e70dc5551cbb212f5be68d36f7bb" - }, - "windows-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_windows.aarch64.zip", - "sha256": "fbb1b15c9a5ae6435cba0dc43f195df32764e5c7b1fcc6a2e625df58ea05642d" - } - } - } -} diff --git a/tests/__responses__/download_error.json b/tests/__responses__/download_error.json new file mode 100644 index 00000000..96592833 --- /dev/null +++ b/tests/__responses__/download_error.json @@ -0,0 +1,6 @@ +{ + "error": { + "code": "404", + "message": "Invalid input" + } +} diff --git a/tests/__responses__/download_macos.json b/tests/__responses__/download_macos.json new file mode 100644 index 00000000..442a24fe --- /dev/null +++ b/tests/__responses__/download_macos.json @@ -0,0 +1,12 @@ +{ + "download": { + "checksums": [ + { + "value": "1384bb85b2d29d177933fc8e894c8f6ac60d83b666435d12e9fca7f50b350459", + "algorithm": "sha256" + } + ], + "url": "https://saucelabs.com/downloads/sauce-connect/5.2.2/sauce-connect-5.2.2_darwin.all.zip", + "version": "5.2.2" + } +} diff --git a/tests/__responses__/download_windows_x86_64.json b/tests/__responses__/download_windows_x86_64.json new file mode 100644 index 00000000..0ee1cd56 --- /dev/null +++ b/tests/__responses__/download_windows_x86_64.json @@ -0,0 +1,12 @@ +{ + "download": { + "checksums": [ + { + "value": "fb932db5af5c4ed3dbdae9c939ae77da5d9440a3b0de60701643518af7b53ff1", + "algorithm": "sha256" + } + ], + "url": "https://saucelabs.com/downloads/sauce-connect/5.2.2/sauce-connect-5.2.2_windows.x86_64.zip", + "version": "5.2.2" + } +} diff --git a/tests/__responses__/latest_versions.json b/tests/__responses__/latest_versions.json deleted file mode 100644 index 05479459..00000000 --- a/tests/__responses__/latest_versions.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "downloads": { - "linux": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_linux.x86_64.tar.gz", - "sha256": "6247e61e39ff054cf524341a681f4045c557be79bcf63c8c501e634ef2d54a41" - }, - "linux-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_linux.aarch64.tar.gz", - "sha256": "b4951cf64a724ceb03cf0ae676a35a0ca721210c0e42426384dcec23159f1fe5" - }, - "osx": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_darwin.all.zip", - "sha256": "d0a29f2df3277e8468c55810ef99685bed632ec6d2eed9fdbd941f2f8faf2354" - }, - "win32": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_windows.x86_64.zip", - "sha256": "d3e692cca8b9311a4822e8a6f8715e787319e70dc5551cbb212f5be68d36f7bb" - }, - "windows-arm64": { - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_windows.aarch64.zip", - "sha256": "fbb1b15c9a5ae6435cba0dc43f195df32764e5c7b1fcc6a2e625df58ea05642d" - } - }, - "info_url": "https://docs.saucelabs.com/secure-connections/sauce-connect-5/installation/", - "latest_version": "5.1.1", - "download_url": "https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_darwin.all.zip", - "sha256": "d0a29f2df3277e8468c55810ef99685bed632ec6d2eed9fdbd941f2f8faf2354", - "status": "LATEST", - "client_version": "5.1.1" -} diff --git a/tests/__responses__/versions.json b/tests/__responses__/versions.json deleted file mode 100644 index dab9b3c0..00000000 --- a/tests/__responses__/versions.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Sauce Connect": { - "download_url": "https://docs.saucelabs.com/secure-connections/sauce-connect/installation/", - "linux": { - "build": "b491a8aaea43d70ef6cb1f37a11191de01fa8446", - "download_url": "https://saucelabs.com/downloads/sc-4.9.2-linux.tar.gz", - "sha1": "5589571bdc186f3f1b05fe6ce68529501a42fb43" - }, - "linux-arm64": { - "build": "b491a8aaea43d70ef6cb1f37a11191de01fa8446", - "download_url": "https://saucelabs.com/downloads/sc-4.9.2-linux-arm64.tar.gz", - "sha1": "8b02c4343b74c36c575817ea4a6eae5fb5718f6c" - }, - "osx": { - "build": "55cc68ffd9a2891e9b717ed459b1d0e970555f9c", - "download_url": "https://saucelabs.com/downloads/sc-4.9.1-osx.zip", - "sha1": "64f9c1bac5d4f5b9acb6fbb629b6df0f5671b4c8" - }, - "version": "4.9.2", - "win32": { - "build": "b491a8aaea43d70ef6cb1f37a11191de01fa8446", - "download_url": "https://saucelabs.com/downloads/sc-4.9.2-win32.zip", - "sha1": "47c19feda3fb684f88acd816e9c8f2e3d4a1e3c0" - } - } -} diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap index 481a63c2..0ad7f1d7 100644 --- a/tests/__snapshots__/index.test.js.snap +++ b/tests/__snapshots__/index.test.js.snap @@ -1,15 +1,31 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should contain all download links 1`] = ` +exports[`should contain expected macos download link 1`] = ` [ [ - "https://api.us-west-1.saucelabs.com/rest/v1/public/tunnels/info/versions", + "https://api.us-west-1.saucelabs.com/rest/v1/public/tunnels/sauce-connect/download", { "responseType": "json", "searchParams": { - "all": true, - "client_host": "darwin-arm64", - "client_version": "5.1.1", + "arch": "x86_64", + "os": "macos", + "version": "5.2.2", + }, + }, + ], +] +`; + +exports[`should contain expected windows download link 1`] = ` +[ + [ + "https://api.us-west-1.saucelabs.com/rest/v1/public/tunnels/sauce-connect/download", + { + "responseType": "json", + "searchParams": { + "arch": "x86_64", + "os": "windows", + "version": "5.2.2", }, }, ], @@ -138,47 +154,18 @@ exports[`should get user by username 1`] = ` ] `; -exports[`should have status latest 1`] = ` -[ - [ - "https://api.us-west-1.saucelabs.com/rest/v1/public/tunnels/info/versions", - { - "responseType": "json", - "searchParams": { - "client_host": "darwin-arm64", - "client_version": "5.1.1", - }, - }, - ], -] -`; - -exports[`startSauceConnect should start sauce connect with fallback default version in case the call to the API failed 1`] = ` -[ - [ - "/foo/bar", - [ - "--proxy-tunnel", - "--tunnel-name=my-tunnel", - "--user=foo", - "--api-key=bar", - "--region=us-west-1", - ], - ], -] -`; - exports[`startSauceConnect should start sauce connect with proper parsed args 1`] = ` [ [ "/foo/bar", [ - "--proxy-tunnel", - "--verbose", - "--tunnel-name=my-tunnel", - "--user=foo", - "--api-key=bar", + "run", + "--proxy-tunnel=abc", + "--verbose=true", + "--username=foo", + "--access-key=bar", "--region=eu-central-1", + "--tunnel-name=my-tunnel", ], ], ] diff --git a/tests/index.test.js b/tests/index.test.js index 6caabeb0..5b07ee16 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -7,9 +7,9 @@ import FormData from 'form-data'; import SauceLabs from '../src'; -import versions from './__responses__/versions.json'; -import allVersions from './__responses__/all_versions.json'; -import latestVersions from './__responses__/latest_versions.json'; +import downloadMacos from './__responses__/download_macos.json'; +import downloadWindows from './__responses__/download_windows_x86_64.json'; +import downloadError from './__responses__/download_error.json'; const instances = []; @@ -423,43 +423,60 @@ test('should fail if file parameter is invalid', async () => { expect(result.message).toContain('Invalid file parameter'); }); -test('should contain all download links', async () => { +test('should contain expected macos download link', async () => { const api = new SauceLabs({user: 'foo', key: 'bar'}); got.mockReturnValue( Promise.resolve({ body: { - ...allVersions, + ...downloadMacos, }, }) ); - const scVersions = await api.scVersions({ - clientVersion: '5.1.1', - clientHost: 'darwin-arm64', - all: true, + const scDownload = await api.scDownload({ + version: '5.2.2', + os: 'macos', + arch: 'x86_64', }); expect(got.mock.calls).toMatchSnapshot(); - expect(scVersions.all_downloads['5.1.1'].linux).toMatchObject({ - download_url: - 'https://saucelabs.com/downloads/sauce-connect/5.1.1/sauce-connect-5.1.1_linux.x86_64.tar.gz', - sha256: '6247e61e39ff054cf524341a681f4045c557be79bcf63c8c501e634ef2d54a41', + expect(scDownload.download).toMatchObject({ + checksums: [ + { + value: + '1384bb85b2d29d177933fc8e894c8f6ac60d83b666435d12e9fca7f50b350459', + algorithm: 'sha256', + }, + ], + url: 'https://saucelabs.com/downloads/sauce-connect/5.2.2/sauce-connect-5.2.2_darwin.all.zip', + version: '5.2.2', }); }); -test('should have status latest', async () => { +test('should contain expected windows download link', async () => { const api = new SauceLabs({user: 'foo', key: 'bar'}); got.mockReturnValue( Promise.resolve({ body: { - ...latestVersions, + ...downloadWindows, }, }) ); - const scVersions = await api.scVersions({ - clientVersion: '5.1.1', - clientHost: 'darwin-arm64', + const scDownload = await api.scDownload({ + version: '5.2.2', + os: 'windows', + arch: 'x86_64', }); expect(got.mock.calls).toMatchSnapshot(); - expect(scVersions.status).toEqual('LATEST'); + expect(scDownload.download).toMatchObject({ + checksums: [ + { + value: + 'fb932db5af5c4ed3dbdae9c939ae77da5d9440a3b0de60701643518af7b53ff1', + algorithm: 'sha256', + }, + ], + url: 'https://saucelabs.com/downloads/sauce-connect/5.2.2/sauce-connect-5.2.2_windows.x86_64.zip', + version: '5.2.2', + }); }); describe('startSauceConnect', () => { @@ -486,42 +503,55 @@ describe('startSauceConnect', () => { expect(spawn.mock.calls).toMatchSnapshot(); expect(logs).toHaveLength(1); - expect(instances).toHaveLength(1); + expect(instances).toHaveLength(2); }); - it('should start sauce connect with latest version if no version is specified in the args', async () => { + it('should throw an error if there is an error response from the download API', async () => { const logs = []; const api = new SauceLabs({user: 'foo', key: 'bar'}); got.mockReturnValue( Promise.resolve({ body: { - data: { - ...versions, - }, + ...downloadError, }, }) ); - setTimeout( - () => - stdoutEmitter.emit( - 'data', - 'Sauce Connect is up, you may start your tests' - ), - 50 - ); - await api.startSauceConnect({ - tunnelName: 'my-tunnel', - 'proxy-tunnel': 'abc', - logger: (log) => logs.push(log), - }); + const err = await api + .startSauceConnect({ + tunnelName: 'my-tunnel', + 'proxy-tunnel': 'abc', + logger: (log) => logs.push(log), + }) + .catch((err) => err); + expect(err.message).toContain('code: 404 message: Invalid input'); }); - it('should start sauce connect with fallback default version in case the call to the API failed', async () => { + it('should throw an error if the call to the download API failed', async () => { const logs = []; const api = new SauceLabs({user: 'foo', key: 'bar'}); got.mockImplementation(() => { throw new Error('Endpoint not available!'); }); + const err = await api + .startSauceConnect({ + tunnelName: 'my-tunnel', + 'proxy-tunnel': 'abc', + logger: (log) => logs.push(log), + }) + .catch((err) => err); + expect(err.message).toContain('Endpoint not available!'); + }); + + it('should start sauce connect with the default version if no version is specified in the args', async () => { + const logs = []; + const api = new SauceLabs({user: 'foo', key: 'bar'}); + got.mockReturnValue( + Promise.resolve({ + body: { + ...downloadMacos, + }, + }) + ); setTimeout( () => stdoutEmitter.emit( @@ -535,11 +565,10 @@ describe('startSauceConnect', () => { 'proxy-tunnel': 'abc', logger: (log) => logs.push(log), }); - expect(spawn.mock.calls).toMatchSnapshot(); }); - it('should properly fail if connection could not be established', async () => { - const errMessage = 'Sauce Connect could not establish a connection'; + it('should properly fail on fatal error', async () => { + const errMessage = 'fatal error exiting: any error message'; const api = new SauceLabs({user: 'foo', key: 'bar'}); setTimeout(() => stdoutEmitter.emit('data', errMessage), 50); const err = await api @@ -552,20 +581,6 @@ describe('startSauceConnect', () => { expect(err.message).toBe(errMessage); }); - it('should properly fail if user is not authorized', async () => { - const errMessage = 'Sauce Connect failed to start - 401 (Unauthorized).'; - const api = new SauceLabs({user: 'foo', key: 'bar'}); - setTimeout(() => stdoutEmitter.emit('data', errMessage), 50); - const err = await api - .startSauceConnect({ - scVersion: '1.2.3', - tunnelName: 'my-tunnel', - 'proxy-tunnel': 'abc', - }) - .catch((err) => err); - expect(err.message).toContain(errMessage); - }); - it('should close sauce connect', async () => { const api = new SauceLabs({user: 'foo', key: 'bar'}); setTimeout( @@ -579,7 +594,7 @@ describe('startSauceConnect', () => { const sc = await api.startSauceConnect({tunnelName: 'my-tunnel'}, true); setTimeout(() => { sc.cp.stdout.emit('data', 'Some other message'); - sc.cp.stdout.emit('data', 'Goodbye'); + sc.cp.stdout.emit('data', 'tunnel was shutdown'); }, 100); await sc.close(); expect(process.kill).toBeCalledWith(123, 'SIGINT'); @@ -594,24 +609,29 @@ describe('startSauceConnect', () => { expect(res).toEqual(new Error('Uuups')); }); - it('should not fail if stderr is expected character', async () => { + it('should fail on Sauce Connect v4', async () => { const api = new SauceLabs({user: 'foo', key: 'bar'}); - setTimeout(() => stderrEmitter.emit('data', '\u001b[K'), 50); - setTimeout( - () => - stdoutEmitter.emit( - 'data', - 'Sauce Connect is up, you may start your tests' - ), - 150 - ); + const scVersion = '4.9.2'; const res = await api - .startSauceConnect({tunnelName: 'my-tunnel'}) + .startSauceConnect({ + tunnelName: 'my-tunnel', + scVersion: scVersion, + }) .catch((err) => err); - expect(res instanceof Error).toBe(false); + expect(res).toEqual( + new Error( + `This Sauce Connect version (${scVersion}) is no longer supported. Please use Sauce Connect 5.` + ) + ); }); }); +it('should fail when tunnelName is not given', async () => { + const api = new SauceLabs({user: 'foo', key: 'bar'}); + const res = await api.startSauceConnect({}).catch((err) => err); + expect(res).toEqual(new Error(`Missing tunnel-name`)); +}); + test('should output failure msg for createJob API', async () => { const response = new Error('Response code 422 (Unprocessable Entity)'); response.statusCode = 422; diff --git a/tests/sauceConnectLoader.test.js b/tests/sauceConnectLoader.test.js index 938e3a0b..aa40ab32 100644 --- a/tests/sauceConnectLoader.test.js +++ b/tests/sauceConnectLoader.test.js @@ -6,11 +6,16 @@ import compressing from 'compressing'; describe('SauceConnectLoader', () => { describe('constructor', () => { - test('should throw if platform is unsupported', () => { - jest.spyOn(utils, 'getPlatform').mockImplementation(() => 'whatever'); - expect( - () => new SauceConnectLoader({sauceConnectVersion: '1.2.3'}) - ).toThrow(ReferenceError, 'Unsupported platform whatever'); + test('should have .exe path for windows platform', () => { + jest.spyOn(utils, 'getPlatform').mockImplementation(() => 'windows'); + const scl = new SauceConnectLoader({sauceConnectVersion: '1.2.3'}); + expect(scl.path).toContain('exe'); + }); + + test('should not have .exe path for windows platform', () => { + jest.spyOn(utils, 'getPlatform').mockImplementation(() => 'windows'); + const scl = new SauceConnectLoader({sauceConnectVersion: '1.2.3'}); + expect(scl.path).toContain('exe'); }); }); @@ -51,8 +56,14 @@ describe('SauceConnectLoader', () => { }); test('should attempt to download', async () => { + let url = 'https://this-is-a-test.com'; + await scl.verifyAlreadyDownloaded({url: url}); + expect(scl._download).toHaveBeenCalledWith(url); + }); + + test('should not attempt to download when url is not provided', async () => { await scl.verifyAlreadyDownloaded(); - expect(scl._download).toHaveBeenCalled(); + expect(scl._download).not.toHaveBeenCalled(); }); }); @@ -99,31 +110,25 @@ describe('SauceConnectLoader', () => { test('should download and uncompress - linux', async () => { jest.spyOn(utils, 'getPlatform').mockImplementation(() => 'linux'); - await scl._download(); - expect(mockHttpsGet).toHaveBeenCalledWith( - scl.url, - expect.any(Function) - ); + const url = 'https://httpbin.org/get'; + await scl._download(url); + expect(mockHttpsGet).toHaveBeenCalledWith(url, expect.any(Function)); expect(mockCompressingLinux).toHaveBeenCalled(); }); - test('should download and uncompress - mac', async () => { - jest.spyOn(utils, 'getPlatform').mockImplementation(() => 'darwin'); - await scl._download(); - expect(mockHttpsGet).toHaveBeenCalledWith( - scl.url, - expect.any(Function) - ); + test('should download and uncompress - macos', async () => { + jest.spyOn(utils, 'getPlatform').mockImplementation(() => 'macos'); + const url = 'https://httpbin.org/get'; + await scl._download(url); + expect(mockHttpsGet).toHaveBeenCalledWith(url, expect.any(Function)); expect(mockCompressingWinMac).toHaveBeenCalled(); }); - test('should download and uncompress - win', async () => { - jest.spyOn(utils, 'getPlatform').mockImplementation(() => 'win32'); - await scl._download(); - expect(mockHttpsGet).toHaveBeenCalledWith( - scl.url, - expect.any(Function) - ); + test('should download and uncompress - windows', async () => { + jest.spyOn(utils, 'getPlatform').mockImplementation(() => 'windows'); + const url = 'https://httpbin.org/get'; + await scl._download(url); + expect(mockHttpsGet).toHaveBeenCalledWith(url, expect.any(Function)); expect(mockCompressingWinMac).toHaveBeenCalled(); }); }); diff --git a/tests/utils.test.js b/tests/utils.test.js index a185c98c..4b55b08b 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -20,11 +20,46 @@ test('createHMAC', async () => { }); test('getPlatform', () => { - expect(getPlatform()).toBe(process.platform); + // Enable monkey patching process.platform. + const originalPlatform = process.platform; + let platform = null; + Object.defineProperty(process, 'platform', {get: () => platform}); + + platform = 'win32'; + expect(getPlatform()).toBe('windows'); + + platform = 'darwin'; + expect(getPlatform()).toBe('macos'); + + platform = 'linux'; + expect(getPlatform()).toBe('linux'); + + // Restore the original value of process.platform. + platform = originalPlatform; +}); + +test('getCPUArch', () => { + let result = process.arch; + if (result == 'x64') { + result = 'x86_64'; + } + expect(getCPUArch()).toBe(result); }); test('getCPUArch', () => { - expect(getCPUArch()).toBe(process.arch); + // Enable monkey patching process.arch. + const originalArch = process.arch; + let arch = null; + Object.defineProperty(process, 'arch', {get: () => arch}); + + arch = 'x64'; + expect(getCPUArch()).toBe('x86_64'); + + arch = 'arm64'; + expect(getCPUArch()).toBe('arm64'); + + // Restore the original value of process.platform. + arch = originalArch; }); test('getAPIHost', () => {