@@ -2,6 +2,65 @@ module.exports = function(file, api) {
22 const j = api . jscodeshift ;
33 const root = j ( file . source ) ;
44
5+ const POSSIBLE_MODULES = [
6+ { expression : { callee : { name : 'module' } } } ,
7+ { expression : { callee : { name : 'moduleFor' } } } ,
8+ { expression : { callee : { name : 'moduleForComponent' } } } ,
9+ { expression : { callee : { name : 'moduleForModel' } } } ,
10+ ] ;
11+
12+ class ModuleInfo {
13+ static isModuleDefinition ( nodePath ) {
14+ return POSSIBLE_MODULES . some ( matcher => j . match ( nodePath , matcher ) ) ;
15+ }
16+
17+ constructor ( p ) {
18+ let calleeName = p . node . expression . callee . name ;
19+ // Find the moduleName and the module's options
20+ let moduleName , subject , options , hasCustomSubject , isNestedModule ;
21+ let calleeArguments = p . node . expression . arguments . slice ( ) ;
22+ let lastArgument = calleeArguments [ calleeArguments . length - 1 ] ;
23+ if ( lastArgument . type === 'ObjectExpression' ) {
24+ options = calleeArguments . pop ( ) ;
25+ }
26+ if ( calleeArguments [ 1 ] && j . match ( calleeArguments [ 1 ] , { type : 'FunctionExpression' } ) ) {
27+ isNestedModule = true ;
28+ moduleName = calleeArguments [ 0 ] ;
29+ } else {
30+ moduleName = calleeArguments [ 1 ] || calleeArguments [ 0 ] ;
31+ subject = calleeArguments [ 0 ] ;
32+ }
33+
34+ let setupIdentifier = calleeName === 'module' ? null : 'setupTest' ;
35+ if ( options ) {
36+ let hasIntegration = options . properties . some ( p => p . key . name === 'integration' ) ;
37+
38+ if ( calleeName === `moduleForComponent` ) {
39+ if ( hasIntegration ) {
40+ setupIdentifier = 'setupRenderingTest' ;
41+ subject = null ;
42+ } else {
43+ subject = j . literal ( `component:${ calleeArguments [ 0 ] . value } ` ) ;
44+ }
45+ } else if ( calleeName === 'moduleForModel' ) {
46+ subject = j . literal ( `model:${ calleeArguments [ 0 ] . value } ` ) ;
47+ }
48+
49+ hasCustomSubject = options . properties . some ( p => p . key . name === 'subject' ) ;
50+ }
51+
52+ this . moduleName = moduleName ;
53+ this . moduleOptions = options ;
54+ this . setupType = setupIdentifier ;
55+ this . subjectContainerKey = subject ;
56+ this . hasCustomSubjectImplementation = hasCustomSubject ;
57+ this . isNestedModule = isNestedModule ;
58+
59+ this . moduleInvocation = null ;
60+ this . moduleCallbackBody = null ;
61+ }
62+ }
63+
564 function ensureImportWithSpecifiers ( { source, specifiers, anchor, positionMethod } ) {
665 let importStatement = ensureImport ( source , anchor , positionMethod ) ;
766 let combinedSpecifiers = new Set ( specifiers ) ;
@@ -106,8 +165,8 @@ module.exports = function(file, api) {
106165 } ,
107166 } )
108167 . forEach ( p => {
109- let [ , , setupType ] = parseModule ( p ) ;
110- emberQUnitSpecifiers . add ( setupType ) ;
168+ let moduleInfo = new ModuleInfo ( p ) ;
169+ emberQUnitSpecifiers . add ( moduleInfo . setupType ) ;
111170 } ) ;
112171 } else {
113172 emberQUnitSpecifiers . add ( mappedName ) ;
@@ -141,56 +200,7 @@ module.exports = function(file, api) {
141200 } ) ;
142201 }
143202
144- function parseModule ( p ) {
145- let calleeName = p . node . expression . callee . name ;
146- // Find the moduleName and the module's options
147- let moduleName , subject , options , hasCustomSubject , isNestedModule ;
148- let calleeArguments = p . node . expression . arguments . slice ( ) ;
149- let lastArgument = calleeArguments [ calleeArguments . length - 1 ] ;
150- if ( lastArgument . type === 'ObjectExpression' ) {
151- options = calleeArguments . pop ( ) ;
152- }
153- if ( calleeArguments [ 1 ] && j . match ( calleeArguments [ 1 ] , { type : 'FunctionExpression' } ) ) {
154- isNestedModule = true ;
155- moduleName = calleeArguments [ 0 ] ;
156- } else {
157- moduleName = calleeArguments [ 1 ] || calleeArguments [ 0 ] ;
158- subject = calleeArguments [ 0 ] ;
159- }
160-
161- let setupIdentifier = calleeName === 'module' ? null : 'setupTest' ;
162- if ( options ) {
163- let hasIntegration = options . properties . some ( p => p . key . name === 'integration' ) ;
164-
165- if ( calleeName === `moduleForComponent` ) {
166- if ( hasIntegration ) {
167- setupIdentifier = 'setupRenderingTest' ;
168- subject = null ;
169- } else {
170- subject = j . literal ( `component:${ calleeArguments [ 0 ] . value } ` ) ;
171- }
172- } else if ( calleeName === 'moduleForModel' ) {
173- subject = j . literal ( `model:${ calleeArguments [ 0 ] . value } ` ) ;
174- }
175-
176- hasCustomSubject = options . properties . some ( p => p . key . name === 'subject' ) ;
177- }
178-
179- return [ moduleName , options , setupIdentifier , subject , hasCustomSubject , isNestedModule ] ;
180- }
181-
182203 function updateModuleForToNestedModule ( ) {
183- const POSSIBLE_MODULES = [
184- { expression : { callee : { name : 'module' } } } ,
185- { expression : { callee : { name : 'moduleFor' } } } ,
186- { expression : { callee : { name : 'moduleForComponent' } } } ,
187- { expression : { callee : { name : 'moduleForModel' } } } ,
188- ] ;
189-
190- function isModuleDefinition ( nodePath ) {
191- return POSSIBLE_MODULES . some ( matcher => j . match ( nodePath , matcher ) ) ;
192- }
193-
194204 const LIFE_CYCLE_METHODS = [
195205 { key : { name : 'before' } , value : { type : 'FunctionExpression' } } ,
196206 { key : { name : 'beforeEach' } , value : { type : 'FunctionExpression' } } ,
@@ -203,20 +213,18 @@ module.exports = function(file, api) {
203213 }
204214
205215 function createModule ( p ) {
206- let [ moduleName , options , setupType , subject , hasCustomSubject , isNestedModule ] = parseModule (
207- p
208- ) ;
216+ let moduleInfo = new ModuleInfo ( p ) ;
209217
210- if ( isNestedModule ) {
218+ if ( moduleInfo . isNestedModule ) {
211219 return null ;
212220 }
213221
214222 let needsHooks = false ;
215223 let moduleSetupExpression ;
216- if ( setupType ) {
224+ if ( moduleInfo . setupType ) {
217225 needsHooks = true ;
218226 moduleSetupExpression = j . expressionStatement (
219- j . callExpression ( j . identifier ( setupType ) , [ j . identifier ( 'hooks' ) ] )
227+ j . callExpression ( j . identifier ( moduleInfo . setupType ) , [ j . identifier ( 'hooks' ) ] )
220228 ) ;
221229 }
222230
@@ -254,13 +262,13 @@ module.exports = function(file, api) {
254262 return node ;
255263 }
256264
257- let moduleInvocation = j . expressionStatement ( buildModule ( moduleName , callback ) ) ;
265+ let moduleInvocation = j . expressionStatement ( buildModule ( moduleInfo . moduleName , callback ) ) ;
258266
259- if ( options ) {
267+ if ( moduleInfo . moduleOptions ) {
260268 let customMethodBeforeEachBody , customMethodBeforeEachExpression ;
261269
262- options . properties . forEach ( property => {
263- if ( setupType ) {
270+ moduleInfo . moduleOptions . properties . forEach ( property => {
271+ if ( moduleInfo . setupType ) {
264272 let expressionCollection = j ( property . value ) ;
265273
266274 updateGetOwnerThisUsage ( expressionCollection ) ;
@@ -323,10 +331,10 @@ module.exports = function(file, api) {
323331 }
324332 } ) ;
325333
326- if ( setupType === 'setupRenderingTest' ) {
334+ if ( moduleInfo . setupType === 'setupRenderingTest' ) {
327335 processExpressionForRenderingTest ( callback ) ;
328336 } else {
329- processSubject ( callback , subject ) ;
337+ processSubject ( callback , moduleInfo . subjectContainerKey ) ;
330338 }
331339 }
332340
@@ -335,7 +343,11 @@ module.exports = function(file, api) {
335343 callback . params = [ ] ;
336344 }
337345
338- return [ moduleInvocation , callback . body . body , setupType , subject , hasCustomSubject ] ;
346+ // [moduleInvocation, callback.body.body, setupType, subject, hasCustomSubject];
347+ moduleInfo . moduleInvocation = moduleInvocation ;
348+ moduleInfo . moduleCallbackBody = callback . body . body ;
349+
350+ return moduleInfo ;
339351 }
340352
341353 function processExpressionForRenderingTest ( testExpression ) {
@@ -464,23 +476,20 @@ module.exports = function(file, api) {
464476 let programPath = root . get ( 'program' ) ;
465477 let bodyPath = programPath . get ( 'body' ) ;
466478
467- let currentModuleCallbackBody , currentTestType , currentSubject , currentHasCustomSubject ;
479+ let currentModuleInfo ;
468480 bodyPath . each ( expressionPath => {
469481 let expression = expressionPath . node ;
470- if ( isModuleDefinition ( expressionPath ) ) {
471- let result = createModule ( expressionPath ) ;
472- if ( result === null ) {
482+ if ( ModuleInfo . isModuleDefinition ( expressionPath ) ) {
483+ let moduleInfo = createModule ( expressionPath ) ;
484+ if ( moduleInfo === null ) {
473485 return ;
474486 }
475- expressionPath . replace ( result [ 0 ] ) ;
476- currentModuleCallbackBody = result [ 1 ] ;
477- currentTestType = result [ 2 ] ;
478- currentSubject = result [ 3 ] ;
479- currentHasCustomSubject = result [ 4 ] ;
480- } else if ( currentModuleCallbackBody ) {
487+ currentModuleInfo = moduleInfo ;
488+ expressionPath . replace ( moduleInfo . moduleInvocation ) ;
489+ } else if ( currentModuleInfo ) {
481490 // calling `path.replace()` essentially just removes
482491 expressionPath . replace ( ) ;
483- currentModuleCallbackBody . push ( expression ) ;
492+ currentModuleInfo . moduleCallbackBody . push ( expression ) ;
484493
485494 let isTest = j . match ( expression , { expression : { callee : { name : 'test' } } } ) ;
486495 if ( isTest ) {
@@ -492,10 +501,13 @@ module.exports = function(file, api) {
492501 // transformed if the call site's scope is the same as the expression passed
493502 updateGetOwnerThisUsage ( j ( expression . expression . arguments [ 1 ] ) ) ;
494503
495- if ( currentTestType === 'setupRenderingTest' ) {
504+ if ( currentModuleInfo . setupType === 'setupRenderingTest' ) {
496505 processExpressionForRenderingTest ( expression ) ;
497- } else if ( currentTestType === 'setupTest' && ! currentHasCustomSubject ) {
498- processSubject ( expression , currentSubject ) ;
506+ } else if (
507+ currentModuleInfo . setupType === 'setupTest' &&
508+ ! currentModuleInfo . hasCustomSubjectImplementation
509+ ) {
510+ processSubject ( expression , currentModuleInfo . subjectContainerKey ) ;
499511 }
500512 }
501513 }
0 commit comments