11import * as yok from "../lib/common/yok" ;
2- import * as stubs from "./stubs" ;
32import * as constants from "../lib/constants" ;
4- import { ChildProcess } from "../lib/common/child-process" ;
53import * as ProjectServiceLib from "../lib/services/project-service" ;
6- import { ProjectNameService } from "../lib/services/project-name-service" ;
7- import * as ProjectDataServiceLib from "../lib/services/project-data-service" ;
8- import * as ProjectHelperLib from "../lib/common/project-helper" ;
9- import { StaticConfig } from "../lib/config" ;
10- import * as NpmLib from "../lib/node-package-manager" ;
11- import * as YarnLib from "../lib/yarn-package-manager" ;
12- import * as PackageManagerLib from "../lib/package-manager" ;
13- import { PackageInstallationManager } from "../lib/package-installation-manager" ;
14- import { FileSystem } from "../lib/common/file-system" ;
15- import * as path from "path" ;
16- import temp = require( "temp" ) ;
17- import helpers = require( "../lib/common/helpers" ) ;
184import { assert } from "chai" ;
19- import { Options } from "../lib/options" ;
20- import { HostInfo } from "../lib/common/host-info" ;
21- import { ProjectTemplatesService } from "../lib/services/project-templates-service" ;
225import { SettingsService } from "../lib/common/test/unit-tests/stubs" ;
23- import { DevicePlatformsConstants } from "../lib/common/mobile/device-platforms-constants" ;
24- import { PacoteService } from "../lib/services/pacote-service" ;
25-
26- const mockProjectNameValidator = {
27- validate : ( ) => true
28- } ;
29-
30- const dummyString : string = "dummyString" ;
31- let hasPromptedForString = false ;
32- const originalIsInteractive = helpers . isInteractive ;
33-
34- temp . track ( ) ;
35-
36- async function prepareTestingPath ( testInjector : IInjector , packageToInstall : string , packageName : string , options ?: INpmInstallOptions ) : Promise < string > {
37- options = options || { dependencyType : "save" } ;
38- const fs = testInjector . resolve < IFileSystem > ( "fs" ) ;
39-
40- const packageInstallationManager = testInjector . resolve < IPackageInstallationManager > ( "packageInstallationManager" ) ;
41- const defaultTemplateDir = temp . mkdirSync ( "project-service" ) ;
42- fs . writeJson ( path . join ( defaultTemplateDir , constants . PACKAGE_JSON_FILE_NAME ) , {
43- "name" : "defaultTemplate" ,
44- "version" : "1.0.0" ,
45- "description" : "dummy" ,
46- "license" : "MIT" ,
47- "readme" : "dummy" ,
48- "repository" : "dummy"
49- } ) ;
50-
51- await packageInstallationManager . install ( packageToInstall , defaultTemplateDir , options ) ;
52- const defaultTemplatePath = path . join ( defaultTemplateDir , constants . NODE_MODULES_FOLDER_NAME , packageName ) ;
53-
54- fs . deleteDirectory ( path . join ( defaultTemplatePath , constants . NODE_MODULES_FOLDER_NAME ) ) ;
55-
56- return defaultTemplatePath ;
57- }
58-
59- class ProjectIntegrationTest {
60- public testInjector : IInjector ;
61-
62- constructor ( ) {
63- this . createTestInjector ( ) ;
64- }
65-
66- public async createProject ( projectOptions : IProjectSettings ) : Promise < ICreateProjectData > {
67- const projectService : IProjectService = this . testInjector . resolve ( "projectService" ) ;
68- if ( ! projectOptions . template ) {
69- projectOptions . template = constants . RESERVED_TEMPLATE_NAMES [ "default" ] ;
70- }
71- return projectService . createProject ( projectOptions ) ;
72- }
73-
74- public async assertProject ( tempFolder : string , projectName : string , appId : string , projectSourceDirectory : string ) : Promise < void > {
75- const fs : IFileSystem = this . testInjector . resolve ( "fs" ) ;
76- const projectDir = path . join ( tempFolder , projectName ) ;
77- const appDirectoryPath = path . join ( projectDir , "app" ) ;
78- const tnsProjectFilePath = path . join ( projectDir , "package.json" ) ;
79- const tnsModulesPath = path . join ( projectDir , constants . NODE_MODULES_FOLDER_NAME , constants . TNS_CORE_MODULES_NAME ) ;
80- const packageJsonContent = fs . readJson ( tnsProjectFilePath ) ;
81-
82- assert . isTrue ( fs . exists ( appDirectoryPath ) ) ;
83- assert . isTrue ( fs . exists ( tnsProjectFilePath ) ) ;
84- assert . isTrue ( fs . exists ( tnsModulesPath ) ) ;
85-
86- assert . isFalse ( fs . isEmptyDir ( appDirectoryPath ) ) ;
87-
88- const actualAppId = packageJsonContent [ "nativescript" ] . id ;
89- const expectedAppId = appId ;
90- assert . equal ( actualAppId , expectedAppId ) ;
91-
92- const tnsCoreModulesRecord = packageJsonContent [ "dependencies" ] [ constants . TNS_CORE_MODULES_NAME ] ;
93- assert . isTrue ( tnsCoreModulesRecord !== null ) ;
94-
95- const sourceDir = projectSourceDirectory ;
96-
97- // assert dependencies and devDependencies are copied from template to real project
98- const sourcePackageJsonContent = fs . readJson ( path . join ( sourceDir , "package.json" ) ) ;
99- const missingDeps = _ . difference ( _ . keys ( sourcePackageJsonContent . dependencies ) , _ . keys ( packageJsonContent . dependencies ) ) ;
100- const missingDevDeps = _ . difference ( _ . keys ( sourcePackageJsonContent . devDependencies ) , _ . keys ( packageJsonContent . devDependencies ) ) ;
101- assert . deepEqual ( missingDeps , [ ] , `All dependencies from template must be copied to project's package.json. Missing ones are: ${ missingDeps . join ( ", " ) } .` ) ;
102- assert . deepEqual ( missingDevDeps , [ ] , `All devDependencies from template must be copied to project's package.json. Missing ones are: ${ missingDevDeps . join ( ", " ) } .` ) ;
103-
104- // assert App_Resources are prepared correctly
105- const appResourcesDir = path . join ( appDirectoryPath , "App_Resources" ) ;
106- const appResourcesContents = fs . readDirectory ( appResourcesDir ) ;
107- assert . deepEqual ( appResourcesContents , [ "Android" , "iOS" ] , "Project's app/App_Resources must contain Android and iOS directories." ) ;
108- }
109-
110- public dispose ( ) : void {
111- this . testInjector = undefined ;
112- }
113-
114- private createTestInjector ( ) : void {
115- this . testInjector = new yok . Yok ( ) ;
116- this . testInjector . register ( "childProcess" , ChildProcess ) ;
117- this . testInjector . register ( "errors" , stubs . ErrorsStub ) ;
118- this . testInjector . register ( 'logger' , stubs . LoggerStub ) ;
119- this . testInjector . register ( "projectService" , ProjectServiceLib . ProjectService ) ;
120- this . testInjector . register ( "projectNameService" , ProjectNameService ) ;
121- this . testInjector . register ( "projectHelper" , ProjectHelperLib . ProjectHelper ) ;
122- this . testInjector . register ( "projectTemplatesService" , ProjectTemplatesService ) ;
123- this . testInjector . register ( "projectNameValidator" , mockProjectNameValidator ) ;
124- this . testInjector . register ( "projectData" , stubs . ProjectDataStub ) ;
125-
126- this . testInjector . register ( "fs" , FileSystem ) ;
127- this . testInjector . register ( "projectDataService" , ProjectDataServiceLib . ProjectDataService ) ;
128- this . testInjector . register ( "staticConfig" , StaticConfig ) ;
129- this . testInjector . register ( "analyticsService" , {
130- track : async ( ) : Promise < any > => undefined ,
131- trackEventActionInGoogleAnalytics : ( data : IEventActionData ) => Promise . resolve ( )
132- } ) ;
133-
134- this . testInjector . register ( "userSettingsService" , {
135- getSettingValue : async ( settingName : string ) : Promise < void > => undefined
136- } ) ;
137- this . testInjector . register ( "npm" , NpmLib . NodePackageManager ) ;
138- this . testInjector . register ( "yarn" , YarnLib . YarnPackageManager ) ;
139- this . testInjector . register ( "packageManager" , PackageManagerLib . PackageManager ) ;
140- this . testInjector . register ( "httpClient" , { } ) ;
141-
142- this . testInjector . register ( "options" , Options ) ;
143- this . testInjector . register ( "hostInfo" , HostInfo ) ;
144- this . testInjector . register ( "prompter" , {
145- confirm : async ( message : string ) : Promise < boolean > => true ,
146- getString : async ( message : string ) : Promise < string > => {
147- hasPromptedForString = true ;
148- return dummyString ;
149- }
150- } ) ;
151- this . testInjector . register ( "packageInstallationManager" , PackageInstallationManager ) ;
152- this . testInjector . register ( "settingsService" , SettingsService ) ;
153- this . testInjector . register ( "devicePlatformsConstants" , DevicePlatformsConstants ) ;
154- this . testInjector . register ( "androidResourcesMigrationService" , {
155- hasMigrated : ( appResourcesDir : string ) : boolean => true
156- } ) ;
157- this . testInjector . register ( "hooksService" , {
158- executeAfterHooks : async ( commandName : string , hookArguments ?: IDictionary < any > ) : Promise < void > => undefined
159- } ) ;
160- this . testInjector . register ( "pacoteService" , PacoteService ) ;
161- this . testInjector . register ( "proxyService" , {
162- getCache : async ( ) : Promise < IProxySettings > => null
163- } ) ;
164- }
165- }
166-
167- describe ( "Project Service Tests" , ( ) => {
168- describe ( "project service integration tests" , ( ) => {
169- let defaultTemplatePath : string ;
170-
171- before ( async ( ) => {
172- const projectIntegrationTest = new ProjectIntegrationTest ( ) ;
173-
174- defaultTemplatePath = await prepareTestingPath ( projectIntegrationTest . testInjector , constants . RESERVED_TEMPLATE_NAMES [ "default" ] , constants . RESERVED_TEMPLATE_NAMES [ "default" ] ) ;
175- } ) ;
6+ import { LoggerStub , ErrorsStub } from "./stubs" ;
7+ import * as path from "path" ;
1768
177- describe ( "project name validation tests" , ( ) => {
178- const validProjectName = "valid" ;
179- const invalidProjectName = "1invalid" ;
180- let projectIntegrationTest : ProjectIntegrationTest ;
181- let tempFolder : string ;
182- let prompter : IPrompter ;
9+ describe ( "projectService" , ( ) => {
10+ describe ( "createProject" , ( ) => {
11+ const invalidProjectName = "1invalid" ;
12+ const dirToCreateProject : string = path . resolve ( "projectDir" ) ;
18313
184- beforeEach ( ( ) => {
185- hasPromptedForString = false ;
186- helpers . isInteractive = ( ) => true ;
187- projectIntegrationTest = new ProjectIntegrationTest ( ) ;
188- tempFolder = temp . mkdirSync ( "project" ) ;
189- prompter = projectIntegrationTest . testInjector . resolve ( "prompter" ) ;
14+ /* tslint:disable:no-empty */
15+ const getTestInjector = ( opts : { projectName : string } ) : IInjector => {
16+ const testInjector = new yok . Yok ( ) ;
17+ testInjector . register ( "packageManager" , {
18+ install : async ( ) => { }
19019 } ) ;
191-
192- afterEach ( ( ) => {
193- helpers . isInteractive = originalIsInteractive ;
20+ testInjector . register ( "errors" , ErrorsStub ) ;
21+ testInjector . register ( "fs" , {
22+ exists : ( ) => true ,
23+ isEmptyDir : ( ) => true ,
24+ createDirectory : ( ) => { } ,
25+ writeJson : ( ) => { } ,
26+ deleteDirectory : ( ) => { } ,
27+ ensureDirectoryExists : ( ) => { } ,
28+ readJson : ( ) => ( { } )
19429 } ) ;
195-
196- it ( "creates project when is interactive and incorrect name is specified and the --force option is set" , async ( ) => {
197- const projectName = invalidProjectName ;
198- await projectIntegrationTest . createProject ( { projectName : projectName , pathToProject : tempFolder , force : true } ) ;
199- await projectIntegrationTest . assertProject ( tempFolder , projectName , `org.nativescript.${ projectName } ` , defaultTemplatePath ) ;
30+ testInjector . register ( "logger" , LoggerStub ) ;
31+ testInjector . register ( "projectDataService" , {
32+ getProjectData : ( projectDir ?: string ) : IProjectData => ( < any > {
33+ getAppResourcesDirectoryPath : ( ) => "appResourcesDirectoryPath"
34+ } ) ,
35+ setNSValue : ( ) => { }
20036 } ) ;
201-
202- it ( "creates project when is interactive and incorrect name is specified and the user confirms to use the incorrect name" , async ( ) => {
203- const projectName = invalidProjectName ;
204- prompter . confirm = ( message : string ) : Promise < boolean > => Promise . resolve ( true ) ;
205-
206- await projectIntegrationTest . createProject ( { projectName : projectName , pathToProject : tempFolder } ) ;
207- await projectIntegrationTest . assertProject ( tempFolder , projectName , `org.nativescript.${ projectName } ` , defaultTemplatePath ) ;
37+ testInjector . register ( "projectData" , { } ) ;
38+ testInjector . register ( "projectNameService" , {
39+ ensureValidName : async ( ) => opts . projectName
20840 } ) ;
209-
210- it ( "prompts for new name when is interactive and incorrect name is specified and the user does not confirm to use the incorrect name" , async ( ) => {
211- const projectName = invalidProjectName ;
212-
213- prompter . confirm = ( message : string ) : Promise < boolean > => Promise . resolve ( false ) ;
214-
215- await projectIntegrationTest . createProject ( { projectName : projectName , pathToProject : tempFolder } ) ;
216- assert . isTrue ( hasPromptedForString ) ;
41+ testInjector . register ( "projectTemplatesService" , {
42+ prepareTemplate : async ( ) => ( {
43+ templateName : constants . RESERVED_TEMPLATE_NAMES [ "default" ] ,
44+ templatePath : "some path" ,
45+ templateVersion : "v2" ,
46+ templatePackageJsonContent : {
47+ dependencies : {
48+ [ "tns-core-modules" ] : "1.0.0"
49+ }
50+ } ,
51+ version : "1.0.0"
52+ } )
21753 } ) ;
218-
219- it ( "creates project when is interactive and incorrect name s specified and the user does not confirm to use the incorrect name and enters incorrect name again several times and then enters correct name" , async ( ) => {
220- const projectName = invalidProjectName ;
221-
222- prompter . confirm = ( message : string ) : Promise < boolean > => Promise . resolve ( false ) ;
223-
224- const incorrectInputsLimit = 5 ;
225- let incorrectInputsCount = 0 ;
226-
227- prompter . getString = async ( message : string ) : Promise < string > => {
228- if ( incorrectInputsCount < incorrectInputsLimit ) {
229- incorrectInputsCount ++ ;
230- } else {
231- hasPromptedForString = true ;
232-
233- return validProjectName ;
234- }
235-
236- return projectName ;
237- } ;
238-
239- await projectIntegrationTest . createProject ( { projectName : projectName , pathToProject : tempFolder } ) ;
240- assert . isTrue ( hasPromptedForString ) ;
54+ testInjector . register ( "staticConfig" , {
55+ PROJECT_FILE_NAME : "package.json"
24156 } ) ;
242-
243- it ( "does not create project when is not interactive and incorrect name is specified" , async ( ) => {
244- const projectName = invalidProjectName ;
245- helpers . isInteractive = ( ) => false ;
246-
247- await assert . isRejected ( projectIntegrationTest . createProject ( { projectName : projectName , pathToProject : tempFolder , force : false } ) ) ;
57+ testInjector . register ( "projectHelper" , {
58+ generateDefaultAppId : ( ) => `org.nativescript.${ opts . projectName } `
59+ } ) ;
60+ testInjector . register ( "packageInstallationManager" , { } ) ;
61+ testInjector . register ( "settingsService" , SettingsService ) ;
62+ testInjector . register ( "hooksService" , {
63+ executeAfterHooks : async ( commandName : string , hookArguments ?: IDictionary < any > ) : Promise < void > => undefined
64+ } ) ;
65+ testInjector . register ( "pacoteService" , {
66+ manifest : ( ) => Promise . resolve ( ) ,
67+ downloadAndExtract : ( ) => Promise . resolve ( ) ,
68+ extractPackage : ( ) => Promise . resolve ( )
24869 } ) ;
24970
250- it ( "creates project when is not interactive and incorrect name is specified and the --force option is set" , async ( ) => {
251- const projectName = invalidProjectName ;
252- helpers . isInteractive = ( ) => false ;
253-
254- await projectIntegrationTest . createProject ( { projectName : projectName , pathToProject : tempFolder , force : true } ) ;
71+ return testInjector ;
72+ } ;
73+ /* tslint:enable:no-empty */
74+
75+ it ( "creates project with invalid name when projectNameService does not fail" , async ( ) => {
76+ const projectName = invalidProjectName ;
77+ const testInjector = getTestInjector ( { projectName } ) ;
78+ const projectService = testInjector . resolve < IProjectService > ( ProjectServiceLib . ProjectService ) ;
79+ const projectCreationData = await projectService . createProject ( { projectName : projectName , pathToProject : dirToCreateProject , force : true , template : constants . RESERVED_TEMPLATE_NAMES [ "default" ] } ) ;
80+ assert . deepEqual ( projectCreationData , { projectName, projectDir : path . join ( dirToCreateProject , projectName ) } ) ;
81+ } ) ;
25582
256- await projectIntegrationTest . assertProject ( tempFolder , projectName , `org.nativescript.${ projectName } ` , defaultTemplatePath ) ;
257- } ) ;
83+ it ( "fails when invalid name is passed when projectNameService fails" , async ( ) => {
84+ const projectName = invalidProjectName ;
85+ const testInjector = getTestInjector ( { projectName } ) ;
86+ const projectNameService = testInjector . resolve < IProjectNameService > ( "projectNameService" ) ;
87+ const err = new Error ( "Invalid name" ) ;
88+ projectNameService . ensureValidName = ( name : string ) => {
89+ throw err ;
90+ } ;
91+ const projectService = testInjector . resolve < IProjectService > ( ProjectServiceLib . ProjectService ) ;
92+ await assert . isRejected ( projectService . createProject ( { projectName : projectName , pathToProject : dirToCreateProject , template : constants . RESERVED_TEMPLATE_NAMES [ "default" ] } ) , err . message ) ;
25893 } ) ;
25994
95+ it ( "fails when project directory is not empty" , async ( ) => {
96+ const projectName = invalidProjectName ;
97+ const testInjector = getTestInjector ( { projectName } ) ;
98+ const fs = testInjector . resolve < IFileSystem > ( "fs" ) ;
99+ fs . isEmptyDir = ( name : string ) => false ;
100+ const projectService = testInjector . resolve < IProjectService > ( ProjectServiceLib . ProjectService ) ;
101+ await assert . isRejected ( projectService . createProject ( { projectName : projectName , pathToProject : dirToCreateProject , template : constants . RESERVED_TEMPLATE_NAMES [ "default" ] } ) , `Path already exists and is not empty ${ path . join ( dirToCreateProject , projectName ) } ` ) ;
102+ } ) ;
260103 } ) ;
261104
262105 describe ( "isValidNativeScriptProject" , ( ) => {
@@ -291,7 +134,7 @@ describe("Project Service Tests", () => {
291134 const testInjector = getTestInjector ( {
292135 projectDir : "projectDir" ,
293136 projectId : "projectId" ,
294- projectIdentifiers : { android : "projectId" , ios : "projectId" } ,
137+ projectIdentifiers : { android : "projectId" , ios : "projectId" } ,
295138 } ) ;
296139
297140 const projectService : IProjectService = testInjector . resolve ( ProjectServiceLib . ProjectService ) ;
@@ -304,7 +147,7 @@ describe("Project Service Tests", () => {
304147 const projectData : any = {
305148 projectDir : "projectDir" ,
306149 projectId : "projectId" ,
307- projectIdentifiers : { android : "projectId" , ios : "projectId" }
150+ projectIdentifiers : { android : "projectId" , ios : "projectId" }
308151 } ;
309152
310153 let returnedProjectData : any = null ;
0 commit comments