Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

browserstack-cypress-cli

Package Overview
Dependencies
Maintainers
1
Versions
75
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

browserstack-cypress-cli - npm Package Compare versions

Comparing version 1.5.2 to 1.6.0

bin/commands/generateReport.js

39

bin/commands/runs.js

@@ -10,3 +10,5 @@ 'use strict';

utils = require("../helpers/utils"),
fileHelpers = require("../helpers/fileHelpers");
fileHelpers = require("../helpers/fileHelpers"),
syncRunner = require("../helpers/syncRunner"),
reportGenerator = require('../helpers/reporterHTML').reportGenerator;

@@ -48,7 +50,9 @@ module.exports = function run(args) {

// Validate browserstack.json values and parallels specified via arguments
return capabilityHelper.validate(bsConfig, args).then(function (validated) {
logger.info(validated);
return capabilityHelper.validate(bsConfig, args).then(function (cypressJson) {
//get the number of spec files
let specFiles = utils.getNumberOfSpecFiles(bsConfig, args, cypressJson);
// accept the number of parallels
utils.setParallels(bsConfig, args);
utils.setParallels(bsConfig, args, specFiles.length);

@@ -60,7 +64,6 @@ // Archive the spec files

return zipUploader.zipUpload(bsConfig, config.fileName).then(function (zip) {
// Create build
return build.createBuild(bsConfig, zip).then(function (data) {
let message = `${data.message}! ${Constants.userMessages.BUILD_CREATED} with build id: ${data.build_id}`;
let dashboardLink = `${Constants.userMessages.VISIT_DASHBOARD} ${config.dashboardUrl}${data.build_id}`;
let dashboardLink = `${Constants.userMessages.VISIT_DASHBOARD} ${data.dashboard_url}`;
utils.exportResults(data.build_id, `${config.dashboardUrl}${data.build_id}`);

@@ -71,6 +74,25 @@ if ((utils.isUndefined(bsConfig.run_settings.parallels) && utils.isUndefined(args.parallels)) || (!utils.isUndefined(bsConfig.run_settings.parallels) && bsConfig.run_settings.parallels == Constants.cliMessages.RUN.DEFAULT_PARALLEL_MESSAGE)) {

if (!args.disableNpmWarning && bsConfig.run_settings.npm_dependencies && Object.keys(bsConfig.run_settings.npm_dependencies).length <= 0) logger.warn(Constants.userMessages.NO_NPM_DEPENDENCIES);
if (bsConfig.run_settings.cypress_version && bsConfig.run_settings.cypress_version !== data.cypress_version) {
let versionMessage = utils.versionChangedMessage(bsConfig.run_settings.cypress_version, data.cypress_version)
logger.warn(versionMessage);
}
if (!args.disableNpmWarning && bsConfig.run_settings.npm_dependencies && Object.keys(bsConfig.run_settings.npm_dependencies).length <= 0) {
logger.warn(Constants.userMessages.NO_NPM_DEPENDENCIES);
logger.warn(Constants.userMessages.NO_NPM_DEPENDENCIES_READ_MORE);
}
if (args.sync) {
syncRunner.pollBuildStatus(bsConfig, data).then((exitCode) => {
// Generate custom report!
reportGenerator(bsConfig, data.build_id, args, function(){
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null);
utils.handleSyncExit(exitCode, data.dashboard_url);
});
});
}
logger.info(message);
logger.info(dashboardLink);
if(!args.sync) logger.info(Constants.userMessages.EXIT_SYNC_CLI_MESSAGE.replace("<build-id>",data.build_id));
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null);

@@ -87,5 +109,4 @@ return;

logger.error(Constants.userMessages.ZIP_UPLOAD_FAILED);
fileHelpers.deleteZip();
utils.sendUsageReport(bsConfig, args, `${err}\n${Constants.userMessages.ZIP_UPLOAD_FAILED}`, Constants.messageTypes.ERROR, 'zip_upload_failed');
}).finally(function () {
fileHelpers.deleteZip();
});

@@ -92,0 +113,0 @@ }).catch(function (err) {

@@ -16,4 +16,6 @@ 'use strict';

logger.info(`Creating tests.zip with files in ${cypressFolderPath}`);
var archive = archiver('zip', {
zlib: { level: 9 } // Sets the compression level.
zlib: {level: 9}, // Sets the compression level.
});

@@ -30,3 +32,3 @@

output.on('close', function () {
resolve("Zipping completed");
resolve('Zipping completed');
});

@@ -46,5 +48,3 @@

Constants.allowedFileTypes.forEach(fileType => {
archive.glob(`**/*.${fileType}`, { cwd: cypressFolderPath, matchBase: true, ignore: ignoreFiles, dot:true });
});
archive.glob(`**/*.+(${Constants.allowedFileTypes.join("|")})`, { cwd: cypressFolderPath, matchBase: true, ignore: ignoreFiles, dot:true });

@@ -58,3 +58,5 @@ let packageJSON = {};

if (typeof runSettings.npm_dependencies === 'object') {
Object.assign(packageJSON, {devDependencies: runSettings.npm_dependencies});
Object.assign(packageJSON, {
devDependencies: runSettings.npm_dependencies,
});
}

@@ -64,10 +66,15 @@

let packageJSONString = JSON.stringify(packageJSON, null, 4);
archive.append(packageJSONString, { name: 'browserstack-package.json' });
archive.append(packageJSONString, {name: 'browserstack-package.json'});
}
// do not add cypress.json if arg provided is false
if (runSettings.cypress_config_file && runSettings.cypress_config_filename !== 'false') {
let cypressJSON = JSON.parse(fs.readFileSync(runSettings.cypressConfigFilePath));
if (
runSettings.cypress_config_file &&
runSettings.cypress_config_filename !== 'false'
) {
let cypressJSON = JSON.parse(
fs.readFileSync(runSettings.cypressConfigFilePath)
);
let cypressJSONString = JSON.stringify(cypressJSON, null, 4);
archive.append(cypressJSONString, { name: 'cypress.json' });
archive.append(cypressJSONString, {name: 'cypress.json'});
}

@@ -74,0 +81,0 @@

@@ -23,7 +23,10 @@ const logger = require("./logger").winstonLogger,

let osBrowserArray = [];
let browsersList = [];
if (bsConfig.browsers) {
bsConfig.browsers.forEach((element) => {
osBrowser = element.os + "-" + element.browser;
osAndBrowser = element.os + " / " + Utils.capitalizeFirstLetter(element.browser);
element.versions.forEach((version) => {
osBrowserArray.push(osBrowser + version);
browsersList.push(`${osAndBrowser} (${version})`);
});

@@ -34,3 +37,3 @@ });

if (obj.devices.length == 0) reject(Constants.validationMessages.EMPTY_BROWSER_LIST);
logger.info(`Browser list: ${osBrowserArray.toString()}`);
logger.info(`Browsers list: ${browsersList.join(", ")}`);

@@ -43,3 +46,2 @@ // Test suite

}
logger.info(`Test suite: bs://${obj.test_suite}`);

@@ -49,3 +51,3 @@ // Local

if (bsConfig.connection_settings && bsConfig.connection_settings.local === true) obj.local = true;
logger.info(`Local is set to: ${obj.local}`);
logger.info(`Local is set to: ${obj.local} (${obj.local ? Constants.userMessages.LOCAL_TRUE : Constants.userMessages.LOCAL_FALSE})`);

@@ -86,2 +88,5 @@ // Local Identifier

}
if (!Utils.isUndefined(bsConfig.run_settings.cypress_version)){
obj.cypress_version = bsConfig.run_settings.cypress_version;
}
}

@@ -108,2 +113,3 @@

return new Promise(function (resolve, reject) {
logger.info(Constants.userMessages.VALIDATING_CONFIG);
if (!bsConfig) reject(Constants.validationMessages.EMPTY_BROWSERSTACK_JSON);

@@ -132,2 +138,3 @@

let cypressConfigFilePath = bsConfig.run_settings.cypressConfigFilePath;
let cypressJson = {};

@@ -150,4 +157,3 @@ if (!fs.existsSync(cypressConfigFilePath) && bsConfig.run_settings.cypress_config_filename !== 'false') reject(Constants.validationMessages.INVALID_CYPRESS_CONFIG_FILE);

}
resolve(Constants.validationMessages.VALIDATED);
resolve(cypressJson);
});

@@ -154,0 +160,0 @@ }

@@ -19,3 +19,5 @@ var config = require('./config.json');

config.fileName = "tests.zip";
config.retries = 5;
config.networkErrorExitCode = 2;
module.exports = config;

@@ -0,19 +1,41 @@

let config = require("./config");
const syncCLI = {
FAILED_SPEC_DETAILS_COL_HEADER: ['Spec', 'Status', 'Browser', 'BrowserStack Session ID'],
LOGS: {
INIT_LOG: "All tests:"
},
INITIAL_DELAY_MULTIPLIER: 10
};
const userMessages = {
BUILD_FAILED: "Build creation failed.",
BUILD_CREATED: "Build created",
BUILD_INFO_FAILED: "Failed to get build info.",
BUILD_STOP_FAILED: "Failed to stop build.",
ZIP_UPLOADER_NOT_REACHABLE: "Could not reach to zip uploader.",
ZIP_UPLOAD_FAILED: "Zip Upload failed.",
CONFIG_FILE_CREATED: "BrowserStack Config File created, you can now run browserstack-cypress --config-file run",
CONFIG_FILE_EXISTS: "File already exists, delete the browserstack.json file manually. skipping...",
DIR_NOT_FOUND: "Given path does not exist. Failed to create browserstack.json in %s",
ZIP_DELETE_FAILED: "Could not delete local file.",
ZIP_DELETED: "Zip file deleted successfully.",
API_DEPRECATED: "This version of API is deprecated, please use latest version of API.",
FAILED_TO_ZIP: "Failed to zip files.",
VISIT_DASHBOARD: "Visit the Automate dashboard for test reporting:",
CONFLICTING_INIT_ARGUMENTS: "Conflicting arguments given. You can use --path only with a file name, and not with a file path.",
NO_PARALLELS: "Your tests will run sequentially. Read more about running your tests in parallel here: https://www.browserstack.com/docs/automate/cypress/run-tests-in-parallel",
NO_NPM_DEPENDENCIES: "No npm dependencies specified. Read more here: https://www.browserstack.com/docs/automate/cypress/npm-packages. You can suppress this warning by using --disable-npm-warning flag."
BUILD_FAILED: "Build creation failed.",
BUILD_GENERATE_REPORT_FAILED: "Generating report for the build <build-id> failed.",
BUILD_CREATED: "Build created",
BUILD_INFO_FAILED: "Failed to get build info.",
BUILD_STOP_FAILED: "Failed to stop build.",
BUILD_REPORT_MESSAGE: "See the entire build report here:",
ZIP_UPLOADER_NOT_REACHABLE: "Could not reach to zip uploader.",
ZIP_UPLOAD_FAILED: "Zip Upload failed.",
CONFIG_FILE_CREATED: "BrowserStack Config File created, you can now run browserstack-cypress --config-file run",
CONFIG_FILE_EXISTS: "File already exists, delete the browserstack.json file manually. skipping...",
DIR_NOT_FOUND: "Given path does not exist. Failed to create browserstack.json in %s",
ZIP_DELETE_FAILED: "Could not delete tests.zip successfully.",
ZIP_DELETED: "Deleted tests.zip successfully.",
API_DEPRECATED: "This version of API is deprecated, please use latest version of API.",
FAILED_TO_ZIP: "Failed to zip files.",
VISIT_DASHBOARD: "Visit the Automate dashboard for real-time test reporting:",
CONFLICTING_INIT_ARGUMENTS: "Conflicting arguments given. You can use --path only with a file name, and not with a file path.",
NO_PARALLELS: "Your specs will run sequentially on a single machine. Read more about running your specs in parallel here: https://www.browserstack.com/docs/automate/cypress/run-tests-in-parallel",
NO_NPM_DEPENDENCIES: "No npm dependencies specified - your specs might fail if they need any packages to be installed before running.",
NO_NPM_DEPENDENCIES_READ_MORE: "Read more about npm dependencies here: https://www.browserstack.com/docs/automate/cypress/npm-packages. You can suppress this warning by using --disable-npm-warning flag.",
VALIDATING_CONFIG: "Validating the config",
UPLOADING_TESTS: "Uploading the tests to BrowserStack",
LOCAL_TRUE: "you will now be able to test localhost / private URLs",
LOCAL_FALSE: "you won't be able to test localhost / private URLs",
EXIT_SYNC_CLI_MESSAGE: "Exiting the CLI, but your build is still running. You can use the --sync option to keep getting test updates. You can also use the build-info <build-id> command now.",
FATAL_NETWORK_ERROR: `fatal: unable to access '${config.buildUrl}': Could not resolve host: ${config.rails_host}`,
RETRY_LIMIT_EXCEEDED: `Max retries exceeded trying to connect to the host (retries: ${config.retries})`,
CHECK_DASHBOARD_AT: "Please check the build status at: ",
CYPRESS_VERSION_CHANGED: "Your build will run using Cypress <actualVersion> instead of Cypress <preferredVersion>. Read more about supported versions here: http://browserstack.com/docs/automate/cypress/supported-versions"
};

@@ -42,40 +64,45 @@

const cliMessages = {
VERSION: {
INFO: "shows version information",
HELP: "Specify --help for available options",
DEMAND: "Requires init, run or poll argument"
},
INIT: {
INFO: "create a browserstack.json file in the folder specified with the default configuration options.",
DESC: "Init in a specified folder"
},
BUILD: {
INFO: "Check status of your build.",
STOP: "Stop your build.",
DEMAND: "Requires a build id.",
DESC: "Path to BrowserStack config",
CONFIG_DEMAND: "config file is required",
INFO_MESSAGE: "Getting information for buildId ",
STOP_MESSAGE: "Stopping build with given buildId "
},
RUN: {
PARALLEL_DESC: "The maximum number of parallels to use to run your test suite",
INFO: "Run your tests on BrowserStack.",
DESC: "Path to BrowserStack config",
CYPRESS_DESC: "Path to Cypress config file",
CONFIG_DEMAND: "config file is required",
CYPRESS_CONFIG_DEMAND: "Cypress config file is required",
BUILD_NAME: "The build name you want to use to name your test runs",
EXCLUDE: "Exclude files matching a pattern from zipping and uploading",
DEFAULT_PARALLEL_MESSAGE: "Here goes the number of parallels you want to run",
SPECS_DESCRIPTION: 'Specify the spec files to run',
ENV_DESCRIPTION: "Specify the environment variables for your spec files"
},
COMMON: {
DISABLE_USAGE_REPORTING: "Disable usage reporting",
USERNAME: "Your BrowserStack username",
ACCESS_KEY: "Your BrowserStack access key",
NO_NPM_WARNING: "No NPM warning if npm_dependencies is empty"
}
}
VERSION: {
INFO: "shows version information",
HELP: "Specify --help for available options",
DEMAND: "Requires init, run or poll argument",
},
INIT: {
INFO: "create a browserstack.json file in the folder specified with the default configuration options.",
DESC: "Init in a specified folder",
},
BUILD: {
INFO: "Check status of your build.",
STOP: "Stop your build.",
DEMAND: "Requires a build id.",
DESC: "Path to BrowserStack config",
CONFIG_DEMAND: "config file is required",
INFO_MESSAGE: "Getting information for buildId ",
STOP_MESSAGE: "Stopping build with given buildId ",
},
RUN: {
PARALLEL_DESC: "The maximum number of parallels to use to run your test suite",
INFO: "Run your tests on BrowserStack.",
DESC: "Path to BrowserStack config",
CYPRESS_DESC: "Path to Cypress config file",
CONFIG_DEMAND: "config file is required",
CYPRESS_CONFIG_DEMAND: "Cypress config file is required",
BUILD_NAME: "The build name you want to use to name your test runs",
EXCLUDE: "Exclude files matching a pattern from zipping and uploading",
DEFAULT_PARALLEL_MESSAGE: "Here goes the number of parallels you want to run",
SPECS_DESCRIPTION: "Specify the spec files to run",
ENV_DESCRIPTION: "Specify the environment variables for your spec files",
SYNC_DESCRIPTION: "Makes the run command in sync",
BUILD_REPORT_MESSAGE: "See the entire build report here",
},
COMMON: {
DISABLE_USAGE_REPORTING: "Disable usage reporting",
USERNAME: "Your BrowserStack username",
ACCESS_KEY: "Your BrowserStack access key",
NO_NPM_WARNING: "No NPM warning if npm_dependencies is empty",
},
GENERATE_REPORT: {
INFO: "Generates the build report"
},
};

@@ -91,7 +118,12 @@ const messageTypes = {

const allowedFileTypes = ['js', 'json', 'txt', 'ts', 'feature', 'features', 'pdf', 'jpg', 'jpeg', 'png', 'zip', 'npmrc'];
const allowedFileTypes = ['js', 'json', 'txt', 'ts', 'feature', 'features', 'pdf', 'jpg', 'jpeg', 'png', 'zip', 'npmrc', 'xml', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'jsx', 'coffee', 'cjsx'];
const filesToIgnoreWhileUploading = ['**/node_modules/**', 'node_modules/**', 'package-lock.json', 'package.json', 'browserstack-package.json', 'tests.zip', 'cypress.json']
const specFileTypes = ['js', 'ts', 'feature']
const DEFAULT_CYPRESS_SPEC_PATH = "cypress/integration"
module.exports = Object.freeze({
syncCLI,
userMessages,

@@ -102,3 +134,5 @@ cliMessages,

allowedFileTypes,
filesToIgnoreWhileUploading
filesToIgnoreWhileUploading,
specFileTypes,
DEFAULT_CYPRESS_SPEC_PATH
});

@@ -5,13 +5,13 @@ 'use strict';

const logger = require("./logger").winstonLogger,
Constants = require("../helpers/constants"),
config = require("../helpers/config");
const logger = require('./logger').winstonLogger,
Constants = require('../helpers/constants'),
config = require('../helpers/config');
exports.write = function(f, message, args, cb) {
exports.write = function (f, message, args, cb) {
message = message || 'Creating';
fs.writeFile(f.path, f.file, function() {
logger.info(message + " file: " + f.path);
cb && cb(args)
fs.writeFile(f.path, f.file, function () {
logger.info(message + ' file: ' + f.path);
cb && cb(args);
});
}
};

@@ -29,12 +29,11 @@ exports.fileExists = function (filePath, cb) {

exports.deleteZip = () => {
return fs.unlink(config.fileName, function (err) {
if (err) {
logger.info(Constants.userMessages.ZIP_DELETE_FAILED);
return 1;
} else {
logger.info(Constants.userMessages.ZIP_DELETED);
return 0;
}
});
}
try {
fs.unlinkSync(config.fileName);
logger.info(Constants.userMessages.ZIP_DELETED);
return 0;
} catch (err) {
logger.info(Constants.userMessages.ZIP_DELETE_FAILED);
return 1;
}
};

@@ -47,2 +46,2 @@ exports.dirExists = function (filePath, cb) {

cb && cb(exists);
}
};

@@ -24,2 +24,12 @@ const winston = require('winston'),

const winstonSyncCliLoggerParams = {
transports: [
new (winston.transports.Console)({
formatter: (options) => {
return (options.message ? options.message : '');
}
}),
]
}
const winstonFileLoggerParams = {

@@ -35,1 +45,2 @@ transports: [

exports.fileLogger = new winston.Logger(winstonFileLoggerParams);
exports.syncCliLogger = new winston.Logger(winstonSyncCliLoggerParams);

@@ -5,6 +5,10 @@ "use strict";

const fs = require("fs");
const glob = require('glob');
const usageReporting = require("./usageReporting"),
logger = require("./logger").winstonLogger,
Constants = require("./constants");
Constants = require("./constants"),
chalk = require('chalk'),
syncCliLogger = require("../helpers/logger").syncCliLogger,
config = require("../helpers/config");

@@ -113,6 +117,18 @@ exports.validateBstackJson = (bsConfigPath) => {

exports.setParallels = (bsConfig, args) => {
exports.setParallels = (bsConfig, args, numOfSpecs) => {
if (!this.isUndefined(args.parallels)) {
bsConfig["run_settings"]["parallels"] = args.parallels;
}
let browserCombinations = this.getBrowserCombinations(bsConfig);
let maxParallels = browserCombinations.length * numOfSpecs;
if (numOfSpecs <= 0) {
bsConfig['run_settings']['parallels'] = browserCombinations.length;
return;
}
if (bsConfig['run_settings']['parallels'] > maxParallels && bsConfig['run_settings']['parallels'] != -1 ) {
logger.warn(
`Using ${maxParallels} machines instead of ${bsConfig['run_settings']['parallels']} that you configured as there are ${numOfSpecs} specs to be run on ${browserCombinations.length} browser combinations.`
);
bsConfig['run_settings']['parallels'] = maxParallels;
}
};

@@ -321,1 +337,49 @@

};
exports.getNumberOfSpecFiles = (bsConfig, args, cypressJson) => {
let testFolderPath = cypressJson.integrationFolder || Constants.DEFAULT_CYPRESS_SPEC_PATH;
let globSearchPatttern = bsConfig.run_settings.specs || `${testFolderPath}/**/*.+(${Constants.specFileTypes.join("|")})`;
let ignoreFiles = args.exclude || bsConfig.run_settings.exclude;
let files = glob.sync(globSearchPatttern, {cwd: bsConfig.run_settings.cypressProjectDir, matchBase: true, ignore: ignoreFiles});
return files;
};
exports.getBrowserCombinations = (bsConfig) => {
let osBrowserArray = [];
let osBrowser = "";
if (bsConfig.browsers) {
bsConfig.browsers.forEach((element) => {
osBrowser = element.os + '-' + element.browser;
element.versions.forEach((version) => {
osBrowserArray.push(osBrowser + version);
});
});
}
return osBrowserArray;
};
exports.capitalizeFirstLetter = (stringToCapitalize) => {
return stringToCapitalize && (stringToCapitalize[0].toUpperCase() + stringToCapitalize.slice(1));
};
exports.handleSyncExit = (exitCode, dashboard_url) => {
if (exitCode === config.networkErrorExitCode) {
syncCliLogger.info(this.getNetworkErrorMessage(dashboard_url));
} else {
syncCliLogger.info(Constants.userMessages.BUILD_REPORT_MESSAGE);
syncCliLogger.info(dashboard_url);
}
process.exit(exitCode);
}
exports.getNetworkErrorMessage = (dashboard_url) => {
let message = Constants.userMessages.FATAL_NETWORK_ERROR + '\n'
+ Constants.userMessages.RETRY_LIMIT_EXCEEDED + '\n'
+ Constants.userMessages.CHECK_DASHBOARD_AT + dashboard_url
return chalk.red(message)
}
exports.versionChangedMessage = (preferredVersion, actualVersion) => {
let message = Constants.userMessages.CYPRESS_VERSION_CHANGED.replace("<preferredVersion>", preferredVersion);
message = message.replace("<actualVersion>", actualVersion);
return message
}

@@ -7,6 +7,8 @@ 'use strict';

Constants = require("./constants"),
utils = require("./utils");
utils = require("./utils"),
fileHelpers = require("./fileHelpers");
const uploadCypressZip = (bsConfig, filePath) => {
return new Promise(function (resolve, reject) {
logger.info(Constants.userMessages.UPLOADING_TESTS);
let options = {

@@ -45,3 +47,4 @@ url: config.uploadUrl,

} else {
logger.info(`Zip uploaded with url: ${responseData.zip_url}`);
logger.info(`Uploaded tests successfully (${responseData.zip_url})`);
fileHelpers.deleteZip();
resolve(responseData);

@@ -48,0 +51,0 @@ }

@@ -198,2 +198,7 @@ #!/usr/bin/env node

type: "boolean"
},
'sync': {
default: false,
describe: Constants.cliMessages.RUN.SYNC_DESCRIPTION,
type: "boolean"
}

@@ -208,4 +213,44 @@ })

})
.command('generate-report', Constants.cliMessages.GENERATE_REPORT.INFO, function(yargs) {
argv = yargs
.usage('usage: $0 generate-report <buildId>')
.demand(1, Constants.cliMessages.BUILD.DEMAND)
.options({
'cf': {
alias: 'config-file',
describe: Constants.cliMessages.BUILD.DESC,
default: 'browserstack.json',
type: 'string',
nargs: 1,
demand: true,
demand: Constants.cliMessages.BUILD.CONFIG_DEMAND
},
'disable-usage-reporting': {
default: undefined,
description: Constants.cliMessages.COMMON.DISABLE_USAGE_REPORTING,
type: "boolean"
},
'u': {
alias: 'username',
describe: Constants.cliMessages.COMMON.USERNAME,
type: "string",
default: undefined
},
'k': {
alias: 'key',
describe: Constants.cliMessages.COMMON.ACCESS_KEY,
type: "string",
default: undefined
},
})
.help('help')
.wrap(null)
.argv
if (checkCommands(yargs, argv, 1)) {
logger.info(Constants.cliMessages.BUILD.INFO_MESSAGE + argv._[1]);
return require('./commands/generateReport')(argv);
}
})
.help('help')
.wrap(null)
.argv
{
"name": "browserstack-cypress-cli",
"version": "1.5.2",
"version": "1.6.0",
"description": "BrowserStack Cypress CLI for Cypress integration with BrowserStack's remote devices.",

@@ -16,2 +16,3 @@ "main": "index.js",

"archiver": "^3.1.1",
"chalk": "^4.1.0",
"fs-extra": "^8.1.0",

@@ -21,4 +22,6 @@ "mkdirp": "^1.0.3",

"requestretry": "^4.1.0",
"table": "^5.4.6",
"winston": "^2.3.1",
"yargs": "^14.2.2"
"yargs": "^14.2.2",
"glob": "^7.1.6"
},

@@ -25,0 +28,0 @@ "repository": {

@@ -191,2 +191,3 @@ const chai = require("chai"),

deleteResultsStub = sandbox.stub();
getNumberOfSpecFilesStub = sandbox.stub().returns([]);
setDefaultsStub = sandbox.stub();

@@ -221,3 +222,4 @@ });

deleteResults: deleteResultsStub,
setDefaults: setDefaultsStub
setDefaults: setDefaultsStub,
getNumberOfSpecFiles: getNumberOfSpecFilesStub
},

@@ -246,2 +248,3 @@ '../helpers/capabilityHelper': {

sinon.assert.calledOnce(getConfigPathStub);
sinon.assert.calledOnce(getNumberOfSpecFilesStub);
sinon.assert.calledOnce(setParallelsStub);

@@ -294,2 +297,3 @@ sinon.assert.calledOnce(setLocalStub);

deleteResultsStub = sandbox.stub();
getNumberOfSpecFilesStub = sandbox.stub().returns([]);
setDefaultsStub = sandbox.stub();

@@ -324,2 +328,3 @@ });

deleteResults: deleteResultsStub,
getNumberOfSpecFiles: getNumberOfSpecFilesStub,
setDefaults: setDefaultsStub

@@ -353,2 +358,3 @@ },

sinon.assert.calledOnce(getConfigPathStub);
sinon.assert.calledOnce(getNumberOfSpecFilesStub);
sinon.assert.calledOnce(setParallelsStub);

@@ -405,2 +411,3 @@ sinon.assert.calledOnce(setLocalStub);

deleteResultsStub = sandbox.stub();
getNumberOfSpecFilesStub = sandbox.stub().returns([]);
setDefaultsStub = sandbox.stub();

@@ -435,2 +442,3 @@ });

deleteResults: deleteResultsStub,
getNumberOfSpecFiles: getNumberOfSpecFilesStub,
setDefaults: setDefaultsStub

@@ -472,2 +480,3 @@ },

sinon.assert.calledOnce(capabilityValidatorStub);
sinon.assert.calledOnce(getNumberOfSpecFilesStub);
sinon.assert.calledOnce(setParallelsStub);

@@ -493,5 +502,2 @@ sinon.assert.calledOnce(setLocalStub);

);
})
.finally(function () {
sinon.assert.calledOnce(deleteZipStub);
});

@@ -532,2 +538,3 @@ });

setLocalIdentifierStub = sandbox.stub();
getNumberOfSpecFilesStub = sandbox.stub().returns([]);
});

@@ -544,6 +551,6 @@

let message = `Success! ${Constants.userMessages.BUILD_CREATED} with build id: random_build_id`;
let dashboardLink = `${Constants.userMessages.VISIT_DASHBOARD} ${dashboardUrl}random_build_id`;
let dashboardLink = `${Constants.userMessages.VISIT_DASHBOARD} ${dashboardUrl}`;
const runs = proxyquire("../../../../bin/commands/runs", {
"../helpers/utils": {
const runs = proxyquire('../../../../bin/commands/runs', {
'../helpers/utils': {
validateBstackJson: validateBstackJsonStub,

@@ -565,20 +572,21 @@ sendUsageReport: sendUsageReportStub,

setDefaults: setDefaultsStub,
isUndefined: isUndefinedStub
isUndefined: isUndefinedStub,
getNumberOfSpecFiles: getNumberOfSpecFilesStub
},
"../helpers/capabilityHelper": {
'../helpers/capabilityHelper': {
validate: capabilityValidatorStub,
},
"../helpers/archiver": {
'../helpers/archiver': {
archive: archiverStub,
},
"../helpers/fileHelpers": {
'../helpers/fileHelpers': {
deleteZip: deleteZipStub,
},
"../helpers/zipUpload": {
'../helpers/zipUpload': {
zipUpload: zipUploadStub,
},
"../helpers/build": {
'../helpers/build': {
createBuild: createBuildStub,
},
"../helpers/config": {
'../helpers/config': {
dashboardUrl: dashboardUrl,

@@ -594,3 +602,3 @@ },

zipUploadStub.returns(Promise.resolve("zip uploaded"));
createBuildStub.returns(Promise.resolve({ message: 'Success', build_id: 'random_build_id' }));
createBuildStub.returns(Promise.resolve({ message: 'Success', build_id: 'random_build_id', dashboard_url: dashboardUrl }));

@@ -606,2 +614,3 @@ return runs(args)

sinon.assert.calledOnce(capabilityValidatorStub);
sinon.assert.calledOnce(getNumberOfSpecFilesStub);
sinon.assert.calledOnce(setParallelsStub);

@@ -625,5 +634,2 @@ sinon.assert.calledOnce(setLocalStub);

);
})
.finally(function () {
sinon.assert.calledOnce(deleteZipStub);
});

@@ -630,0 +636,0 @@ });

@@ -47,2 +47,34 @@ const chai = require("chai"),

it("handle cypress version passed", () => {
let zip_url = "bs://<random>";
let cypress_version = "version"
let bsConfig = {
auth: {
username: "random",
access_key: "random",
},
browsers: [
{
browser: "chrome",
os: "Windows 10",
versions: ["78", "77"],
},
],
run_settings: {
cypress_version: cypress_version
},
connection_settings: {
local: true
}
};
return capabilityHelper
.caps(bsConfig, { zip_url: zip_url })
.then(function (data) {
chai.assert.equal(JSON.parse(data).cypress_version, cypress_version);
})
.catch((error) => {
chai.assert.fail("Promise error");
});
});
it("handle empty test_suite", () => {

@@ -87,2 +119,5 @@ let zip_url = undefined;

],
run_settings: {
cypress_version: "cypress_version"
},
connection_settings: {

@@ -566,6 +601,3 @@ local: true

.then(function (data) {
chai.assert.equal(data, Constants.validationMessages.VALIDATED);
})
.catch((error) => {
chai.assert.fail("Promise error");
chai.assert.deepEqual(data, {});
});

@@ -580,6 +612,3 @@ });

.then(function (data) {
chai.assert.equal(data, Constants.validationMessages.VALIDATED);
})
.catch((error) => {
chai.assert.fail("Promise error");
chai.assert.deepEqual(data, {});
});

@@ -593,6 +622,3 @@ });

.then(function (data) {
chai.assert.equal(data, Constants.validationMessages.VALIDATED);
})
.catch((error) => {
chai.assert.fail("Promise error");
chai.assert.deepEqual(data, {});
});

@@ -606,6 +632,3 @@ });

.then(function (data) {
chai.assert.equal(data, Constants.validationMessages.VALIDATED);
})
.catch((error) => {
chai.assert.fail("Promise error");
chai.assert.deepEqual(data, {});
});

@@ -620,6 +643,3 @@ });

.then(function (data) {
chai.assert.equal(data, Constants.validationMessages.VALIDATED);
})
.catch((error) => {
chai.assert.fail("Promise error");
chai.assert.deepEqual(data, {});
});

@@ -634,6 +654,3 @@ });

.then(function (data) {
chai.assert.equal(data, Constants.validationMessages.VALIDATED);
})
.catch((error) => {
chai.assert.fail("Promise error");
chai.assert.deepEqual(data, {});
});

@@ -640,0 +657,0 @@ });

@@ -1,6 +0,8 @@

const chai = require("chai"),
sinon = require("sinon"),
const chai = require('chai'),
sinon = require('sinon'),
expect = chai.expect,
assert = chai.assert,
chaiAsPromised = require("chai-as-promised");
chaiAsPromised = require('chai-as-promised'),
fs = require('fs-extra'),
fileHelpers = require('../../../../bin/helpers/fileHelpers');

@@ -85,28 +87,17 @@ const logger = require("../../../../bin/helpers/logger").winstonLogger,

it("deleteZip returns 0 on success", () => {
let unlinkStub = sandbox.stub().yields();
let unlinkStub = sinon.stub(fs, 'unlinkSync').returns(true);
const fileHelpers = proxyquire("../../../../bin/helpers/fileHelpers", {
"fs-extra": {
unlink: unlinkStub,
},
});
let result = fileHelpers.deleteZip();
sinon.assert.calledOnce(unlinkStub);
assert.equal(result, 0);
fs.unlinkSync.restore();
});
it("deleteZip returns 1 on failure", () => {
let unlinkStub = sandbox.stub().yields(new Error("random-error"));
const fileHelpers = proxyquire("../../../../bin/helpers/fileHelpers", {
"fs-extra": {
unlink: unlinkStub,
},
});
let unlinkStub = sinon.stub(fs, 'unlinkSync').yields(new Error("random-error"));
let result = fileHelpers.deleteZip();
sinon.assert.calledOnce(unlinkStub);
assert.equal(result, 1);
fs.unlinkSync.restore();
});
});

@@ -8,2 +8,4 @@ 'use strict';

chaiAsPromised = require('chai-as-promised'),
glob = require('glob'),
chalk = require('chalk'),
fs = require('fs');

@@ -14,3 +16,4 @@

logger = require('../../../../bin/helpers/logger').winstonLogger,
testObjects = require('../../support/fixtures/testObjects');
testObjects = require('../../support/fixtures/testObjects'),
syncLogger = require("../../../../bin/helpers/logger").syncCliLogger;

@@ -142,2 +145,13 @@ chai.use(chaiAsPromised);

describe('setParallels', () => {
var sandbox;
beforeEach(() => {
sandbox = sinon.createSandbox();
sandbox.stub(utils,'getBrowserCombinations').returns(['a','b']);
});
afterEach(() => {
sandbox.restore();
sinon.restore();
});
it('should set bsconfig parallels equal to value provided in args', () => {

@@ -149,3 +163,4 @@ let bsConfig = {

};
utils.setParallels(bsConfig, {parallels: 100});
utils.setParallels(bsConfig, {parallels: 100}, 100);
expect(bsConfig['run_settings']['parallels']).to.be.eq(100);

@@ -160,5 +175,36 @@ });

};
utils.setParallels(bsConfig, {parallels: undefined});
utils.setParallels(bsConfig, {parallels: undefined}, 10);
expect(bsConfig['run_settings']['parallels']).to.be.eq(10);
});
it('should set bsconfig parallels to browserCombinations.length if numOfSpecs is zero', () => {
let bsConfig = {
run_settings: {
parallels: 10,
},
};
utils.setParallels(bsConfig, {parallels: undefined}, 0);
expect(bsConfig['run_settings']['parallels']).to.be.eq(2);
});
it('shouldnot set bsconfig parallels if parallels is -1', () => {
let bsConfig = {
run_settings: {
parallels: -1,
},
};
utils.setParallels(bsConfig, {parallels: undefined}, 2);
expect(bsConfig['run_settings']['parallels']).to.be.eq(-1);
});
it('should set bsconfig parallels if parallels is greater than numOfSpecs * combinations', () => {
let bsConfig = {
run_settings: {
parallels: 100,
},
};
utils.setParallels(bsConfig, {parallels: undefined}, 2);
expect(bsConfig['run_settings']['parallels']).to.be.eq(4);
});
});

@@ -189,33 +235,18 @@

describe('validateBstackJson', () => {
it('should reject with SyntaxError for empty file', () => {
let bsConfigPath = path.join(
process.cwd(),
'test',
'test_files',
'dummy_bstack.json'
);
expect(utils.validateBstackJson(bsConfigPath)).to.be.rejectedWith(
SyntaxError
);
describe("validateBstackJson", () => {
it("should reject with SyntaxError for empty file", () => {
let bsConfigPath = path.join(process.cwd(), 'test', 'test_files', 'dummy_bstack.json');
return utils.validateBstackJson(bsConfigPath).catch((error)=>{
sinon.match(error, "Invalid browserstack.json file")
});
});
it('should resolve with data for valid json', () => {
let bsConfigPath = path.join(
process.cwd(),
'test',
'test_files',
'dummy_bstack_2.json'
);
it("should resolve with data for valid json", () => {
let bsConfigPath = path.join(process.cwd(), 'test', 'test_files', 'dummy_bstack_2.json');
expect(utils.validateBstackJson(bsConfigPath)).to.be.eventually.eql({});
});
it('should reject with SyntaxError for invalid json file', () => {
let bsConfigPath = path.join(
process.cwd(),
'test',
'test_files',
'dummy_bstack_3.json'
);
expect(utils.validateBstackJson(bsConfigPath)).to.be.rejectedWith(
SyntaxError
);
it("should reject with SyntaxError for invalid json file", () => {
let bsConfigPath = path.join(process.cwd(), 'test', 'test_files', 'dummy_bstack_3.json');
return utils.validateBstackJson(bsConfigPath).catch((error) => {
sinon.match(error, "Invalid browserstack.json file")
});
});

@@ -308,3 +339,3 @@ });

it('should call sendUsageReport', () => {
sandbox = sinon.createSandbox();
let sandbox = sinon.createSandbox();
sendUsageReportStub = sandbox

@@ -964,2 +995,163 @@ .stub(utils, 'sendUsageReport')

});
describe('getNumberOfSpecFiles', () => {
it('glob search pattern should be equal to bsConfig.run_settings.specs', () => {
let getNumberOfSpecFilesStub = sinon.stub(glob, 'sync');
let bsConfig = {
run_settings: {
specs: 'specs',
cypressProjectDir: 'cypressProjectDir',
exclude: 'exclude',
},
};
utils.getNumberOfSpecFiles(bsConfig,{},{});
sinon.assert.calledOnce(getNumberOfSpecFilesStub);
sinon.assert.calledOnceWithExactly(getNumberOfSpecFilesStub, 'specs', {
cwd: 'cypressProjectDir',
matchBase: true,
ignore: 'exclude',
});
glob.sync.restore();
});
it('glob search pattern should be equal to default', () => {
let getNumberOfSpecFilesStub = sinon.stub(glob, 'sync');
let bsConfig = {
run_settings: {
cypressProjectDir: 'cypressProjectDir',
exclude: 'exclude',
},
};
utils.getNumberOfSpecFiles(bsConfig,{},{});
sinon.assert.calledOnceWithExactly(getNumberOfSpecFilesStub, `cypress/integration/**/*.+(${constant.specFileTypes.join("|")})`, {
cwd: 'cypressProjectDir',
matchBase: true,
ignore: 'exclude',
});
glob.sync.restore();
});
it('glob search pattern should be equal to default with integrationFolder', () => {
let getNumberOfSpecFilesStub = sinon.stub(glob, 'sync');
let bsConfig = {
run_settings: {
cypressProjectDir: 'cypressProjectDir',
exclude: 'exclude',
},
};
utils.getNumberOfSpecFiles(bsConfig, {}, { "integrationFolder": "specs"});
sinon.assert.calledOnceWithExactly(
getNumberOfSpecFilesStub,
`specs/**/*.+(${constant.specFileTypes.join('|')})`,
{
cwd: 'cypressProjectDir',
matchBase: true,
ignore: 'exclude',
}
);
glob.sync.restore();
});
});
describe('capitalizeFirstLetter', () => {
it('should capitalize First Letter ', () => {
expect(utils.capitalizeFirstLetter("chrome")).to.eq("Chrome");
});
it('should return null if value passed is null', () => {
expect(utils.capitalizeFirstLetter(null)).to.eq(null);
});
});
describe('getBrowserCombinations', () => {
it('returns correct number of browserCombinations for one combination', () => {
let bsConfig = {
browsers: [
{
browser: 'chrome',
os: 'OS X Mojave',
versions: ['85'],
},
]
};
chai.assert.deepEqual(utils.getBrowserCombinations(bsConfig), ['OS X Mojave-chrome85']);
});
it('returns correct number of browserCombinations for multiple combinations', () => {
let bsConfig = {
browsers: [
{
browser: 'chrome',
os: 'OS X Mojave',
versions: ['85'],
},
{
browser: 'chrome',
os: 'OS X Catalina',
versions: ['85','84'],
},
],
};
chai.assert.deepEqual(utils.getBrowserCombinations(bsConfig), [
'OS X Mojave-chrome85',
'OS X Catalina-chrome85',
'OS X Catalina-chrome84'
]);
});
});
describe('#handleSyncExit', () => {
let processStub;
beforeEach(function () {
processStub = sinon.stub(process, 'exit');
});
afterEach(function () {
processStub.restore();
});
it('should print network error message when exit code is set to network error code', () => {
let dashboard_url = "dashboard_url", exitCode = 2;
let getNetworkErrorMessageStub = sinon.stub(utils, 'getNetworkErrorMessage');
utils.handleSyncExit(exitCode, dashboard_url);
sinon.assert.calledOnce(getNetworkErrorMessageStub);
sinon.assert.calledOnceWithExactly(processStub, exitCode);
getNetworkErrorMessageStub.restore();
});
it('should print dashboard link when exit code is not network error code', () => {
let dashboard_url = "dashboard_url", exitCode = 1;
let syncCliLoggerStub = sinon.stub(syncLogger, 'info');
utils.handleSyncExit(exitCode, dashboard_url);
sinon.assert.calledTwice(syncCliLoggerStub);
sinon.assert.calledOnceWithExactly(processStub, exitCode);
});
});
describe('#getNetworkErrorMessage', () => {
it('should return the error message in red color', () => {
let dashboard_url = "dashboard_url";
let message = constant.userMessages.FATAL_NETWORK_ERROR + '\n'
+ constant.userMessages.RETRY_LIMIT_EXCEEDED + '\n'
+ constant.userMessages.CHECK_DASHBOARD_AT + dashboard_url
expect(utils.getNetworkErrorMessage(dashboard_url)).to.eq(chalk.red(message))
});
});
describe('#versionChangedMessage', () => {
it('should return proper error message with placeholders replaced', () => {
let preferredVersion = "v1", actualVersion = "v2";
let message = constant.userMessages.CYPRESS_VERSION_CHANGED.replace("<preferredVersion>", preferredVersion).replace("<actualVersion>", actualVersion);
expect(utils.versionChangedMessage(preferredVersion, actualVersion)).to.eq(message)
});
})
});

@@ -25,2 +25,3 @@ const chai = require("chai"),

createReadStreamStub = sandbox.stub(fs, "createReadStream");
deleteZipStub = sandbox.stub().returns(true);
});

@@ -123,7 +124,10 @@

const zipUploader = proxyquire("../../../../bin/helpers/zipUpload", {
"./utils": {
const zipUploader = proxyquire('../../../../bin/helpers/zipUpload', {
'./utils': {
getUserAgent: getUserAgentStub,
},
request: { post: requestStub },
request: {post: requestStub},
'./fileHelpers': {
deleteZip: deleteZipStub,
},
});

@@ -137,2 +141,3 @@

sinon.assert.calledOnce(createReadStreamStub);
sinon.assert.calledOnce(deleteZipStub);
chai.assert.equal(data.zip_url, zip_url);

@@ -139,0 +144,0 @@ })

@@ -13,2 +13,15 @@ const sampleBsConfig = {

const sampleBsConfigWithParallels = {
auth: {
username: "random-username",
access_key: "random-access-key",
},
run_settings: {
cypress_proj_dir: "random path",
cypressConfigFilePath: "random path",
cypressProjectDir: "random path",
parallels: 10,
},
};
const initSampleArgs = {

@@ -33,2 +46,12 @@ _: ["init"],

const generateReportInputArgs = {
_: ["generate-report", "f3c94f7203792d03a75be3912d19354fe0961e53"],
cf: "browserstack.json",
"config-file": "browserstack.json",
configFile: "browserstack.json",
"disable-usage-reporting": undefined,
disableUsageReporting: undefined,
$0: "browserstack-cypress",
};
const buildInfoSampleBody = {

@@ -119,2 +142,3 @@ build_id: "random_hashed_id",

sampleBsConfig,
sampleBsConfigWithParallels,
initSampleArgs,

@@ -127,2 +151,3 @@ buildInfoSampleArgs,

runSampleArgs,
generateReportInputArgs,
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc