@@ -201,24 +201,37 @@ Cypress.on('command:end', (command) => {
201201 } ) ;
202202} ) ;
203203
204- Cypress . Commands . overwrite ( 'log' , ( originalFn , ...args ) => {
204+ /*
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. Hooking
209+ * command:enqueue runs synchronously at the user's cy.log() call site.
210+ */
211+ Cypress . on ( 'command:enqueue' , ( attrs ) => {
212+ if ( ! Cypress . env ( 'BROWSERSTACK_O11Y_LOGS' ) ) return ;
213+ if ( ! attrs || attrs . name !== 'log' ) return ;
214+ const args = attrs . args || [ ] ;
205215 if ( args . includes ( 'test_observability_log' ) || args . includes ( 'test_observability_command' ) ) return ;
206216 const message = args . reduce ( ( result , logItem ) => {
207217 if ( typeof logItem === 'object' ) {
208218 return [ result , JSON . stringify ( logItem ) ] . join ( ' ' ) ;
209219 }
210-
211220 return [ result , logItem ? logItem . toString ( ) : '' ] . join ( ' ' ) ;
212221 } , '' ) ;
213222 eventsQueue . push ( {
214223 task : 'test_observability_log' ,
215224 data : {
216- ' level' : 'info' ,
225+ level : 'info' ,
217226 message,
218227 timestamp : new Date ( ) . toISOString ( )
219228 } ,
220229 options : { log : false }
221230 } ) ;
231+ } ) ;
232+
233+ Cypress . Commands . overwrite ( 'log' , ( originalFn , ...args ) => {
234+ if ( args . includes ( 'test_observability_log' ) || args . includes ( 'test_observability_command' ) ) return ;
222235 originalFn ( ...args ) ;
223236} ) ;
224237
0 commit comments