@hyperfixi/testing-framework 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assertions-BImuwZsB.d.mts +458 -0
- package/dist/assertions-BImuwZsB.d.ts +458 -0
- package/dist/assertions.d.mts +1 -0
- package/dist/assertions.d.ts +1 -0
- package/dist/assertions.js +625 -0
- package/dist/assertions.js.map +1 -0
- package/dist/assertions.mjs +621 -0
- package/dist/assertions.mjs.map +1 -0
- package/dist/index.d.mts +246 -0
- package/dist/index.d.ts +246 -0
- package/dist/index.js +1465 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1435 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +121 -0
- package/src/assertions.test.ts +490 -0
- package/src/assertions.ts +779 -0
- package/src/index.test.ts +490 -0
- package/src/index.ts +601 -0
- package/src/jsdom.d.ts +7 -0
- package/src/multilingual/README.md +295 -0
- package/src/multilingual/bundle-builder.ts +345 -0
- package/src/multilingual/cli.ts +224 -0
- package/src/multilingual/index.ts +68 -0
- package/src/multilingual/orchestrator.ts +275 -0
- package/src/multilingual/pattern-loader.ts +176 -0
- package/src/multilingual/reporters/console-reporter.ts +294 -0
- package/src/multilingual/reporters/json-reporter.ts +99 -0
- package/src/multilingual/reporters/regression-reporter.ts +263 -0
- package/src/multilingual/tools/analyze-failures.ts +326 -0
- package/src/multilingual/types.ts +268 -0
- package/src/multilingual/validators/parse-diagnostics.ts +337 -0
- package/src/multilingual/validators/parse-validator.ts +193 -0
- package/src/multilingual/validators/size-validator.ts +187 -0
- package/src/runner.test.ts +813 -0
- package/src/runner.ts +639 -0
- package/src/types.ts +505 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for Testing Framework
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Test environment types
|
|
7
|
+
*/
|
|
8
|
+
export type TestEnvironment = 'jsdom' | 'browser' | 'node' | 'playwright' | 'puppeteer';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Test status
|
|
12
|
+
*/
|
|
13
|
+
export type TestStatus = 'pending' | 'running' | 'passed' | 'failed' | 'skipped';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Browser type for testing
|
|
17
|
+
*/
|
|
18
|
+
export type BrowserType = 'chromium' | 'firefox' | 'webkit' | 'chrome' | 'edge';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Test configuration
|
|
22
|
+
*/
|
|
23
|
+
export interface TestConfig {
|
|
24
|
+
environment: TestEnvironment;
|
|
25
|
+
browser?: BrowserType;
|
|
26
|
+
timeout: number;
|
|
27
|
+
retries: number;
|
|
28
|
+
parallel: boolean;
|
|
29
|
+
maxWorkers: number;
|
|
30
|
+
headless: boolean;
|
|
31
|
+
viewport: {
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
};
|
|
35
|
+
baseURL?: string;
|
|
36
|
+
setupFiles: string[];
|
|
37
|
+
teardownFiles: string[];
|
|
38
|
+
reporters: TestReporter[];
|
|
39
|
+
coverage: {
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
threshold: number;
|
|
42
|
+
include: string[];
|
|
43
|
+
exclude: string[];
|
|
44
|
+
};
|
|
45
|
+
fixtures: Record<string, any>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Test suite definition
|
|
50
|
+
*/
|
|
51
|
+
export interface TestSuite {
|
|
52
|
+
name: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
setup?: () => Promise<void> | void;
|
|
55
|
+
teardown?: () => Promise<void> | void;
|
|
56
|
+
beforeEach?: () => Promise<void> | void;
|
|
57
|
+
afterEach?: () => Promise<void> | void;
|
|
58
|
+
tests: TestCase[];
|
|
59
|
+
suites: TestSuite[];
|
|
60
|
+
timeout?: number;
|
|
61
|
+
retries?: number;
|
|
62
|
+
skip?: boolean;
|
|
63
|
+
only?: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Test case definition
|
|
68
|
+
*/
|
|
69
|
+
export interface TestCase {
|
|
70
|
+
name: string;
|
|
71
|
+
description?: string;
|
|
72
|
+
fn: TestFunction;
|
|
73
|
+
timeout?: number;
|
|
74
|
+
retries?: number;
|
|
75
|
+
skip?: boolean;
|
|
76
|
+
only?: boolean;
|
|
77
|
+
tags: string[];
|
|
78
|
+
fixtures: Record<string, any>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Test function signature
|
|
83
|
+
*/
|
|
84
|
+
export type TestFunction = (context: TestContext) => Promise<void> | void;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Test context
|
|
88
|
+
*/
|
|
89
|
+
export interface TestContext {
|
|
90
|
+
// Test metadata
|
|
91
|
+
suite: string;
|
|
92
|
+
test: string;
|
|
93
|
+
environment: TestEnvironment;
|
|
94
|
+
|
|
95
|
+
// Browser context (if applicable)
|
|
96
|
+
page?: any; // Browser page instance
|
|
97
|
+
browser?: any; // Browser instance
|
|
98
|
+
|
|
99
|
+
// DOM context
|
|
100
|
+
document?: Document;
|
|
101
|
+
window?: Window;
|
|
102
|
+
|
|
103
|
+
// HyperFixi context
|
|
104
|
+
hyperfixi?: any;
|
|
105
|
+
|
|
106
|
+
// Utilities
|
|
107
|
+
fixtures: Record<string, any>;
|
|
108
|
+
expect: ExpectAPI;
|
|
109
|
+
assert: AssertAPI;
|
|
110
|
+
|
|
111
|
+
// Lifecycle methods
|
|
112
|
+
skip: () => void;
|
|
113
|
+
fail: (message: string) => never;
|
|
114
|
+
timeout: (ms: number) => void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Test result
|
|
119
|
+
*/
|
|
120
|
+
export interface TestResult {
|
|
121
|
+
suite: string;
|
|
122
|
+
test: string;
|
|
123
|
+
status: TestStatus;
|
|
124
|
+
duration: number;
|
|
125
|
+
error?: TestError | undefined;
|
|
126
|
+
logs: TestLog[];
|
|
127
|
+
screenshots: string[];
|
|
128
|
+
coverage?: CoverageData | undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Test error
|
|
133
|
+
*/
|
|
134
|
+
export interface TestError {
|
|
135
|
+
name: string;
|
|
136
|
+
message: string;
|
|
137
|
+
stack?: string | undefined;
|
|
138
|
+
code?: string | undefined;
|
|
139
|
+
expected?: any | undefined;
|
|
140
|
+
actual?: any | undefined;
|
|
141
|
+
diff?: string | undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Test log entry
|
|
146
|
+
*/
|
|
147
|
+
export interface TestLog {
|
|
148
|
+
level: 'info' | 'warn' | 'error' | 'debug';
|
|
149
|
+
message: string;
|
|
150
|
+
timestamp: number;
|
|
151
|
+
source?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Coverage data
|
|
156
|
+
*/
|
|
157
|
+
export interface CoverageData {
|
|
158
|
+
lines: {
|
|
159
|
+
total: number;
|
|
160
|
+
covered: number;
|
|
161
|
+
percentage: number;
|
|
162
|
+
};
|
|
163
|
+
functions: {
|
|
164
|
+
total: number;
|
|
165
|
+
covered: number;
|
|
166
|
+
percentage: number;
|
|
167
|
+
};
|
|
168
|
+
branches: {
|
|
169
|
+
total: number;
|
|
170
|
+
covered: number;
|
|
171
|
+
percentage: number;
|
|
172
|
+
};
|
|
173
|
+
statements: {
|
|
174
|
+
total: number;
|
|
175
|
+
covered: number;
|
|
176
|
+
percentage: number;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Test reporter interface
|
|
182
|
+
*/
|
|
183
|
+
export interface TestReporter {
|
|
184
|
+
name: string;
|
|
185
|
+
onStart: (suites: TestSuite[]) => void;
|
|
186
|
+
onSuiteStart: (suite: TestSuite) => void;
|
|
187
|
+
onSuiteEnd: (suite: TestSuite, results: TestResult[]) => void;
|
|
188
|
+
onTestStart: (test: TestCase) => void;
|
|
189
|
+
onTestEnd: (test: TestCase, result: TestResult) => void;
|
|
190
|
+
onEnd: (results: TestResult[]) => void;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Expect API for assertions
|
|
195
|
+
*/
|
|
196
|
+
export interface ExpectAPI {
|
|
197
|
+
(actual: any): ExpectMatcher;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Expect matcher interface
|
|
202
|
+
*/
|
|
203
|
+
export interface ExpectMatcher {
|
|
204
|
+
// Basic matchers
|
|
205
|
+
toBe(expected: any): void;
|
|
206
|
+
toEqual(expected: any): void;
|
|
207
|
+
toStrictEqual(expected: any): void;
|
|
208
|
+
toBeTruthy(): void;
|
|
209
|
+
toBeFalsy(): void;
|
|
210
|
+
toBeNull(): void;
|
|
211
|
+
toBeUndefined(): void;
|
|
212
|
+
toBeDefined(): void;
|
|
213
|
+
toBeNaN(): void;
|
|
214
|
+
toBeInstanceOf(constructor: any): void;
|
|
215
|
+
toBeCloseTo(expected: number, precision?: number): void;
|
|
216
|
+
|
|
217
|
+
// String matchers
|
|
218
|
+
toMatch(expected: string | RegExp): void;
|
|
219
|
+
toContain(expected: any): void;
|
|
220
|
+
toHaveLength(expected: number): void;
|
|
221
|
+
|
|
222
|
+
// Array/Object matchers
|
|
223
|
+
toContainEqual(expected: any): void;
|
|
224
|
+
toHaveProperty(path: string, value?: any): void;
|
|
225
|
+
toMatchObject(expected: object): void;
|
|
226
|
+
|
|
227
|
+
// Promise matchers
|
|
228
|
+
resolves: ExpectMatcher;
|
|
229
|
+
rejects: ExpectMatcher;
|
|
230
|
+
|
|
231
|
+
// DOM matchers
|
|
232
|
+
toBeInTheDocument(): void;
|
|
233
|
+
toBeVisible(): void;
|
|
234
|
+
toBeDisabled(): void;
|
|
235
|
+
toBeEnabled(): void;
|
|
236
|
+
toBeChecked(): void;
|
|
237
|
+
toHaveClass(className: string): void;
|
|
238
|
+
toHaveAttribute(name: string, value?: string): void;
|
|
239
|
+
toHaveTextContent(text: string | RegExp): void;
|
|
240
|
+
toHaveValue(value: any): void;
|
|
241
|
+
|
|
242
|
+
// HyperScript specific matchers
|
|
243
|
+
toHaveCompiledScript(): void;
|
|
244
|
+
toHaveExecutedScript(): void;
|
|
245
|
+
toHaveTriggeredEvent(eventType: string): void;
|
|
246
|
+
toHaveUpdatedElement(): void;
|
|
247
|
+
|
|
248
|
+
// Negation
|
|
249
|
+
not: ExpectMatcher;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Assert API
|
|
254
|
+
*/
|
|
255
|
+
export interface AssertAPI {
|
|
256
|
+
// Basic assertions
|
|
257
|
+
ok(value: any, message?: string): void;
|
|
258
|
+
equal(actual: any, expected: any, message?: string): void;
|
|
259
|
+
strictEqual(actual: any, expected: any, message?: string): void;
|
|
260
|
+
notEqual(actual: any, expected: any, message?: string): void;
|
|
261
|
+
notStrictEqual(actual: any, expected: any, message?: string): void;
|
|
262
|
+
|
|
263
|
+
// Type assertions
|
|
264
|
+
throws(fn: () => void, error?: any, message?: string): void;
|
|
265
|
+
doesNotThrow(fn: () => void, message?: string): void;
|
|
266
|
+
ifError(value: any): void;
|
|
267
|
+
|
|
268
|
+
// Async assertions
|
|
269
|
+
rejects(promise: Promise<any>, error?: any, message?: string): Promise<void>;
|
|
270
|
+
doesNotReject(promise: Promise<any>, message?: string): Promise<void>;
|
|
271
|
+
|
|
272
|
+
// DOM assertions
|
|
273
|
+
elementExists(selector: string, message?: string): void;
|
|
274
|
+
elementVisible(selector: string, message?: string): void;
|
|
275
|
+
elementHasClass(selector: string, className: string, message?: string): void;
|
|
276
|
+
elementHasText(selector: string, text: string, message?: string): void;
|
|
277
|
+
|
|
278
|
+
// HyperScript assertions
|
|
279
|
+
scriptCompiled(script: string, message?: string): void;
|
|
280
|
+
scriptExecuted(script: string, message?: string): void;
|
|
281
|
+
eventTriggered(eventType: string, message?: string): void;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Page object model interface
|
|
286
|
+
*/
|
|
287
|
+
export interface PageObject {
|
|
288
|
+
name: string;
|
|
289
|
+
url?: string;
|
|
290
|
+
selectors: Record<string, string>;
|
|
291
|
+
actions: Record<string, (...args: any[]) => Promise<any>>;
|
|
292
|
+
assertions: Record<string, (...args: any[]) => Promise<void>>;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Test data provider interface
|
|
297
|
+
*/
|
|
298
|
+
export interface TestDataProvider {
|
|
299
|
+
name: string;
|
|
300
|
+
provide: () => Promise<any[]> | any[];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Test fixture interface
|
|
305
|
+
*/
|
|
306
|
+
export interface TestFixture {
|
|
307
|
+
name: string;
|
|
308
|
+
setup: (context: TestContext) => Promise<any> | any;
|
|
309
|
+
teardown?: (fixture: any, context: TestContext) => Promise<void> | void;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Browser testing options
|
|
314
|
+
*/
|
|
315
|
+
export interface BrowserTestOptions {
|
|
316
|
+
browser: BrowserType;
|
|
317
|
+
headless: boolean;
|
|
318
|
+
viewport: { width: number; height: number };
|
|
319
|
+
deviceScaleFactor?: number;
|
|
320
|
+
userAgent?: string;
|
|
321
|
+
locale?: string;
|
|
322
|
+
timezone?: string;
|
|
323
|
+
permissions?: string[];
|
|
324
|
+
geolocation?: { latitude: number; longitude: number };
|
|
325
|
+
screenshot?: {
|
|
326
|
+
mode: 'off' | 'on' | 'only-on-failure';
|
|
327
|
+
fullPage: boolean;
|
|
328
|
+
};
|
|
329
|
+
video?: {
|
|
330
|
+
enabled: boolean;
|
|
331
|
+
dir: string;
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* E2E test step
|
|
337
|
+
*/
|
|
338
|
+
export interface E2EStep {
|
|
339
|
+
name: string;
|
|
340
|
+
action: E2EAction;
|
|
341
|
+
selector?: string;
|
|
342
|
+
value?: any;
|
|
343
|
+
options?: Record<string, any>;
|
|
344
|
+
screenshot?: boolean;
|
|
345
|
+
wait?: number | string;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* E2E action types
|
|
350
|
+
*/
|
|
351
|
+
export type E2EAction =
|
|
352
|
+
| 'goto'
|
|
353
|
+
| 'click'
|
|
354
|
+
| 'type'
|
|
355
|
+
| 'select'
|
|
356
|
+
| 'check'
|
|
357
|
+
| 'uncheck'
|
|
358
|
+
| 'hover'
|
|
359
|
+
| 'focus'
|
|
360
|
+
| 'blur'
|
|
361
|
+
| 'scroll'
|
|
362
|
+
| 'wait'
|
|
363
|
+
| 'screenshot'
|
|
364
|
+
| 'evaluate'
|
|
365
|
+
| 'assert';
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Visual regression test configuration
|
|
369
|
+
*/
|
|
370
|
+
export interface VisualTestConfig {
|
|
371
|
+
threshold: number;
|
|
372
|
+
includeAA: boolean;
|
|
373
|
+
animations: 'disabled' | 'allow';
|
|
374
|
+
clip?: {
|
|
375
|
+
x: number;
|
|
376
|
+
y: number;
|
|
377
|
+
width: number;
|
|
378
|
+
height: number;
|
|
379
|
+
};
|
|
380
|
+
fullPage: boolean;
|
|
381
|
+
mask?: string[];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Performance test metrics
|
|
386
|
+
*/
|
|
387
|
+
export interface PerformanceMetrics {
|
|
388
|
+
loadTime: number;
|
|
389
|
+
domContentLoaded: number;
|
|
390
|
+
firstPaint: number;
|
|
391
|
+
firstContentfulPaint: number;
|
|
392
|
+
largestContentfulPaint: number;
|
|
393
|
+
cumulativeLayoutShift: number;
|
|
394
|
+
firstInputDelay: number;
|
|
395
|
+
memoryUsage: {
|
|
396
|
+
usedJSHeapSize: number;
|
|
397
|
+
totalJSHeapSize: number;
|
|
398
|
+
jsHeapSizeLimit: number;
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Accessibility test result
|
|
404
|
+
*/
|
|
405
|
+
export interface AccessibilityResult {
|
|
406
|
+
violations: AccessibilityViolation[];
|
|
407
|
+
passes: AccessibilityPass[];
|
|
408
|
+
incomplete: AccessibilityIncomplete[];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Accessibility violation
|
|
413
|
+
*/
|
|
414
|
+
export interface AccessibilityViolation {
|
|
415
|
+
id: string;
|
|
416
|
+
impact: 'minor' | 'moderate' | 'serious' | 'critical';
|
|
417
|
+
description: string;
|
|
418
|
+
help: string;
|
|
419
|
+
helpUrl: string;
|
|
420
|
+
nodes: AccessibilityNode[];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Accessibility pass
|
|
425
|
+
*/
|
|
426
|
+
export interface AccessibilityPass {
|
|
427
|
+
id: string;
|
|
428
|
+
description: string;
|
|
429
|
+
nodes: AccessibilityNode[];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Accessibility incomplete
|
|
434
|
+
*/
|
|
435
|
+
export interface AccessibilityIncomplete {
|
|
436
|
+
id: string;
|
|
437
|
+
description: string;
|
|
438
|
+
nodes: AccessibilityNode[];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Accessibility node
|
|
443
|
+
*/
|
|
444
|
+
export interface AccessibilityNode {
|
|
445
|
+
target: string[];
|
|
446
|
+
html: string;
|
|
447
|
+
impact?: 'minor' | 'moderate' | 'serious' | 'critical';
|
|
448
|
+
any: AccessibilityCheckResult[];
|
|
449
|
+
all: AccessibilityCheckResult[];
|
|
450
|
+
none: AccessibilityCheckResult[];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Accessibility check result
|
|
455
|
+
*/
|
|
456
|
+
export interface AccessibilityCheckResult {
|
|
457
|
+
id: string;
|
|
458
|
+
data: any;
|
|
459
|
+
relatedNodes: any[];
|
|
460
|
+
impact: string;
|
|
461
|
+
message: string;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Test runner interface
|
|
466
|
+
*/
|
|
467
|
+
export interface TestRunner {
|
|
468
|
+
run: (suites: TestSuite[], config: TestConfig) => Promise<TestResult[]>;
|
|
469
|
+
runSuite: (suite: TestSuite, config: TestConfig) => Promise<TestResult[]>;
|
|
470
|
+
runTest: (test: TestCase, context: TestContext) => Promise<TestResult>;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Test discovery options
|
|
475
|
+
*/
|
|
476
|
+
export interface TestDiscoveryOptions {
|
|
477
|
+
patterns: string[];
|
|
478
|
+
ignore: string[];
|
|
479
|
+
recursive: boolean;
|
|
480
|
+
extensions: string[];
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Mock function interface
|
|
485
|
+
*/
|
|
486
|
+
export interface MockFunction {
|
|
487
|
+
(...args: any[]): any;
|
|
488
|
+
mockReturnValue: (value: any) => MockFunction;
|
|
489
|
+
mockReturnValueOnce: (value: any) => MockFunction;
|
|
490
|
+
mockResolvedValue: (value: any) => MockFunction;
|
|
491
|
+
mockRejectedValue: (error: any) => MockFunction;
|
|
492
|
+
mockImplementation: (fn: (...args: any[]) => any) => MockFunction;
|
|
493
|
+
mockClear: () => MockFunction;
|
|
494
|
+
mockReset: () => MockFunction;
|
|
495
|
+
mockRestore: () => MockFunction;
|
|
496
|
+
calls: any[][];
|
|
497
|
+
results: Array<{ type: 'return' | 'throw'; value: any }>;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Spy function interface
|
|
502
|
+
*/
|
|
503
|
+
export interface SpyFunction extends MockFunction {
|
|
504
|
+
original: Function;
|
|
505
|
+
}
|