1+ import * as assert from 'assert' ;
2+
3+ // You can import and use all API from the \'vscode\' module
4+ // as well as import your extension to test it
5+ import * as vscode from 'vscode' ;
6+ import { TextDocument , TextLine , Position , Range } from 'vscode' ;
7+ import * as path from 'path' ;
8+ import * as settings from '../client/common/configSettings' ;
9+ import * as fs from 'fs-extra' ;
10+ import { initialize , closeActiveWindows } from './initialize' ;
11+ import { execPythonFile } from '../client/common/utils' ;
12+ import { extractVariable , extractMethod } from '../client/providers/simpleRefactorProvider' ;
13+ import { RefactorProxy } from '../client/refactor/proxy' ;
14+
15+ let EXTENSION_DIR = path . join ( __dirname , '..' , '..' ) ;
16+ let pythonSettings = settings . PythonSettings . getInstance ( ) ;
17+
18+ const refactorSourceFile = path . join ( __dirname , '..' , '..' , 'src' , 'test' , 'pythonFiles' , 'refactoring' , 'standAlone' , 'refactor.py' ) ;
19+ const refactorTargetFile = path . join ( __dirname , '..' , '..' , 'out' , 'test' , 'pythonFiles' , 'refactoring' , 'standAlone' , 'refactor.py' ) ;
20+ let isPython3 = true ;
21+ let isTRAVIS = ( process . env [ 'TRAVIS' ] + '' ) === 'true' ;
22+
23+ interface RenameResponse {
24+ results : [ { diff : string } ] ;
25+ }
26+
27+ class MockOutputChannel implements vscode . OutputChannel {
28+ constructor ( name : string ) {
29+ this . name = name ;
30+ this . output = '' ;
31+ }
32+ name : string ;
33+ output : string ;
34+ append ( value : string ) {
35+ this . output += value ;
36+ }
37+ appendLine ( value : string ) { this . append ( value ) ; this . append ( '\n' ) ; }
38+ clear ( ) { }
39+ show ( preservceFocus ?: boolean ) : void ;
40+ show ( column ?: vscode . ViewColumn , preserveFocus ?: boolean ) : void ;
41+ show ( x ?: any , y ?: any ) : void { }
42+ hide ( ) { }
43+ dispose ( ) { }
44+ }
45+ class MockTextDocument implements vscode . TextDocument {
46+ uri : vscode . Uri ;
47+ fileName : string ;
48+ isUntitled : boolean ;
49+ languageId : string ;
50+ version : number ;
51+ isDirty : boolean ;
52+ constructor ( private sourceFile : string , private offsets : [ { position : Position , offset : number } ] ) {
53+ }
54+ save ( ) : Thenable < boolean > {
55+ return Promise . resolve ( true ) ;
56+ }
57+ lineCount : number ;
58+ lineAt ( position : Position | number ) : TextLine {
59+ return null ;
60+ }
61+ offsetAt ( position : Position ) : number {
62+ return this . offsets . filter ( item => item . position . isEqual ( position ) ) [ 0 ] . offset ;
63+ }
64+ positionAt ( offset : number ) : Position {
65+ return null ;
66+ }
67+ getText ( range ?: Range ) : string {
68+ return fs . readFileSync ( this . sourceFile , 'utf-8' ) ;
69+ }
70+ getWordRangeAtPosition ( position : Position ) : Range {
71+ return null ;
72+ }
73+ validateRange ( range : Range ) : Range {
74+ return null ;
75+ }
76+ validatePosition ( position : Position ) : Position {
77+ return null ;
78+ }
79+ }
80+
81+ suiteSetup ( done => {
82+ fs . copySync ( refactorSourceFile , refactorTargetFile , { clobber : true } ) ;
83+ initialize ( ) . then ( ( ) => {
84+ new Promise < string > ( resolve => {
85+ // Support for travis
86+ let version = process . env [ 'TRAVIS_PYTHON_VERSION' ] ;
87+ if ( typeof version === 'string' ) {
88+ return resolve ( version ) ;
89+ }
90+ // Support for local tests
91+ execPythonFile ( 'python' , [ '--version' ] , __dirname , true ) . then ( resolve ) ;
92+ } ) . then ( version => {
93+ isPython3 = version . indexOf ( '3.' ) >= 0 ;
94+ done ( ) ;
95+ } ) ;
96+ } ) ;
97+ } ) ;
98+
99+ suiteTeardown ( done => {
100+ // deleteFile(targetPythonFileToLint).then(done, done);
101+ done ( ) ;
102+ } ) ;
103+
104+ suite ( 'Method Extraction' , ( ) => {
105+ setup ( ( ) => {
106+ if ( fs . existsSync ( refactorTargetFile ) ) {
107+ fs . unlinkSync ( refactorTargetFile ) ;
108+ }
109+ fs . copySync ( refactorSourceFile , refactorTargetFile , { clobber : true } ) ;
110+ } ) ;
111+ teardown ( done => {
112+ closeActiveWindows ( ) . then ( ( ) => {
113+ setTimeout ( function ( ) {
114+ RefactorProxy . pythonPath = null ;
115+ done ( ) ;
116+ } , 1000 ) ;
117+ } ) ;
118+ } ) ;
119+
120+ function testingMethodExtraction ( shouldError : boolean , pythonSettings : settings . IPythonSettings , startPos : Position , endPos : Position ) {
121+ let ch = new MockOutputChannel ( 'Python' ) ;
122+ let textDocument : vscode . TextDocument ;
123+ let textEditor : vscode . TextEditor ;
124+ let rangeOfTextToExtract = new vscode . Range ( startPos , endPos ) ;
125+ let proxy = new RefactorProxy ( EXTENSION_DIR , pythonSettings , path . dirname ( refactorTargetFile ) ) ;
126+ let mockTextDoc = new MockTextDocument ( refactorTargetFile , [
127+ { position : startPos , offset : 8346 } ,
128+ { position : endPos , offset : 8519 }
129+ ] ) ;
130+
131+ 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` ;
132+
133+ return proxy . extractMethod < RenameResponse > ( mockTextDoc , 'myNewMethod' , refactorTargetFile , rangeOfTextToExtract )
134+ . then ( response => {
135+ assert . equal ( response . results . length , 1 , 'Invalid number of items in response' ) ;
136+ assert . equal ( response . results [ 0 ] . diff , DIFF , 'Invalid DIFF' ) ;
137+ } ) . catch ( error => {
138+ if ( shouldError ) {
139+ // Wait a minute this shouldn't work, what's going on
140+ assert . equal ( true , true , 'Error raised as expected' ) ;
141+ return ;
142+ }
143+
144+ return Promise . reject ( error ) ;
145+ } ) ;
146+ }
147+
148+ test ( 'Extract Method' , done => {
149+ let startPos = new vscode . Position ( 239 , 0 ) ;
150+ let endPos = new vscode . Position ( 241 , 35 ) ;
151+ testingMethodExtraction ( false , pythonSettings , startPos , endPos ) . then ( ( ) => done ( ) , done ) ;
152+ } ) ;
153+
154+ test ( 'Extract Method will fail if complete statements are not selected' , done => {
155+ let startPos = new vscode . Position ( 239 , 30 ) ;
156+ let endPos = new vscode . Position ( 241 , 35 ) ;
157+ testingMethodExtraction ( true , pythonSettings , startPos , endPos ) . then ( ( ) => done ( ) , done ) ;
158+ } ) ;
159+
160+ test ( 'Extract Method will try to find Python 2.x' , done => {
161+ let startPos = new vscode . Position ( 239 , 0 ) ;
162+ let endPos = new vscode . Position ( 241 , 35 ) ;
163+ let clonedSettings = JSON . parse ( JSON . stringify ( pythonSettings ) ) ;
164+ clonedSettings . python2Path = 'python3' ;
165+ testingMethodExtraction ( false , clonedSettings , startPos , endPos ) . then ( ( ) => done ( ) , done ) ;
166+ } ) ;
167+
168+ test ( 'Extract Method will not work in Python 3.x' , done => {
169+ let startPos = new vscode . Position ( 239 , 0 ) ;
170+ let endPos = new vscode . Position ( 241 , 35 ) ;
171+ let clonedSettings = JSON . parse ( JSON . stringify ( pythonSettings ) ) ;
172+ clonedSettings . pythonPath = 'python3' ;
173+ clonedSettings . python2Path = 'python3' ;
174+ testingMethodExtraction ( true , clonedSettings , startPos , endPos ) . then ( ( ) => done ( ) , done ) ;
175+ } ) ;
176+
177+ if ( ! isTRAVIS ) {
178+ function testingMethodExtractionEndToEnd ( shouldError : boolean , pythonSettings : settings . IPythonSettings , startPos : Position , endPos : Position ) {
179+ let ch = new MockOutputChannel ( 'Python' ) ;
180+ let textDocument : vscode . TextDocument ;
181+ let textEditor : vscode . TextEditor ;
182+ let rangeOfTextToExtract = new vscode . Range ( startPos , endPos ) ;
183+
184+ return vscode . workspace . openTextDocument ( refactorTargetFile ) . then ( document => {
185+ textDocument = document ;
186+ return vscode . window . showTextDocument ( textDocument ) ;
187+ } ) . then ( editor => {
188+ editor . selections = [ new vscode . Selection ( rangeOfTextToExtract . start , rangeOfTextToExtract . end ) ] ;
189+ editor . selection = new vscode . Selection ( rangeOfTextToExtract . start , rangeOfTextToExtract . end ) ;
190+ textEditor = editor ;
191+ return ;
192+ } ) . then ( ( ) => {
193+ return extractMethod ( EXTENSION_DIR , textEditor , rangeOfTextToExtract , ch , path . dirname ( refactorTargetFile ) , false , pythonSettings ) . then ( ( ) => {
194+ if ( shouldError ) {
195+ // Wait a minute this shouldn't work, what's going on
196+ throw new Error ( 'This should fail, but seems to have worked' ) ;
197+ }
198+ assert . equal ( ch . output . length , 0 , 'Output channel is not empty' ) ;
199+ assert . equal ( textDocument . lineAt ( 241 ) . text . trim ( ) . indexOf ( 'def newmethod' ) , 0 , 'New Method not created' ) ;
200+ assert . equal ( textDocument . lineAt ( 239 ) . text . trim ( ) . startsWith ( 'self.newmethod' ) , true , 'New Method not being used' ) ;
201+ } ) . catch ( error => {
202+ if ( shouldError ) {
203+ // Wait a minute this shouldn't work, what's going on
204+ assert . equal ( true , true , 'Error raised as expected' ) ;
205+ return ;
206+ }
207+
208+ return Promise . reject ( error ) ;
209+ } ) ;
210+ } , error => {
211+ if ( shouldError ) {
212+ // Wait a minute this shouldn't work, what's going on
213+ assert . equal ( true , true , 'Error raised as expected' ) ;
214+ }
215+ else {
216+ assert . fail ( error + '' , null , 'Method extraction failed\n' + ch . output ) ;
217+ return Promise . reject ( error ) ;
218+ }
219+ } ) ;
220+ }
221+
222+ test ( 'Extract Method (end to end)' , done => {
223+ let startPos = new vscode . Position ( 239 , 0 ) ;
224+ let endPos = new vscode . Position ( 241 , 35 ) ;
225+ testingMethodExtractionEndToEnd ( false , pythonSettings , startPos , endPos ) . then ( ( ) => done ( ) , done ) ;
226+ } ) ;
227+
228+ test ( 'Extract Method will fail if complete statements are not selected' , done => {
229+ let startPos = new vscode . Position ( 239 , 30 ) ;
230+ let endPos = new vscode . Position ( 241 , 35 ) ;
231+ testingMethodExtractionEndToEnd ( true , pythonSettings , startPos , endPos ) . then ( ( ) => done ( ) , done ) ;
232+ } ) ;
233+
234+ test ( 'Extract Method will try to find Python 2.x (end to end)' , done => {
235+ let startPos = new vscode . Position ( 239 , 0 ) ;
236+ let endPos = new vscode . Position ( 241 , 35 ) ;
237+ let clonedSettings = JSON . parse ( JSON . stringify ( pythonSettings ) ) ;
238+ clonedSettings . python2Path = 'python3' ;
239+ testingMethodExtractionEndToEnd ( false , clonedSettings , startPos , endPos ) . then ( ( ) => done ( ) , done ) ;
240+ } ) ;
241+
242+ test ( 'Extract Method will not work in Python 3.x (end to end)' , done => {
243+ let startPos = new vscode . Position ( 239 , 0 ) ;
244+ let endPos = new vscode . Position ( 241 , 35 ) ;
245+ let clonedSettings = JSON . parse ( JSON . stringify ( pythonSettings ) ) ;
246+ clonedSettings . pythonPath = 'python3' ;
247+ clonedSettings . python2Path = 'python3' ;
248+ testingMethodExtractionEndToEnd ( true , clonedSettings , startPos , endPos ) . then ( ( ) => done ( ) , done ) ;
249+ } ) ;
250+ }
251+ } ) ;
0 commit comments