import { returnOrThrow } from '../../instance/instance' ; import { ApplyProp , Expectation , ReturnValue } from '../expectation' ; import { MATCHER_SYMBOL } from '../matcher' ; import { CallMap , ExpectationRepository } from './expectation-repository' ; import { Property } from '../../proxy' ; export type CountableExpectation = { expectation : Expectation ; matchCount : number ; } ; export abstract class BaseRepository implements ExpectationRepository { protected readonly expectations = new Map < Property , CountableExpectation [ ] > ( ) ; private readonly expectedCallStats : CallMap = new Map ( ) ; private readonly unexpectedCallStats : CallMap = new Map ( ) ; add ( expectation ) { const { property } = expectation ; const expectations = this . expectations . get ( property ) || [ ] ; this . expectations . set ( property , [ ... expectations , { expectation , matchCount : 0 , } , ] ) ; } clear ( ) { this . expectations . clear ( ) ; this . expectedCallStats . clear ( ) ; this . unexpectedCallStats . clear ( ) ; } get ( property ) { const expectations = this . expectations . get ( property ) ; if ( expectations && expectations . length ) { this . recordExpected ( property , undefined ) ; const propertyExpectation = expectations . find ( ( e ) => e . expectation . matches ( undefined ) ) ; if ( propertyExpectation ) { this . countAndConsume ( propertyExpectation ) ; return propertyExpectation . expectation . returnValue ; } return { value : ( ... args ) => { const callExpectation = expectations . find ( ( e ) => e . expectation . matches ( args ) ) ; if ( callExpectation ) { this . recordExpected ( property , args ) ; this . countAndConsume ( callExpectation ) ; return returnOrThrow ( callExpectation . expectation . returnValue ) ; } this . recordUnexpected ( property , args ) ; return this . getValueForUnexpectedCall ( property , args ) ; } , } ; } switch ( property ) { case 'toString' : return { value : ( ) => 'mock' } ; case '@@toStringTag' : case Symbol . toStringTag : case 'name' : return { value : 'mock' } ; case '$$typeof' : case 'constructor' : case '@@__IMMUTABLE_ITERABLE__@@' : case '@@__IMMUTABLE_RECORD__@@' : return { value : null } ; case MATCHER_SYMBOL : return { value : false } ; case ApplyProp : return { value : ( ... args ) => { this . recordUnexpected ( property , args ) ; return this . getValueForUnexpectedCall ( property , args ) ; } , } ; default : this . recordUnexpected ( property , undefined ) ; return this . getValueForUnexpectedAccess ( property ) ; } } getAllProperties ( ) { return Array . from ( this . expectations . keys ( ) ) ; } getCallStats ( ) { return { expected : this . expectedCallStats , unexpected : this . unexpectedCallStats , } ; } getUnmet ( ) { return ( [ ] as Expectation [ ] ) . concat ( ... Array . from ( this . expectations . values ( ) ) . map ( ( expectations ) => expectations . filter ( ( e ) => e . expectation . min > e . matchCount ) . map ( ( e ) => e . expectation ) ) ) ; } /**
     * We got a method call that doesn't match any expectation,
     * what should we return?
     */ protected abstract getValueForUnexpectedCall ( property , args ) ; /**
     * We got a property access that doesn't match any expectation,
     * what should we return?
     */ protected abstract getValueForUnexpectedAccess ( property ) ; protected abstract consumeExpectation ( expectation ) ; /**
     * Record an expected property access/method call.
     */ private recordExpected ( property , args ) { const calls = this . expectedCallStats . get ( property ) || [ ] ; this . expectedCallStats . set ( property , [ ... calls , { arguments : args } ] ) ; } /**
     * Record an unexpected property access/method call.
     */ private recordUnexpected ( property , args ) { const calls = this . unexpectedCallStats . get ( property ) || [ ] ; this . unexpectedCallStats . set ( property , [ ... calls , { arguments : args } ] ) ; } private countAndConsume ( expectation ) { expectation . matchCount ++ ; this . consumeExpectation ( expectation ) ; } }