Skip to content

Commit 7456aa4

Browse files
zvirjamrcrane
authored andcommitted
[jasmine] Improve various library types (#38412)
* Move Func helper to jasmine namespace * Move ImplementationCallback to jasmine namespace * Align tests between different versions * Fix typings for spyOnAllFunctcions * Improve typing for function matchers * Refactor AsymmetricMatcher to handle all cases * Enhance basic expectation docs and typing Improve toBe(), toEqual(), toMatch(), toHaveClass() documentation to avoid confusion around them. * Enhance any() and anything() * Add missing typing for non-named spy object * Specify that jasminewd2 depends on jasmine v2 The jasminewd2 library supports jasmine 2 only, therefore point typings to the correct reference. * Add NonTypedSpyObj for complex scenarios * Improve tool for spy names extraction * Remove undefined Any property from matchers
1 parent 7564c78 commit 7456aa4

6 files changed

Lines changed: 660 additions & 224 deletions

File tree

types/jasmine/index.d.ts

Lines changed: 105 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@
1414
// Moshe Kolodny <https://github.com/kolodny>
1515
// Stephen Farrar <https://github.com/stephenfarrar>
1616
// Mochamad Arfin <https://github.com/ndunks>
17+
// Alex Povar <https://github.com/zvirja>
1718
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
1819
// TypeScript Version: 2.8
1920
// For ddescribe / iit use : https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/karma-jasmine/karma-jasmine.d.ts
2021

21-
type ImplementationCallback = (() => PromiseLike<any>) | (() => void) | ((done: DoneFn) => void);
22+
/**
23+
* @deprecated Use {@link jasmine.ImplementationCallback} instead.
24+
*/
25+
type ImplementationCallback = jasmine.ImplementationCallback;
2226

2327
/**
2428
* Create a group of specs (often called a suite).
@@ -48,23 +52,23 @@ declare function xdescribe(description: string, specDefinitions: () => void): vo
4852
* @param assertion Function that contains the code of your test. If not provided the test will be pending.
4953
* @param timeout Custom timeout for an async spec.
5054
*/
51-
declare function it(expectation: string, assertion?: ImplementationCallback, timeout?: number): void;
55+
declare function it(expectation: string, assertion?: jasmine.ImplementationCallback, timeout?: number): void;
5256

5357
/**
5458
* A focused `it`. If suites or specs are focused, only those that are focused will be executed.
5559
* @param expectation Textual description of what this spec is checking
5660
* @param assertion Function that contains the code of your test. If not provided the test will be pending.
5761
* @param timeout Custom timeout for an async spec.
5862
*/
59-
declare function fit(expectation: string, assertion?: ImplementationCallback, timeout?: number): void;
63+
declare function fit(expectation: string, assertion?: jasmine.ImplementationCallback, timeout?: number): void;
6064

6165
/**
6266
* A temporarily disabled `it`. The spec will report as pending and will not be executed.
6367
* @param expectation Textual description of what this spec is checking
6468
* @param assertion Function that contains the code of your test. If not provided the test will be pending.
6569
* @param timeout Custom timeout for an async spec.
6670
*/
67-
declare function xit(expectation: string, assertion?: ImplementationCallback, timeout?: number): void;
71+
declare function xit(expectation: string, assertion?: jasmine.ImplementationCallback, timeout?: number): void;
6872

6973
/**
7074
* Mark a spec as pending, expectation results will be ignored.
@@ -78,30 +82,30 @@ declare function pending(reason?: string): void;
7882
* @param action Function that contains the code to setup your specs.
7983
* @param timeout Custom timeout for an async beforeEach.
8084
*/
81-
declare function beforeEach(action: ImplementationCallback, timeout?: number): void;
85+
declare function beforeEach(action: jasmine.ImplementationCallback, timeout?: number): void;
8286

8387
/**
8488
* Run some shared teardown after each of the specs in the describe in which it is called.
8589
* @param action Function that contains the code to teardown your specs.
8690
* @param timeout Custom timeout for an async afterEach.
8791
*/
88-
declare function afterEach(action: ImplementationCallback, timeout?: number): void;
92+
declare function afterEach(action: jasmine.ImplementationCallback, timeout?: number): void;
8993

9094
/**
9195
* Run some shared setup once before all of the specs in the describe are run.
9296
* Note: Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
9397
* @param action Function that contains the code to setup your specs.
9498
* @param timeout Custom timeout for an async beforeAll.
9599
*/
96-
declare function beforeAll(action: ImplementationCallback, timeout?: number): void;
100+
declare function beforeAll(action: jasmine.ImplementationCallback, timeout?: number): void;
97101

98102
/**
99103
* Run some shared teardown once before all of the specs in the describe are run.
100104
* Note: Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
101105
* @param action Function that contains the code to teardown your specs.
102106
* @param timeout Custom timeout for an async afterAll
103107
*/
104-
declare function afterAll(action: ImplementationCallback, timeout?: number): void;
108+
declare function afterAll(action: jasmine.ImplementationCallback, timeout?: number): void;
105109

106110
/**
107111
* Create an expectation for a spec.
@@ -174,55 +178,69 @@ declare function spyOnProperty<T>(object: T, property: keyof T, accessType?: 'ge
174178
* Installs spies on all writable and configurable properties of an object.
175179
* @param object The object upon which to install the `Spy`s.
176180
*/
177-
declare function spyOnAllFunctions(object: object): jasmine.Spy;
181+
declare function spyOnAllFunctions<T>(object: T): jasmine.SpyObj<T>;
178182

179183
declare function runs(asyncMethod: Function): void;
180184
declare function waitsFor(latchMethod: () => boolean, failureMessage?: string, timeout?: number): void;
181185
declare function waits(timeout?: number): void;
182186

183187
declare namespace jasmine {
184-
type ExpectedRecursive<T> = T | ObjectContaining<T> | AsymmetricMatcher | {
188+
type Func = (...args: any[]) => any;
189+
190+
// Use trick with prototype to allow abstract classes.
191+
// More info: https://stackoverflow.com/a/38642922/2009373
192+
type Constructor = Function & { prototype: any };
193+
194+
type ImplementationCallback = (() => PromiseLike<any>) | ((done: DoneFn) => void);
195+
196+
type ExpectedRecursive<T> = T | ObjectContaining<T> | AsymmetricMatcher<any> | {
185197
[K in keyof T]: ExpectedRecursive<T[K]> | Any;
186198
};
187-
type Expected<T> = T | ObjectContaining<T> | AsymmetricMatcher | Any | Spy | {
199+
type Expected<T> = T | ObjectContaining<T> | AsymmetricMatcher<any> | Any | Spy | {
188200
[K in keyof T]: ExpectedRecursive<T[K]>;
189201
};
190202
type SpyObjMethodNames<T = undefined> =
191203
T extends undefined ?
192-
(ReadonlyArray<string> | {[methodName: string]: any}) :
193-
(ReadonlyArray<keyof T> | {[P in keyof T]?: ReturnType<T[P] extends (...args: any[]) => any ? T[P] : any>});
204+
(ReadonlyArray<string> | { [methodName: string]: any }) :
205+
(ReadonlyArray<keyof T> | { [P in keyof T]?: T[P] extends Func ? ReturnType<T[P]> : any });
194206

195207
function clock(): Clock;
196208

197209
var matchersUtil: MatchersUtil;
198210

199-
function any(aclass: any): Any;
211+
/**
212+
* That will succeed if the actual value being compared is an instance of the specified class/constructor.
213+
*/
214+
function any(aclass: Constructor | Symbol): AsymmetricMatcher<any>;
200215

201-
function anything(): Any;
216+
/**
217+
* That will succeed if the actual value being compared is not `null` and not `undefined`.
218+
*/
219+
function anything(): AsymmetricMatcher<any>;
202220

203221
/**
204222
* That will succeed if the actual value being compared is `true` or anything truthy.
205223
* @since 3.1.0
206224
*/
207-
function truthy(): Truthy;
225+
function truthy(): AsymmetricMatcher<any>;
208226

209227
/**
210228
* That will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey.
211229
* @since 3.1.0
212230
*/
213-
function falsy(): Falsy;
231+
function falsy(): AsymmetricMatcher<any>;
214232

215233
/**
216234
* That will succeed if the actual value being compared is empty.
217235
* @since 3.1.0
218236
*/
219-
function empty(): Empty;
237+
function empty(): AsymmetricMatcher<any>;
220238

221239
/**
222240
* That will succeed if the actual value being compared is not empty.
223241
* @since 3.1.0
224242
*/
225-
function notEmpty(): NotEmpty;
243+
function notEmpty(): AsymmetricMatcher<any>;
226244

227245
function arrayContaining<T>(sample: ArrayLike<T>): ArrayContaining<T>;
228246
function arrayWithExactContents<T>(sample: ArrayLike<T>): ArrayContaining<T>;
@@ -233,7 +251,7 @@ declare namespace jasmine {
233251
function createSpyObj<T>(baseName: string, methodNames: SpyObjMethodNames<T>): SpyObj<T>;
234252

235253
function createSpyObj(methodNames: SpyObjMethodNames): any;
236-
function createSpyObj<T>(methodNames: SpyObjMethodNames): SpyObj<T>;
254+
function createSpyObj<T>(methodNames: SpyObjMethodNames<T>): SpyObj<T>;
237255

238256
function pp(value: any): string;
239257

@@ -243,39 +261,34 @@ declare namespace jasmine {
243261

244262
function addMatchers(matchers: CustomMatcherFactories): void;
245263

246-
function stringMatching(str: string | RegExp): Any;
264+
function stringMatching(str: string | RegExp): AsymmetricMatcher<string>;
247265

248266
function formatErrorMsg(domain: string, usage: string): (msg: string) => string;
249267

250-
interface Any {
268+
interface Any extends AsymmetricMatcher<any> {
251269
(...params: any[]): any; // jasmine.Any can also be a function
252270
new (expectedClass: any): any;
253271

254272
jasmineMatches(other: any): boolean;
255273
jasmineToString(): string;
256274
}
257275

258-
interface AsymmetricMatcher<T extends string = string> {
259-
asymmetricMatch(other: any): boolean;
260-
jasmineToString?(): T;
261-
}
262-
263-
interface Truthy extends AsymmetricMatcher<'<jasmine.truthy>'> { }
264-
interface Falsy extends AsymmetricMatcher<'<jasmine.falsy>'> { }
265-
interface Empty extends AsymmetricMatcher<'<jasmine.empty>'> { }
266-
interface NotEmpty extends AsymmetricMatcher<'<jasmine.notEmpty>'> { }
276+
interface AsymmetricMatcher<TValue> {
277+
asymmetricMatch(other: TValue, customTesters: ReadonlyArray<CustomEqualityTester>): boolean;
278+
jasmineToString?(): string;
279+
}
267280

268281
// taken from TypeScript lib.core.es6.d.ts, applicable to CustomMatchers.contains()
269282
interface ArrayLike<T> {
270283
length: number;
271284
[n: number]: T;
272285
}
273286

274-
interface ArrayContaining<T> extends AsymmetricMatcher {
287+
interface ArrayContaining<T> extends AsymmetricMatcher<any> {
275288
new?(sample: ArrayLike<T>): ArrayLike<T>;
276289
}
277290

278-
interface ObjectContaining<T> {
291+
interface ObjectContaining<T> extends AsymmetricMatcher<any> {
279292
new?(sample: {[K in keyof T]?: any}): {[K in keyof T]?: any};
280293

281294
jasmineMatches(other: any, mismatchKeys: any[], mismatchValues: any[]): boolean;
@@ -492,19 +505,33 @@ declare namespace jasmine {
492505
message(): any;
493506

494507
/**
508+
* Expect the actual value to be `===` to the expected value.
495509
*
496-
* @param expected the actual value to be === to the expected value.
510+
* @param expected - The expected value to compare against.
497511
* @param expectationFailOutput
512+
* @example
513+
* expect(thing).toBe(realThing);
498514
*/
499515
toBe(expected: Expected<T>, expectationFailOutput?: any): boolean;
500516

501517
/**
502-
*
503-
* @param expected the actual value to be equal to the expected, using deep equality comparison.
518+
* Expect the actual value to be equal to the expected, using deep equality comparison.
519+
* @param expected - Expected value.
504520
* @param expectationFailOutput
521+
* @example
522+
* expect(bigObject).toEqual({ "foo": ['bar', 'baz'] });
505523
*/
506524
toEqual(expected: Expected<T>, expectationFailOutput?: any): boolean;
525+
526+
/**
527+
* Expect the actual value to match a regular expression.
528+
* @param expected - Value to look for in the string.
529+
* @example
530+
* expect("my string").toMatch(/string$/);
531+
* expect("other string").toMatch("her");
532+
*/
507533
toMatch(expected: string | RegExp, expectationFailOutput?: any): boolean;
534+
508535
toBeDefined(expectationFailOutput?: any): boolean;
509536
toBeUndefined(expectationFailOutput?: any): boolean;
510537
toBeNull(expectationFailOutput?: any): boolean;
@@ -527,23 +554,61 @@ declare namespace jasmine {
527554
toThrowMatching(predicate: (thrown: any) => boolean): boolean;
528555
toBeNegativeInfinity(expectationFailOutput?: any): boolean;
529556
toBePositiveInfinity(expectationFailOutput?: any): boolean;
530-
toHaveClass(expected: any, expectationFailOutput?: any): boolean;
557+
558+
/**
559+
* Expect the actual value to be a DOM element that has the expected class.
560+
* @since 3.0.0
561+
* @param expected - The class name to test for.
562+
* @example
563+
* var el = document.createElement('div');
564+
* el.className = 'foo bar baz';
565+
* expect(el).toHaveClass('bar');
566+
*/
567+
toHaveClass(expected: string, expectationFailOutput?: any): boolean;
531568

532569
/**
533570
* Add some context for an expect.
534571
* @param message - Additional context to show when the matcher fails
535572
*/
536573
withContext(message: string): Matchers<T>;
537574

575+
/**
576+
* Invert the matcher following this expect.
577+
*/
538578
not: Matchers<T>;
539-
540-
Any: Any;
541579
}
542580

543581
interface ArrayLikeMatchers<T> extends Matchers<ArrayLike<T>> {
582+
/**
583+
* Expect the actual value to be `===` to the expected value.
584+
*
585+
* @param expected - The expected value to compare against.
586+
* @param expectationFailOutput
587+
* @example
588+
* expect(thing).toBe(realThing);
589+
*/
544590
toBe(expected: Expected<ArrayLike<T>> | ArrayContaining<T>, expectationFailOutput?: any): boolean;
591+
592+
/**
593+
* Expect the actual value to be equal to the expected, using deep equality comparison.
594+
* @param expected - Expected value.
595+
* @param expectationFailOutput
596+
* @example
597+
* expect(bigObject).toEqual({ "foo": ['bar', 'baz'] });
598+
*/
545599
toEqual(expected: Expected<ArrayLike<T>> | ArrayContaining<T>, expectationFailOutput?: any): boolean;
600+
546601
toContain(expected: Expected<T>, expectationFailOutput?: any): boolean;
602+
603+
/**
604+
* Add some context for an expect.
605+
* @param message - Additional context to show when the matcher fails.
606+
*/
607+
withContext(message: string): ArrayLikeMatchers<T>;
608+
609+
/**
610+
* Invert the matcher following this expect.
611+
*/
547612
not: ArrayLikeMatchers<T>;
548613
}
549614

@@ -745,7 +810,7 @@ declare namespace jasmine {
745810
}
746811

747812
type SpyObj<T> = T & {
748-
[k in keyof T]: T[k] extends Function ? T[k] & Spy : T[k];
813+
[K in keyof T]: T[K] extends Function ? T[K] & Spy : T[K];
749814
};
750815

751816
interface SpyAnd {

0 commit comments

Comments
 (0)