1+ /// <reference path="./harness.ts" />
2+ declare namespace Chai {
3+ interface ChaiStatic {
4+ util : UtilStatic ;
5+ }
6+
7+ interface UtilStatic {
8+ objDisplay ( obj : any ) : string ;
9+ }
10+
11+ interface AssertStatic {
12+ sameMembers < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) : void ;
13+ notSameMembers < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) : void ;
14+ includeMembersOnce < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) : void ;
15+ includeMembers < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) : void ;
16+ notIncludeMembers < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) : void ;
17+ }
18+ }
19+
20+ // patch assert.sameMembers to support other iterables
21+ _chai . use ( ( chai : Chai . ChaiStatic , util : Chai . UtilStatic ) => {
22+ function isIdenticalTo < T > ( left : Iterable < T > , right : Iterable < T > ) {
23+ if ( ! ( left instanceof core . SortedSet ) && right instanceof core . SortedSet ) {
24+ [ left , right ] = [ right , left ] ;
25+ }
26+
27+ const set = asReadonlySet ( left ) ;
28+ const seen = set instanceof core . SortedSet ? new core . SortedSet < T > ( set . comparer ) : new Set < T > ( ) ;
29+ const iterator = Array . isArray ( right ) ? ts . arrayIterator ( right ) : right [ Symbol . iterator ] ( ) ;
30+ for ( let { value, done } = iterator . next ( ) ; ! done ; { value, done } = iterator . next ( ) ) {
31+ if ( ! set . has ( value ) ) return false ;
32+ seen . add ( value ) ;
33+ }
34+ return set . size === seen . size ;
35+ }
36+
37+ function isSubsetOf < T > ( subset : Iterable < T > , superset : Iterable < T > , unique ?: boolean ) {
38+ const set = asReadonlySet ( superset ) ;
39+ const seen = unique ? set instanceof core . SortedSet ? new core . SortedSet < T > ( set . comparer ) : new Set < T > ( ) : undefined ;
40+ const iterator = Array . isArray ( subset ) ? ts . arrayIterator ( subset ) : subset [ Symbol . iterator ] ( ) ;
41+ for ( let { value, done } = iterator . next ( ) ; ! done ; { value, done } = iterator . next ( ) ) {
42+ if ( ! set . has ( value ) ) return false ;
43+ if ( seen ) {
44+ if ( seen . has ( value ) ) return false ;
45+ seen . add ( value ) ;
46+ }
47+ }
48+ return true ;
49+ }
50+
51+ function overlaps < T > ( left : Iterable < T > , right : Iterable < T > ) {
52+ if ( ! ( left instanceof core . SortedSet ) && right instanceof core . SortedSet ) {
53+ [ left , right ] = [ right , left ] ;
54+ }
55+
56+ const set = asReadonlySet ( left ) ;
57+ const iterator = Array . isArray ( right ) ? ts . arrayIterator ( right ) : right [ Symbol . iterator ] ( ) ;
58+ for ( let { value, done } = iterator . next ( ) ; ! done ; { value, done } = iterator . next ( ) ) {
59+ if ( set . has ( value ) ) return true ;
60+ }
61+ return false ;
62+ }
63+
64+ function asReadonlySet < T > ( iterable : Iterable < T > ) : ReadonlySet < T > {
65+ return iterable instanceof Set ? iterable :
66+ iterable instanceof core . SortedSet ? iterable :
67+ new Set ( iterable ) ;
68+ }
69+
70+ function asReadonlyArray < T > ( iterable : Iterable < T > ) : ReadonlyArray < T > {
71+ return Array . isArray ( iterable ) ? iterable : Array . from ( iterable ) ;
72+ }
73+
74+ // patch `assert.sameMembers` to support any iterable
75+ chai . assert . sameMembers = < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) => {
76+ if ( ! isIdenticalTo ( actual , expected ) ) {
77+ actual = asReadonlyArray ( actual ) ;
78+ expected = asReadonlyArray ( expected ) ;
79+ assert . fail ( actual , expected , message || `expected ${ util . objDisplay ( actual ) } to have the same members as ${ util . objDisplay ( expected ) } ` ) ;
80+ }
81+ } ;
82+
83+ // patch `assert.notSameMembers` to support any iterable
84+ chai . assert . notSameMembers = < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) => {
85+ if ( isIdenticalTo ( actual , expected ) ) {
86+ actual = asReadonlyArray ( actual ) ;
87+ expected = asReadonlyArray ( expected ) ;
88+ assert . fail ( actual , expected , message || `expected ${ util . objDisplay ( actual ) } to not have the same members as ${ util . objDisplay ( expected ) } ` ) ;
89+ }
90+ } ;
91+
92+ chai . assert . includeMembersOnce = < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) => {
93+ if ( ! isSubsetOf ( expected , actual , /*unique*/ true ) ) {
94+ actual = asReadonlyArray ( actual ) ;
95+ expected = asReadonlyArray ( expected ) ;
96+ assert . fail ( actual , expected , message || `expected ${ util . objDisplay ( actual ) } to include the members of ${ util . objDisplay ( expected ) } only once` ) ;
97+ }
98+ } ;
99+
100+ // patch `assert.includeMembers` to support any iterable
101+ chai . assert . includeMembers = < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) => {
102+ if ( ! isSubsetOf ( expected , actual ) ) {
103+ actual = asReadonlyArray ( actual ) ;
104+ expected = asReadonlyArray ( expected ) ;
105+ assert . fail ( actual , expected , message || `expected ${ util . objDisplay ( actual ) } to include the members of ${ util . objDisplay ( expected ) } ` ) ;
106+ }
107+ } ;
108+
109+ // patch `assert.notIncludeMembers` to support any iterable
110+ chai . assert . notIncludeMembers = < T > ( actual : Iterable < T > , expected : Iterable < T > , message ?: string ) => {
111+ if ( overlaps ( expected , actual ) ) {
112+ actual = asReadonlyArray ( actual ) ;
113+ expected = asReadonlyArray ( expected ) ;
114+ assert . fail ( actual , expected , message || `expected ${ util . objDisplay ( actual ) } to not include the members of ${ util . objDisplay ( expected ) } ` ) ;
115+ }
116+ } ;
117+ } ) ;
0 commit comments