| 
1 | 1 | /* Event listeners + custom commands for Cypress */  | 
2 | 2 | 
 
  | 
3 | 3 | /* Used to detect Gherkin steps */  | 
 | 4 | + | 
 | 5 | +const util = require('util');  | 
 | 6 | + | 
 | 7 | +let eventsQueue = [];  | 
 | 8 | +let testRunStarted = false;  | 
 | 9 | + | 
 | 10 | +const browserStackLog = (message) => {  | 
 | 11 | + | 
 | 12 | +  if (!Cypress.env('BROWSERSTACK_LOGS')) return;  | 
 | 13 | +  cy.task('browserstack_log', message);  | 
 | 14 | +}  | 
 | 15 | + | 
 | 16 | +const shouldSkipCommand = (command) => {  | 
 | 17 | +  if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) {  | 
 | 18 | +    return true;  | 
 | 19 | +  }  | 
 | 20 | +  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))));  | 
 | 21 | +}  | 
 | 22 | + | 
4 | 23 | Cypress.on('log:added', (log) => {  | 
5 |  | -    return () => {  | 
6 |  | -      return cy.now('task', 'test_observability_step', {  | 
7 |  | -                log  | 
8 |  | -              }, {log: false})  | 
 | 24 | +  return () => {  | 
 | 25 | +    if (shouldSkipCommand(command)) {  | 
 | 26 | +      return;  | 
9 | 27 |     }  | 
10 |  | -  });  | 
11 |  | -    | 
 | 28 | +    eventsQueue.push({  | 
 | 29 | +      task: 'test_observability_step',  | 
 | 30 | +      data: {  | 
 | 31 | +        log,  | 
 | 32 | +        started_at: new Date().toISOString(),  | 
 | 33 | +        finished_at: new Date().toISOString()  | 
 | 34 | +      },  | 
 | 35 | +      options: { log: false }  | 
 | 36 | +    });  | 
 | 37 | +  }  | 
 | 38 | +});  | 
 | 39 | + | 
12 | 40 | Cypress.on('command:start', (command) => {  | 
13 |  | -  if(!command || !command.attributes) return;  | 
14 |  | -  if(command.attributes.name == 'log' || (command.attributes.name == 'task' && (command.attributes.args.includes('test_observability_command') || command.attributes.args.includes('test_observability_log')))) {  | 
 | 41 | + | 
 | 42 | +  if (!command || !command.attributes) return;  | 
 | 43 | +  if (shouldSkipCommand(command)) {  | 
15 | 44 |     return;  | 
16 | 45 |   }  | 
 | 46 | + | 
17 | 47 |   /* Send command details */  | 
18 |  | -  cy.now('task', 'test_observability_command', {  | 
19 |  | -    type: 'COMMAND_START',  | 
20 |  | -    command: {  | 
21 |  | -      attributes: {  | 
22 |  | -        id: command.attributes.id,  | 
23 |  | -        name: command.attributes.name,  | 
24 |  | -        args: command.attributes.args  | 
25 |  | -      },  | 
26 |  | -      state: 'pending'  | 
27 |  | -    }  | 
28 |  | -  }, {log: false}).then((res) => {  | 
29 |  | -  }).catch((err) => {  | 
 | 48 | +  eventsQueue.push({  | 
 | 49 | +    task: 'test_observability_command',  | 
 | 50 | +    data: {  | 
 | 51 | +      type: 'COMMAND_START',  | 
 | 52 | +      command: {  | 
 | 53 | +        attributes: {  | 
 | 54 | +          id: command.attributes.id,  | 
 | 55 | +          name: command.attributes.name,  | 
 | 56 | +          args: command.attributes.args  | 
 | 57 | +        },  | 
 | 58 | +        state: 'pending',  | 
 | 59 | +        started_at: new Date().toISOString(),  | 
 | 60 | +        location: testRunStarted ? 'test' : 'hook'  | 
 | 61 | +      }  | 
 | 62 | +    },  | 
 | 63 | +    options: { log: false }  | 
30 | 64 |   });  | 
31 |  | - | 
32 | 65 |   /* Send platform details */  | 
33 |  | -  cy.now('task', 'test_observability_platform_details', {  | 
34 |  | -    testTitle: Cypress.currentTest.title,  | 
35 |  | -    browser: Cypress.browser,  | 
36 |  | -    platform: Cypress.platform,  | 
37 |  | -    cypressVersion: Cypress.version  | 
38 |  | -  }, {log: false}).then((res) => {  | 
39 |  | -  }).catch((err) => {  | 
 | 66 | +  let testTitle = '';  | 
 | 67 | +  try {  | 
 | 68 | +    const runner = Cypress.mocha.getRunner();  | 
 | 69 | +    const ctx = runner.suite.ctx;  | 
 | 70 | +    testTitle = ctx.currentTest.title || ctx._runnable.title;  | 
 | 71 | +  } catch (error) {  | 
 | 72 | +    // Silently handle if any property is undefined  | 
 | 73 | +  }  | 
 | 74 | + | 
 | 75 | +  eventsQueue.push({  | 
 | 76 | +    task: 'test_observability_platform_details',  | 
 | 77 | +    data: {  | 
 | 78 | +      testTitle,  | 
 | 79 | +      browser: Cypress.browser,  | 
 | 80 | +      platform: Cypress.platform,  | 
 | 81 | +      cypressVersion: Cypress.version  | 
 | 82 | +    },  | 
 | 83 | +    options: { log: false }  | 
40 | 84 |   });  | 
41 | 85 | });  | 
42 | 86 | 
 
  | 
43 | 87 | Cypress.on('command:retry', (command) => {  | 
44 |  | -  if(!command || !command.attributes) return;  | 
45 |  | -  if(command.attributes.name == 'log' || (command.attributes.name == 'task' && (command.attributes.args.includes('test_observability_command') || command.attributes.args.includes('test_observability_log')))) {  | 
 | 88 | +  if (!command || !command.attributes) return;  | 
 | 89 | +  if (shouldSkipCommand(command)) {  | 
46 | 90 |     return;  | 
47 | 91 |   }  | 
48 |  | -  cy.now('task', 'test_observability_command', {  | 
49 |  | -    type: 'COMMAND_RETRY',  | 
50 |  | -    command: {  | 
51 |  | -      _log: command._log,  | 
52 |  | -      error: {  | 
53 |  | -        message: command && command.error ? command.error.message : null,  | 
54 |  | -        isDefaultAssertionErr: command && command.error ? command.error.isDefaultAssertionErr : null  | 
 | 92 | +  eventsQueue.push({  | 
 | 93 | +    task: 'test_observability_command',  | 
 | 94 | +    data: {  | 
 | 95 | +      type: 'COMMAND_RETRY',  | 
 | 96 | +      command: {  | 
 | 97 | +        _log: command._log,  | 
 | 98 | +        error: {  | 
 | 99 | +          message: command && command.error ? command.error.message : null,  | 
 | 100 | +          isDefaultAssertionErr: command && command.error ? command.error.isDefaultAssertionErr : null  | 
 | 101 | +        },  | 
 | 102 | +        location: testRunStarted ? 'test' : 'hook'  | 
55 | 103 |       }  | 
56 |  | -    }  | 
57 |  | -  }, {log: false}).then((res) => {  | 
58 |  | -  }).catch((err) => {  | 
 | 104 | +    },  | 
 | 105 | +    options: { log: false }  | 
59 | 106 |   });  | 
60 | 107 | });  | 
61 | 108 | 
 
  | 
62 | 109 | Cypress.on('command:end', (command) => {  | 
63 |  | -  if(!command || !command.attributes) return;  | 
64 |  | -  if(command.attributes.name == 'log' || (command.attributes.name == 'task' && (command.attributes.args.includes('test_observability_command') || command.attributes.args.includes('test_observability_log')))) {  | 
 | 110 | +  if (!command || !command.attributes) return;  | 
 | 111 | +  if (shouldSkipCommand(command)) {  | 
65 | 112 |     return;  | 
66 | 113 |   }  | 
67 |  | -  cy.now('task', 'test_observability_command', {  | 
68 |  | -    'type': 'COMMAND_END',  | 
69 |  | -    'command': {  | 
70 |  | -      'attributes': {  | 
71 |  | -        'id': command.attributes.id,  | 
72 |  | -        'name': command.attributes.name,  | 
73 |  | -        'args': command.attributes.args  | 
74 |  | -      },  | 
75 |  | -      'state': command.state  | 
76 |  | -    }  | 
77 |  | -  }, {log: false}).then((res) => {  | 
78 |  | -  }).catch((err) => {  | 
 | 114 | +  eventsQueue.push({  | 
 | 115 | +    task: 'test_observability_command',  | 
 | 116 | +    data: {  | 
 | 117 | +      'type': 'COMMAND_END',  | 
 | 118 | +      'command': {  | 
 | 119 | +        'attributes': {  | 
 | 120 | +          'id': command.attributes.id,  | 
 | 121 | +          'name': command.attributes.name,  | 
 | 122 | +          'args': command.attributes.args  | 
 | 123 | +        },  | 
 | 124 | +        'state': command.state,  | 
 | 125 | +        finished_at: new Date().toISOString(),  | 
 | 126 | +        location: testRunStarted ? 'test' : 'hook'  | 
 | 127 | +      }  | 
 | 128 | +    },  | 
 | 129 | +    options: { log: false }  | 
79 | 130 |   });  | 
80 | 131 | });  | 
81 | 132 | 
 
  | 
82 | 133 | Cypress.Commands.overwrite('log', (originalFn, ...args) => {  | 
83 |  | -  if(args.includes('test_observability_log') || args.includes('test_observability_command')) return;  | 
 | 134 | +  if (args.includes('test_observability_log') || args.includes('test_observability_command')) return;  | 
84 | 135 |   const message = args.reduce((result, logItem) => {  | 
85 | 136 |     if (typeof logItem === 'object') {  | 
86 | 137 |       return [result, JSON.stringify(logItem)].join(' ');  | 
87 | 138 |     }  | 
88 | 139 | 
 
  | 
89 | 140 |     return [result, logItem ? logItem.toString() : ''].join(' ');  | 
90 | 141 |   }, '');  | 
91 |  | -  cy.now('task', 'test_observability_log', {  | 
92 |  | -    'level': 'info',  | 
93 |  | -    message,  | 
94 |  | -  }, {log: false}).then((res) => {  | 
95 |  | -  }).catch((err) => {  | 
 | 142 | +  eventsQueue.push({  | 
 | 143 | +    task: 'test_observability_log',  | 
 | 144 | +    data: {  | 
 | 145 | +      'level': 'info',  | 
 | 146 | +      message,  | 
 | 147 | +      timestamp: new Date().toISOString()  | 
 | 148 | +    },  | 
 | 149 | +    options: { log: false }  | 
96 | 150 |   });  | 
97 | 151 |   originalFn(...args);  | 
98 | 152 | });  | 
99 | 153 | 
 
  | 
100 | 154 | Cypress.Commands.add('trace', (message, file) => {  | 
101 |  | -  cy.now('task', 'test_observability_log', {  | 
102 |  | -    level: 'trace',  | 
103 |  | -    message,  | 
104 |  | -    file,  | 
105 |  | -  }).then((res) => {  | 
106 |  | -  }).catch((err) => {  | 
 | 155 | +  eventsQueue.push({  | 
 | 156 | +    task: 'test_observability_log',  | 
 | 157 | +    data: {  | 
 | 158 | +      level: 'trace',  | 
 | 159 | +      message,  | 
 | 160 | +      file,  | 
 | 161 | +    },  | 
 | 162 | +    options: { log: false }  | 
107 | 163 |   });  | 
108 | 164 | });  | 
109 | 165 | 
 
  | 
110 | 166 | Cypress.Commands.add('logDebug', (message, file) => {  | 
111 |  | -  cy.now('task', 'test_observability_log', {  | 
112 |  | -    level: 'debug',  | 
113 |  | -    message,  | 
114 |  | -    file,  | 
115 |  | -  }).then((res) => {  | 
116 |  | -  }).catch((err) => {  | 
 | 167 | +  eventsQueue.push({  | 
 | 168 | +    task: 'test_observability_log',  | 
 | 169 | +    data: {  | 
 | 170 | +      level: 'debug',  | 
 | 171 | +      message,  | 
 | 172 | +      file,  | 
 | 173 | +    },  | 
 | 174 | +    options: { log: false }  | 
117 | 175 |   });  | 
118 | 176 | });  | 
119 | 177 | 
 
  | 
120 | 178 | Cypress.Commands.add('info', (message, file) => {  | 
121 |  | -  cy.now('task', 'test_observability_log', {  | 
122 |  | -    level: 'info',  | 
123 |  | -    message,  | 
124 |  | -    file,  | 
125 |  | -  }).then((res) => {  | 
126 |  | -  }).catch((err) => {  | 
 | 179 | +  eventsQueue.push({  | 
 | 180 | +    task: 'test_observability_log',  | 
 | 181 | +    data: {  | 
 | 182 | +      level: 'info',  | 
 | 183 | +      message,  | 
 | 184 | +      file,  | 
 | 185 | +    },  | 
 | 186 | +    options: { log: false }  | 
127 | 187 |   });  | 
128 | 188 | });  | 
129 | 189 | 
 
  | 
130 | 190 | Cypress.Commands.add('warn', (message, file) => {  | 
131 |  | -  cy.now('task', 'test_observability_log', {  | 
132 |  | -    level: 'warn',  | 
133 |  | -    message,  | 
134 |  | -    file,  | 
135 |  | -  }).then((res) => {  | 
136 |  | -  }).catch((err) => {  | 
 | 191 | +  eventsQueue.push({  | 
 | 192 | +    task: 'test_observability_log',  | 
 | 193 | +    data: {  | 
 | 194 | +      level: 'warn',  | 
 | 195 | +      message,  | 
 | 196 | +      file,  | 
 | 197 | +    },  | 
 | 198 | +    options: { log: false }  | 
137 | 199 |   });  | 
138 | 200 | });  | 
139 | 201 | 
 
  | 
140 | 202 | Cypress.Commands.add('error', (message, file) => {  | 
141 |  | -  cy.now('task', 'test_observability_log', {  | 
142 |  | -    level: 'error',  | 
143 |  | -    message,  | 
144 |  | -    file,  | 
145 |  | -  }).then((res) => {  | 
146 |  | -  }).catch((err) => {  | 
 | 203 | +  eventsQueue.push({  | 
 | 204 | +    task: 'test_observability_log',  | 
 | 205 | +    data: {  | 
 | 206 | +      level: 'error',  | 
 | 207 | +      message,  | 
 | 208 | +      file,  | 
 | 209 | +    },  | 
 | 210 | +    options: { log: false }  | 
147 | 211 |   });  | 
148 | 212 | });  | 
149 | 213 | 
 
  | 
150 | 214 | Cypress.Commands.add('fatal', (message, file) => {  | 
151 |  | -  cy.now('task', 'test_observability_log', {  | 
152 |  | -    level: 'fatal',  | 
153 |  | -    message,  | 
154 |  | -    file,  | 
155 |  | -  }).then((res) => {  | 
156 |  | -  }).catch((err) => {  | 
 | 215 | +  eventsQueue.push({  | 
 | 216 | +    task: 'test_observability_log',  | 
 | 217 | +    data: {  | 
 | 218 | +      level: 'fatal',  | 
 | 219 | +      message,  | 
 | 220 | +      file,  | 
 | 221 | +    },  | 
 | 222 | +    options: { log: false }  | 
157 | 223 |   });  | 
158 | 224 | });  | 
 | 225 | + | 
 | 226 | +beforeEach(() => {  | 
 | 227 | +  /* browserstack internal helper hook */  | 
 | 228 | + | 
 | 229 | +  if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) {  | 
 | 230 | +    return;  | 
 | 231 | +  }  | 
 | 232 | + | 
 | 233 | +  if (eventsQueue.length > 0) {  | 
 | 234 | +    eventsQueue.forEach(event => {  | 
 | 235 | +      cy.task(event.task, event.data, event.options);  | 
 | 236 | +    });  | 
 | 237 | +  }  | 
 | 238 | +  eventsQueue = [];  | 
 | 239 | +  testRunStarted = true;  | 
 | 240 | +});  | 
 | 241 | + | 
 | 242 | +afterEach(function() {  | 
 | 243 | +  /* browserstack internal helper hook */  | 
 | 244 | +  if (!Cypress.env('BROWSERSTACK_O11Y_LOGS')) {  | 
 | 245 | +    return;  | 
 | 246 | +  }  | 
 | 247 | + | 
 | 248 | +  if (eventsQueue.length > 0) {  | 
 | 249 | +    eventsQueue.forEach(event => {  | 
 | 250 | +      cy.task(event.task, event.data, event.options);  | 
 | 251 | +    });  | 
 | 252 | +  }  | 
 | 253 | +    | 
 | 254 | +  eventsQueue = [];  | 
 | 255 | +  testRunStarted = false;  | 
 | 256 | +});  | 
0 commit comments