Create custom jasmine matcher using Typescript

jasmine, jasmine-matchers, typescript

Solution

Daf's answer mostly worked for me I just noticed an issue with his sample code and the way he named his files. I also happened upon another unrelated issue. Hence a new answer.

- For some reason my app does not like it when the interface file has the same name as the matcher file. e.g foo.ts and foo.d.ts. For my app it needed to be foo.ts and foo-interface.d.ts or something like it.

- Also don't import interfaces from foo.ts into foo-interface.d.ts it also does not seem to like this.

Matcher - custom-matchers.ts

import MatchersUtil = jasmine.MatchersUtil;
import CustomMatcherFactories = jasmine.CustomMatcherFactories;
import CustomEqualityTester = jasmine.CustomEqualityTester;
import CustomMatcher = jasmine.CustomMatcher;
import CustomMatcherResult = jasmine.CustomMatcherResult;

export const SomeCustomMatchers: CustomMatcherFactories = {
    toReallyEqual: function (util: MatchersUtil, customEqualityTester: CustomEqualityTester[]): CustomMatcher {
        return {
            compare: function (actual: any, expected: any, anotherCustomArg: any): CustomMatcherResult {

                // Your checks here.
                const passes = actual === expected;

                // Result and message generation.
                return {
                    pass: passes,
                    message: passes ? `Actual equals expected`
                                    : `Actual does not equal expected`,
                }
            }
        }
    }
};

NOTE that `compare` function can have as many custom-parameters as we want (or even Variadic), and that ONLY first-argument is required/reserved (to know actual-value); but if the function name begins with "`toHave`" (instead of `toReallyEqual`), then the second argument is reserved for "`key: string`" (to know object's field name, I mean, Jasmine2 will loop for us).

Also, we could relay on Jasmine for message-generation, like:

message: util.buildFailureMessage('toReallyEqual', passes, actual, expected, anotherCustomArg),

Interface file - matcher-types.d.ts - cannot be the same name as your matcher file

declare namespace jasmine {
    interface Matchers<T> {
        toReallyEqual(expected: any, anotherCustomArg: any, expectationFailOutput?: any): boolean;
    }
}

Custom matcher test

describe('Hello', () => {

    beforeEach(() => {
        jasmine.addMatchers(SomeCustomMatchers)
    });

    it('should allow custom matchers', () => {
        expect('foo').toReallyEqual('foo');
        expect('bar').not.toReallyEqual('test');
    })
});

Problem

I'm using jasmine on an angular2 project and having some trouble writing a custom matcher for a test. I want to be able to compare two relatively complex objects. I found this article which claims to solve the issue but it simply results in a typescript error stating that it doesn't recognize the new method on jasmine's `Matchers` object. The relevant code is this: ``` declare module jasmine { interface Matchers { toBeNumeric(): void; } } ``` Another article gives a similar, but slightly different solution that gives the same error. ``` declare namespace jasmine { interface Matchers { toHaveText(expected: string): boolean; } } ``` I tried this ``` let m: jasmine.Matchers = expect(someSpy.someMethod).toHaveBeenCalled(); ``` and got this error: Type 'jasmine.Matchers' is not assignable to type 'jasmine.Matchers'. Two different types with this name exist, but they are unrelated. That seems to indicate that the `declare namespace jasmine` statement is creating a new `jasmine` namespace rather than extending the existing one. So how can I create my own matcher that typescript will be happy with?

Original source

Related problems