Skip to content
6 changes: 5 additions & 1 deletion bin/commands/runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ module.exports = function run(args, rawArgs) {
logger.debug("Completed setting the configs");

if(!isBrowserstackInfra) {
if(process.env.BS_TESTOPS_BUILD_COMPLETED) {
setEventListeners(bsConfig);
}

return runCypressTestsLocally(bsConfig, args, rawArgs);
}

Expand All @@ -203,7 +207,7 @@ module.exports = function run(args, rawArgs) {
setAccessibilityEventListeners(bsConfig);
}
if(process.env.BS_TESTOPS_BUILD_COMPLETED) {
// setEventListeners(bsConfig);
setEventListeners(bsConfig);
}
markBlockEnd('validateConfig');
logger.debug("Completed configs validation");
Expand Down
290 changes: 194 additions & 96 deletions bin/testObservability/cypress/index.js
Original file line number Diff line number Diff line change
@@ -1,158 +1,256 @@
/* Event listeners + custom commands for Cypress */

/* Used to detect Gherkin steps */

const util = require('util');

let eventsQueue = [];
let testRunStarted = false;

const browserStackLog = (message) => {

if (!Cypress.env('BROWSERSTACK_LOGS')) return;
cy.task('browserstack_log', message);
}

const shouldSkipCommand = (command) => {
if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) {
return true;
}
return command.attributes.name == 'log' || (command.attributes.name == 'task' && (['test_observability_platform_details', 'test_observability_step', 'test_observability_command', 'browserstack_log', 'test_observability_log'].some(event => command.attributes.args.includes(event))));
}

Cypress.on('log:added', (log) => {
return () => {
return cy.now('task', 'test_observability_step', {
log
}, {log: false})
return () => {
if (shouldSkipCommand(command)) {
return;
}
});

eventsQueue.push({
task: 'test_observability_step',
data: {
log,
started_at: new Date().toISOString(),
finished_at: new Date().toISOString()
},
options: { log: false }
});
}
});

Cypress.on('command:start', (command) => {
if(!command || !command.attributes) return;
if(command.attributes.name == 'log' || (command.attributes.name == 'task' && (command.attributes.args.includes('test_observability_command') || command.attributes.args.includes('test_observability_log')))) {

if (!command || !command.attributes) return;
if (shouldSkipCommand(command)) {
return;
}

/* Send command details */
cy.now('task', 'test_observability_command', {
type: 'COMMAND_START',
command: {
attributes: {
id: command.attributes.id,
name: command.attributes.name,
args: command.attributes.args
},
state: 'pending'
}
}, {log: false}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_command',
data: {
type: 'COMMAND_START',
command: {
attributes: {
id: command.attributes.id,
name: command.attributes.name,
args: command.attributes.args
},
state: 'pending',
started_at: new Date().toISOString(),
location: testRunStarted ? 'test' : 'hook'
}
},
options: { log: false }
});

/* Send platform details */
cy.now('task', 'test_observability_platform_details', {
testTitle: Cypress.currentTest.title,
browser: Cypress.browser,
platform: Cypress.platform,
cypressVersion: Cypress.version
}, {log: false}).then((res) => {
}).catch((err) => {
let testTitle = '';
try {
const runner = Cypress.mocha.getRunner();
const ctx = runner.suite.ctx;
testTitle = ctx.currentTest.title || ctx._runnable.title;
} catch (error) {
// Silently handle if any property is undefined
}

eventsQueue.push({
task: 'test_observability_platform_details',
data: {
testTitle,
browser: Cypress.browser,
platform: Cypress.platform,
cypressVersion: Cypress.version
},
options: { log: false }
});
});

Cypress.on('command:retry', (command) => {
if(!command || !command.attributes) return;
if(command.attributes.name == 'log' || (command.attributes.name == 'task' && (command.attributes.args.includes('test_observability_command') || command.attributes.args.includes('test_observability_log')))) {
if (!command || !command.attributes) return;
if (shouldSkipCommand(command)) {
return;
}
cy.now('task', 'test_observability_command', {
type: 'COMMAND_RETRY',
command: {
_log: command._log,
error: {
message: command && command.error ? command.error.message : null,
isDefaultAssertionErr: command && command.error ? command.error.isDefaultAssertionErr : null
eventsQueue.push({
task: 'test_observability_command',
data: {
type: 'COMMAND_RETRY',
command: {
_log: command._log,
error: {
message: command && command.error ? command.error.message : null,
isDefaultAssertionErr: command && command.error ? command.error.isDefaultAssertionErr : null
},
location: testRunStarted ? 'test' : 'hook'
}
}
}, {log: false}).then((res) => {
}).catch((err) => {
},
options: { log: false }
});
});

Cypress.on('command:end', (command) => {
if(!command || !command.attributes) return;
if(command.attributes.name == 'log' || (command.attributes.name == 'task' && (command.attributes.args.includes('test_observability_command') || command.attributes.args.includes('test_observability_log')))) {
if (!command || !command.attributes) return;
if (shouldSkipCommand(command)) {
return;
}
cy.now('task', 'test_observability_command', {
'type': 'COMMAND_END',
'command': {
'attributes': {
'id': command.attributes.id,
'name': command.attributes.name,
'args': command.attributes.args
},
'state': command.state
}
}, {log: false}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_command',
data: {
'type': 'COMMAND_END',
'command': {
'attributes': {
'id': command.attributes.id,
'name': command.attributes.name,
'args': command.attributes.args
},
'state': command.state,
finished_at: new Date().toISOString(),
location: testRunStarted ? 'test' : 'hook'
}
},
options: { log: false }
});
});

Cypress.Commands.overwrite('log', (originalFn, ...args) => {
if(args.includes('test_observability_log') || args.includes('test_observability_command')) return;
if (args.includes('test_observability_log') || args.includes('test_observability_command')) return;
const message = args.reduce((result, logItem) => {
if (typeof logItem === 'object') {
return [result, JSON.stringify(logItem)].join(' ');
}

return [result, logItem ? logItem.toString() : ''].join(' ');
}, '');
cy.now('task', 'test_observability_log', {
'level': 'info',
message,
}, {log: false}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_log',
data: {
'level': 'info',
message,
timestamp: new Date().toISOString()
},
options: { log: false }
});
originalFn(...args);
});

Cypress.Commands.add('trace', (message, file) => {
cy.now('task', 'test_observability_log', {
level: 'trace',
message,
file,
}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_log',
data: {
level: 'trace',
message,
file,
},
options: { log: false }
});
});

Cypress.Commands.add('logDebug', (message, file) => {
cy.now('task', 'test_observability_log', {
level: 'debug',
message,
file,
}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_log',
data: {
level: 'debug',
message,
file,
},
options: { log: false }
});
});

Cypress.Commands.add('info', (message, file) => {
cy.now('task', 'test_observability_log', {
level: 'info',
message,
file,
}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_log',
data: {
level: 'info',
message,
file,
},
options: { log: false }
});
});

Cypress.Commands.add('warn', (message, file) => {
cy.now('task', 'test_observability_log', {
level: 'warn',
message,
file,
}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_log',
data: {
level: 'warn',
message,
file,
},
options: { log: false }
});
});

Cypress.Commands.add('error', (message, file) => {
cy.now('task', 'test_observability_log', {
level: 'error',
message,
file,
}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_log',
data: {
level: 'error',
message,
file,
},
options: { log: false }
});
});

Cypress.Commands.add('fatal', (message, file) => {
cy.now('task', 'test_observability_log', {
level: 'fatal',
message,
file,
}).then((res) => {
}).catch((err) => {
eventsQueue.push({
task: 'test_observability_log',
data: {
level: 'fatal',
message,
file,
},
options: { log: false }
});
});

beforeEach(() => {
/* browserstack internal helper hook */

if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) {
return;
}

if (eventsQueue.length > 0) {
eventsQueue.forEach(event => {
cy.task(event.task, event.data, event.options);
});
}
eventsQueue = [];
testRunStarted = true;
});

afterEach(function() {
/* browserstack internal helper hook */
if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) {
return;
}

if (eventsQueue.length > 0) {
eventsQueue.forEach(event => {
cy.task(event.task, event.data, event.options);
});
}

eventsQueue = [];
testRunStarted = false;
});
3 changes: 1 addition & 2 deletions bin/testObservability/helper/cleanupQueueSync.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
* Sending all the remaining queues for synchronous manner
*/

const RequestQueueHandler = require('./requestQueueHandler');
const requestHandler = require('./requestQueueHandler');

const shutdown = async () => {
const requestHandler = new RequestQueueHandler();
requestHandler.queue = require(process.argv[2].trim());
await requestHandler.shutdown();
}
Expand Down
Loading