1
+ import { getFailureLogs } from '../../src/tools/getFailureLogs' ;
2
+ import * as automate from '../../src/tools/failurelogs-utils/automate' ;
3
+ import * as appAutomate from '../../src/tools/failurelogs-utils/app-automate' ;
4
+
5
+ jest . mock ( '../../src/config' , ( ) => ( {
6
+ __esModule : true ,
7
+ default : {
8
+ browserstackUsername : 'fake-user' ,
9
+ browserstackAccessKey : 'fake-key' ,
10
+ } ,
11
+ } ) ) ;
12
+
13
+ jest . mock ( '../../src/lib/instrumentation' , ( ) => ( {
14
+ trackMCP : jest . fn ( )
15
+ } ) ) ;
16
+
17
+ // Mock the utility functions with implementations
18
+ jest . mock ( '../../src/tools/failurelogs-utils/automate' , ( ) => ( {
19
+ retrieveNetworkFailures : jest . fn ( ) ,
20
+ retrieveSessionFailures : jest . fn ( ) ,
21
+ retrieveConsoleFailures : jest . fn ( ) ,
22
+ filterSessionFailures : jest . fn ( ( text : string ) => {
23
+ const lines = text . split ( '\n' ) ;
24
+ return lines . filter ( ( line : string ) =>
25
+ line . includes ( 'ERROR' ) ||
26
+ line . includes ( 'EXCEPTION' ) ||
27
+ line . includes ( 'FATAL' )
28
+ ) ;
29
+ } ) ,
30
+ filterConsoleFailures : jest . fn ( ( text : string ) => {
31
+ const lines = text . split ( '\n' ) ;
32
+ return lines . filter ( ( line : string ) =>
33
+ line . includes ( 'Failed to load resource' ) ||
34
+ line . includes ( 'Uncaught TypeError' )
35
+ ) ;
36
+ } ) ,
37
+ } ) ) ;
38
+
39
+ jest . mock ( '../../src/tools/failurelogs-utils/app-automate' , ( ) => ( {
40
+ retrieveDeviceLogs : jest . fn ( ) ,
41
+ retrieveAppiumLogs : jest . fn ( ) ,
42
+ retrieveCrashLogs : jest . fn ( ) ,
43
+ filterDeviceFailures : jest . fn ( ( ) => [ ] ) ,
44
+ filterAppiumFailures : jest . fn ( ( ) => [ ] ) ,
45
+ filterCrashFailures : jest . fn ( ( ) => [ ] ) ,
46
+ } ) ) ;
47
+
48
+ // Mock fetch
49
+ const mockFetch = jest . fn ( ) ;
50
+ global . fetch = mockFetch ;
51
+
52
+ describe ( 'BrowserStack Failure Logs' , ( ) => {
53
+ const mockSessionId = 'test-session-id' ;
54
+ const mockBuildId = 'test-build-id' ;
55
+ const auth = Buffer . from ( 'fake-user:fake-key' ) . toString ( 'base64' ) ;
56
+
57
+ beforeEach ( ( ) => {
58
+ jest . clearAllMocks ( ) ;
59
+ mockFetch . mockClear ( ) ;
60
+ } ) ;
61
+
62
+ afterEach ( ( ) => {
63
+ jest . resetAllMocks ( ) ;
64
+ } ) ;
65
+
66
+ describe ( 'getFailureLogs - Input Validation' , ( ) => {
67
+ it ( 'should throw error if sessionId is not provided' , async ( ) => {
68
+ await expect ( getFailureLogs ( {
69
+ sessionId : '' ,
70
+ logTypes : [ 'networkLogs' ] ,
71
+ sessionType : 'automate'
72
+ } ) ) . rejects . toThrow ( 'Session ID is required' ) ;
73
+ } ) ;
74
+
75
+ it ( 'should throw error if buildId is not provided for app-automate session' , async ( ) => {
76
+ await expect ( getFailureLogs ( {
77
+ sessionId : 'test-session' ,
78
+ logTypes : [ 'deviceLogs' ] ,
79
+ sessionType : 'app-automate'
80
+ } ) ) . rejects . toThrow ( 'Build ID is required for app-automate sessions' ) ;
81
+ } ) ;
82
+
83
+ it ( 'should return error for invalid log types' , async ( ) => {
84
+ const result = await getFailureLogs ( {
85
+ sessionId : 'test-session' ,
86
+ logTypes : [ 'invalidLogType' ] as any ,
87
+ sessionType : 'automate'
88
+ } ) ;
89
+
90
+ expect ( result . content [ 0 ] . isError ) . toBe ( true ) ;
91
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Invalid log type' ) ;
92
+ } ) ;
93
+
94
+ it ( 'should return error when mixing session types' , async ( ) => {
95
+ const automateResult = await getFailureLogs ( {
96
+ sessionId : 'test-session' ,
97
+ logTypes : [ 'deviceLogs' ] ,
98
+ sessionType : 'automate'
99
+ } ) ;
100
+
101
+ const appAutomateResult = await getFailureLogs ( {
102
+ sessionId : 'test-session' ,
103
+ buildId : 'test-build' ,
104
+ logTypes : [ 'networkLogs' ] ,
105
+ sessionType : 'app-automate'
106
+ } ) ;
107
+
108
+ expect ( automateResult . content [ 0 ] . isError ) . toBe ( true ) ;
109
+ expect ( appAutomateResult . content [ 0 ] . isError ) . toBe ( true ) ;
110
+ } ) ;
111
+ } ) ;
112
+
113
+ describe ( 'Automate Session Logs' , ( ) => {
114
+ const mockNetworkFailures = {
115
+ failures : [
116
+ {
117
+ startedDateTime : '2024-03-20T10:00:00Z' ,
118
+ request : { method : 'GET' , url : 'https://test.com' } ,
119
+ response : { status : 404 , statusText : 'Not Found' }
120
+ }
121
+ ] ,
122
+ totalFailures : 1
123
+ } ;
124
+
125
+ beforeEach ( ( ) => {
126
+ // Reset all mocks
127
+ jest . clearAllMocks ( ) ;
128
+
129
+ // Setup mock implementations with resolved values
130
+ jest . mocked ( automate . retrieveNetworkFailures ) . mockResolvedValue ( mockNetworkFailures ) ;
131
+ jest . mocked ( automate . retrieveSessionFailures ) . mockResolvedValue ( [ '[ERROR] Test failed' ] ) ;
132
+ jest . mocked ( automate . retrieveConsoleFailures ) . mockResolvedValue ( [ 'Uncaught TypeError' ] ) ;
133
+ } ) ;
134
+
135
+ it ( 'should fetch network logs successfully' , async ( ) => {
136
+ // Mock successful response with failures
137
+ const mockFailures = [
138
+ {
139
+ startedDateTime : '2024-03-20T10:00:00Z' ,
140
+ request : { method : 'GET' , url : 'https://test.com' } ,
141
+ response : { status : 404 , statusText : 'Not Found' }
142
+ }
143
+ ] ;
144
+ jest . mocked ( automate . retrieveNetworkFailures ) . mockResolvedValue ( mockFailures ) ;
145
+
146
+ const result = await getFailureLogs ( {
147
+ sessionId : mockSessionId ,
148
+ logTypes : [ 'networkLogs' ] ,
149
+ sessionType : 'automate'
150
+ } ) ;
151
+
152
+ expect ( automate . retrieveNetworkFailures ) . toHaveBeenCalledWith ( mockSessionId ) ;
153
+ expect ( result . content [ 0 ] . type ) . toBe ( 'text' ) ;
154
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Network Failures (1 found)' ) ;
155
+ } ) ;
156
+
157
+ it ( 'should fetch session logs successfully' , async ( ) => {
158
+ const result = await getFailureLogs ( {
159
+ sessionId : mockSessionId ,
160
+ logTypes : [ 'sessionLogs' ] ,
161
+ sessionType : 'automate'
162
+ } ) ;
163
+
164
+ expect ( automate . retrieveSessionFailures ) . toHaveBeenCalledWith ( mockSessionId ) ;
165
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Session Failures (1 found)' ) ;
166
+ expect ( result . content [ 0 ] . text ) . toContain ( '[ERROR] Test failed' ) ;
167
+ } ) ;
168
+
169
+ it ( 'should fetch console logs successfully' , async ( ) => {
170
+ const result = await getFailureLogs ( {
171
+ sessionId : mockSessionId ,
172
+ logTypes : [ 'consoleLogs' ] ,
173
+ sessionType : 'automate'
174
+ } ) ;
175
+
176
+ expect ( automate . retrieveConsoleFailures ) . toHaveBeenCalledWith ( mockSessionId ) ;
177
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Console Failures (1 found)' ) ;
178
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Uncaught TypeError' ) ;
179
+ } ) ;
180
+ } ) ;
181
+
182
+ describe ( 'App-Automate Session Logs' , ( ) => {
183
+ const mockDeviceLogs = [ 'Fatal Exception: NullPointerException' ] ;
184
+ const mockAppiumLogs = [ 'Error: Element not found' ] ;
185
+ const mockCrashLogs = [ 'Application crashed due to signal 11' ] ;
186
+
187
+ beforeEach ( ( ) => {
188
+ jest . mocked ( appAutomate . retrieveDeviceLogs ) . mockResolvedValue ( mockDeviceLogs ) ;
189
+ jest . mocked ( appAutomate . retrieveAppiumLogs ) . mockResolvedValue ( mockAppiumLogs ) ;
190
+ jest . mocked ( appAutomate . retrieveCrashLogs ) . mockResolvedValue ( mockCrashLogs ) ;
191
+ } ) ;
192
+
193
+ it ( 'should fetch device logs successfully' , async ( ) => {
194
+ const result = await getFailureLogs ( {
195
+ sessionId : mockSessionId ,
196
+ buildId : mockBuildId ,
197
+ logTypes : [ 'deviceLogs' ] ,
198
+ sessionType : 'app-automate'
199
+ } ) ;
200
+
201
+ expect ( appAutomate . retrieveDeviceLogs ) . toHaveBeenCalledWith ( mockSessionId , mockBuildId ) ;
202
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Device Failures (1 found)' ) ;
203
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Fatal Exception' ) ;
204
+ } ) ;
205
+
206
+ it ( 'should fetch appium logs successfully' , async ( ) => {
207
+ const result = await getFailureLogs ( {
208
+ sessionId : mockSessionId ,
209
+ buildId : mockBuildId ,
210
+ logTypes : [ 'appiumLogs' ] ,
211
+ sessionType : 'app-automate'
212
+ } ) ;
213
+
214
+ expect ( appAutomate . retrieveAppiumLogs ) . toHaveBeenCalledWith ( mockSessionId , mockBuildId ) ;
215
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Appium Failures (1 found)' ) ;
216
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Element not found' ) ;
217
+ } ) ;
218
+
219
+ it ( 'should fetch crash logs successfully' , async ( ) => {
220
+ const result = await getFailureLogs ( {
221
+ sessionId : mockSessionId ,
222
+ buildId : mockBuildId ,
223
+ logTypes : [ 'crashLogs' ] ,
224
+ sessionType : 'app-automate'
225
+ } ) ;
226
+
227
+ expect ( appAutomate . retrieveCrashLogs ) . toHaveBeenCalledWith ( mockSessionId , mockBuildId ) ;
228
+ expect ( result . content [ 0 ] . text ) . toContain ( 'Crash Failures (1 found)' ) ;
229
+ expect ( result . content [ 0 ] . text ) . toContain ( 'signal 11' ) ;
230
+ } ) ;
231
+ } ) ;
232
+
233
+ describe ( 'Error Handling' , ( ) => {
234
+ it ( 'should handle empty log responses' , async ( ) => {
235
+ jest . mocked ( automate . retrieveNetworkFailures ) . mockResolvedValue ( [ ] ) ;
236
+
237
+ const result = await getFailureLogs ( {
238
+ sessionId : mockSessionId ,
239
+ logTypes : [ 'networkLogs' ] ,
240
+ sessionType : 'automate'
241
+ } ) ;
242
+
243
+ expect ( result . content [ 0 ] . text ) . toBe ( 'No network failures found' ) ;
244
+ } ) ;
245
+ } ) ;
246
+
247
+ describe ( 'Log Filtering' , ( ) => {
248
+ beforeEach ( ( ) => {
249
+ // Reset mock implementations before each test
250
+ jest . mocked ( automate . filterSessionFailures ) . mockImplementation ( ( text : string ) => {
251
+ const lines = text . split ( '\n' ) ;
252
+ return lines . filter ( ( line : string ) =>
253
+ line . includes ( 'ERROR' ) ||
254
+ line . includes ( 'EXCEPTION' ) ||
255
+ line . includes ( 'FATAL' )
256
+ ) ;
257
+ } ) ;
258
+
259
+ jest . mocked ( automate . filterConsoleFailures ) . mockImplementation ( ( text : string ) => {
260
+ const lines = text . split ( '\n' ) ;
261
+ return lines . filter ( ( line : string ) =>
262
+ line . includes ( 'Failed to load resource' ) ||
263
+ line . includes ( 'Uncaught TypeError' )
264
+ ) ;
265
+ } ) ;
266
+
267
+ jest . mocked ( appAutomate . filterDeviceFailures ) . mockReturnValue ( [ ] ) ;
268
+ jest . mocked ( appAutomate . filterAppiumFailures ) . mockReturnValue ( [ ] ) ;
269
+ jest . mocked ( appAutomate . filterCrashFailures ) . mockReturnValue ( [ ] ) ;
270
+ } ) ;
271
+
272
+ it ( 'should filter session logs correctly' , ( ) => {
273
+ const logText = `
274
+ [INFO] Starting test
275
+ [ERROR] Test failed
276
+ [INFO] Continuing
277
+ [EXCEPTION] NullPointerException
278
+ [FATAL] Process crashed
279
+ [INFO] Test completed
280
+ ` ;
281
+
282
+ const result = jest . mocked ( automate . filterSessionFailures ) ( logText ) ;
283
+ expect ( result ) . toEqual ( [
284
+ '[ERROR] Test failed' ,
285
+ '[EXCEPTION] NullPointerException' ,
286
+ '[FATAL] Process crashed'
287
+ ] ) ;
288
+ } ) ;
289
+
290
+ it ( 'should filter console logs correctly' , ( ) => {
291
+ const logText = `
292
+ console.log('Starting test')
293
+ console.error('Failed to load resource')
294
+ console.info('Test progress')
295
+ console.error('Uncaught TypeError')
296
+ ` ;
297
+
298
+ const result = jest . mocked ( automate . filterConsoleFailures ) ( logText ) ;
299
+ expect ( result ) . toEqual ( [
300
+ "console.error('Failed to load resource')" ,
301
+ "console.error('Uncaught TypeError')"
302
+ ] ) ;
303
+ } ) ;
304
+
305
+ it ( 'should handle empty inputs in filters' , ( ) => {
306
+ const emptyResult : string [ ] = [ ] ;
307
+ jest . mocked ( automate . filterSessionFailures ) . mockReturnValue ( emptyResult ) ;
308
+ jest . mocked ( automate . filterConsoleFailures ) . mockReturnValue ( emptyResult ) ;
309
+ jest . mocked ( appAutomate . filterDeviceFailures ) . mockReturnValue ( emptyResult ) ;
310
+ jest . mocked ( appAutomate . filterAppiumFailures ) . mockReturnValue ( emptyResult ) ;
311
+ jest . mocked ( appAutomate . filterCrashFailures ) . mockReturnValue ( emptyResult ) ;
312
+
313
+ expect ( automate . filterSessionFailures ( '' ) ) . toEqual ( [ ] ) ;
314
+ expect ( automate . filterConsoleFailures ( '' ) ) . toEqual ( [ ] ) ;
315
+ expect ( appAutomate . filterDeviceFailures ( '' ) ) . toEqual ( [ ] ) ;
316
+ expect ( appAutomate . filterAppiumFailures ( '' ) ) . toEqual ( [ ] ) ;
317
+ expect ( appAutomate . filterCrashFailures ( '' ) ) . toEqual ( [ ] ) ;
318
+ } ) ;
319
+ } ) ;
320
+ } ) ;
0 commit comments