From 593249e091241aade2505f0c28256447031de6ca Mon Sep 17 00:00:00 2001 From: prathams-cmd Date: Thu, 12 Feb 2026 19:40:41 +0530 Subject: [PATCH 1/5] Added Cucumber/Gherkin step detection and assertion logging for test observability --- bin/testObservability/cypress/index.js | 99 +++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 8 deletions(-) diff --git a/bin/testObservability/cypress/index.js b/bin/testObservability/cypress/index.js index 41b725de..1571165e 100644 --- a/bin/testObservability/cypress/index.js +++ b/bin/testObservability/cypress/index.js @@ -1,6 +1,7 @@ /* Event listeners + custom commands for Cypress */ /* Used to detect Gherkin steps */ +const STEP_KEYWORDS = ['given', 'when', 'then', 'and', 'but', '*']; let eventsQueue = []; let testRunStarted = false; @@ -18,20 +19,102 @@ const shouldSkipCommand = (command) => { 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 () => { - if (shouldSkipCommand(command)) { - return; - } +// Cypress.on('log:added', (log) => { +// 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('log:changed', (attrs) => { + if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) return; + if (!attrs) return; + if (attrs.state !== 'passed' && attrs.state !== 'failed') return; + + if (attrs.name === 'assert') { + const assertMessage = (attrs.message || '').replace(/\*\*/g, ''); + + eventsQueue.push({ + task: 'test_observability_command', + data: { + type: 'COMMAND_START', + command: { + attributes: { + id: attrs.id, + name: 'assert', + args: [assertMessage] + }, + state: 'pending', + started_at: new Date(attrs.createdAtTimestamp).toISOString(), + location: testRunStarted ? 'test' : 'hook' + } + }, + options: { log: false } + }); + + eventsQueue.push({ + task: 'test_observability_command', + data: { + type: 'COMMAND_END', + command: { + attributes: { + id: attrs.id, + name: 'assert', + args: [assertMessage] + }, + state: attrs.state, + finished_at: new Date(attrs.updatedAtTimestamp).toISOString(), + location: testRunStarted ? 'test' : 'hook' + } + }, + options: { log: false } + }); + } + + const keyword = (attrs.name || '').trim(); + if (STEP_KEYWORDS.includes(keyword.toLowerCase())) { + const text = (attrs.message || '').replace(/\*\*/g, ''); + eventsQueue.push({ task: 'test_observability_step', data: { - log, - started_at: new Date().toISOString(), - finished_at: new Date().toISOString() + log: { + name: 'step', + chainerId: attrs.chainerId, + consoleProps: { step: { keyword, text } } + }, + started_at: new Date(attrs.createdAtTimestamp).toISOString(), + finished_at: new Date(attrs.updatedAtTimestamp).toISOString() }, options: { log: false } }); + + if (attrs.state === 'failed') { + eventsQueue.push({ + task: 'test_observability_step', + data: { + log: { + name: 'then', + type: 'child', + chainerId: attrs.chainerId, + state: attrs.state, + err: attrs.err + }, + finished_at: new Date(attrs.updatedAtTimestamp).toISOString() + }, + options: { log: false } + }); + } } }); From 10897499c6984a150040194ae43c9f9fcb53f006 Mon Sep 17 00:00:00 2001 From: prathams-cmd Date: Mon, 16 Feb 2026 20:11:07 +0530 Subject: [PATCH 2/5] PR review changes --- bin/testObservability/cypress/index.js | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/bin/testObservability/cypress/index.js b/bin/testObservability/cypress/index.js index 1571165e..40b86f69 100644 --- a/bin/testObservability/cypress/index.js +++ b/bin/testObservability/cypress/index.js @@ -19,30 +19,13 @@ const shouldSkipCommand = (command) => { 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 () => { -// 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('log:changed', (attrs) => { if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) return; if (!attrs) return; if (attrs.state !== 'passed' && attrs.state !== 'failed') return; if (attrs.name === 'assert') { - const assertMessage = (attrs.message || '').replace(/\*\*/g, ''); + const assertMessage = (attrs.message || '') eventsQueue.push({ task: 'test_observability_command', @@ -83,7 +66,7 @@ Cypress.on('log:changed', (attrs) => { const keyword = (attrs.name || '').trim(); if (STEP_KEYWORDS.includes(keyword.toLowerCase())) { - const text = (attrs.message || '').replace(/\*\*/g, ''); + const text = (attrs.message || '') eventsQueue.push({ task: 'test_observability_step', From fca13f9f8512bd4319349abc539d7466852de5f3 Mon Sep 17 00:00:00 2001 From: prathams-cmd Date: Wed, 18 Feb 2026 12:15:26 +0530 Subject: [PATCH 3/5] Check for cypress version 13 or more --- bin/testObservability/cypress/index.js | 132 +++++++++++++------------ 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/bin/testObservability/cypress/index.js b/bin/testObservability/cypress/index.js index 40b86f69..3fd7e9b0 100644 --- a/bin/testObservability/cypress/index.js +++ b/bin/testObservability/cypress/index.js @@ -19,87 +19,89 @@ const shouldSkipCommand = (command) => { 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:changed', (attrs) => { - if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) return; - if (!attrs) return; - if (attrs.state !== 'passed' && attrs.state !== 'failed') return; +if (parseInt(Cypress.version.split('.')[0], 10) >= 13) { + Cypress.on('log:changed', (attrs) => { + if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) return; + if (!attrs) return; + if (attrs.state !== 'passed' && attrs.state !== 'failed') return; - if (attrs.name === 'assert') { - const assertMessage = (attrs.message || '') + if (attrs.name === 'assert') { + const assertMessage = (attrs.message || '') - eventsQueue.push({ - task: 'test_observability_command', - data: { - type: 'COMMAND_START', - command: { - attributes: { - id: attrs.id, - name: 'assert', - args: [assertMessage] - }, - state: 'pending', - started_at: new Date(attrs.createdAtTimestamp).toISOString(), - location: testRunStarted ? 'test' : 'hook' - } - }, - options: { log: false } - }); - - eventsQueue.push({ - task: 'test_observability_command', - data: { - type: 'COMMAND_END', - command: { - attributes: { - id: attrs.id, - name: 'assert', - args: [assertMessage] - }, - state: attrs.state, - finished_at: new Date(attrs.updatedAtTimestamp).toISOString(), - location: testRunStarted ? 'test' : 'hook' - } - }, - options: { log: false } - }); - } - - const keyword = (attrs.name || '').trim(); - if (STEP_KEYWORDS.includes(keyword.toLowerCase())) { - const text = (attrs.message || '') + eventsQueue.push({ + task: 'test_observability_command', + data: { + type: 'COMMAND_START', + command: { + attributes: { + id: attrs.id, + name: 'assert', + args: [assertMessage] + }, + state: 'pending', + started_at: new Date(attrs.createdAtTimestamp).toISOString(), + location: testRunStarted ? 'test' : 'hook' + } + }, + options: { log: false } + }); - eventsQueue.push({ - task: 'test_observability_step', - data: { - log: { - name: 'step', - chainerId: attrs.chainerId, - consoleProps: { step: { keyword, text } } + eventsQueue.push({ + task: 'test_observability_command', + data: { + type: 'COMMAND_END', + command: { + attributes: { + id: attrs.id, + name: 'assert', + args: [assertMessage] + }, + state: attrs.state, + finished_at: new Date(attrs.updatedAtTimestamp).toISOString(), + location: testRunStarted ? 'test' : 'hook' + } }, - started_at: new Date(attrs.createdAtTimestamp).toISOString(), - finished_at: new Date(attrs.updatedAtTimestamp).toISOString() - }, - options: { log: false } - }); + options: { log: false } + }); + } + + const keyword = (attrs.name || '').trim(); + if (STEP_KEYWORDS.includes(keyword.toLowerCase())) { + const text = (attrs.message || '') - if (attrs.state === 'failed') { eventsQueue.push({ task: 'test_observability_step', data: { log: { - name: 'then', - type: 'child', + name: 'step', chainerId: attrs.chainerId, - state: attrs.state, - err: attrs.err + consoleProps: { step: { keyword, text } } }, + started_at: new Date(attrs.createdAtTimestamp).toISOString(), finished_at: new Date(attrs.updatedAtTimestamp).toISOString() }, options: { log: false } }); + + if (attrs.state === 'failed') { + eventsQueue.push({ + task: 'test_observability_step', + data: { + log: { + name: 'then', + type: 'child', + chainerId: attrs.chainerId, + state: attrs.state, + err: attrs.err + }, + finished_at: new Date(attrs.updatedAtTimestamp).toISOString() + }, + options: { log: false } + }); + } } - } -}); + }); +} Cypress.on('command:start', (command) => { From 4c57f3b432d7587b3503a2c58eb289db5ac596db Mon Sep 17 00:00:00 2001 From: prathams-cmd Date: Wed, 18 Feb 2026 15:02:59 +0530 Subject: [PATCH 4/5] Early return for no timestamp --- bin/testObservability/cypress/index.js | 133 ++++++++++++------------- 1 file changed, 66 insertions(+), 67 deletions(-) diff --git a/bin/testObservability/cypress/index.js b/bin/testObservability/cypress/index.js index 3fd7e9b0..3f904920 100644 --- a/bin/testObservability/cypress/index.js +++ b/bin/testObservability/cypress/index.js @@ -19,89 +19,88 @@ const shouldSkipCommand = (command) => { 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)))); } -if (parseInt(Cypress.version.split('.')[0], 10) >= 13) { - Cypress.on('log:changed', (attrs) => { - if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) return; - if (!attrs) return; - if (attrs.state !== 'passed' && attrs.state !== 'failed') return; +Cypress.on('log:changed', (attrs) => { + if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) return; + if (!attrs) return; + if (!attrs.createdAtTimestamp || !attrs.updatedAtTimestamp) return; + if (attrs.state !== 'passed' && attrs.state !== 'failed') return; - if (attrs.name === 'assert') { - const assertMessage = (attrs.message || '') + if (attrs.name === 'assert') { + const assertMessage = (attrs.message || '') - eventsQueue.push({ - task: 'test_observability_command', - data: { - type: 'COMMAND_START', - command: { - attributes: { - id: attrs.id, - name: 'assert', - args: [assertMessage] - }, - state: 'pending', - started_at: new Date(attrs.createdAtTimestamp).toISOString(), - location: testRunStarted ? 'test' : 'hook' - } - }, - options: { log: false } - }); + eventsQueue.push({ + task: 'test_observability_command', + data: { + type: 'COMMAND_START', + command: { + attributes: { + id: attrs.id, + name: 'assert', + args: [assertMessage] + }, + state: 'pending', + started_at: new Date(attrs.createdAtTimestamp).toISOString(), + location: testRunStarted ? 'test' : 'hook' + } + }, + options: { log: false } + }); - eventsQueue.push({ - task: 'test_observability_command', - data: { - type: 'COMMAND_END', - command: { - attributes: { - id: attrs.id, - name: 'assert', - args: [assertMessage] - }, - state: attrs.state, - finished_at: new Date(attrs.updatedAtTimestamp).toISOString(), - location: testRunStarted ? 'test' : 'hook' - } - }, - options: { log: false } - }); - } + eventsQueue.push({ + task: 'test_observability_command', + data: { + type: 'COMMAND_END', + command: { + attributes: { + id: attrs.id, + name: 'assert', + args: [assertMessage] + }, + state: attrs.state, + finished_at: new Date(attrs.updatedAtTimestamp).toISOString(), + location: testRunStarted ? 'test' : 'hook' + } + }, + options: { log: false } + }); + } + + const keyword = (attrs.name || '').trim(); + if (STEP_KEYWORDS.includes(keyword.toLowerCase())) { + const text = (attrs.message || '') - const keyword = (attrs.name || '').trim(); - if (STEP_KEYWORDS.includes(keyword.toLowerCase())) { - const text = (attrs.message || '') + eventsQueue.push({ + task: 'test_observability_step', + data: { + log: { + name: 'step', + chainerId: attrs.chainerId, + consoleProps: { step: { keyword, text } } + }, + started_at: new Date(attrs.createdAtTimestamp).toISOString(), + finished_at: new Date(attrs.updatedAtTimestamp).toISOString() + }, + options: { log: false } + }); + if (attrs.state === 'failed') { eventsQueue.push({ task: 'test_observability_step', data: { log: { - name: 'step', + name: 'then', + type: 'child', chainerId: attrs.chainerId, - consoleProps: { step: { keyword, text } } + state: attrs.state, + err: attrs.err }, - started_at: new Date(attrs.createdAtTimestamp).toISOString(), finished_at: new Date(attrs.updatedAtTimestamp).toISOString() }, options: { log: false } }); - - if (attrs.state === 'failed') { - eventsQueue.push({ - task: 'test_observability_step', - data: { - log: { - name: 'then', - type: 'child', - chainerId: attrs.chainerId, - state: attrs.state, - err: attrs.err - }, - finished_at: new Date(attrs.updatedAtTimestamp).toISOString() - }, - options: { log: false } - }); - } } - }); -} + } +}); Cypress.on('command:start', (command) => { From b273c02f20b6c087bda28c529b4e80ca28920d6b Mon Sep 17 00:00:00 2001 From: prathams-cmd Date: Fri, 20 Feb 2026 16:40:02 +0530 Subject: [PATCH 5/5] Added displayName field for step-definition --- bin/testObservability/cypress/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/testObservability/cypress/index.js b/bin/testObservability/cypress/index.js index 3f904920..ef65708d 100644 --- a/bin/testObservability/cypress/index.js +++ b/bin/testObservability/cypress/index.js @@ -65,7 +65,8 @@ Cypress.on('log:changed', (attrs) => { }); } - const keyword = (attrs.name || '').trim(); + const keyword = (attrs.displayName || attrs.name || '').trim(); + if (STEP_KEYWORDS.includes(keyword.toLowerCase())) { const text = (attrs.message || '')