Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@nightwatch/browserstack

Package Overview
Dependencies
Maintainers
7
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nightwatch/browserstack - npm Package Compare versions

Comparing version
3.7.1
to
3.8.0
+15
-5
nightwatch/globals.js

@@ -263,3 +263,3 @@ const LocalTunnel = require('../src/local-tunnel');

eventBroadcaster.on('TestRunStarted', async (test) => {
process.env.VALID_ALLY_PLATFORM = accessibilityAutomation.validateA11yCaps(browser);
process.env.VALID_ALLY_PLATFORM = process.env.BROWSERSTACK_APP_AUTOMATE ? accessibilityAutomation.validateAppA11yCaps(test.metadata.sessionCapabilities) : accessibilityAutomation.validateA11yCaps(browser);
await accessibilityAutomation.beforeEachExecution(test);

@@ -361,3 +361,5 @@ if (testRunner !== 'cucumber'){

accessibilityAutomation.commandWrapper();
helper.patchBrowserTerminateCommand();
if (!process.env.BROWSERSTACK_APP_AUTOMATE){
helper.patchBrowserTerminateCommand();
};
}

@@ -494,4 +496,10 @@ } catch (err){

async beforeEach(settings) {
browser.getAccessibilityResults = () => { return accessibilityAutomation.getAccessibilityResults() };
browser.getAccessibilityResultsSummary = () => { return accessibilityAutomation.getAccessibilityResultsSummary() };
if (helper.isAppAccessibilitySession()){
browser.getAccessibilityResults = () => { return accessibilityAutomation.getAppAccessibilityResults(browser) };
browser.getAccessibilityResultsSummary = () => { return accessibilityAutomation.getAppAccessibilityResultsSummary(browser) };
} else {
browser.getAccessibilityResults = () => { return accessibilityAutomation.getAccessibilityResults() };
browser.getAccessibilityResultsSummary = () => { return accessibilityAutomation.getAccessibilityResultsSummary() };
}
// await accessibilityAutomation.beforeEachExecution(browser);
},

@@ -537,3 +545,5 @@

accessibilityAutomation.commandWrapper();
helper.patchBrowserTerminateCommand();
if (!process.env.BROWSERSTACK_APP_AUTOMATE){
helper.patchBrowserTerminateCommand();
};
}

@@ -540,0 +550,0 @@ } catch (err){

{
"name": "@nightwatch/browserstack",
"version": "3.7.1",
"version": "3.8.0",
"description": "Nightwatch plugin for integration with browserstack.",

@@ -5,0 +5,0 @@ "main": "index.js",

const path = require('path');
const helper = require('./utils/helper');
const Logger = require('./utils/logger');
const {APP_ALLY_ENDPOINT, APP_ALLY_ISSUES_SUMMARY_ENDPOINT, APP_ALLY_ISSUES_ENDPOINT} = require('./utils/constants');
const util = require('util');

@@ -165,2 +166,20 @@ const AccessibilityScripts = require('./scripts/accessibilityScripts');

validateAppA11yCaps(capabilities = {}) {
/* Check if the current driver platform is eligible for AppAccessibility scan */
if (
capabilities?.platformName &&
String(capabilities?.platformName).toLowerCase() === 'android' &&
capabilities?.platformVersion &&
parseInt(capabilities?.platformVersion?.toString()) < 11
) {
Logger.warn(
'App Accessibility Automation tests are supported on OS version 11 and above for Android devices.'
);
return false;
}
return true;
}
async beforeEachExecution(testMetaData) {

@@ -173,4 +192,10 @@ try {

this.currentTest.accessibilityScanStarted = true;
this._isAccessibilitySession = this.validateA11yCaps(browser);
this._isAppAccessibility = helper.isAppAccessibilitySession();
if (this._isAppAccessibility) {
this._isAccessibilitySession = this.validateAppA11yCaps(testMetaData.metadata.sessionCapabilities);
} else {
this._isAccessibilitySession = this.validateA11yCaps(browser);
}
if (this.isAccessibilityAutomationSession() && browser && this._isAccessibilitySession) {

@@ -272,6 +297,5 @@ try {

if (this.currentTest.shouldScanTestForAccessibility === false) {
Logger.info('Skipping Accessibility scan for this test as it\'s disabled.');
return;
}
try {

@@ -285,2 +309,12 @@ const browser = browserInstance;

}
if (helper.isAppAccessibilitySession()){
const results = await browser.executeScript(
helper.formatString(AccessibilityScripts.performScan, JSON.stringify(this.getParamsForAppAccessibility(commandName))),
{}
);
Logger.debug(util.inspect(results));
return results;
}
AccessibilityAutomation.pendingAllyReq++;

@@ -304,5 +338,75 @@ const results = await browser.executeAsyncScript(AccessibilityScripts.performScan, {

async getAppAccessibilityResults(browser) {
if (!helper.isBrowserstackInfra()) {
return [];
}
if (!helper.isAppAccessibilitySession()) {
Logger.warn('Not an Accessibility Automation session, cannot retrieve Accessibility results.');
return [];
}
try {
const apiUrl = `${APP_ALLY_ENDPOINT}/${APP_ALLY_ISSUES_ENDPOINT}`;
const apiRespone = await this.getAppA11yResultResponse(apiUrl, browser, browser.sessionId);
const result = apiRespone?.data?.data?.issues;
Logger.debug(`Results: ${JSON.stringify(result)}`);
return result;
} catch (error) {
Logger.error('No accessibility results were found.');
Logger.debug(`getAppAccessibilityResults Failed. Error: ${error}`);
return [];
}
}
async getAppAccessibilityResultsSummary(browser) {
if (!helper.isBrowserstackInfra()) {
return {};
}
if (!helper.isAppAccessibilitySession()) {
Logger.warn('Not an Accessibility Automation session, cannot retrieve Accessibility results summary.');
return {};
}
try {
const apiUrl = `${APP_ALLY_ENDPOINT}/${APP_ALLY_ISSUES_SUMMARY_ENDPOINT}`;
const apiRespone = await this.getAppA11yResultResponse(apiUrl, browser, browser.sessionId);
const result = apiRespone?.data?.data?.summary;
Logger.debug(`Results Summary: ${JSON.stringify(result)}`);
return result;
} catch (error) {
Logger.error('No accessibility result summary were found.');
Logger.debug(`getAppAccessibilityResultsSummary Failed. Error: ${error}`);
return {};
}
}
async getAppA11yResultResponse(apiUrl, browser, sessionId){
Logger.debug('Performing scan before getting results/results summary');
await this.performScan(browser);
const upperTimeLimit = process.env.BSTACK_A11Y_POLLING_TIMEOUT ? Date.now() + parseInt(process.env.BSTACK_A11Y_POLLING_TIMEOUT) * 1000 : Date.now() + 30000;
const params = {test_run_uuid: process.env.TEST_RUN_UUID, session_id: sessionId, timestamp: Date.now()}; // Query params to pass
const header = {Authorization: `Bearer ${process.env.BSTACK_A11Y_JWT}`};
const apiRespone = await helper.pollApi(apiUrl, params, header, upperTimeLimit);
Logger.debug(`Polling Result: ${JSON.stringify(apiRespone.message)}`);
return apiRespone;
}
async saveAccessibilityResults(browser, dataForExtension = {}) {
Logger.debug('Performing scan before saving results');
await this.performScan(browser);
if (helper.isAppAccessibilitySession()){
return;
}
const results = await browser.executeAsyncScript(AccessibilityScripts.saveTestResults, dataForExtension);

@@ -344,3 +448,8 @@

originalCommand.command = async function(...args) {
await accessibilityInstance.performScan(browser, commandName);
if (
!commandName.includes('execute') ||
!accessibilityInstance.shouldPatchExecuteScript(args.length ? args[0] : null)
) {
await accessibilityInstance.performScan(browser, commandName);
}

@@ -356,4 +465,26 @@ return originalCommandFn.apply(this, args);

}
shouldPatchExecuteScript(script) {
if (!script || typeof script !== 'string') {
return true;
}
return (
script.toLowerCase().indexOf('browserstack_executor') !== -1 ||
script.toLowerCase().indexOf('browserstack_accessibility_automation_script') !== -1
);
}
getParamsForAppAccessibility(commandName) {
return {
'thTestRunUuid': process.env.TEST_RUN_UUID,
'thBuildUuid': process.env.BROWSERSTACK_TESTHUB_UUID,
'thJwtToken': process.env.BROWSERSTACK_TESTHUB_JWT,
'authHeader': process.env.BSTACK_A11Y_JWT,
'scanTimestamp': Date.now(),
'method': commandName
};
}
}
module.exports = AccessibilityAutomation;

@@ -238,2 +238,3 @@ const os = require('os');

}
process.env.IS_APP_ACCESSIBILITY = accessibilityAutomation.isAccessibilityAutomationSession() && helper.isAppAutomate();

@@ -240,0 +241,0 @@ }

@@ -21,3 +21,5 @@ exports.BATCH_SIZE = 1000;

exports.ACCESSIBILITY_URL= 'https://accessibility.browserstack.com/api';
exports.APP_ALLY_ENDPOINT = 'https://app-accessibility.browserstack.com/automate';
exports.APP_ALLY_ISSUES_SUMMARY_ENDPOINT ='api/v1/issues-summary';
exports.APP_ALLY_ISSUES_ENDPOINT = 'api/v1/issues';
// Maximum size of VCS info which is allowed

@@ -24,0 +26,0 @@ exports.MAX_GIT_META_DATA_SIZE_IN_BYTES = 64 * 1024;

@@ -20,2 +20,3 @@ const os = require('os');

const {execSync} = require('child_process');
const request = require('@cypress/request');

@@ -105,2 +106,6 @@ console = {};

exports.isAppAccessibilitySession = () => {
return process.env.IS_APP_ACCESSIBILITY === 'true';
};
exports.isAccessibilityEnabled = (settings) => {

@@ -1310,1 +1315,100 @@ if (process.argv.includes('--disable-accessibility')) {return false}

exports.formatString = (template, ...values) => {
let i = 0;
if (template === null) {
return '';
}
return template.replace(/%s/g, () => {
const value = values[i++];
return value !== null && value !== undefined ? value : '';
});
};
exports.pollApi = async (url, params, headers, upperLimit, startTime = Date.now()) => {
params.timestamp = Math.round(Date.now() / 1000);
Logger.debug(`current timestamp ${params.timestamp}`);
try {
const queryString = new URLSearchParams(params).toString();
const fullUrl = `${url}?${queryString}`;
const response = await new Promise((resolve, reject) => {
request({
method: 'GET',
url: fullUrl,
headers: headers,
json: false
}, (error, response, body) => {
if (error) {
reject(error);
} else {
resolve(response);
}
});
});
const responseData = JSON.parse(response.body);
if (response.statusCode === 404) {
const nextPollTime = parseInt(response.headers?.next_poll_time, 10) * 1000;
Logger.debug(`nextPollTime: ${nextPollTime}`);
if (isNaN(nextPollTime)) {
Logger.warn('Invalid or missing `nextPollTime` header. Stopping polling.');
return {
data: {},
headers: response.headers || {},
message: 'Invalid nextPollTime header value. Polling stopped.'
};
}
// Stop polling if the upper time limit is reached
if (nextPollTime > upperLimit) {
Logger.warn('Polling stopped due to upper time limit.');
return {
data: {},
headers: response.headers || {},
message: 'Polling stopped due to upper time limit.'
};
}
const elapsedTime = Math.max(0, nextPollTime - Date.now());
Logger.debug(
`elapsedTime ${elapsedTime} nextPollTimes ${nextPollTime} upperLimit ${upperLimit}`
);
Logger.debug(`Polling for results again in ${elapsedTime}ms`);
// Wait for the specified time and poll again
await new Promise((resolve) => setTimeout(resolve, elapsedTime));
return exports.pollApi(url, params, headers, upperLimit, startTime);
}
return {
data: responseData,
headers: response.headers,
message: 'Polling succeeded.'
};
} catch (error) {
if (error.response) {
throw {
data: {},
headers: {},
message: error.response.body ? JSON.parse(error.response.body).message : 'Unknown error'
};
} else {
Logger.error(`Unexpected error occurred: ${error}`);
return {data: {}, headers: {}, message: 'Unexpected error occurred.'};
}
}
};