Home Reference Source Test

spec/pattern-matching/ObjectMatcher.spec.ts

import 'mocha';

import { given } from 'mocha-testdata';

import { TinyType } from '../../src';
import { ObjectMatcher } from '../../src/pattern-matching';
import { expect } from '../expect';

describe('pattern-matching', () => {
    describe('ObjectMatcher', () => {

        describe('when working with Tiny Types', () => {

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

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

            given(
                [new Name('Jan'), `matched "Jan"`],
                [new Name('John'), `matched "John"`],
                [new Name('Sara'), `else, received "Name(value=Sara)"`],
            ).it('matches equal objects', (input: Name, expectedMessage: string) => {
                const result = new ObjectMatcher<TinyType, string>(input)
                    .when(new Name('Jan'), _ => `matched "Jan"`)
                    .when(new Name('John'), _ => `matched "John"`)
                    .else(_ => `else, received "${_}"`);

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

            it('matches identical objects', () => {
                const input = {field: 'value'};

                const result = new ObjectMatcher(input)
                    .when(input, _ => `matched by identity`)
                    .else(_ => `else, received "${_}"`);

                expect(result).to.equal(`matched by identity`);
            });

            given(
                [new Name('Jan'), `matched by equality`],
                [new Name('John'), `matched by type`],
                [new EmailAddress('jan@example.com'), `else, received "EmailAddress(value=jan@example.com)"`],
            ).it('can be mixed', (input: Name, expectedMessage: string) => {
                const result = new ObjectMatcher<TinyType, string>(input)
                    .when(new Name('Jan'), _ => `matched by equality`)
                    .when(Name, _ => `matched by type`)
                    .else(_ => `else, received "${_}"`);

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

        describe('when working with regular classes', () => {
            abstract class DomainEvent {
                constructor(public readonly timestamp: Date) {
                }
            }

            class AccountCreated extends DomainEvent {
                constructor(public readonly account_name: string, timestamp: Date) {
                    super(timestamp);
                }
            }

            class AccountConfirmed extends DomainEvent {
                constructor(public readonly account_name: string,
                    public readonly email: string,
                    timestamp: Date,) {
                    super(timestamp);
                }
            }

            class UnclassifiedEvent extends DomainEvent {
            }

            given(
                [
                    new AccountCreated('jan-molak', new Date()),
                    `AccountCreated`,
                ],
                [
                    new AccountConfirmed('jan-molak', 'jan.molak@serenity.io', new Date()),
                    `AccountConfirmed`,
                ],
                [
                    new UnclassifiedEvent(new Date()),
                    `UnclassifiedEvent`,
                ],
            ).it('matches object by constructor function', (input: DomainEvent, expected_result: string) => {

                const result = new ObjectMatcher<DomainEvent, string>(input)
                    .when(AccountCreated, _ => `AccountCreated`)
                    .when(AccountConfirmed, _ => `AccountConfirmed`)
                    .when(DomainEvent, _ => `UnclassifiedEvent`)
                    .else(_ => `else, received "${_}"`);

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

            // todo: mixed constructor/tiny?

            given(
                [
                    new AccountCreated('jan-molak', new Date()),
                    `Account created for jan-molak`,
                ],
                [
                    new AccountConfirmed('jan-molak', 'jan.molak@serenity.io', new Date()),
                    `Account confirmed for jan-molak at jan.molak@serenity.io`,
                ],
                [
                    new UnclassifiedEvent(new Date()),
                    `Some DomainEvent received`,
                ],
            ).it('matches object by constructor function', (input: DomainEvent, expected_result: string) => {

                const result = new ObjectMatcher<DomainEvent, string>(input)
                    .when(AccountCreated, ({account_name}: AccountCreated) => `Account created for ${ account_name }`)
                    .when(AccountConfirmed, ({account_name, email}: AccountConfirmed) => `Account confirmed for ${ account_name } at ${ email }`)
                    .when(DomainEvent, ({timestamp}: DomainEvent) => `Some DomainEvent received`)
                    .else(_ => `else, received "${_}"`);

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