Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions bin/commands/runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const {
checkAccessibilityPlatform,
supportFileCleanup
} = require('../accessibility-automation/helper');
const { isTurboScaleSession, getTurboScaleGridDetails } = require('../helpers/atsHelper');

module.exports = function run(args, rawArgs) {

Expand Down Expand Up @@ -66,6 +67,8 @@ module.exports = function run(args, rawArgs) {
const [isTestObservabilitySession, isBrowserstackInfra] = setTestObservabilityFlags(bsConfig);
const checkAccessibility = checkAccessibilityPlatform(bsConfig);
const isAccessibilitySession = bsConfig.run_settings.accessibility || checkAccessibility;
const turboScaleSession = isTurboScaleSession(bsConfig);
Constants.turboScaleObj.enabled = turboScaleSession;

utils.setUsageReportingFlag(bsConfig, args.disableUsageReporting);

Expand Down Expand Up @@ -144,6 +147,32 @@ module.exports = function run(args, rawArgs) {
if (isAccessibilitySession && isBrowserstackInfra) {
await createAccessibilityTestRun(bsConfig);
}

if (turboScaleSession) {
// Local is only required in case user is running on trial grid and wants to access private website.
// Even then, it will be spawned separately via browserstack-cli ats connect-grid command and not via browserstack-cypress-cli
// Hence whenever running on ATS, need to make local as false
bsConfig.connection_settings.local = false;

const gridDetails = await getTurboScaleGridDetails(bsConfig);

if (gridDetails && Object.keys(gridDetails).length > 0) {
Constants.turboScaleObj.gridDetails = gridDetails;

if (gridDetails.isTrialGrid) {
logger.info('Will be running the build on Trial Grid. Ensure you are using connect-grid command if using a private website');
}

Constants.turboScaleObj.gridUrl = gridDetails.cypressUrl;
Constants.turboScaleObj.uploadUrl = gridDetails.cypressUrl + '/upload';
Constants.turboScaleObj.buildUrl = gridDetails.cypressUrl + '/build';

logger.debug(`Automate TurboScale Grid URL set to ${gridDetails.url}`);
} else {
process.exitCode = Constants.ERROR_EXIT_CODE;
return;
}
}
}

const { packagesInstalled } = !isBrowserstackInfra ? false : await packageInstaller.packageSetupAndInstaller(bsConfig, config.packageDirName, {markBlockStart, markBlockEnd});
Expand Down
80 changes: 80 additions & 0 deletions bin/helpers/atsHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const request = require('request'),
logger = require('./logger').winstonLogger,
utils = require('./utils'),
config = require('./config');
Constants = require('./constants');

exports.isTurboScaleSession = (bsConfig) => {
// env var will override config
if (process.env.BROWSERSTACK_TURBOSCALE && process.env.BROWSERSTACK_TURBOSCALE === 'true') {
return true;
}

if (bsConfig.run_settings && bsConfig.run_settings.turboScale) {
return true;
}

return false;
};

exports.getTurboScaleOptions = (bsConfig) => {
if (bsConfig.run_settings && bsConfig.run_settings.turboScaleOptions) {
return bsConfig.run_settings.turboScaleOptions;
}

return {};
};

exports.getTurboScaleGridName = (bsConfig) => {
// env var will override config
if (process.env.BROWSERSTACK_TURBOSCALE_GRID_NAME) {
return process.env.BROWSERSTACK_TURBOSCALE_GRID_NAME;
}

if (bsConfig.run_settings && bsConfig.run_settings.turboScaleOptions && bsConfig.run_settings.turboScaleOptions.gridName) {
return bsConfig.run_settings.turboScaleOptions.gridName;
}

return 'NO_GRID_NAME_PASSED';
};

exports.getTurboScaleGridDetails = async (bsConfig) => {
try {
const gridName = this.getTurboScaleGridName(bsConfig);

return new Promise((resolve, reject) => {
let options = {
url: `${config.turboScaleAPIUrl}/grids/${gridName}`,
auth: {
username: bsConfig.auth.username,
password: bsConfig.auth.access_key,
},
headers: {
'User-Agent': utils.getUserAgent(),
}
};
let responseData = {};
request.get(options, function (err, resp, data) {
if(err) {
logger.warn(utils.formatRequest(err, resp, data));
utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'get_ats_details_failed', null, rawArgs);
resolve({});
} else {
try {
responseData = JSON.parse(data);
} catch (e) {
responseData = {};
}
if(resp.statusCode != 200) {
logger.warn(`Warn: Get Automate TurboScale Details Request failed with status code ${resp.statusCode}`);
utils.sendUsageReport(bsConfig, args, responseData["error"], Constants.messageTypes.ERROR, 'get_ats_details_failed', null, rawArgs);
resolve({});
}
resolve(responseData);
}
});
});
} catch (err) {
logger.error(`Failed to find TurboScale Grid: ${err}: ${err.stack}`);
}
};
4 changes: 4 additions & 0 deletions bin/helpers/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const createBuild = (bsConfig, zip) => {
body: data
}

if (Constants.turboScaleObj.enabled) {
options.url = Constants.turboScaleObj.buildUrl;
}

request.post(options, function (err, resp, body) {
if (err) {
logger.error(utils.formatRequest(err, resp, body));
Expand Down
4 changes: 4 additions & 0 deletions bin/helpers/checkUploaded.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ const checkUploadedMd5 = (bsConfig, args, instrumentBlocks) => {
body: JSON.stringify(data)
};

if (Constants.turboScaleObj.enabled) {
options.url = config.turboScaleMd5Sum;
}

instrumentBlocks.markBlockStart("checkAlreadyUploaded.railsCheck");
request.post(options, function (err, resp, body) {
if (err) {
Expand Down
8 changes: 6 additions & 2 deletions bin/helpers/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ config.packageFileName = "bstackPackages.tar.gz";
config.packageDirName = "tmpBstackPackages";
config.retries = 5;
config.networkErrorExitCode = 2;
config.compiledConfigJsDirName = 'tmpBstackCompiledJs'
config.configJsonFileName = 'tmpCypressConfig.json'
config.compiledConfigJsDirName = 'tmpBstackCompiledJs';
config.configJsonFileName = 'tmpCypressConfig.json';

// turboScale
config.turboScaleMd5Sum = `${config.turboScaleUrl}/md5sumcheck`;
config.turboScaleBuildsUrl = `${config.turboScaleUrl}/builds`;

module.exports = config;
4 changes: 3 additions & 1 deletion bin/helpers/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
"rails_host": "https://api.browserstack.com",
"dashboardUrl": "https://automate.browserstack.com/dashboard/v2/builds/",
"usageReportingUrl": "https://eds.browserstack.com:443/send_event_cy_internal",
"localTestingListUrl": "https://www.browserstack.com/local/v1/list"
"localTestingListUrl": "https://www.browserstack.com/local/v1/list",
"turboScaleUrl": "https://grid.browserstack.com/packages/cypress",
"turboScaleAPIUrl": "https://api.browserstack.com/automate-turboscale/v1"
}
3 changes: 3 additions & 0 deletions bin/helpers/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,8 @@ const CYPRESS_CONFIG_FILE_NAMES = Object.keys(CYPRESS_CONFIG_FILE_MAPPING);

const CYPRESS_V10_AND_ABOVE_CONFIG_FILE_EXTENSIONS = ['js', 'ts', 'cjs', 'mjs']

const turboScaleObj = {};

module.exports = Object.freeze({
syncCLI,
userMessages,
Expand All @@ -449,6 +451,7 @@ module.exports = Object.freeze({
hashingOptions,
packageInstallerOptions,
specFileTypes,
turboScaleObj,
DEFAULT_CYPRESS_SPEC_PATH,
DEFAULT_CYPRESS_10_SPEC_PATH,
SPEC_TOTAL_CHAR_LIMIT,
Expand Down
10 changes: 8 additions & 2 deletions bin/helpers/sync/syncSpecsLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ let terminalWidth = (process.stdout.columns) * 0.9;
let lineSeparator = Constants.syncCLI.DEFAULT_LINE_SEP;
if (!isNaN(terminalWidth)) lineSeparator = "\n" + "-".repeat(terminalWidth);

let getOptions = (auth, build_id) => {
return {
let getOptions = (auth, build_id) => {
const options = {
url: `${config.buildUrlV2}${build_id}`,
auth: {
user: auth.username,
Expand All @@ -37,6 +37,12 @@ let getOptions = (auth, build_id) => {
"User-Agent": utils.getUserAgent()
}
};

if (Constants.turboScaleObj.enabled) {
options.url = `${config.turboScaleBuildsUrl}/${build_id}`;
}

return options;
}

let getTableConfig = (termWidth) => {
Expand Down
6 changes: 6 additions & 0 deletions bin/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,11 @@ exports.generateUploadParams = (bsConfig, filePath, md5data, fileDetails) => {
"User-Agent": exports.getUserAgent(),
}
}

if (Constants.turboScaleObj.enabled) {
options.url = Constants.turboScaleObj.uploadUrl;
}

return options
}

Expand Down Expand Up @@ -965,6 +970,7 @@ exports.setLocalArgs = (bsConfig, args) => {
local_args['proxyPort'] = bsConfig["connection_settings"]["proxyPort"];
if (bsConfig["connection_settings"]["useCaCertificate"])
local_args['useCaCertificate'] = bsConfig["connection_settings"]["useCaCertificate"];

local_args['daemon'] = true;
local_args['enable-logging-for-api'] = true
local_args['source'] = `cypress:${usageReporting.cli_version_and_path(bsConfig).version}`;
Expand Down
113 changes: 113 additions & 0 deletions test/unit/bin/helpers/atsHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const { expect } = require("chai");
const chai = require("chai"),
chaiAsPromised = require("chai-as-promised"),
sinon = require('sinon'),
request = require('request');

const logger = require("../../../../bin/helpers/logger").winstonLogger,
testObjects = require("../../support/fixtures/testObjects"),
utils = require('../../../../bin/helpers/utils'),
atsHelper = require('../../../../bin/helpers/atsHelper');

chai.use(chaiAsPromised);
logger.transports["console.info"].silent = true;

describe('#atsHelper', () => {
let bsConfig = testObjects.sampleBsConfig;
let sendUsageReportStub = null, requestStub = null;

beforeEach(() => {
sendUsageReportStub = sinon.stub(utils, 'sendUsageReport');
requestStub = sinon.stub(request, "get");
});

afterEach(() => {
sinon.restore();
});

describe('#isTurboScaleSession', () => {
it('return true if env var set to true', () => {
process.env.BROWSERSTACK_TURBOSCALE = "true";
expect(atsHelper.isTurboScaleSession(bsConfig)).to.be.true;
delete BROWSERSTACK_TURBOSCALE;
});

it('return false if env var set to incorrect value', () => {
process.env.BROWSERSTACK_TURBOSCALE = "false";
expect(atsHelper.isTurboScaleSession(bsConfig)).to.be.false;
delete BROWSERSTACK_TURBOSCALE;
});

it('return true if set in config', () => {
expect(atsHelper.isTurboScaleSession(testObjects.sampleATSBsConfig)).to.be.true;
});

it('return false if not set in config as well as env var', () => {
expect(atsHelper.isTurboScaleSession(bsConfig)).to.be.false;
});
});

describe('#getTurboScaleOptions', () => {
it('return empty object if not set', () => {
expect(atsHelper.getTurboScaleOptions(testObjects.sampleATSBsConfig)).to.deep.include({});
});

it('return obj if set in config', () => {
expect(atsHelper.getTurboScaleOptions(testObjects.sampleATSBsConfigWithOptions)).to.deep.include({ gridName: 'abc' });
});
});

describe('#getTurboScaleGridName', () => {
it('return value set by env var', () => {
process.env.BROWSERSTACK_TURBOSCALE_GRID_NAME = "my-grid";
expect(atsHelper.getTurboScaleGridName(testObjects.sampleATSBsConfigWithOptions)).to.eq('my-grid');
delete process.env.BROWSERSTACK_TURBOSCALE_GRID_NAME;
});

it('return value set in config', () => {
expect(atsHelper.getTurboScaleGridName(testObjects.sampleATSBsConfigWithOptions)).to.eq('abc');
});

it('return NO_GRID_NAME_PASSED if value not set in config as well as env var', () => {
expect(atsHelper.getTurboScaleGridName(bsConfig)).to.eq('NO_GRID_NAME_PASSED');
});
});

describe('#getTurboScaleGridDetails', () => {
it('resolve with body', () => {
const body = {
id: '123',
name: 'grid-name',
url: 'https://abc.com/wd/hub',
playwrightUrl: 'wss://abc.com/playwright',
cypressUrl: 'https://abc.com/cypress',
isTrialGrid: false,
customRepeaters: ''
};

requestStub.yields(null, { statusCode: 200 }, body);
sendUsageReportStub.notCalled;
atsHelper.getTurboScaleGridDetails(testObjects.sampleATSBsConfigWithOptions).then((result) => {
expect(result).to.eq(body);
})
});

it('reject with empty object', () => {
const body = {
id: '123',
name: 'grid-name',
url: 'https://abc.com/wd/hub',
playwrightUrl: 'wss://abc.com/playwright',
cypressUrl: 'https://abc.com/cypress',
isTrialGrid: false,
customRepeaters: ''
};

requestStub.yields(null, { statusCode: 422 }, body);
atsHelper.getTurboScaleGridDetails(testObjects.sampleATSBsConfigWithOptions).then((result) => {
expect(result).to.eq({});
expect(sendUsageReportStub.calledOnce).to.be.true;
})
});
});
});
33 changes: 32 additions & 1 deletion test/unit/support/fixtures/testObjects.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,35 @@ const runSampleRawArgs = {
$0: "browserstack-cypress",
};

const sampleATSBsConfig = {
auth: {
username: "random-username",
access_key: "random-access-key",
},
run_settings: {
cypress_proj_dir: "random path",
cypressConfigFilePath: "random path",
cypressProjectDir: "random path",
turboScale: true,
}
};

const sampleATSBsConfigWithOptions = {
auth: {
username: "random-username",
access_key: "random-access-key",
},
run_settings: {
cypress_proj_dir: "random path",
cypressConfigFilePath: "random path",
cypressProjectDir: "random path",
turboScale: true,
turboScaleOptions: {
gridName: 'abc'
}
}
};

module.exports = Object.freeze({
sampleBsConfig,
sampleBsConfigWithParallels,
Expand All @@ -194,5 +223,7 @@ module.exports = Object.freeze({
generateReportInputArgs,
generateReportInputRawArgs,
generateDownloadsInputArgs,
generateDownloadsInputRawArgs
generateDownloadsInputRawArgs,
sampleATSBsConfig,
sampleATSBsConfigWithOptions
});