diff --git a/src/client/providers/renameProvider.ts b/src/client/providers/renameProvider.ts index f8e5a148fbe0..827a4b213a3e 100644 --- a/src/client/providers/renameProvider.ts +++ b/src/client/providers/renameProvider.ts @@ -51,7 +51,7 @@ export class PythonRenameProvider implements vscode.RenameProvider { const workspaceRoot = workspaceFolder ? workspaceFolder.uri.fsPath : __dirname; const pythonSettings = PythonSettings.getInstance(workspaceFolder ? workspaceFolder.uri : undefined); - const proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, workspaceRoot); + const proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, workspaceRoot, this.serviceContainer); return proxy.rename(document, newName, document.uri.fsPath, range).then(response => { const fileDiffs = response.results.map(fileChanges => fileChanges.diff); return getWorkspaceEditsFromPatch(fileDiffs, workspaceRoot); diff --git a/src/client/providers/simpleRefactorProvider.ts b/src/client/providers/simpleRefactorProvider.ts index d583ef5bf298..f6b1772dc19f 100644 --- a/src/client/providers/simpleRefactorProvider.ts +++ b/src/client/providers/simpleRefactorProvider.ts @@ -22,7 +22,7 @@ export function activateSimplePythonRefactorProvider(context: vscode.ExtensionCo vscode.window.activeTextEditor!, vscode.window.activeTextEditor!.selection, // tslint:disable-next-line:no-empty - outputChannel).catch(() => { }); + outputChannel, serviceContainer).catch(() => { }); sendTelemetryWhenDone(REFACTOR_EXTRACT_VAR, promise, stopWatch); }); context.subscriptions.push(disposable); @@ -33,7 +33,7 @@ export function activateSimplePythonRefactorProvider(context: vscode.ExtensionCo vscode.window.activeTextEditor!, vscode.window.activeTextEditor!.selection, // tslint:disable-next-line:no-empty - outputChannel).catch(() => { }); + outputChannel, serviceContainer).catch(() => { }); sendTelemetryWhenDone(REFACTOR_EXTRACT_FUNCTION, promise, stopWatch); }); context.subscriptions.push(disposable); @@ -42,7 +42,7 @@ export function activateSimplePythonRefactorProvider(context: vscode.ExtensionCo // Exported for unit testing export function extractVariable(extensionDir: string, textEditor: vscode.TextEditor, range: vscode.Range, // tslint:disable-next-line:no-any - outputChannel: vscode.OutputChannel): Promise { + outputChannel: vscode.OutputChannel, serviceContainer: IServiceContainer): Promise { let workspaceFolder = vscode.workspace.getWorkspaceFolder(textEditor.document.uri); if (!workspaceFolder && Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0) { @@ -53,7 +53,7 @@ export function extractVariable(extensionDir: string, textEditor: vscode.TextEdi return validateDocumentForRefactor(textEditor).then(() => { const newName = `newvariable${new Date().getMilliseconds().toString()}`; - const proxy = new RefactorProxy(extensionDir, pythonSettings, workspaceRoot); + const proxy = new RefactorProxy(extensionDir, pythonSettings, workspaceRoot, serviceContainer); const rename = proxy.extractVariable(textEditor.document, newName, textEditor.document.uri.fsPath, range, textEditor.options).then(response => { return response.results[0].diff; }); @@ -65,7 +65,7 @@ export function extractVariable(extensionDir: string, textEditor: vscode.TextEdi // Exported for unit testing export function extractMethod(extensionDir: string, textEditor: vscode.TextEditor, range: vscode.Range, // tslint:disable-next-line:no-any - outputChannel: vscode.OutputChannel): Promise { + outputChannel: vscode.OutputChannel, serviceContainer: IServiceContainer): Promise { let workspaceFolder = vscode.workspace.getWorkspaceFolder(textEditor.document.uri); if (!workspaceFolder && Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0) { @@ -76,7 +76,7 @@ export function extractMethod(extensionDir: string, textEditor: vscode.TextEdito return validateDocumentForRefactor(textEditor).then(() => { const newName = `newmethod${new Date().getMilliseconds().toString()}`; - const proxy = new RefactorProxy(extensionDir, pythonSettings, workspaceRoot); + const proxy = new RefactorProxy(extensionDir, pythonSettings, workspaceRoot, serviceContainer); const rename = proxy.extractMethod(textEditor.document, newName, textEditor.document.uri.fsPath, range, textEditor.options).then(response => { return response.results[0].diff; }); diff --git a/src/client/refactor/proxy.ts b/src/client/refactor/proxy.ts index 9be8271a396e..9cc5b684a642 100644 --- a/src/client/refactor/proxy.ts +++ b/src/client/refactor/proxy.ts @@ -1,33 +1,37 @@ -'use strict'; +// tslint:disable:no-any no-empty member-ordering prefer-const prefer-template no-var-self import * as child_process from 'child_process'; import * as path from 'path'; import * as vscode from 'vscode'; +import { Uri } from 'vscode'; import { IPythonSettings } from '../common/configSettings'; -import { mergeEnvVariables } from '../common/envFileParser'; -import { getCustomEnvVarsSync, getWindowsLineEndingCount, IS_WINDOWS } from '../common/utils'; +import '../common/extensions'; +import { createDeferred, Deferred } from '../common/helpers'; +import { IPythonExecutionFactory } from '../common/process/types'; +import { getWindowsLineEndingCount, IS_WINDOWS } from '../common/utils'; +import { IServiceContainer } from '../ioc/types'; export class RefactorProxy extends vscode.Disposable { - private _process: child_process.ChildProcess; + private _process?: child_process.ChildProcess; private _extensionDir: string; private _previousOutData: string = ''; private _previousStdErrData: string = ''; private _startedSuccessfully: boolean = false; - private _commandResolve: (value?: any | PromiseLike) => void; + private _commandResolve?: (value?: any | PromiseLike) => void; private _commandReject: (reason?: any) => void; - private _initializeReject: (reason?: any) => void; - constructor(extensionDir: string, private pythonSettings: IPythonSettings, private workspaceRoot: string) { + private initialized: Deferred; + constructor(extensionDir: string, private pythonSettings: IPythonSettings, private workspaceRoot: string, + private serviceContainer: IServiceContainer) { super(() => { }); this._extensionDir = extensionDir; } - dispose() { + public dispose() { try { - this._process.kill(); + this._process!.kill(); + } catch (ex) { } - catch (ex) { - } - this._process = null; + this._process = undefined; } private getOffsetAt(document: vscode.TextDocument, position: vscode.Position): number { if (!IS_WINDOWS) { @@ -43,52 +47,52 @@ export class RefactorProxy extends vscode.Disposable { return offset - winEols; } - rename(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { + public rename(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { if (!options) { - options = vscode.window.activeTextEditor.options; + options = vscode.window.activeTextEditor!.options; } - let command = { - "lookup": "rename", - "file": filePath, - "start": this.getOffsetAt(document, range.start).toString(), - "id": "1", - "name": name, - "indent_size": options.tabSize + const command = { + lookup: 'rename', + file: filePath, + start: this.getOffsetAt(document, range.start).toString(), + id: '1', + name: name, + indent_size: options.tabSize }; return this.sendCommand(JSON.stringify(command)); } - extractVariable(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { + public extractVariable(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { if (!options) { - options = vscode.window.activeTextEditor.options; + options = vscode.window.activeTextEditor!.options; } - let command = { - "lookup": "extract_variable", - "file": filePath, - "start": this.getOffsetAt(document, range.start).toString(), - "end": this.getOffsetAt(document, range.end).toString(), - "id": "1", - "name": name, - "indent_size": options.tabSize + const command = { + lookup: 'extract_variable', + file: filePath, + start: this.getOffsetAt(document, range.start).toString(), + end: this.getOffsetAt(document, range.end).toString(), + id: '1', + name: name, + indent_size: options.tabSize }; return this.sendCommand(JSON.stringify(command)); } - extractMethod(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { + public extractMethod(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { if (!options) { - options = vscode.window.activeTextEditor.options; + options = vscode.window.activeTextEditor!.options; } // Ensure last line is an empty line if (!document.lineAt(document.lineCount - 1).isEmptyOrWhitespace && range.start.line === document.lineCount - 1) { return Promise.reject('Missing blank line at the end of document (PEP8).'); } - let command = { - "lookup": "extract_method", - "file": filePath, - "start": this.getOffsetAt(document, range.start).toString(), - "end": this.getOffsetAt(document, range.end).toString(), - "id": "1", - "name": name, - "indent_size": options.tabSize + const command = { + lookup: 'extract_method', + file: filePath, + start: this.getOffsetAt(document, range.start).toString(), + end: this.getOffsetAt(document, range.end).toString(), + id: '1', + name: name, + indent_size: options.tabSize }; return this.sendCommand(JSON.stringify(command)); } @@ -98,39 +102,30 @@ export class RefactorProxy extends vscode.Disposable { return new Promise((resolve, reject) => { this._commandResolve = resolve; this._commandReject = reject; - this._process.stdin.write(command + '\n'); + this._process!.stdin.write(command + '\n'); }); }); } - private initialize(pythonPath: string): Promise { - return new Promise((resolve, reject) => { - this._initializeReject = reject; - let environmentVariables: Object & { [key: string]: string } = { 'PYTHONUNBUFFERED': '1' }; - let customEnvironmentVars = getCustomEnvVarsSync(vscode.Uri.file(this.workspaceRoot)); - if (customEnvironmentVars) { - environmentVariables = mergeEnvVariables(environmentVariables, customEnvironmentVars); + private async initialize(pythonPath: string): Promise { + const pythonProc = await this.serviceContainer.get(IPythonExecutionFactory).create(Uri.file(this.workspaceRoot)); + this.initialized = createDeferred(); + const args = ['refactor.py', this.workspaceRoot]; + const cwd = path.join(this._extensionDir, 'pythonFiles'); + const result = pythonProc.execObservable(args, { cwd }); + this._process = result.proc; + result.out.subscribe(output => { + if (output.source === 'stdout') { + if (!this._startedSuccessfully && output.out.startsWith('STARTED')) { + this._startedSuccessfully = true; + return this.initialized.resolve(); + } + this.onData(output.out); + } else { + this.handleStdError(output.out); } - environmentVariables = mergeEnvVariables(environmentVariables); - this._process = child_process.spawn(pythonPath, ['refactor.py', this.workspaceRoot], - { - cwd: path.join(this._extensionDir, 'pythonFiles'), - env: environmentVariables - }); - this._process.stderr.setEncoding('utf8'); - this._process.stderr.on('data', this.handleStdError.bind(this)); - this._process.on('error', this.handleError.bind(this)); + }, error => this.handleError(error)); - let that = this; - this._process.stdout.setEncoding('utf8'); - this._process.stdout.on('data', (data: string) => { - let dataStr: string = data + ''; - if (!that._startedSuccessfully && dataStr.startsWith('STARTED')) { - that._startedSuccessfully = true; - return resolve(); - } - that.onData(data); - }); - }); + return this.initialized.promise; } private handleStdError(data: string) { // Possible there was an exception in parsing the data returned @@ -140,34 +135,32 @@ export class RefactorProxy extends vscode.Disposable { try { errorResponse = dataStr.split(/\r?\n/g).filter(line => line.length > 0).map(resp => JSON.parse(resp)); this._previousStdErrData = ''; - } - catch (ex) { + } catch (ex) { console.error(ex); // Possible we've only received part of the data, hence don't clear previousData return; } if (typeof errorResponse[0].message !== 'string' || errorResponse[0].message.length === 0) { - errorResponse[0].message = errorResponse[0].traceback.split(/\r?\n/g).pop(); + errorResponse[0].message = errorResponse[0].traceback.splitLines().pop()!; } let errorMessage = errorResponse[0].message + '\n' + errorResponse[0].traceback; if (this._startedSuccessfully) { this._commandReject(`Refactor failed. ${errorMessage}`); - } - else { + } else { if (typeof errorResponse[0].type === 'string' && errorResponse[0].type === 'ModuleNotFoundError') { - this._initializeReject('Not installed'); + this.initialized.reject('Not installed'); return; } - this._initializeReject(`Refactor failed. ${errorMessage}`); + this.initialized.reject(`Refactor failed. ${errorMessage}`); } } private handleError(error: Error) { if (this._startedSuccessfully) { return this._commandReject(error); } - this._initializeReject(error); + this.initialized.reject(error); } private onData(data: string) { if (!this._commandResolve) { return; } @@ -179,13 +172,12 @@ export class RefactorProxy extends vscode.Disposable { try { response = dataStr.split(/\r?\n/g).filter(line => line.length > 0).map(resp => JSON.parse(resp)); this._previousOutData = ''; - } - catch (ex) { + } catch (ex) { // Possible we've only received part of the data, hence don't clear previousData return; } this.dispose(); - this._commandResolve(response[0]); - this._commandResolve = null; + this._commandResolve!(response[0]); + this._commandResolve = undefined; } } diff --git a/src/test/refactor/extension.refactor.extract.method.test.ts b/src/test/refactor/extension.refactor.extract.method.test.ts index 436c1f8f519f..ee3c2b34a9fc 100644 --- a/src/test/refactor/extension.refactor.extract.method.test.ts +++ b/src/test/refactor/extension.refactor.extract.method.test.ts @@ -1,16 +1,16 @@ -import * as assert from 'assert'; +// tslint:disable:interface-name no-any max-func-body-length estrict-plus-operands -// You can import and use all API from the \'vscode\' module -// as well as import your extension to test it -import * as vscode from 'vscode'; -import * as path from 'path'; +import * as assert from 'assert'; import * as fs from 'fs-extra'; -import { PythonSettings } from '../../client/common/configSettings'; -import { initialize, closeActiveWindows, IS_TRAVIS, wait, initializeTest } from './../initialize'; +import * as path from 'path'; +import * as vscode from 'vscode'; import { Position } from 'vscode'; +import { PythonSettings } from '../../client/common/configSettings'; +import { getTextEditsFromPatch } from '../../client/common/editor'; import { extractMethod } from '../../client/providers/simpleRefactorProvider'; import { RefactorProxy } from '../../client/refactor/proxy'; -import { getTextEditsFromPatch } from '../../client/common/editor'; +import { UnitTestIocContainer } from '../unittests/serviceRegistry'; +import { closeActiveWindows, initialize, initializeTest, IS_TRAVIS, wait } from './../initialize'; import { MockOutputChannel } from './../mockClasses'; const EXTENSION_DIR = path.join(__dirname, '..', '..', '..'); @@ -26,9 +26,11 @@ suite('Method Extraction', () => { const oldExecuteCommand = vscode.commands.executeCommand; const options: vscode.TextEditorOptions = { cursorStyle: vscode.TextEditorCursorStyle.Line, insertSpaces: true, lineNumbers: vscode.TextEditorLineNumbersStyle.Off, tabSize: 4 }; - suiteSetup(() => { + let ioc: UnitTestIocContainer; + suiteSetup(async () => { fs.copySync(refactorSourceFile, refactorTargetFile, { overwrite: true }); - return initialize(); + await initialize(); + initializeDI(); }); suiteTeardown(() => { vscode.commands.executeCommand = oldExecuteCommand; @@ -48,20 +50,28 @@ suite('Method Extraction', () => { return closeActiveWindows(); }); + function initializeDI() { + ioc = new UnitTestIocContainer(); + ioc.registerCommonTypes(); + ioc.registerProcessTypes(); + ioc.registerVariableTypes(); + } + function testingMethodExtraction(shouldError: boolean, startPos: Position, endPos: Position) { const pythonSettings = PythonSettings.getInstance(vscode.Uri.file(refactorTargetFile)); - let rangeOfTextToExtract = new vscode.Range(startPos, endPos); - let proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, path.dirname(refactorTargetFile)); + const rangeOfTextToExtract = new vscode.Range(startPos, endPos); + const proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, path.dirname(refactorTargetFile), ioc.serviceContainer); let expectedTextEdits: vscode.TextEdit[]; let ignoreErrorHandling = false; let mockTextDoc: vscode.TextDocument; + // tslint:disable-next-line:no-multiline-string const DIFF = `--- a/refactor.py\n+++ b/refactor.py\n@@ -237,9 +237,12 @@\n try:\n self._process_request(self._input.readline())\n except Exception as ex:\n- message = ex.message + ' \\n' + traceback.format_exc()\n- sys.stderr.write(str(len(message)) + ':' + message)\n- sys.stderr.flush()\n+ self.myNewMethod(ex)\n+\n+ def myNewMethod(self, ex):\n+ message = ex.message + ' \\n' + traceback.format_exc()\n+ sys.stderr.write(str(len(message)) + ':' + message)\n+ sys.stderr.flush()\n \n if __name__ == '__main__':\n RopeRefactoring().watch()\n`; return new Promise((resolve, reject) => { vscode.workspace.openTextDocument(refactorTargetFile).then(textDocument => { mockTextDoc = textDocument; expectedTextEdits = getTextEditsFromPatch(textDocument.getText(), DIFF); resolve(); - }, error => reject(error)); + }, reject); }) .then(() => proxy.extractMethod(mockTextDoc, 'myNewMethod', refactorTargetFile, rangeOfTextToExtract, options)) .then(response => { @@ -69,16 +79,16 @@ suite('Method Extraction', () => { ignoreErrorHandling = true; assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); } - let textEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); + const textEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); assert.equal(response.results.length, 1, 'Invalid number of items in response'); assert.equal(textEdits.length, expectedTextEdits.length, 'Invalid number of Text Edits'); textEdits.forEach(edit => { - let foundEdit = expectedTextEdits.filter(item => item.newText === edit.newText && item.range.isEqual(edit.range)); + const foundEdit = expectedTextEdits.filter(item => item.newText === edit.newText && item.range.isEqual(edit.range)); assert.equal(foundEdit.length, 1, 'Edit not found'); }); - }).catch(error => { + }).catch((error: any) => { if (ignoreErrorHandling) { - return Promise.reject(error); + return Promise.reject(error!); } if (shouldError) { // Wait a minute this shouldn't work, what's going on @@ -86,27 +96,27 @@ suite('Method Extraction', () => { return; } - return Promise.reject(error); + return Promise.reject(error!); }); } - test('Extract Method', done => { - let startPos = new vscode.Position(239, 0); - let endPos = new vscode.Position(241, 35); - testingMethodExtraction(false, startPos, endPos).then(() => done(), done); + test('Extract Method', async () => { + const startPos = new vscode.Position(239, 0); + const endPos = new vscode.Position(241, 35); + await testingMethodExtraction(false, startPos, endPos); }); - test('Extract Method will fail if complete statements are not selected', done => { - let startPos = new vscode.Position(239, 30); - let endPos = new vscode.Position(241, 35); - testingMethodExtraction(true, startPos, endPos).then(() => done(), done); + test('Extract Method will fail if complete statements are not selected', async () => { + const startPos = new vscode.Position(239, 30); + const endPos = new vscode.Position(241, 35); + await testingMethodExtraction(true, startPos, endPos); }); function testingMethodExtractionEndToEnd(shouldError: boolean, startPos: Position, endPos: Position) { - let ch = new MockOutputChannel('Python'); + const ch = new MockOutputChannel('Python'); let textDocument: vscode.TextDocument; let textEditor: vscode.TextEditor; - let rangeOfTextToExtract = new vscode.Range(startPos, endPos); + const rangeOfTextToExtract = new vscode.Range(startPos, endPos); let ignoreErrorHandling = false; return vscode.workspace.openTextDocument(refactorTargetFile).then(document => { @@ -119,7 +129,7 @@ suite('Method Extraction', () => { textEditor = editor; return; }).then(() => { - return extractMethod(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch).then(() => { + return extractMethod(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch, ioc.serviceContainer).then(() => { if (shouldError) { ignoreErrorHandling = true; assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); @@ -129,9 +139,9 @@ suite('Method Extraction', () => { assert.equal(ch.output.length, 0, 'Output channel is not empty'); assert.equal(textDocument.lineAt(241).text.trim().indexOf('def newmethod'), 0, 'New Method not created'); assert.equal(textDocument.lineAt(239).text.trim().startsWith('self.newmethod'), true, 'New Method not being used'); - }).catch(error => { + }).catch((error: any) => { if (ignoreErrorHandling) { - return Promise.reject(error); + return Promise.reject(error!); } if (shouldError) { // Wait a minute this shouldn't work, what's going on @@ -139,7 +149,7 @@ suite('Method Extraction', () => { return; } - return Promise.reject(error); + return Promise.reject(error!); }); }, error => { if (ignoreErrorHandling) { @@ -148,9 +158,9 @@ suite('Method Extraction', () => { if (shouldError) { // Wait a minute this shouldn't work, what's going on assert.equal(true, true, 'Error raised as expected'); - } - else { - assert.fail(error + '', null, 'Method extraction failed\n' + ch.output, ''); + } else { + // tslint:disable-next-line:prefer-template restrict-plus-operands + assert.fail(error, null, 'Method extraction failed\n' + ch.output, ''); return Promise.reject(error); } }); @@ -158,16 +168,16 @@ suite('Method Extraction', () => { // This test fails on linux (text document not getting updated in time) if (!IS_TRAVIS) { - test('Extract Method (end to end)', done => { - let startPos = new vscode.Position(239, 0); - let endPos = new vscode.Position(241, 35); - testingMethodExtractionEndToEnd(false, startPos, endPos).then(() => done(), done); + test('Extract Method (end to end)', async () => { + const startPos = new vscode.Position(239, 0); + const endPos = new vscode.Position(241, 35); + await testingMethodExtractionEndToEnd(false, startPos, endPos); }); } - test('Extract Method will fail if complete statements are not selected', done => { - let startPos = new vscode.Position(239, 30); - let endPos = new vscode.Position(241, 35); - testingMethodExtractionEndToEnd(true, startPos, endPos).then(() => done(), done); + test('Extract Method will fail if complete statements are not selected', async () => { + const startPos = new vscode.Position(239, 30); + const endPos = new vscode.Position(241, 35); + await testingMethodExtractionEndToEnd(true, startPos, endPos); }); }); diff --git a/src/test/refactor/extension.refactor.extract.var.test.ts b/src/test/refactor/extension.refactor.extract.var.test.ts index 06144c333e18..5ce6cc3f743c 100644 --- a/src/test/refactor/extension.refactor.extract.var.test.ts +++ b/src/test/refactor/extension.refactor.extract.var.test.ts @@ -1,16 +1,16 @@ -import * as assert from 'assert'; +// tslint:disable:interface-name no-any max-func-body-length estrict-plus-operands -// You can import and use all API from the \'vscode\' module -// as well as import your extension to test it -import * as vscode from 'vscode'; -import * as path from 'path'; +import * as assert from 'assert'; import * as fs from 'fs-extra'; -import { PythonSettings } from '../../client/common/configSettings'; -import { closeActiveWindows, initialize, initializeTest, IS_TRAVIS, wait } from './../initialize'; +import * as path from 'path'; +import * as vscode from 'vscode'; import { Position } from 'vscode'; +import { PythonSettings } from '../../client/common/configSettings'; +import { getTextEditsFromPatch } from '../../client/common/editor'; import { extractVariable } from '../../client/providers/simpleRefactorProvider'; import { RefactorProxy } from '../../client/refactor/proxy'; -import { getTextEditsFromPatch } from '../../client/common/editor'; +import { UnitTestIocContainer } from '../unittests/serviceRegistry'; +import { closeActiveWindows, initialize, initializeTest, IS_TRAVIS, wait } from './../initialize'; import { MockOutputChannel } from './../mockClasses'; const EXTENSION_DIR = path.join(__dirname, '..', '..', '..'); @@ -25,6 +25,7 @@ suite('Variable Extraction', () => { // Hack hac hack const oldExecuteCommand = vscode.commands.executeCommand; const options: vscode.TextEditorOptions = { cursorStyle: vscode.TextEditorCursorStyle.Line, insertSpaces: true, lineNumbers: vscode.TextEditorLineNumbersStyle.Off, tabSize: 4 }; + let ioc: UnitTestIocContainer; suiteSetup(async () => { fs.copySync(refactorSourceFile, refactorTargetFile, { overwrite: true }); await initialize(); @@ -34,6 +35,7 @@ suite('Variable Extraction', () => { return closeActiveWindows(); }); setup(async () => { + initializeDI(); if (fs.existsSync(refactorTargetFile)) { await wait(500); fs.unlinkSync(refactorTargetFile); @@ -47,10 +49,17 @@ suite('Variable Extraction', () => { return closeActiveWindows(); }); + function initializeDI() { + ioc = new UnitTestIocContainer(); + ioc.registerCommonTypes(); + ioc.registerProcessTypes(); + ioc.registerVariableTypes(); + } + function testingVariableExtraction(shouldError: boolean, startPos: Position, endPos: Position) { const pythonSettings = PythonSettings.getInstance(vscode.Uri.file(refactorTargetFile)); - let rangeOfTextToExtract = new vscode.Range(startPos, endPos); - let proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, path.dirname(refactorTargetFile)); + const rangeOfTextToExtract = new vscode.Range(startPos, endPos); + const proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, path.dirname(refactorTargetFile), ioc.serviceContainer); let expectedTextEdits: vscode.TextEdit[]; let ignoreErrorHandling = false; let mockTextDoc: vscode.TextDocument; @@ -60,7 +69,7 @@ suite('Variable Extraction', () => { mockTextDoc = textDocument; expectedTextEdits = getTextEditsFromPatch(textDocument.getText(), DIFF); resolve(); - }, error => reject(error)) + }, reject); }) .then(() => proxy.extractVariable(mockTextDoc, 'myNewVariable', refactorTargetFile, rangeOfTextToExtract, options)) .then(response => { @@ -68,16 +77,16 @@ suite('Variable Extraction', () => { ignoreErrorHandling = true; assert.fail(null, null, 'Extraction should fail with an error', ''); } - let textEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); + const textEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); assert.equal(response.results.length, 1, 'Invalid number of items in response'); assert.equal(textEdits.length, expectedTextEdits.length, 'Invalid number of Text Edits'); textEdits.forEach(edit => { - let foundEdit = expectedTextEdits.filter(item => item.newText === edit.newText && item.range.isEqual(edit.range)); + const foundEdit = expectedTextEdits.filter(item => item.newText === edit.newText && item.range.isEqual(edit.range)); assert.equal(foundEdit.length, 1, 'Edit not found'); }); - }).catch(error => { + }).catch((error: any) => { if (ignoreErrorHandling) { - return Promise.reject(error); + return Promise.reject(error!); } if (shouldError) { // Wait a minute this shouldn't work, what's going on @@ -85,27 +94,27 @@ suite('Variable Extraction', () => { return; } - return Promise.reject(error); + return Promise.reject(error!); }); } - test('Extract Variable', done => { - let startPos = new vscode.Position(234, 29); - let endPos = new vscode.Position(234, 38); - testingVariableExtraction(false, startPos, endPos).then(() => done(), done); + test('Extract Variable', async () => { + const startPos = new vscode.Position(234, 29); + const endPos = new vscode.Position(234, 38); + testingVariableExtraction(false, startPos, endPos); }); - test('Extract Variable fails if whole string not selected', done => { - let startPos = new vscode.Position(234, 20); - let endPos = new vscode.Position(234, 38); - testingVariableExtraction(true, startPos, endPos).then(() => done(), done); + test('Extract Variable fails if whole string not selected', async () => { + const startPos = new vscode.Position(234, 20); + const endPos = new vscode.Position(234, 38); + testingVariableExtraction(true, startPos, endPos); }); function testingVariableExtractionEndToEnd(shouldError: boolean, startPos: Position, endPos: Position) { - let ch = new MockOutputChannel('Python'); + const ch = new MockOutputChannel('Python'); let textDocument: vscode.TextDocument; let textEditor: vscode.TextEditor; - let rangeOfTextToExtract = new vscode.Range(startPos, endPos); + const rangeOfTextToExtract = new vscode.Range(startPos, endPos); let ignoreErrorHandling = false; return vscode.workspace.openTextDocument(refactorTargetFile).then(document => { textDocument = document; @@ -117,7 +126,7 @@ suite('Variable Extraction', () => { textEditor = editor; return; }).then(() => { - return extractVariable(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch).then(() => { + return extractVariable(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch, ioc.serviceContainer).then(() => { if (shouldError) { ignoreErrorHandling = true; assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); @@ -128,9 +137,9 @@ suite('Variable Extraction', () => { assert.equal(textDocument.lineAt(234).text.trim().indexOf('newvariable'), 0, 'New Variable not created'); assert.equal(textDocument.lineAt(234).text.trim().endsWith('= "STARTED"'), true, 'Started Text Assigned to variable'); assert.equal(textDocument.lineAt(235).text.indexOf('(newvariable') >= 0, true, 'New Variable not being used'); - }).catch(error => { + }).catch((error: any) => { if (ignoreErrorHandling) { - return Promise.reject(error); + return Promise.reject(error!); } if (shouldError) { // Wait a minute this shouldn't work, what's going on @@ -138,7 +147,7 @@ suite('Variable Extraction', () => { return; } - return Promise.reject(error); + return Promise.reject(error)!; }); }, error => { if (ignoreErrorHandling) { @@ -147,8 +156,8 @@ suite('Variable Extraction', () => { if (shouldError) { // Wait a minute this shouldn't work, what's going on assert.equal(true, true, 'Error raised as expected'); - } - else { + } else { + // tslint:disable-next-line:prefer-template restrict-plus-operands assert.fail(error + '', null, 'Variable extraction failed\n' + ch.output, ''); return Promise.reject(error); } @@ -157,16 +166,16 @@ suite('Variable Extraction', () => { // This test fails on linux (text document not getting updated in time) if (!IS_TRAVIS) { - test('Extract Variable (end to end)', done => { - let startPos = new vscode.Position(234, 29); - let endPos = new vscode.Position(234, 38); - testingVariableExtractionEndToEnd(false, startPos, endPos).then(() => done(), done); + test('Extract Variable (end to end)', async () => { + const startPos = new vscode.Position(234, 29); + const endPos = new vscode.Position(234, 38); + await testingVariableExtractionEndToEnd(false, startPos, endPos); }); } - test('Extract Variable fails if whole string not selected (end to end)', done => { - let startPos = new vscode.Position(234, 20); - let endPos = new vscode.Position(234, 38); - testingVariableExtractionEndToEnd(true, startPos, endPos).then(() => done(), done); + test('Extract Variable fails if whole string not selected (end to end)', async () => { + const startPos = new vscode.Position(234, 20); + const endPos = new vscode.Position(234, 38); + await testingVariableExtractionEndToEnd(true, startPos, endPos); }); });