diff --git a/bin/lib/create.js b/bin/lib/create.js index f7be5941..f96b2781 100755 --- a/bin/lib/create.js +++ b/bin/lib/create.js @@ -32,6 +32,18 @@ var CordovaError = require('cordova-common').CordovaError; var AndroidStudio = require('../templates/cordova/lib/AndroidStudio'); var AndroidManifest = require('../templates/cordova/lib/AndroidManifest'); +// Export all helper functions, and make sure internally within this module, we +// reference these methods via the `exports` object - this helps with testing +// (since we can then mock and control behaviour of all of these functions) +exports.validatePackageName = validatePackageName; +exports.validateProjectName = validateProjectName; +exports.setShellFatal = setShellFatal; +exports.copyJsAndLibrary = copyJsAndLibrary; +exports.copyScripts = copyScripts; +exports.copyBuildRules = copyBuildRules; +exports.writeProjectProperties = writeProjectProperties; +exports.prepBuildFiles = prepBuildFiles; + function setShellFatal (value, func) { var oldVal = shell.config.fatal; shell.config.fatal = value; @@ -64,7 +76,7 @@ function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) { shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www')); // Don't fail if there are no old jars. - setShellFatal(false, function () { + exports.setShellFatal(false, function () { shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function (oldJar) { console.log('Deleting ' + oldJar); shell.rm('-f', oldJar); @@ -183,6 +195,7 @@ function validatePackageName (package_name) { // http://developer.android.com/guide/topics/manifest/manifest-element.html#package // Enforce underscore limitation var msg = 'Error validating package name. '; + if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) { return Q.reject(new CordovaError(msg + 'Package name must look like: com.company.Name')); } @@ -257,9 +270,9 @@ exports.create = function (project_path, config, options, events) { var target_api = check_reqs.get_target(); // Make the package conform to Java package types - return validatePackageName(package_name) + return exports.validatePackageName(package_name) .then(function () { - validateProjectName(project_name); + exports.validateProjectName(project_name); }).then(function () { // Log the given values for the project events.emit('log', 'Creating Cordova project for the Android platform:'); @@ -271,7 +284,7 @@ exports.create = function (project_path, config, options, events) { events.emit('verbose', 'Copying android template project to ' + project_path); - setShellFatal(true, function () { + exports.setShellFatal(true, function () { var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project'); var app_path = path.join(project_path, 'app', 'src', 'main'); @@ -285,7 +298,7 @@ exports.create = function (project_path, config, options, events) { shell.mkdir(path.join(app_path, 'libs')); // copy cordova.js, cordova.jar - copyJsAndLibrary(project_path, options.link, safe_activity_name); + exports.copyJsAndLibrary(project_path, options.link, safe_activity_name); // Set up ther Android Studio paths var java_path = path.join(app_path, 'java'); @@ -314,12 +327,12 @@ exports.create = function (project_path, config, options, events) { var manifest_path = path.join(app_path, 'AndroidManifest.xml'); manifest.write(manifest_path); - copyScripts(project_path); - copyBuildRules(project_path); + exports.copyScripts(project_path); + exports.copyBuildRules(project_path); }); // Link it to local android install. - writeProjectProperties(project_path, target_api); - prepBuildFiles(project_path, 'studio'); + exports.writeProjectProperties(project_path, target_api); + exports.prepBuildFiles(project_path, 'studio'); events.emit('log', generateDoneMessage('create', options.link)); }).thenResolve(project_path); }; @@ -363,15 +376,11 @@ exports.update = function (projectPath, options, events) { var projectName = manifest.getActivity().getName(); var target_api = check_reqs.get_target(); - copyJsAndLibrary(projectPath, options.link, projectName, isLegacy); - copyScripts(projectPath); - copyBuildRules(projectPath, isLegacy); - writeProjectProperties(projectPath, target_api); - prepBuildFiles(projectPath, builder); + exports.copyJsAndLibrary(projectPath, options.link, projectName, isLegacy); + exports.copyScripts(projectPath); + exports.copyBuildRules(projectPath, isLegacy); + exports.writeProjectProperties(projectPath, target_api); + exports.prepBuildFiles(projectPath, builder); events.emit('log', generateDoneMessage('update', options.link)); }).thenResolve(projectPath); }; - -// For testing -exports.validatePackageName = validatePackageName; -exports.validateProjectName = validateProjectName; diff --git a/package.json b/package.json index 574de80c..5d71ecb6 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "scripts": { "test": "npm run eslint && npm run unit-tests && npm run e2e-tests", "unit-tests": "jasmine --config=spec/unit/jasmine.json", - "cover": "istanbul cover --root bin/templates/cordova --print detail jasmine -- --config=spec/unit/jasmine.json", + "cover": "istanbul cover --root bin --print detail jasmine -- --config=spec/unit/jasmine.json", "e2e-tests": "jasmine --config=spec/e2e/jasmine.json", "eslint": "eslint bin && eslint spec" }, diff --git a/spec/e2e/create.spec.js b/spec/e2e/create.spec.js deleted file mode 100644 index 75c448be..00000000 --- a/spec/e2e/create.spec.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - */ - -var actions = require('./helpers/projectActions.js'); - -var CREATE_TIMEOUT = 180000; - -function createAndBuild (projectname, projectid, done) { - actions.createProject(projectname, projectid, function (error) { - expect(error).toBe(null); - actions.buildProject(projectid, function (error) { - expect(error).toBe(null); - actions.removeProject(projectid); - done(); - }); - }); -} - -describe('create', function () { - - it('Test#001 : create project with ascii name, no spaces', function (done) { - var projectname = 'testcreate'; - var projectid = 'com.test.create.app1'; - - createAndBuild(projectname, projectid, done); - }, CREATE_TIMEOUT); - - it('Test#002 : create project with ascii name, and spaces', function (done) { - var projectname = 'test create'; - var projectid = 'com.test.create.app2'; - - createAndBuild(projectname, projectid, done); - }, CREATE_TIMEOUT); - - it('Test#003 : create project with unicode name, no spaces', function (done) { - var projectname = '応応応応用用用用'; - var projectid = 'com.test.create.app3'; - - createAndBuild(projectname, projectid, done); - }, CREATE_TIMEOUT); - - it('Test#004 : create project with unicode name, and spaces', function (done) { - var projectname = '応応応応 用用用用'; - var projectid = 'com.test.create.app4'; - - createAndBuild(projectname, projectid, done); - }, CREATE_TIMEOUT); - - it('Test#005 : create project with ascii+unicode name, no spaces', function (done) { - var projectname = '応応応応hello用用用用'; - var projectid = 'com.test.create.app5'; - - createAndBuild(projectname, projectid, done); - }, CREATE_TIMEOUT); - - it('Test#006 : create project with ascii+unicode name, and spaces', function (done) { - var projectname = '応応応応 hello 用用用用'; - var projectid = 'com.test.create.app6'; - - createAndBuild(projectname, projectid, done); - }, CREATE_TIMEOUT); - -}); diff --git a/spec/e2e/update.spec.js b/spec/e2e/update.spec.js deleted file mode 100644 index 7a6e4e9f..00000000 --- a/spec/e2e/update.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - */ - -var actions = require('./helpers/projectActions.js'); -var shell = require('shelljs'); -var fs = require('fs'); -var util = require('util'); -var platformOld = { version: '4.0.0', path: 'cordova-android-old' }; -var platformEdge = { version: getCurrentVersion(), path: '.' }; - -var DOWNLOAD_TIMEOUT = 2 * 60 * 1000; -var UPDATE_TIMEOUT = 90 * 1000; -var PLATFORM_GIT_URL = 'https://github.com/apache/cordova-android'; - -function getCurrentVersion () { - return fs.readFileSync('VERSION').toString().trim(); -} - -function testUpdate (projectname, projectid, createfrom, updatefrom, doBuild, done) { - actions.createProject(projectname, projectid, createfrom.path, function (error) { - expect(error).toBe(null); - actions.updateProject(projectid, updatefrom.path, function (error) { - expect(error).toBe(null); - actions.getPlatformVersion(projectid, function (v) { - expect(v).toEqual(updatefrom.version); - if (doBuild) { - actions.buildProject(projectid, function (error) { - expect(error).toBe(null); - actions.removeProject(projectid); - done(); - }); - } else { - actions.removeProject(projectid); - done(); - } - }); - }); - }); -} - -describe('preparing fixtures', function () { - - it('Test#001 : cloning old platform', function (done) { - var command = util.format('git clone %s --depth=1 --branch %s %s', - PLATFORM_GIT_URL, platformOld.version, platformOld.path); - shell.rm('-rf', platformOld.path); - shell.exec(command, { silent: true }, function (err) { - expect(err).toBe(0); - done(); - }); - }, DOWNLOAD_TIMEOUT); - -}); - -describe('update', function () { - - it('Test#002 : should update major version and build the project', function (done) { - var projectname = 'testupdate'; - var projectid = 'com.test.update.app1'; - - testUpdate(projectname, projectid, platformOld, platformEdge, true, done); - - }, UPDATE_TIMEOUT); - -}); - -describe('cleanup', function () { - - it('Test#004 : remove cloned old platform', function () { - shell.rm('-rf', platformOld.path); - }); - -}); diff --git a/spec/unit/create.spec.js b/spec/unit/create.spec.js index 0f38f4f0..8fdbdb81 100644 --- a/spec/unit/create.spec.js +++ b/spec/unit/create.spec.js @@ -17,89 +17,264 @@ under the License. */ -var create = require('../../bin/lib/create'); +var rewire = require('rewire'); +var create = rewire('../../bin/lib/create'); +var check_reqs = require('../../bin/templates/cordova/lib/check_reqs'); +var fs = require('fs'); +var path = require('path'); +var Q = require('q'); +var shell = require('shelljs'); describe('create', function () { - describe('validatePackageName', function () { - var valid = [ - 'org.apache.mobilespec', - 'com.example', - 'com.floors42.package', - 'ball8.ball8.ball8ball' - ]; - var invalid = [ - '', - 'com.class.is.bad', - '0com.example.mobilespec', - 'c-m.e@a!p%e.mobilespec', - 'notenoughdots', - '.starts.with.a.dot', - 'ends.with.a.dot.', - '_underscore.anything', - 'underscore._something', - '_underscore._all._the._things', - '8.ball', - '8ball.ball', - 'ball8.8ball', - 'ball8.com.8ball' - ]; - - valid.forEach(function (package_name) { - it('Test#001 : should accept ' + package_name, function (done) { - return create.validatePackageName(package_name).then(function () { - // resolved - done(); - }).fail(function (err) { - expect(err).toBeUndefined(); + describe('validatePackageName helper method', function () { + describe('happy path (valid package names)', function () { + var valid = [ + 'org.apache.mobilespec', + 'com.example', + 'com.floors42.package', + 'ball8.ball8.ball8ball' + ]; + valid.forEach(function (package_name) { + it('Test#001 : should accept ' + package_name, function (done) { + create.validatePackageName(package_name).fail(fail).done(done); }); }); }); - invalid.forEach(function (package_name) { - it('Test#002 : should reject ' + package_name, function (done) { - return create.validatePackageName(package_name).then(function () { - // shouldn't be here - expect(true).toBe(false); - }).fail(function (err) { + describe('failure cases (invalid package names)', function () { + it('should reject empty package names', function (done) { + create.validatePackageName('').then(fail).fail(function (err) { expect(err).toBeDefined(); - done(); - }); + expect(err.message).toContain('Error validating package name'); + }).done(done); + }); + it('should reject package names containing "class"', function (done) { + create.validatePackageName('com.class.is.bad').then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Error validating package name'); + }).done(done); + }); + it('should reject package names that do not start with a latin letter', function (done) { + create.validatePackageName('_un.der.score').then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Error validating package name'); + }).done(done); + }); + it('should reject package names with terms that do not start with a latin letter', function (done) { + create.validatePackageName('un._der.score').then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Error validating package name'); + }).done(done); + }); + it('should reject package names containing non-alphanumeric or underscore characters', function (done) { + create.validatePackageName('th!$.!$.b@d').then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Error validating package name'); + }).done(done); + }); + it('should reject package names that do not contain enough dots', function (done) { + create.validatePackageName('therearenodotshere').then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Error validating package name'); + }).done(done); + }); + it('should reject package names that end with a dot', function (done) { + create.validatePackageName('this.is.a.complete.sentence.').then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Error validating package name'); + }).done(done); }); }); }); - describe('validateProjectName', function () { - var valid = [ - 'mobilespec', - 'package_name', - 'PackageName', - 'CordovaLib' - ]; - var invalid = [ - '', - '0startswithdigit', - 'CordovaActivity' - ]; - - valid.forEach(function (project_name) { - it('Test#003 : should accept ' + project_name, function (done) { - return create.validateProjectName(project_name).then(function () { - // resolved - done(); - }).fail(function (err) { - expect(err).toBeUndefined(); + describe('validateProjectName helper method', function () { + describe('happy path (valid project names)', function () { + var valid = [ + 'mobilespec', + 'package_name', + 'PackageName', + 'CordovaLib' + ]; + valid.forEach(function (project_name) { + it('Test#003 : should accept ' + project_name, function (done) { + create.validateProjectName(project_name).fail(fail).done(done); }); }); }); - - invalid.forEach(function (project_name) { - it('Test#004 : should reject ' + project_name, function (done) { - return create.validateProjectName(project_name).then(function () { - // shouldn't be here - expect(true).toBe(false); - }).fail(function (err) { + describe('failure cases (invalid project names)', function () { + it('should reject empty project names', function (done) { + create.validateProjectName('').then(fail).fail(function (err) { expect(err).toBeDefined(); - done(); - }); + expect(err.message).toContain('Project name cannot be empty'); + }).done(done); + }); + it('should reject "CordovaActivity" as a project name', function (done) { + create.validateProjectName('CordovaActivity').then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Project name cannot be CordovaActivity'); + }).done(done); + }); + it('should reject project names that begin with a number', function (done) { + create.validateProjectName('1337').then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Project name must not begin with a number'); + }).done(done); + }); + }); + }); + describe('main method', function () { + var config_mock; + var events_mock; + var Manifest_mock = function () {}; + var revert_manifest_mock; + var project_path = path.join('some', 'path'); + var default_templates = path.join(__dirname, '..', '..', 'bin', 'templates', 'project'); + var fake_android_target = 'android-1337'; + beforeEach(function () { + Manifest_mock.prototype = jasmine.createSpyObj('AndroidManifest instance mock', ['setPackageId', 'setTargetSdkVersion', 'getActivity', 'setName', 'write']); + Manifest_mock.prototype.setPackageId.and.returnValue(new Manifest_mock()); + Manifest_mock.prototype.setTargetSdkVersion.and.returnValue(new Manifest_mock()); + Manifest_mock.prototype.getActivity.and.returnValue(new Manifest_mock()); + Manifest_mock.prototype.setName.and.returnValue(new Manifest_mock()); + spyOn(create, 'validatePackageName').and.returnValue(Q()); + spyOn(create, 'validateProjectName').and.returnValue(Q()); + spyOn(create, 'setShellFatal').and.callFake(function (noop, cb) { cb(); }); + spyOn(create, 'copyJsAndLibrary'); + spyOn(create, 'copyScripts'); + spyOn(create, 'copyBuildRules'); + spyOn(create, 'writeProjectProperties'); + spyOn(create, 'prepBuildFiles'); + revert_manifest_mock = create.__set__('AndroidManifest', Manifest_mock); + spyOn(fs, 'existsSync').and.returnValue(false); + spyOn(shell, 'cp'); + spyOn(shell, 'mkdir'); + spyOn(shell, 'sed'); + config_mock = jasmine.createSpyObj('ConfigParser mock instance', ['packageName', 'name', 'android_activityName']); + events_mock = jasmine.createSpyObj('EventEmitter mock instance', ['emit']); + spyOn(check_reqs, 'get_target').and.returnValue(fake_android_target); + }); + afterEach(function () { + revert_manifest_mock(); + }); + describe('parameter values and defaults', function () { + it('should have a default package name of my.cordova.project', function (done) { + config_mock.packageName.and.returnValue(undefined); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.validatePackageName).toHaveBeenCalledWith('my.cordova.project'); + }).fail(fail).done(done); + }); + it('should use the ConfigParser-provided package name, if exists', function (done) { + config_mock.packageName.and.returnValue('org.apache.cordova'); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.validatePackageName).toHaveBeenCalledWith('org.apache.cordova'); + }).fail(fail).done(done); + }); + it('should have a default project name of CordovaExample', function (done) { + config_mock.name.and.returnValue(undefined); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.validateProjectName).toHaveBeenCalledWith('CordovaExample'); + }).fail(fail).done(done); + }); + it('should use the ConfigParser-provided project name, if exists', function (done) { + config_mock.name.and.returnValue('MySweetAppName'); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.validateProjectName).toHaveBeenCalledWith('MySweetAppName'); + }).fail(fail).done(done); + }); + it('should replace any non-word characters (including unicode and spaces) in the ConfigParser-provided project name with underscores', function (done) { + config_mock.name.and.returnValue('応応応応 hello 用用用用'); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.validateProjectName).toHaveBeenCalledWith('_____hello_____'); + }).fail(fail).done(done); + }); + it('should have a default activity name of MainActivity', function (done) { + config_mock.android_activityName.and.returnValue(undefined); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(Manifest_mock.prototype.setName).toHaveBeenCalledWith('MainActivity'); + }).fail(fail).done(done); + }); + it('should use the activityName provided via options parameter, if exists', function (done) { + config_mock.android_activityName.and.returnValue(undefined); + create.create(project_path, config_mock, {activityName: 'AwesomeActivity'}, events_mock).then(function () { + expect(Manifest_mock.prototype.setName).toHaveBeenCalledWith('AwesomeActivity'); + }).fail(fail).done(done); + }); + it('should use the ConfigParser-provided activity name, if exists', function (done) { + config_mock.android_activityName.and.returnValue('AmazingActivity'); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(Manifest_mock.prototype.setName).toHaveBeenCalledWith('AmazingActivity'); + }).fail(fail).done(done); + }); + }); + describe('failure', function () { + it('should fail if the target path already exists', function (done) { + fs.existsSync.and.returnValue(true); + create.create(project_path, config_mock, {}, events_mock).then(fail).fail(function (err) { + expect(err).toBeDefined(); + expect(err.message).toContain('Project already exists!'); + }).done(done); + }); + }); + describe('happy path', function () { + it('should copy project templates from a specified custom template', function (done) { + create.create(project_path, config_mock, {customTemplate: '/template/path'}, events_mock).then(function () { + expect(shell.cp).toHaveBeenCalledWith('-r', path.join('/template/path', 'assets'), project_path); + expect(shell.cp).toHaveBeenCalledWith('-r', path.join('/template/path', 'res'), project_path); + expect(shell.cp).toHaveBeenCalledWith(path.join('/template/path', 'gitignore'), path.join(project_path, '.gitignore')); + }).fail(fail).done(done); + }); + it('should copy project templates from the default templates location if no custom template is provided', function (done) { + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(shell.cp).toHaveBeenCalledWith('-r', path.join(default_templates, 'assets'), project_path); + expect(shell.cp).toHaveBeenCalledWith('-r', path.join(default_templates, 'res'), project_path); + expect(shell.cp).toHaveBeenCalledWith(path.join(default_templates, 'gitignore'), path.join(project_path, '.gitignore')); + }).fail(fail).done(done); + }); + it('should copy JS and library assets', function (done) { + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.copyJsAndLibrary).toHaveBeenCalled(); + }).fail(fail).done(done); + }); + it('should create a java src directory based on the provided project package name', function (done) { + config_mock.packageName.and.returnValue('org.apache.cordova'); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(shell.mkdir).toHaveBeenCalledWith('-p', path.join(project_path, 'src', 'org', 'apache', 'cordova')); + }).fail(fail).done(done); + }); + it('should copy, rename and interpolate the template Activity java class with the project-specific activity name and package name', function (done) { + config_mock.packageName.and.returnValue('org.apache.cordova'); + config_mock.android_activityName.and.returnValue('CEEDEEVEE'); + var activity_path = path.join(project_path, 'src', 'org', 'apache', 'cordova', 'CEEDEEVEE.java'); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(shell.cp).toHaveBeenCalledWith('-f', path.join(default_templates, 'Activity.java'), activity_path); + expect(shell.sed).toHaveBeenCalledWith('-i', /__ACTIVITY__/, 'CEEDEEVEE', activity_path); + expect(shell.sed).toHaveBeenCalledWith('-i', /__ID__/, 'org.apache.cordova', activity_path); + }).fail(fail).done(done); + }); + it('should interpolate the project name into strings.xml', function (done) { + config_mock.name.and.returnValue('IncredibleApp'); + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(shell.sed).toHaveBeenCalledWith('-i', /__NAME__/, 'IncredibleApp', path.join(project_path, 'res', 'values', 'strings.xml')); + }).fail(fail).done(done); + }); + it('should copy template scripts into generated project', function (done) { + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.copyScripts).toHaveBeenCalledWith(project_path); + }).fail(fail).done(done); + }); + it('should copy build rules / gradle files into generated project', function (done) { + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.copyBuildRules).toHaveBeenCalledWith(project_path); + }).fail(fail).done(done); + }); + it('should write project.properties file with project details and target API', function (done) { + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.writeProjectProperties).toHaveBeenCalledWith(project_path, fake_android_target); + }).fail(fail).done(done); + }); + it('should prepare build files', function (done) { + create.create(project_path, config_mock, {}, events_mock).then(function () { + expect(create.prepBuildFiles).toHaveBeenCalledWith(project_path); + }).fail(fail).done(done); }); }); });