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);
});
});
});