Skip to content

Commit 9b15623

Browse files
committed
Adding Tests for the failure logs
1 parent 0657297 commit 9b15623

File tree

2 files changed

+335
-18
lines changed

2 files changed

+335
-18
lines changed

src/tools/failurelogs-utils/automate.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,21 @@ export async function retrieveNetworkFailures(sessionId: string): Promise<any> {
3434
);
3535

3636
// Return only the failure entries with some context
37-
return {
38-
failures: failureEntries.map((entry: any) => ({
39-
startedDateTime: entry.startedDateTime,
40-
request: {
41-
method: entry.request?.method,
42-
url: entry.request?.url,
43-
queryString: entry.request?.queryString,
44-
},
45-
response: {
46-
status: entry.response?.status,
47-
statusText: entry.response?.statusText,
48-
_error: entry.response?._error,
49-
},
50-
serverIPAddress: entry.serverIPAddress,
51-
time: entry.time,
52-
})),
53-
totalFailures: failureEntries.length,
54-
};
37+
return failureEntries.map((entry: any) => ({
38+
startedDateTime: entry.startedDateTime,
39+
request: {
40+
method: entry.request?.method,
41+
url: entry.request?.url,
42+
queryString: entry.request?.queryString,
43+
},
44+
response: {
45+
status: entry.response?.status,
46+
statusText: entry.response?.statusText,
47+
_error: entry.response?._error,
48+
},
49+
serverIPAddress: entry.serverIPAddress,
50+
time: entry.time,
51+
}));
5552
}
5653

5754
// SESSION LOGS

tests/tools/getFailureLogs.test.ts

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
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

Comments
 (0)