Home Reference Source Test

spec/match.spec.ts

import 'mocha';

import { given } from 'mocha-testdata';

import { match, TinyType, TinyTypeOf } from '../src';
import { IdentityMatcher, ObjectMatcher, StringMatcher } from '../src/pattern-matching';
import { expect } from './expect';

/** @test {match} */
describe(`::match`, () => {

    /** @test {match} */
    describe('default rules', () => {
        it(`uses the default rule when no identical match is found for a string value`, () => {
            const result = match('four')
                .when('five', _ => `high five`)
                .when('six', _ => `got your six`)
                .else(n => `got ${n}`);

            expect(result).to.equal('got four');
        });

        it(`uses the default rule when no identical match is found for a numerical value`, () => {
            const result = match(4)
                .when(5, _ => `high five`)
                .when(6, _ => `got your six`)
                .else(n => `got ${n}`);

            expect(result).to.equal('got 4');
        });
    });

    describe('when selecting a matcher', () => {
        abstract class DomainEvent {
            constructor(public readonly timestamp: Date = new Date()) {
            }
        }

        class ConcreteEvent extends DomainEvent {
        }

        class EmaiAddress extends TinyType {
            constructor(public readonly value: string) {
                super();
            }
        }

        given([
            { input: 5,                                     pattern: 1,                                     expected_matcher: IdentityMatcher },
            { input: Symbol('some'),                        pattern: Symbol('other'),                       expected_matcher: IdentityMatcher },
            { input: 'hello',                               pattern: 'hello',                               expected_matcher: StringMatcher },
            { input: 'hello',                               pattern: /^[Hh]ello/,                           expected_matcher: StringMatcher },
            { input: new EmaiAddress('user@domain.org'),    pattern: new EmaiAddress('user@domain.org'),    expected_matcher: ObjectMatcher },
            { input: new ConcreteEvent(),                   pattern: ConcreteEvent,                         expected_matcher: ObjectMatcher },
            { input: new ConcreteEvent(),                   pattern: DomainEvent,                           expected_matcher: ObjectMatcher },
        ]).
        it(`uses a matcher appropriate to the input`, ({input, pattern, expected_matcher}) => {
            expect(match(input).when(pattern, _ => _)).to.be.instanceOf(expected_matcher);
        });
    });

    /**
     * @test {match}
     * @test {TinyType}
     * @test {TinyTypeOf}
     */
    describe('when working with TinyTypes', () => {

        class AccountId extends TinyTypeOf<number>() {}
        abstract class Command extends TinyTypeOf<AccountId>() {}
        class OpenAccount extends Command {}
        class CloseAccount extends Command {}
        class SuspendAccount extends Command {}

        given([
            { command: new OpenAccount(new AccountId(42)),      expected: 'Open AccountId(value=42)'                            },
            { command: new CloseAccount(new AccountId(42)),     expected: 'Close AccountId(value=42)'                           },
            { command: new SuspendAccount(new AccountId(42)),   expected: 'Command: SuspendAccount(value=AccountId(value=42))'  },
            { command: null,                                    expected: 'Unrecognised input: null'                            },
        ]).
        it('matches a TinyType by type', ({ command, expected }) => {
            const result = match(command)
                .when(OpenAccount,  ({ value }) => `Open ${ value }`)
                .when(CloseAccount, ({ value }) => `Close ${ value }`)
                .when(Command,      _ => `Command: ${ _ }`)
                .else(_ => `Unrecognised input: ${_}`);

            expect(result).to.equal(expected);
        });

        given([
            { command: new OpenAccount(new AccountId(42)),      expected: 'Open AccountId(value=42)'                            },
            { command: new CloseAccount(new AccountId(42)),     expected: 'Close AccountId(value=42)'                           },
            { command: new SuspendAccount(new AccountId(42)),   expected: 'Command: SuspendAccount(value=AccountId(value=42))'  },
            { command: null,                                    expected: 'Unrecognised input: null'                            },
        ]).
        it('matches a TinyType by value', ({ command, expected }) => {
            const result = match(command)
                .when(new OpenAccount(new AccountId(42)),   ({ value }) => `Open ${ value }`)
                .when(new CloseAccount(new AccountId(42)),  ({ value }) => `Close ${ value }`)
                .when(new SuspendAccount(new AccountId(42)), _ => `Command: ${ _ }`)
                .else(_ => `Unrecognised input: ${_}`);

            expect(result).to.equal(expected);
        });
    });
});