Skip to content

Afd 1923 fix spec pattern and lls #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
91f80b6
add code to set correct default spec folder
pranavj1001 Oct 14, 2022
613812b
add specs
pranavj1001 Nov 17, 2022
2959b3e
update specs
pranavj1001 Nov 18, 2022
8d6fedb
Merge branch 'master' into AFD-1355-cypress-10-default-spec-folder
prajwaldhawarikar-bs Feb 27, 2023
b089471
Merge branch 'master' into AFD-1355-cypress-10-default-spec-folder
prajwaldhawarikar-bs Mar 3, 2023
e8b4b68
js config implementation
prajwaldhawarikar-bs Mar 3, 2023
1099cbe
SOme UT fix
prajwaldhawarikar-bs Mar 6, 2023
89cc1eb
TS support and fixes
prajwaldhawarikar-bs Mar 7, 2023
bcf86c8
Add UTs
prajwaldhawarikar-bs Mar 7, 2023
844c9a9
Add UTs
prajwaldhawarikar-bs Mar 8, 2023
d9e8c02
Add instrumentation if cypress config read fails
prajwaldhawarikar-bs Mar 8, 2023
0263f33
remove console logs
prajwaldhawarikar-bs Mar 8, 2023
da2c8c1
Add warn message
prajwaldhawarikar-bs Mar 13, 2023
8102831
Added UT
prajwaldhawarikar-bs Mar 14, 2023
5e5cdd2
Add warn in constants
prajwaldhawarikar-bs Mar 14, 2023
57e01b6
Add fallback logic
prajwaldhawarikar-bs Mar 15, 2023
8d17b51
block for get number of specs
prajwaldhawarikar-bs Mar 16, 2023
70e4525
Merge branch 'master' into AFD-1923-fix-spec-pattern-and-lls
prajwaldhawarikar-bs Mar 17, 2023
892efc5
Merge branch 'AFD-1923-fix-spec-pattern-and-lls' into AFD-1921-use-de…
prajwaldhawarikar-bs Mar 17, 2023
272054f
Merge branch 'AFD-1923-fix-spec-pattern-and-lls' into AFD-1922-defaul…
prajwaldhawarikar-bs Mar 17, 2023
649bf75
fix AFD-2103
prajwaldhawarikar-bs Mar 22, 2023
fd65a7a
Merge branch 'AFD-1923-fix-spec-pattern-and-lls' into AFD-1922-defaul…
prajwaldhawarikar-bs Mar 22, 2023
0f439d4
Merge branch 'AFD-1923-fix-spec-pattern-and-lls' into AFD-1921-use-de…
prajwaldhawarikar-bs Mar 22, 2023
eec0ab1
Merge pull request #539 from browserstack/AFD-1922-default-folder-v10…
agrawalsaurabhs Mar 29, 2023
cc68672
Merge pull request #538 from browserstack/AFD-1921-use-default-port-c…
agrawalsaurabhs Mar 29, 2023
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
11 changes: 8 additions & 3 deletions bin/commands/runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ module.exports = function run(args, rawArgs) {
// set the no-wrap
utils.setNoWrap(bsConfig, args);

// set record feature caps
utils.setRecordCaps(bsConfig, args);
const { packagesInstalled } = await packageInstaller.packageSetupAndInstaller(bsConfig, config.packageDirName, {markBlockStart, markBlockEnd});

// set build tag caps
utils.setBuildTags(bsConfig, args);
Expand Down Expand Up @@ -129,8 +128,11 @@ module.exports = function run(args, rawArgs) {
logger.debug("Completed configs validation");
markBlockStart('preArchiveSteps');
logger.debug("Started pre-archive steps");

//get the number of spec files
markBlockStart('getNumberOfSpecFiles');
let specFiles = utils.getNumberOfSpecFiles(bsConfig, args, cypressConfigFile);
markBlockEnd('getNumberOfSpecFiles');

bsConfig['run_settings']['video_config'] = utils.getVideoConfig(cypressConfigFile);

Expand All @@ -140,6 +142,9 @@ module.exports = function run(args, rawArgs) {
// accept the number of parallels
utils.setParallels(bsConfig, args, specFiles.length);

// set record feature caps
utils.setRecordCaps(bsConfig, args, cypressConfigFile);

// warn if specFiles cross our limit
utils.warnSpecLimit(bsConfig, args, specFiles, rawArgs, buildReportData);
markBlockEnd('preArchiveSteps');
Expand All @@ -153,7 +158,7 @@ module.exports = function run(args, rawArgs) {

logger.debug("Started caching npm dependencies.");
markBlockStart('zip.packageInstaller');
return packageInstaller.packageWrapper(bsConfig, config.packageDirName, config.packageFileName, md5data, {markBlockStart, markBlockEnd}).then(function (packageData) {
return packageInstaller.packageWrapper(bsConfig, config.packageDirName, config.packageFileName, md5data, {markBlockStart, markBlockEnd}, packagesInstalled).then(function (packageData) {
logger.debug("Completed caching npm dependencies.")
markBlockEnd('zip.packageInstaller');

Expand Down
48 changes: 24 additions & 24 deletions bin/helpers/capabilityHelper.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const fs = require('fs'),
path = require('path');
const { readCypressConfigFile } = require('./readCypressConfigUtil');

const logger = require("./logger").winstonLogger,
Constants = require("./constants"),
Expand Down Expand Up @@ -194,34 +195,30 @@ const validate = (bsConfig, args) => {
logger.debug(`Checking for cypress config file at ${cypressConfigFilePath}`);
if (!fs.existsSync(cypressConfigFilePath) && bsConfig.run_settings.cypress_config_filename !== 'false') reject(Constants.validationMessages.INVALID_CYPRESS_CONFIG_FILE);

if (bsConfig.run_settings.cypressTestSuiteType === Constants.CYPRESS_V10_AND_ABOVE_TYPE) {
logger.debug(`Validating ${bsConfig.run_settings.cypress_config_filename}`);
// TODO: add validations for cypress_config_filename
} else {
logger.debug("Validating cypress.json");
try {
if (bsConfig.run_settings.cypress_config_filename !== 'false') {

if (bsConfig.run_settings.cypressTestSuiteType === Constants.CYPRESS_V10_AND_ABOVE_TYPE) {
if (cypressConfigFilePath.endsWith("cypress.config.js")) {
cypressConfigFile = require(cypressConfigFilePath);
} else {
cypressConfigFile = {};
}
} else {
let cypressJsonContent = fs.readFileSync(cypressConfigFilePath);
cypressConfigFile = JSON.parse(cypressJsonContent);
logger.debug(`Validating ${bsConfig.run_settings.cypress_config_filename}`);
try {
if (bsConfig.run_settings.cypress_config_filename !== 'false') {
if (bsConfig.run_settings.cypressTestSuiteType === Constants.CYPRESS_V10_AND_ABOVE_TYPE) {
const completeCypressConfigFile = readCypressConfigFile(bsConfig)
if (!Utils.isUndefined(completeCypressConfigFile)) {
// check if cypress config was exported using export default
cypressConfigFile = !Utils.isUndefined(completeCypressConfigFile.default) ? completeCypressConfigFile.default : completeCypressConfigFile
}

// Cypress Json Base Url & Local true check
if (!Utils.isUndefined(cypressConfigFile.baseUrl) && cypressConfigFile.baseUrl.includes("localhost") && !Utils.getLocalFlag(bsConfig.connection_settings)) reject(Constants.validationMessages.LOCAL_NOT_SET.replace("<baseUrlValue>", cypressConfigFile.baseUrl));

// Detect if the user is not using the right directory structure, and throw an error
if (!Utils.isUndefined(cypressConfigFile.integrationFolder) && !Utils.isCypressProjDirValid(bsConfig.run_settings.cypressProjectDir,cypressConfigFile.integrationFolder)) reject(Constants.validationMessages.INCORRECT_DIRECTORY_STRUCTURE);
// TODO: add validations for cypress_config_filename
} else {
let cypressJsonContent = fs.readFileSync(cypressConfigFilePath);
cypressConfigFile = JSON.parse(cypressJsonContent);
}
} catch(error){
reject(Constants.validationMessages.INVALID_CYPRESS_JSON)

// Cypress Json Base Url & Local true check
if (!Utils.isUndefined(cypressConfigFile.baseUrl) && cypressConfigFile.baseUrl.includes("localhost") && !Utils.getLocalFlag(bsConfig.connection_settings)) reject(Constants.validationMessages.LOCAL_NOT_SET.replace("<baseUrlValue>", cypressConfigFile.baseUrl));

// Detect if the user is not using the right directory structure, and throw an error
if (!Utils.isUndefined(cypressConfigFile.integrationFolder) && !Utils.isCypressProjDirValid(bsConfig.run_settings.cypressProjectDir,cypressConfigFile.integrationFolder)) reject(Constants.validationMessages.INCORRECT_DIRECTORY_STRUCTURE);
}
} catch(error){
reject(Constants.validationMessages.INVALID_CYPRESS_JSON)
}

//check if home_directory is present or not in user run_settings
Expand Down Expand Up @@ -289,6 +286,9 @@ const validate = (bsConfig, args) => {
if (!Utils.isUndefined(bsConfig.run_settings.nodeVersion) && typeof(bsConfig.run_settings.nodeVersion) === 'string' && !bsConfig.run_settings.nodeVersion.match(/^(\d+\.)?(\d+\.)?(\*|\d+)$/))
logger.warn(Constants.validationMessages.NODE_VERSION_PARSING_ERROR);

if(!Utils.isUndefined(cypressConfigFile.port)) {
logger.warn(Constants.userMessages.CYPRESS_PORT_WARNING.replace("<x>", cypressConfigFile.port));
}
resolve(cypressConfigFile);
});
}
Expand Down
2 changes: 2 additions & 0 deletions bin/helpers/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ config.packageFileName = "bstackPackages.tar.gz";
config.packageDirName = "tmpBstackPackages";
config.retries = 5;
config.networkErrorExitCode = 2;
config.compiledConfigJsDirName = 'tmpBstackCompiledJs'
config.configJsonFileName = 'tmpCypressConfig.json'

module.exports = config;
17 changes: 13 additions & 4 deletions bin/helpers/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ const userMessages = {
"There was some issue while checking if zip is already uploaded.",
ZIP_DELETE_FAILED: "Could not delete tests.zip successfully.",
ZIP_DELETED: "Deleted tests.zip successfully.",
NPM_INSTALL_AND_UPLOAD:
"Installing required dependencies and building the package to upload to BrowserStack",
NPM_INSTALL:
"Installing required dependencies",
NPM_UPLOAD:
"Building the package to upload to BrowserStack",
NPM_DELETE_FAILED: "Could not delete the dependency packages.",
NPM_DELETED: "Deleted dependency packages successfully.",
API_DEPRECATED:
Expand Down Expand Up @@ -111,7 +113,9 @@ const userMessages = {
SPEC_LIMIT_SUCCESS_MESSAGE:
"Spec timeout specified as <x> minutes. If any of your specs exceed the specified time limit, it would be forcibly killed by BrowserStack",
NO_CONNECTION_WHILE_UPDATING_UPLOAD_PROGRESS_BAR:
"Unable to determine zip upload progress due to undefined/null connection request"
"Unable to determine zip upload progress due to undefined/null connection request",
CYPRESS_PORT_WARNING:
"The requested port number <x> is ignored. The default BrowserStack port will be used for this execution"
};

const validationMessages = {
Expand Down Expand Up @@ -367,6 +371,7 @@ const packageInstallerOptions = {
const specFileTypes = ["js", "ts", "feature", "jsx", "coffee", "cjsx"];

const DEFAULT_CYPRESS_SPEC_PATH = "cypress/integration";
const DEFAULT_CYPRESS_10_SPEC_PATH = "cypress/e2e";

const SPEC_TOTAL_CHAR_LIMIT = 32243;
const METADATA_CHAR_BUFFER_PER_SPEC = 175;
Expand Down Expand Up @@ -419,6 +424,8 @@ const CYPRESS_CONFIG_FILE_MAPPING = {

const CYPRESS_CONFIG_FILE_NAMES = Object.keys(CYPRESS_CONFIG_FILE_MAPPING);

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

module.exports = Object.freeze({
syncCLI,
userMessages,
Expand All @@ -432,6 +439,7 @@ module.exports = Object.freeze({
packageInstallerOptions,
specFileTypes,
DEFAULT_CYPRESS_SPEC_PATH,
DEFAULT_CYPRESS_10_SPEC_PATH,
SPEC_TOTAL_CHAR_LIMIT,
METADATA_CHAR_BUFFER_PER_SPEC,
usageReportingConstants,
Expand All @@ -448,5 +456,6 @@ module.exports = Object.freeze({
CYPRESS_V9_AND_OLDER_TYPE,
CYPRESS_V10_AND_ABOVE_TYPE,
CYPRESS_CONFIG_FILE_MAPPING,
CYPRESS_CONFIG_FILE_NAMES
CYPRESS_CONFIG_FILE_NAMES,
CYPRESS_V10_AND_ABOVE_CONFIG_FILE_EXTENSIONS
});
44 changes: 33 additions & 11 deletions bin/helpers/packageInstaller.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,13 @@ const packageArchiver = (packageDir, packageFile) => {
})
}

const packageWrapper = (bsConfig, packageDir, packageFile, md5data, instrumentBlocks) => {
const packageSetupAndInstaller = (bsConfig, packageDir, instrumentBlocks) => {
return new Promise(function (resolve) {
let obj = {
packageArchieveCreated: false
packagesInstalled: false
};
if (md5data.packageUrlPresent || !utils.isTrueString(bsConfig.run_settings.cache_dependencies)) {
logger.debug("Skipping the caching of npm packages since BrowserStack has already cached your npm dependencies that have not changed since the last run.")
return resolve(obj);
}
logger.info(Constants.userMessages.NPM_INSTALL_AND_UPLOAD);

logger.info(Constants.userMessages.NPM_INSTALL);
instrumentBlocks.markBlockStart("packageInstaller.folderSetup");
logger.debug("Started setting up package folder");
return setupPackageFolder(bsConfig.run_settings, packageDir).then((_result) => {
Expand All @@ -150,10 +147,34 @@ const packageWrapper = (bsConfig, packageDir, packageFile, md5data, instrumentBl
}).then((_result) => {
logger.debug("Completed installing dependencies");
instrumentBlocks.markBlockEnd("packageInstaller.packageInstall");
instrumentBlocks.markBlockStart("packageInstaller.packageArchive");
logger.debug("Started archiving node_modules")
return packageArchiver(packageDir, packageFile);
}).then((_result) => {
Object.assign(obj, { packagesInstalled: true });
return resolve(obj);
}).catch((err) => {
logger.warn(`Error occured while installing npm dependencies. Dependencies will be installed in runtime. This will have a negative impact on performance. Reach out to browserstack.com/contact, if you persistantly face this issue.`);
obj.error = err.stack ? err.stack.toString().substring(0,100) : err.toString().substring(0,100);
return resolve(obj);
})
})
}

const packageWrapper = (bsConfig, packageDir, packageFile, md5data, instrumentBlocks, packagesInstalled) => {
return new Promise(function (resolve) {
let obj = {
packageArchieveCreated: false
};
if (!packagesInstalled) {
logger.debug("Skipping the caching of npm packages since package installed failed")
return resolve(obj);
}
if (md5data.packageUrlPresent || !utils.isTrueString(bsConfig.run_settings.cache_dependencies)) {
logger.debug("Skipping the caching of npm packages since BrowserStack has already cached your npm dependencies that have not changed since the last run.")
return resolve(obj);
}
logger.info(Constants.userMessages.NPM_UPLOAD);
instrumentBlocks.markBlockStart("packageInstaller.packageArchive");
logger.debug("Started archiving node_modules")
return packageArchiver(packageDir, packageFile)
.then((_result) => {
logger.debug("Archiving of node_modules completed");
instrumentBlocks.markBlockEnd("packageInstaller.packageArchive");
Object.assign(obj, { packageArchieveCreated: true });
Expand All @@ -167,3 +188,4 @@ const packageWrapper = (bsConfig, packageDir, packageFile, md5data, instrumentBl
}

exports.packageWrapper = packageWrapper;
exports.packageSetupAndInstaller = packageSetupAndInstaller;
98 changes: 98 additions & 0 deletions bin/helpers/readCypressConfigUtil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"use strict";
const path = require("path");
const fs = require("fs");
const cp = require('child_process');

const config = require('./config');
const constants = require("./constants");
const utils = require("./utils");
const logger = require('./logger').winstonLogger;

exports.detectLanguage = (cypress_config_filename) => {
const extension = cypress_config_filename.split('.').pop()
return constants.CYPRESS_V10_AND_ABOVE_CONFIG_FILE_EXTENSIONS.includes(extension) ? extension : 'js'
}

exports.convertTsConfig = (bsConfig, cypress_config_filepath, bstack_node_modules_path) => {
const cypress_config_filename = bsConfig.run_settings.cypress_config_filename
const working_dir = path.dirname(cypress_config_filepath);
const complied_js_dir = path.join(working_dir, config.compiledConfigJsDirName)
cp.execSync(`rm -rf ${config.compiledConfigJsDirName}`, { cwd: working_dir })
cp.execSync(`mkdir ${config.compiledConfigJsDirName}`, { cwd: working_dir })

let tsc_command = `NODE_PATH=${bstack_node_modules_path} ${bstack_node_modules_path}/typescript/bin/tsc --outDir ${complied_js_dir} --listEmittedFiles true --allowSyntheticDefaultImports --module commonjs --declaration false ${cypress_config_filepath}`
let tsc_output
try {
logger.debug(`Running: ${tsc_command}`)
tsc_output = cp.execSync(tsc_command, { cwd: working_dir })
} catch (err) {
// error while compiling ts files
logger.debug(err.message);
logger.debug(err.output.toString());
tsc_output = err.output // if there is an error, tsc adds output of complilation to err.output key
} finally {
logger.debug(`Saved compiled js output at: ${complied_js_dir}`);
logger.debug(`Finding compiled cypress config file in: ${complied_js_dir}`);

const lines = tsc_output.toString().split('\n');
let foundLine = null;
for (let i = 0; i < lines.length; i++) {
if (lines[i].indexOf(`${path.parse(cypress_config_filename).name}.js`) > -1) {
foundLine = lines[i]
break;
}
}
if (foundLine === null) {
logger.error(`No compiled cypress config found. There might some error running ${tsc_command} command`)
return null
} else {
const compiled_cypress_config_filepath = foundLine.split('TSFILE: ').pop()
logger.debug(`Found compiled cypress config file: ${compiled_cypress_config_filepath}`);
return compiled_cypress_config_filepath
}
}
}

exports.loadJsFile = (cypress_config_filepath, bstack_node_modules_path) => {
const require_module_helper_path = `${__dirname}/requireModule.js`
cp.execSync(`NODE_PATH=${bstack_node_modules_path} node ${require_module_helper_path} ${cypress_config_filepath}`)
const cypress_config = JSON.parse(fs.readFileSync(config.configJsonFileName).toString())
if (fs.existsSync(config.configJsonFileName)) {
fs.unlinkSync(config.configJsonFileName)
}
return cypress_config
}

exports.readCypressConfigFile = (bsConfig) => {
const cypress_config_filepath = path.resolve(bsConfig.run_settings.cypressConfigFilePath)
try {
const cypress_config_filename = bsConfig.run_settings.cypress_config_filename
const bstack_node_modules_path = `${path.resolve(config.packageDirName)}/node_modules`
const conf_lang = this.detectLanguage(cypress_config_filename)

logger.debug(`cypress config path: ${cypress_config_filepath}`);

if (conf_lang == 'js' || conf_lang == 'cjs') {
return this.loadJsFile(cypress_config_filepath, bstack_node_modules_path)
} else if (conf_lang === 'ts') {
const compiled_cypress_config_filepath = this.convertTsConfig(bsConfig, cypress_config_filepath, bstack_node_modules_path)
return this.loadJsFile(compiled_cypress_config_filepath, bstack_node_modules_path)
}
} catch (error) {
const errorMessage = `Error while reading cypress config: ${error.message}`
const errorCode = 'cypress_config_file_read_failed'
logger.error(errorMessage)
utils.sendUsageReport(
bsConfig,
null,
errorMessage,
constants.messageTypes.WARNING,
errorCode,
null,
null
)
} finally {
const working_dir = path.dirname(cypress_config_filepath);
cp.execSync(`rm -rf ${config.compiledConfigJsDirName}`, { cwd: working_dir })
}
}
14 changes: 14 additions & 0 deletions bin/helpers/requireModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// NOTE: DO NOT change the name or location of file, the execution of this file is invoked using fixed path
// helper file to load and read js modules
const fs = require('fs');
const config = require('./config');
const moduleName = process.argv[2];

const mod = require(moduleName)

if (fs.existsSync(config.configJsonFileName)) {
fs.unlinkSync(config.configJsonFileName)
}

// write module in temporary json file
fs.writeFileSync(config.configJsonFileName, JSON.stringify(mod))
Loading