1
1
import * as chai from 'chai' ;
2
2
import * as sinon from 'sinon' ;
3
3
import * as ts from 'typescript' ;
4
- import { CompletionItemKind , CompletionList , TextDocumentIdentifier , TextDocumentItem } from 'vscode-languageserver' ;
5
- import { Hover , Location , SignatureHelp , SymbolInformation , SymbolKind , WorkspaceEdit } from 'vscode-languageserver-types' ;
4
+ import { CompletionItemKind , CompletionList , DiagnosticSeverity , TextDocumentIdentifier , TextDocumentItem , WorkspaceEdit } from 'vscode-languageserver' ;
5
+ import { Command , Diagnostic , Hover , Location , SignatureHelp , SymbolInformation , SymbolKind } from 'vscode-languageserver-types' ;
6
6
import { LanguageClient , RemoteLanguageClient } from '../lang-handler' ;
7
7
import { TextDocumentContentParams , WorkspaceFilesParams } from '../request-type' ;
8
8
import { SymbolLocationInformation } from '../request-type' ;
@@ -16,7 +16,7 @@ const assert = chai.assert;
16
16
/**
17
17
* Enforcing strict mode to make tests pass on Windows
18
18
*/
19
- import { setStrict } from '../util' ;
19
+ import { setStrict , uri2path } from '../util' ;
20
20
setStrict ( true ) ;
21
21
22
22
export interface TestContext {
@@ -53,6 +53,7 @@ export const initializeTypeScriptService = (createService: TypeScriptServiceFact
53
53
return Array . from ( files . keys ( ) ) . map ( uri => ( { uri } ) ) ;
54
54
} ) ;
55
55
this . client . xcacheGet . returns ( null ) ;
56
+ this . client . workspaceApplyEdit . returns ( Promise . resolve ( { applied : true } ) ) ;
56
57
this . service = createService ( this . client ) ;
57
58
58
59
await this . service . initialize ( {
@@ -2310,6 +2311,126 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor
2310
2311
} ) ;
2311
2312
} ) ;
2312
2313
2314
+ describe ( 'textDocumentCodeAction()' , function ( this : TestContext & ISuiteCallbackContext ) {
2315
+ beforeEach ( initializeTypeScriptService ( createService , rootUri , new Map ( [
2316
+ [ rootUri + 'package.json' , JSON . stringify ( { name : 'mypkg' } ) ] ,
2317
+ [ rootUri + 'a.ts' , [
2318
+ 'class A {' ,
2319
+ '\tconstructor() {' ,
2320
+ '\t\tmissingThis = 33;' ,
2321
+ '\t}' ,
2322
+ '}' ,
2323
+ 'const a = new A();'
2324
+ ] . join ( '\n' ) ]
2325
+ ] ) ) as any ) ;
2326
+
2327
+ afterEach ( shutdownService as any ) ;
2328
+
2329
+ it ( 'suggests a missing this' , async function ( this : TestContext & ITestCallbackContext ) {
2330
+ await this . service . textDocumentDidOpen ( {
2331
+ textDocument : {
2332
+ uri : rootUri + 'a.ts' ,
2333
+ languageId : 'typescript' ,
2334
+ text : [
2335
+ 'class A {' ,
2336
+ '\tmissingThis: number;' ,
2337
+ '\tconstructor() {' ,
2338
+ '\t\tmissingThis = 33;' ,
2339
+ '\t}' ,
2340
+ '}' ,
2341
+ 'const a = new A();'
2342
+ ] . join ( '\n' ) ,
2343
+ version : 1
2344
+ }
2345
+ } ) ;
2346
+
2347
+ const firstDiagnostic : Diagnostic = {
2348
+ range : {
2349
+ start : { line : 3 , character : 4 } ,
2350
+ end : { line : 3 , character : 15 }
2351
+ } ,
2352
+ message : 'Cannot find name \'missingThis\'. Did you mean the instance member \'this.missingThis\'?' ,
2353
+ severity : DiagnosticSeverity . Error ,
2354
+ code : 2663 ,
2355
+ source : 'ts'
2356
+ } ;
2357
+ const actions : Command [ ] = await this . service . textDocumentCodeAction ( {
2358
+ textDocument : {
2359
+ uri : rootUri + 'a.ts'
2360
+ } ,
2361
+ range : firstDiagnostic . range ,
2362
+ context : {
2363
+ diagnostics : [ firstDiagnostic ]
2364
+ }
2365
+ } ) . toArray ( ) . map ( patches => apply ( null , patches ) ) . toPromise ( ) ;
2366
+ assert . deepEqual ( actions , [ {
2367
+ title : 'Add \'this.\' to unresolved variable.' ,
2368
+ command : 'codeFix' ,
2369
+ arguments : [ {
2370
+ fileName : uri2path ( rootUri + 'a.ts' ) ,
2371
+ textChanges : [ {
2372
+ span : { start : 49 , length : 13 } ,
2373
+ newText : '\t\tthis.missingThis'
2374
+ } ]
2375
+ } ]
2376
+ } ] ) ;
2377
+
2378
+ } ) ;
2379
+ } ) ;
2380
+
2381
+ describe ( 'workspaceExecuteCommand()' , function ( this : TestContext & ISuiteCallbackContext ) {
2382
+ beforeEach ( initializeTypeScriptService ( createService , rootUri , new Map ( [
2383
+ [ rootUri + 'package.json' , JSON . stringify ( { name : 'mypkg' } ) ] ,
2384
+ [ rootUri + 'a.ts' , [
2385
+ 'class A {' ,
2386
+ ' constructor() {' ,
2387
+ ' missingThis = 33;' ,
2388
+ ' }' ,
2389
+ '}' ,
2390
+ 'const a = new A();'
2391
+ ] . join ( '\n' ) ]
2392
+ ] ) ) as any ) ;
2393
+
2394
+ afterEach ( shutdownService as any ) ;
2395
+
2396
+ describe ( 'codeFix' , ( ) => {
2397
+ it ( 'should apply a WorkspaceEdit for the passed FileTextChanges' , async function ( this : TestContext & ITestCallbackContext ) {
2398
+ await this . service . workspaceExecuteCommand ( {
2399
+ command : 'codeFix' ,
2400
+ arguments : [ {
2401
+ fileName : uri2path ( rootUri + 'a.ts' ) ,
2402
+ textChanges : [ {
2403
+ span : { start : 50 , length : 15 } ,
2404
+ newText : '\t\tthis.missingThis'
2405
+ } ]
2406
+ } ]
2407
+ } ) . toArray ( ) . map ( patches => apply ( null , patches ) ) . toPromise ( ) ;
2408
+
2409
+ sinon . assert . calledOnce ( this . client . workspaceApplyEdit ) ;
2410
+ const workspaceEdit = this . client . workspaceApplyEdit . lastCall . args [ 0 ] ;
2411
+ assert . deepEqual ( workspaceEdit , {
2412
+ edit : {
2413
+ changes : {
2414
+ [ rootUri + 'a.ts' ] : [ {
2415
+ newText : '\t\tthis.missingThis' ,
2416
+ range : {
2417
+ end : {
2418
+ character : 9 ,
2419
+ line : 5
2420
+ } ,
2421
+ start : {
2422
+ character : 0 ,
2423
+ line : 3
2424
+ }
2425
+ }
2426
+ } ]
2427
+ }
2428
+ }
2429
+ } ) ;
2430
+ } ) ;
2431
+ } ) ;
2432
+ } ) ;
2433
+
2313
2434
describe ( 'Special file names' , function ( this : TestContext ) {
2314
2435
2315
2436
beforeEach ( initializeTypeScriptService ( createService , rootUri , new Map ( [
0 commit comments