|
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