@@ -914,9 +914,26 @@ namespace FourSlash {
914914 }
915915
916916 if ( ts . hasProperty ( options , "exact" ) ) {
917- ts . Debug . assert ( ! ts . hasProperty ( options , "includes" ) && ! ts . hasProperty ( options , "excludes" ) ) ;
917+ ts . Debug . assert ( ! ts . hasProperty ( options , "includes" ) && ! ts . hasProperty ( options , "excludes" ) && ! ts . hasProperty ( options , "unsorted" ) ) ;
918918 if ( options . exact === undefined ) throw this . raiseError ( "Expected no completions" ) ;
919- this . verifyCompletionsAreExactly ( actualCompletions . entries , toArray ( options . exact ) , options . marker ) ;
919+ this . verifyCompletionsAreExactly ( actualCompletions . entries , options . exact , options . marker ) ;
920+ }
921+ else if ( options . unsorted ) {
922+ ts . Debug . assert ( ! ts . hasProperty ( options , "includes" ) && ! ts . hasProperty ( options , "excludes" ) ) ;
923+ for ( const expectedEntry of options . unsorted ) {
924+ const name = typeof expectedEntry === "string" ? expectedEntry : expectedEntry . name ;
925+ const found = nameToEntries . get ( name ) ;
926+ if ( ! found ) throw this . raiseError ( `Unsorted: completion '${ name } ' not found.` ) ;
927+ if ( ! found . length ) throw this . raiseError ( `Unsorted: no completions with name '${ name } ' remain unmatched.` ) ;
928+ this . verifyCompletionEntry ( found . shift ( ) ! , expectedEntry ) ;
929+ }
930+ if ( actualCompletions . entries . length !== options . unsorted . length ) {
931+ const unmatched : string [ ] = [ ] ;
932+ nameToEntries . forEach ( entries => {
933+ unmatched . push ( ...entries . map ( e => e . name ) ) ;
934+ } ) ;
935+ this . raiseError ( `Additional completions found not included in 'unsorted': ${ unmatched . join ( "\n" ) } ` ) ;
936+ }
920937 }
921938 else {
922939 if ( options . includes ) {
@@ -993,7 +1010,11 @@ namespace FourSlash {
9931010 }
9941011 }
9951012
996- private verifyCompletionsAreExactly ( actual : readonly ts . CompletionEntry [ ] , expected : readonly FourSlashInterface . ExpectedCompletionEntry [ ] , marker ?: ArrayOrSingle < string | Marker > ) {
1013+ private verifyCompletionsAreExactly ( actual : readonly ts . CompletionEntry [ ] , expected : ArrayOrSingle < FourSlashInterface . ExpectedCompletionEntry > | FourSlashInterface . ExpectedExactCompletionsPlus , marker ?: ArrayOrSingle < string | Marker > ) {
1014+ if ( ! ts . isArray ( expected ) ) {
1015+ expected = [ expected ] ;
1016+ }
1017+
9971018 // First pass: test that names are right. Then we'll test details.
9981019 assert . deepEqual ( actual . map ( a => a . name ) , expected . map ( e => typeof e === "string" ? e : e . name ) , marker ? "At marker " + JSON . stringify ( marker ) : undefined ) ;
9991020
@@ -1004,6 +1025,16 @@ namespace FourSlash {
10041025 }
10051026 this . verifyCompletionEntry ( completion , expectedCompletion ) ;
10061027 } ) ;
1028+
1029+ // All completions were correct in the sort order given. If that order was produced by a function
1030+ // like `completion.globalsPlus`, ensure the "plus" array was sorted in the same way.
1031+ const { plusArgument, plusFunctionName } = expected as FourSlashInterface . ExpectedExactCompletionsPlus ;
1032+ if ( plusArgument ) {
1033+ assert . deepEqual (
1034+ plusArgument ,
1035+ expected . filter ( entry => plusArgument . includes ( entry ) ) ,
1036+ `At marker ${ JSON . stringify ( marker ) } : Argument to '${ plusFunctionName } ' was incorrectly sorted.` ) ;
1037+ }
10071038 }
10081039
10091040 /** Use `getProgram` instead of accessing this directly. */
0 commit comments