|
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