import { Answerable, AnswersQuestions, Question } from '@serenity-js/core'; import { formatted } from '@serenity-js/core/lib/io'; import { ExpectationMet, ExpectationNotMet, Outcome } from './outcomes'; /** * todo: document usage examples */ export abstract class Expectation implements Question<(actual: Actual) => Promise>> { static thatActualShould(relationshipName: string, expectedValue: Answerable): { soThat: (statement: (actual: A, expected: E) => boolean) => Expectation, } { return ({ soThat: (statement: (actual: A, expected: E) => boolean): Expectation => { return new DynamicallyGeneratedExpectation(relationshipName, statement, expectedValue); }, }); } static to(relationshipName: string): { soThatActual: (...expectations: Array>) => Expectation, } { return { soThatActual: (expectation: Expectation): Expectation => { return new ExpectationAlias(relationshipName, expectation); }, }; } abstract answeredBy(actor: AnswersQuestions): (actual: Actual) => Promise>; abstract toString(): string; } /** * @package */ class DynamicallyGeneratedExpectation implements Expectation { constructor( private readonly description: string, private readonly statement: (actual: Actual, expected: Expected) => boolean, private readonly expectedValue: Answerable, ) { } answeredBy(actor: AnswersQuestions): (actual: Actual) => Promise> { return (actual: Actual) => actor.answer(this.expectedValue) .then(expected => { return this.statement(actual, expected) ? new ExpectationMet(this.toString(), expected, actual) : new ExpectationNotMet(this.toString(), expected, actual); }); } toString(): string { return `${ this.description } ${ formatted `${this.expectedValue}` }`; } } /** * @package */ class ExpectationAlias implements Expectation { constructor( private readonly description: string, private readonly expectation: Expectation, ) { } answeredBy(actor: AnswersQuestions): (actual: Actual) => Promise> { return (actual: Actual) => this.expectation.answeredBy(actor)(actual).then(_ => _ instanceof ExpectationMet ? new ExpectationMet(this.description, _.expected, _.actual) : new ExpectationNotMet(_.message, _.expected, _.actual)); } toString(): string { return this.description; } }