Skip to content

Commit b4cb261

Browse files
committed
dispatch cy.log via Cypress.backend, bypassing the queue
1 parent 0fb6a7c commit b4cb261

2 files changed

Lines changed: 18 additions & 23 deletions

File tree

bin/testObservability/cypress/index.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,18 @@ Cypress.on('command:end', (command) => {
202202
});
203203

204204
/*
205-
* cy.log capture must happen at command-enqueue time, not command-execute time.
206-
* If a test body throws synchronously (e.g. a failing chai assertion) before the
207-
* Cypress queue drains, queued commands are dropped — so an execute-time wrapper
208-
* never fires and pre-throw cy.log calls are lost from the timeline. The
209-
* command:enqueued event runs synchronously at the user's cy.log() call site.
205+
* cy.log capture must happen at command-enqueue time AND must bypass Cypress's
206+
* test command queue. When a test body throws synchronously (e.g. a failing
207+
* chai assertion), Cypress drops every pending command in the test queue —
208+
* which means an execute-time wrapper on cy.log never runs, and even a
209+
* deferred cy.task(...) inside the SDK's afterEach is just another queued
210+
* command that won't survive the drop.
211+
*
212+
* Cypress.backend('task', ...) emits directly over the runner-to-Node
213+
* websocket (see Cypress driver), bypassing the queue entirely. Combined
214+
* with command:enqueued (which fires synchronously at the user's cy.log()
215+
* call site, before the throw), the log reaches the Node-side task handler
216+
* regardless of whether the test passes or fails.
210217
*/
211218
Cypress.on('command:enqueued', (attrs) => {
212219
if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) return;
@@ -219,14 +226,16 @@ Cypress.on('command:enqueued', (attrs) => {
219226
}
220227
return [result, logItem ? logItem.toString() : ''].join(' ');
221228
}, '');
222-
eventsQueue.push({
229+
Cypress.backend('task', {
223230
task: 'test_observability_log',
224-
data: {
231+
arg: {
225232
level: 'info',
226233
message,
227234
timestamp: new Date().toISOString()
228235
},
229-
options: { log: false }
236+
timeout: 60000
237+
}).catch(() => {
238+
/* Don't let observability failures bubble into the test */
230239
});
231240
});
232241

bin/testObservability/reporter/index.js

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -628,24 +628,10 @@ class MyReporter {
628628

629629
appendTestItemLog = async (log) => {
630630
try {
631-
/*
632-
* SDK-5709: keep the hash gate on hook side (avoids attributing a log to
633-
* a stale hook pointer between hooks). Drop it on the test side. In
634-
* Cypress, EVENT_TEST_FAIL fires synchronously when a test body throws
635-
* — *before* afterEach runs. The SDK's internal afterEach flushes its
636-
* cy.log buffer via cy.task during afterEach, so by the time those logs
637-
* reach this listener, runStatusMarkedHash[testAnalyticsId] is already
638-
* set and the log gets dropped without ever attaching a test_run_uuid.
639-
* Without the gate, the log uploads with test_run_uuid even after
640-
* TestRunFinished — accepted by the analytics backend (uploadTestSteps
641-
* takes the same path with no gate).
642-
*/
643631
if(this.current_hook && ( this.current_hook.hookAnalyticsId && !this.runStatusMarkedHash[this.current_hook.hookAnalyticsId] )) {
644632
log.hook_run_uuid = this.current_hook.hookAnalyticsId;
645633
}
646-
if(!log.hook_run_uuid && this.current_test && this.current_test.testAnalyticsId) {
647-
log.test_run_uuid = this.current_test.testAnalyticsId;
648-
}
634+
if(!log.hook_run_uuid && this.current_test && ( this.current_test.testAnalyticsId && !this.runStatusMarkedHash[this.current_test.testAnalyticsId] )) log.test_run_uuid = this.current_test.testAnalyticsId;
649635
if(log.hook_run_uuid || log.test_run_uuid) {
650636
await uploadEventData({
651637
event_type: 'LogCreated',

0 commit comments

Comments
 (0)