Skip to content

Commit 9bf91db

Browse files
Merge pull request certinia#721 from lukecotter/chore-lana-tests
test: add unit tests for lana vscode extensions
2 parents 94611f1 + d1c46fc commit 9bf91db

12 files changed

Lines changed: 3316 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ node_modules
77
dist/
88
build/
99
out/
10+
coverage/

jest.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ export default {
4444
...defaultConfig,
4545
displayName: 'lana',
4646
rootDir: '<rootDir>/lana',
47+
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.ts'],
48+
moduleNameMapper: {
49+
...defaultConfig.moduleNameMapper,
50+
'^vscode$': '<rootDir>/src/__tests__/mocks/vscode.ts',
51+
'^apex-log-parser$': '<rootDir>/../apex-log-parser/src/index.ts',
52+
},
4753
transformIgnorePatterns: [
4854
// allow lit/@lit transformation
4955
'<rootDir>/node_modules/(?!@?lit)',
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/*
2+
* Copyright (c) 2026 Certinia Inc. All rights reserved.
3+
*/
4+
5+
/**
6+
* Factory functions for building test data in lana tests.
7+
*/
8+
9+
import type { ApexLog, LogEvent } from 'apex-log-parser';
10+
11+
import { createMockExtensionContext, type MockExtensionContext } from '../mocks/vscode.js';
12+
13+
/**
14+
* Partial type for creating mock LogEvent objects.
15+
* Only requires the fields you want to set, everything else gets defaults.
16+
*/
17+
type PartialLogEvent = Partial<{
18+
type: string | null;
19+
text: string;
20+
timestamp: number;
21+
exitStamp: number | null;
22+
children: LogEvent[];
23+
parent: LogEvent | null;
24+
duration: { self: number; total: number };
25+
soqlCount: { self: number; total: number };
26+
soqlRowCount: { self: number; total: number };
27+
dmlCount: { self: number; total: number };
28+
dmlRowCount: { self: number; total: number };
29+
totalThrownCount: number;
30+
lineNumber: number | 'EXTERNAL' | null;
31+
namespace: string;
32+
logLine: string;
33+
isExit: boolean;
34+
isParent: boolean;
35+
isTruncated: boolean;
36+
}>;
37+
38+
/**
39+
* Creates a mock LogEvent with sensible defaults.
40+
* All properties are optional - specify only what you need for the test.
41+
*/
42+
export function createMockLogEvent(overrides: PartialLogEvent = {}): LogEvent {
43+
const base = {
44+
logParser: {} as unknown,
45+
parent: null,
46+
children: [],
47+
type: 'METHOD_ENTRY' as const,
48+
logLine: '',
49+
text: 'Test Event',
50+
acceptsText: false,
51+
isExit: false,
52+
isParent: false,
53+
isTruncated: false,
54+
nextLineIsExit: false,
55+
lineNumber: null,
56+
namespace: 'default',
57+
hasValidSymbols: false,
58+
suffix: null,
59+
discontinuity: false,
60+
timestamp: 1000000,
61+
exitStamp: 2000000,
62+
subCategory: 'Method' as const,
63+
cpuType: 'method' as const,
64+
duration: { self: 1000000, total: 1000000 },
65+
dmlRowCount: { self: 0, total: 0 },
66+
soqlRowCount: { self: 0, total: 0 },
67+
soslRowCount: { self: 0, total: 0 },
68+
dmlCount: { self: 0, total: 0 },
69+
soqlCount: { self: 0, total: 0 },
70+
soslCount: { self: 0, total: 0 },
71+
totalThrownCount: 0,
72+
exitTypes: [],
73+
recalculateDurations: jest.fn(),
74+
};
75+
76+
return { ...base, ...overrides } as unknown as LogEvent;
77+
}
78+
79+
/**
80+
* Partial type for creating mock ApexLog objects.
81+
*/
82+
type PartialApexLog = Partial<{
83+
children: LogEvent[];
84+
timestamp: number;
85+
exitStamp: number;
86+
size: number;
87+
namespaces: string[];
88+
duration: { self: number; total: number };
89+
}>;
90+
91+
/**
92+
* Creates a mock ApexLog with sensible defaults.
93+
* Useful for testing components that work with parsed log data.
94+
*/
95+
export function createMockApexLog(overrides: PartialApexLog = {}): ApexLog {
96+
const base = {
97+
logParser: {} as unknown,
98+
parent: null,
99+
children: [],
100+
type: null,
101+
logLine: '',
102+
text: 'LOG_ROOT',
103+
acceptsText: false,
104+
isExit: false,
105+
isParent: false,
106+
isTruncated: false,
107+
nextLineIsExit: false,
108+
lineNumber: null,
109+
namespace: '',
110+
hasValidSymbols: false,
111+
suffix: null,
112+
discontinuity: false,
113+
timestamp: 0,
114+
exitStamp: 0,
115+
subCategory: '' as const,
116+
cpuType: '' as const,
117+
duration: { self: 0, total: 0 },
118+
dmlRowCount: { self: 0, total: 0 },
119+
soqlRowCount: { self: 0, total: 0 },
120+
soslRowCount: { self: 0, total: 0 },
121+
dmlCount: { self: 0, total: 0 },
122+
soqlCount: { self: 0, total: 0 },
123+
soslCount: { self: 0, total: 0 },
124+
totalThrownCount: 0,
125+
exitTypes: [],
126+
recalculateDurations: jest.fn(),
127+
setTimes: jest.fn(),
128+
size: 0,
129+
debugLevels: [],
130+
namespaces: [],
131+
logIssues: [],
132+
parsingErrors: [],
133+
governorLimits: {
134+
soqlQueries: { used: 0, limit: 0 },
135+
soslQueries: { used: 0, limit: 0 },
136+
queryRows: { used: 0, limit: 0 },
137+
dmlStatements: { used: 0, limit: 0 },
138+
publishImmediateDml: { used: 0, limit: 0 },
139+
dmlRows: { used: 0, limit: 0 },
140+
cpuTime: { used: 0, limit: 0 },
141+
heapSize: { used: 0, limit: 0 },
142+
callouts: { used: 0, limit: 0 },
143+
emailInvocations: { used: 0, limit: 0 },
144+
futureCalls: { used: 0, limit: 0 },
145+
queueableJobsAddedToQueue: { used: 0, limit: 0 },
146+
mobileApexPushCalls: { used: 0, limit: 0 },
147+
byNamespace: new Map(),
148+
snapshots: [],
149+
},
150+
executionEndTime: 0,
151+
};
152+
153+
return { ...base, ...overrides } as unknown as ApexLog;
154+
}
155+
156+
/**
157+
* Mock Display object for Context.
158+
*/
159+
export interface MockDisplay {
160+
output: jest.Mock;
161+
showErrorMessage: jest.Mock;
162+
showInformationMessage: jest.Mock;
163+
showWarningMessage: jest.Mock;
164+
}
165+
166+
export function createMockDisplay(): MockDisplay {
167+
return {
168+
output: jest.fn(),
169+
showErrorMessage: jest.fn(),
170+
showInformationMessage: jest.fn(),
171+
showWarningMessage: jest.fn(),
172+
};
173+
}
174+
175+
/**
176+
* Mock Context for testing command handlers and features.
177+
*/
178+
export interface MockContext {
179+
context: MockExtensionContext;
180+
display: MockDisplay;
181+
workspaces: { uri: { fsPath: string }; name: string }[];
182+
symbolFinder: { findSymbol: jest.Mock };
183+
findSymbol: jest.Mock;
184+
}
185+
186+
/**
187+
* Creates a mock Context object for testing.
188+
* Includes mocked ExtensionContext, Display, and symbolFinder.
189+
*/
190+
export function createMockContext(overrides: Partial<MockContext> = {}): MockContext {
191+
const display = createMockDisplay();
192+
const context = createMockExtensionContext();
193+
194+
const base: MockContext = {
195+
context,
196+
display,
197+
workspaces: [],
198+
symbolFinder: { findSymbol: jest.fn().mockResolvedValue([]) },
199+
findSymbol: jest.fn().mockResolvedValue([]),
200+
};
201+
202+
return { ...base, ...overrides };
203+
}
204+
205+
/**
206+
* Creates a simple event tree for testing hierarchical event searches.
207+
* Returns parent with nested children at specified depths.
208+
*/
209+
export function createMockEventTree(config: {
210+
rootTimestamp: number;
211+
rootExitStamp: number;
212+
childConfigs?: Array<{
213+
timestamp: number;
214+
exitStamp: number;
215+
children?: Array<{ timestamp: number; exitStamp: number }>;
216+
}>;
217+
}): LogEvent {
218+
const root = createMockLogEvent({
219+
timestamp: config.rootTimestamp,
220+
exitStamp: config.rootExitStamp,
221+
children: [],
222+
});
223+
224+
if (config.childConfigs) {
225+
root.children = config.childConfigs.map((childConfig) => {
226+
const child = createMockLogEvent({
227+
timestamp: childConfig.timestamp,
228+
exitStamp: childConfig.exitStamp,
229+
parent: root,
230+
children: [],
231+
});
232+
233+
if (childConfig.children) {
234+
child.children = childConfig.children.map((grandchildConfig) =>
235+
createMockLogEvent({
236+
timestamp: grandchildConfig.timestamp,
237+
exitStamp: grandchildConfig.exitStamp,
238+
parent: child,
239+
}),
240+
);
241+
}
242+
243+
return child;
244+
});
245+
}
246+
247+
return root;
248+
}

0 commit comments

Comments
 (0)