Menardi closed pull request #458: Unit tests for AndroidProject and Builders URL: https://github.com/apache/cordova-android/pull/458
This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/spec/unit/AndroidProject.spec.js b/spec/unit/AndroidProject.spec.js new file mode 100644 index 000000000..9af09fca3 --- /dev/null +++ b/spec/unit/AndroidProject.spec.js @@ -0,0 +1,134 @@ +/** + 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. +*/ + +const path = require('path'); +const rewire = require('rewire'); + +describe('AndroidProject', () => { + const PROJECT_DIR = 'platforms/android'; + let AndroidProject; + let AndroidStudioSpy; + + beforeEach(() => { + AndroidProject = rewire('../../bin/templates/cordova/lib/AndroidProject'); + + AndroidStudioSpy = jasmine.createSpyObj('AndroidStudio', ['isAndroidStudioProject']); + AndroidProject.__set__('AndroidStudio', AndroidStudioSpy); + }); + + describe('constructor', () => { + it('should set the project directory', () => { + const project = new AndroidProject(PROJECT_DIR); + expect(project.projectDir).toBe(PROJECT_DIR); + }); + + it('should set www folder correctly if not Android Studio project', () => { + AndroidStudioSpy.isAndroidStudioProject.and.returnValue(false); + + const project = new AndroidProject(PROJECT_DIR); + expect(project.www).toBe(path.join(PROJECT_DIR, 'assets/www')); + }); + + it('should set www folder correctly if it is an Android Studio project', () => { + AndroidStudioSpy.isAndroidStudioProject.and.returnValue(true); + + const project = new AndroidProject(PROJECT_DIR); + expect(project.www).toBe(path.join(PROJECT_DIR, 'app/src/main/assets/www')); + }); + }); + + describe('getProjectFile', () => { + it('should create and return a new project if one does not exist', () => { + const newProject = AndroidProject.getProjectFile(PROJECT_DIR); + + expect(newProject).toEqual(jasmine.any(AndroidProject)); + }); + + it('should cache created projects', () => { + const newProject = AndroidProject.getProjectFile(PROJECT_DIR); + const secondProject = AndroidProject.getProjectFile(PROJECT_DIR); + + expect(newProject).toEqual(jasmine.any(AndroidProject)); + expect(secondProject).toBe(newProject); + }); + }); + + describe('purgeCache', () => { + beforeEach(() => { + AndroidProject.__set__('projectFileCache', { + project1: 'test', + project2: 'anothertest', + project3: 'finaltest' + }); + }); + + it('should only remove the specified project from the cache', () => { + const projectToRemove = 'project2'; + AndroidProject.purgeCache(projectToRemove); + + const cache = AndroidProject.__get__('projectFileCache'); + expect(Object.keys(cache).length).toBe(2); + expect(cache[projectToRemove]).toBe(undefined); + }); + + it('should remove all projects from cache', () => { + AndroidProject.purgeCache(); + + const cache = AndroidProject.__get__('projectFileCache'); + expect(Object.keys(cache).length).toBe(0); + }); + }); + + describe('getPackageName', () => { + let AndroidManifestSpy; + let AndroidManifestFns; + let androidProject; + + beforeEach(() => { + AndroidManifestFns = jasmine.createSpyObj('AndroidManifestFns', ['getPackageId']); + AndroidManifestSpy = jasmine.createSpy('AndroidManifest').and.returnValue(AndroidManifestFns); + AndroidProject.__set__('AndroidManifest', AndroidManifestSpy); + + androidProject = new AndroidProject(PROJECT_DIR); + }); + + it('should get the package name from the project root manifest', () => { + AndroidStudioSpy.isAndroidStudioProject.and.returnValue(false); + + androidProject.getPackageName(); + + expect(AndroidManifestSpy).toHaveBeenCalledWith(path.join(PROJECT_DIR, 'AndroidManifest.xml')); + }); + + it('should get the package name from the Android Studio manifest', () => { + AndroidStudioSpy.isAndroidStudioProject.and.returnValue(true); + + androidProject.getPackageName(); + + expect(AndroidManifestSpy).toHaveBeenCalledWith(path.join(PROJECT_DIR, 'app/src/main/AndroidManifest.xml')); + }); + + it('should return the package name', () => { + const packageName = 'io.cordova.unittest'; + AndroidManifestFns.getPackageId.and.returnValue(packageName); + + expect(androidProject.getPackageName()).toBe(packageName); + }); + }); +}); diff --git a/spec/unit/builders/GenericBuilder.spec.js b/spec/unit/builders/GenericBuilder.spec.js new file mode 100644 index 000000000..bb09b8d0b --- /dev/null +++ b/spec/unit/builders/GenericBuilder.spec.js @@ -0,0 +1,91 @@ +/* + 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. +*/ + +const rewire = require('rewire'); + +describe('GenericBuilder', () => { + let GenericBuilder; + let genericBuilder; + + beforeEach(() => { + GenericBuilder = rewire('../../../bin/templates/cordova/lib/builders/GenericBuilder'); + GenericBuilder.__set__('__dirname', 'projecttest/platforms/android/app'); + + genericBuilder = new GenericBuilder(); + }); + + describe('constructor', () => { + it('should set the project directory to the passed argument', () => { + const projectDir = 'unit/test/dir'; + const genericBuilder = new GenericBuilder(projectDir); + + expect(genericBuilder.root).toBe(projectDir); + }); + + it('should set the project directory to three folders back', () => { + expect(genericBuilder.root).toMatch(/projecttest$/); + }); + }); + + describe('prepEnv', () => { + it('should return a promise', () => { + expect(genericBuilder.prepEnv().then).toEqual(jasmine.any(Function)); + }); + }); + + describe('build', () => { + it('should return a promise', () => { + expect(genericBuilder.build().then).toEqual(jasmine.any(Function)); + }); + }); + + describe('clean', () => { + it('should return a promise', () => { + expect(genericBuilder.clean().then).toEqual(jasmine.any(Function)); + }); + }); + + describe('apkSorter', () => { + it('should sort APKs from most recent to oldest, deprioritising unsigned arch-specific builds', () => { + const APKs = { + 'app-debug.apk': new Date('2018-04-20'), + 'app-release.apk': new Date('2018-05-20'), + 'app-debug-x86.apk': new Date('2018-06-01'), + 'app-release-x86.apk': new Date('2018-06-20'), + 'app-debug-arm.apk': new Date('2018-05-24'), + 'app-release-arm.apk': new Date('2018-06-24'), + 'app-release-unsigned.apk': new Date('2018-06-28') + }; + + const expectedResult = ['app-release.apk', 'app-debug.apk', 'app-release-unsigned.apk', + 'app-release-arm.apk', 'app-debug-arm.apk', 'app-release-x86.apk', 'app-debug-x86.apk']; + + const fsSpy = jasmine.createSpyObj('fs', ['statSync']); + fsSpy.statSync.and.callFake(filename => { + return { mtime: APKs[filename].getTime() }; + }); + GenericBuilder.__set__('fs', fsSpy); + + const apkArray = Object.keys(APKs); + const sortedApks = apkArray.sort(GenericBuilder.__get__('apkSorter')); + + expect(sortedApks).toEqual(expectedResult); + }); + }); +}); diff --git a/spec/unit/builders/GradleBuilder.spec.js b/spec/unit/builders/GradleBuilder.spec.js index f8ad1a4fd..aa1390ab3 100644 --- a/spec/unit/builders/GradleBuilder.spec.js +++ b/spec/unit/builders/GradleBuilder.spec.js @@ -1,28 +1,249 @@ +/* + 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 -var Gradle_builder = require('../../../bin/templates/cordova/lib/builders/GradleBuilder.js'); -var fs = require('fs'); -var Q = require('q'); -var superspawn = require('cordova-common').superspawn; -var builder; + 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. +*/ + +const fs = require('fs'); +const Q = require('q'); +const rewire = require('rewire'); +const superspawn = require('cordova-common').superspawn; +const path = require('path'); + +const CordovaError = require('cordova-common').CordovaError; describe('Gradle Builder', function () { + const rootDir = '/root'; + + let builder; + let GradleBuilder; + let spawnSpy; + beforeEach(function () { - spyOn(fs, 'existsSync').and.returnValue(true); - builder = new Gradle_builder('/root'); - var deferred = Q.defer(); - spyOn(superspawn, 'spawn').and.returnValue(deferred.promise); + GradleBuilder = rewire('../../../bin/templates/cordova/lib/builders/GradleBuilder'); + builder = new GradleBuilder(rootDir); + spawnSpy = spyOn(superspawn, 'spawn'); + spawnSpy.and.returnValue(Q.defer().promise); }); - describe('runGradleWrapper method', function () { - it('should run the provided gradle command if a gradle wrapper does not already exist', function () { - fs.existsSync.and.returnValue(false); + describe('getArgs', () => { + beforeEach(() => { + spyOn(fs, 'existsSync').and.returnValue(true); + }); + + it('should set release argument', () => { + const args = builder.getArgs('release', {}); + expect(args[0]).toBe('cdvBuildRelease'); + }); + + it('should set debug argument', () => { + const args = builder.getArgs('debug', {}); + expect(args[0]).toBe('cdvBuildDebug'); + }); + + it('should add architecture if it is passed', () => { + const arch = 'unittest'; + const args = builder.getArgs('debug', { arch }); + + expect(args).toContain(`-PcdvBuildArch=${arch}`); + }); + }); + + describe('runGradleWrapper', () => { + it('should run the provided gradle command if a gradle wrapper does not already exist', () => { + spyOn(fs, 'existsSync').and.returnValue(false); builder.runGradleWrapper('/my/sweet/gradle'); - expect(superspawn.spawn).toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object)); + expect(spawnSpy).toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object)); }); - it('should do nothing if a gradle wrapper exists in the project directory', function () { - fs.existsSync.and.returnValue(true); + + it('should do nothing if a gradle wrapper exists in the project directory', () => { + spyOn(fs, 'existsSync').and.returnValue(true); builder.runGradleWrapper('/my/sweet/gradle'); - expect(superspawn.spawn).not.toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object)); + expect(spawnSpy).not.toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object)); + }); + }); + + describe('extractRealProjectNameFromManifest', () => { + it('should get the project name from the Android Manifest', () => { + const projectName = 'unittestproject'; + const projectId = `io.cordova.${projectName}`; + const manifest = `<?xml version="1.0" encoding="utf-8"?> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="${projectId}"></manifest>`; + + spyOn(fs, 'readFileSync').and.returnValue(manifest); + + expect(builder.extractRealProjectNameFromManifest()).toBe(projectName); + }); + + it('should throw an error if there is no package in the Android manifest', () => { + const manifest = `<?xml version="1.0" encoding="utf-8"?> + <manifest xmlns:android="http://schemas.android.com/apk/res/android"></manifest>`; + + spyOn(fs, 'readFileSync').and.returnValue(manifest); + + expect(() => builder.extractRealProjectNameFromManifest()).toThrow(jasmine.any(CordovaError)); + }); + }); + + describe('build', () => { + beforeEach(() => { + spyOn(builder, 'getArgs'); + }); + + it('should set to build type to debug', () => { + const opts = { buildType: 'debug' }; + builder.build(opts); + expect(builder.getArgs).toHaveBeenCalledWith('debug', opts); + }); + + it('should set build type to release', () => { + const opts = { buildType: 'release' }; + builder.build(opts); + expect(builder.getArgs).toHaveBeenCalledWith('release', opts); + }); + + it('should default build type to release', () => { + const opts = {}; + builder.build(opts); + expect(builder.getArgs).toHaveBeenCalledWith('release', opts); + }); + + it('should spawn gradle with correct args', () => { + const testArgs = ['test', 'args', '-c']; + builder.getArgs.and.returnValue(testArgs); + + builder.build({}); + + expect(spawnSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), testArgs, jasmine.anything()); + }); + + it('should reject if the spawn fails', () => { + const errorMessage = 'ERROR: Failed to spawn'; + spawnSpy.and.returnValue(Q.reject(errorMessage)); + + return builder.build({}).then( + () => fail('Unexpectedly resolved'), + err => { + expect(err).toBe(errorMessage); + } + ); + }); + + it('should check the Android target if failed to find target', () => { + const checkReqsSpy = jasmine.createSpyObj('check_reqs', ['check_android_target']); + const errorMessage = 'ERROR: failed to find target with hash string'; + + GradleBuilder.__set__('check_reqs', checkReqsSpy); + checkReqsSpy.check_android_target.and.returnValue(Q.resolve()); + spawnSpy.and.returnValue(Q.reject(errorMessage)); + + return builder.build({}).then( + () => fail('Unexpectedly resolved'), + err => { + expect(checkReqsSpy.check_android_target).toHaveBeenCalledWith(errorMessage); + expect(err).toBe(errorMessage); + } + ); + }); + }); + + describe('clean', () => { + let shellSpy; + + beforeEach(() => { + shellSpy = jasmine.createSpyObj('shell', ['rm']); + GradleBuilder.__set__('shell', shellSpy); + spyOn(builder, 'getArgs'); + spawnSpy.and.returnValue(Promise.resolve()); + }); + + it('should get arguments for cleaning', () => { + const opts = {}; + builder.clean(opts); + + expect(builder.getArgs).toHaveBeenCalledWith('clean', opts); + }); + + it('should spawn gradle', () => { + const opts = {}; + const gradleArgs = ['test', 'args', '-f']; + builder.getArgs.and.returnValue(gradleArgs); + + return builder.clean(opts).then(() => { + expect(spawnSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), gradleArgs, jasmine.anything()); + }); + }); + + it('should remove "out" folder', () => { + return builder.clean({}).then(() => { + expect(shellSpy.rm).toHaveBeenCalledWith('-rf', path.join(rootDir, 'out')); + }); + }); + + it('should remove signing files if they are autogenerated', () => { + const debugSigningFile = path.join(rootDir, 'debug-signing.properties'); + const releaseSigningFile = path.join(rootDir, 'release-signing.properties'); + + const isAutoGeneratedSpy = jasmine.createSpy('isAutoGenerated'); + GradleBuilder.__set__('isAutoGenerated', isAutoGeneratedSpy); + isAutoGeneratedSpy.and.returnValue(true); + + return builder.clean({}).then(() => { + expect(shellSpy.rm).toHaveBeenCalledWith(jasmine.any(String), debugSigningFile); + expect(shellSpy.rm).toHaveBeenCalledWith(jasmine.any(String), releaseSigningFile); + }); + }); + + it('should not remove signing files if they are not autogenerated', () => { + const debugSigningFile = path.join(rootDir, 'debug-signing.properties'); + const releaseSigningFile = path.join(rootDir, 'release-signing.properties'); + + const isAutoGeneratedSpy = jasmine.createSpy('isAutoGenerated'); + GradleBuilder.__set__('isAutoGenerated', isAutoGeneratedSpy); + isAutoGeneratedSpy.and.returnValue(false); + + return builder.clean({}).then(() => { + expect(shellSpy.rm).not.toHaveBeenCalledWith(jasmine.any(String), debugSigningFile); + expect(shellSpy.rm).not.toHaveBeenCalledWith(jasmine.any(String), releaseSigningFile); + }); + }); + }); + + describe('isAutoGenerated', () => { + let fsSpy; + + beforeEach(() => { + fsSpy = jasmine.createSpyObj('fs', ['existsSync', 'readFileSync']); + fsSpy.existsSync.and.returnValue(true); + GradleBuilder.__set__('fs', fsSpy); + }); + + it('should return true if the file contains the autogenerated marker', () => { + const fileContents = `# DO NOT MODIFY - YOUR CHANGES WILL BE ERASED!`; + fsSpy.readFileSync.and.returnValue(fileContents); + + expect(GradleBuilder.__get__('isAutoGenerated')()).toBe(true); + }); + + it('should return false if the file does not contain the autogenerated marker', () => { + const fileContents = `# My modified file`; + fsSpy.readFileSync.and.returnValue(fileContents); + + expect(GradleBuilder.__get__('isAutoGenerated')()).toBe(false); }); }); }); diff --git a/spec/unit/builders/StudioBuilder.spec.js b/spec/unit/builders/StudioBuilder.spec.js new file mode 100644 index 000000000..5ff53eef0 --- /dev/null +++ b/spec/unit/builders/StudioBuilder.spec.js @@ -0,0 +1,249 @@ +/* + 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. +*/ + +const fs = require('fs'); +const path = require('path'); +const Q = require('q'); +const rewire = require('rewire'); + +const CordovaError = require('cordova-common').CordovaError; + +describe('StudioBuilder', () => { + const rootDir = '/root'; + + let builder; + let StudioBuilder; + let spawnSpy; + + beforeEach(() => { + spawnSpy = jasmine.createSpy('spawn').and.returnValue(Q.defer().promise); + StudioBuilder = rewire('../../../bin/templates/cordova/lib/builders/StudioBuilder'); + StudioBuilder.__set__('spawn', spawnSpy); + + builder = new StudioBuilder(rootDir); + }); + + describe('getArgs', () => { + beforeEach(() => { + spyOn(fs, 'existsSync').and.returnValue(true); + }); + + it('should set release argument', () => { + const args = builder.getArgs('release', {}); + expect(args[0]).toBe('cdvBuildRelease'); + }); + + it('should set debug argument', () => { + const args = builder.getArgs('debug', {}); + expect(args[0]).toBe('cdvBuildDebug'); + }); + + it('should add architecture if it is passed', () => { + const arch = 'unittest'; + const args = builder.getArgs('debug', { arch }); + + expect(args).toContain(`-PcdvBuildArch=${arch}`); + }); + }); + + describe('runGradleWrapper', () => { + it('should run the provided gradle command if a gradle wrapper does not already exist', () => { + spyOn(fs, 'existsSync').and.returnValue(false); + builder.runGradleWrapper('/my/sweet/gradle'); + expect(spawnSpy).toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object)); + }); + + it('should do nothing if a gradle wrapper exists in the project directory', () => { + spyOn(fs, 'existsSync').and.returnValue(true); + builder.runGradleWrapper('/my/sweet/gradle'); + expect(spawnSpy).not.toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object)); + }); + }); + + describe('extractRealProjectNameFromManifest', () => { + it('should get the project name from the Android Manifest', () => { + const projectName = 'unittestproject'; + const projectId = `io.cordova.${projectName}`; + const manifest = `<?xml version="1.0" encoding="utf-8"?> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="${projectId}"></manifest>`; + + spyOn(fs, 'readFileSync').and.returnValue(manifest); + + expect(builder.extractRealProjectNameFromManifest()).toBe(projectName); + }); + + it('should throw an error if there is no package in the Android manifest', () => { + const manifest = `<?xml version="1.0" encoding="utf-8"?> + <manifest xmlns:android="http://schemas.android.com/apk/res/android"></manifest>`; + + spyOn(fs, 'readFileSync').and.returnValue(manifest); + + expect(() => builder.extractRealProjectNameFromManifest()).toThrow(jasmine.any(CordovaError)); + }); + }); + + describe('build', () => { + beforeEach(() => { + spyOn(builder, 'getArgs'); + }); + + it('should set to build type to debug', () => { + const opts = { buildType: 'debug' }; + builder.build(opts); + expect(builder.getArgs).toHaveBeenCalledWith('debug', opts); + }); + + it('should set build type to release', () => { + const opts = { buildType: 'release' }; + builder.build(opts); + expect(builder.getArgs).toHaveBeenCalledWith('release', opts); + }); + + it('should default build type to release', () => { + const opts = {}; + builder.build(opts); + expect(builder.getArgs).toHaveBeenCalledWith('release', opts); + }); + + it('should spawn gradle with correct args', () => { + const testArgs = ['test', 'args', '-c']; + builder.getArgs.and.returnValue(testArgs); + + builder.build({}); + + expect(spawnSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), testArgs, jasmine.anything()); + }); + + it('should reject if the spawn fails', () => { + const errorMessage = 'ERROR: Failed to spawn'; + spawnSpy.and.returnValue(Q.reject(errorMessage)); + + return builder.build({}).then( + () => fail('Unexpectedly resolved'), + err => { + expect(err).toBe(errorMessage); + } + ); + }); + + it('should check the Android target if failed to find target', () => { + const checkReqsSpy = jasmine.createSpyObj('check_reqs', ['check_android_target']); + const errorMessage = 'ERROR: failed to find target with hash string'; + + StudioBuilder.__set__('check_reqs', checkReqsSpy); + checkReqsSpy.check_android_target.and.returnValue(Q.resolve()); + spawnSpy.and.returnValue(Q.reject(errorMessage)); + + return builder.build({}).then( + () => fail('Unexpectedly resolved'), + err => { + expect(checkReqsSpy.check_android_target).toHaveBeenCalledWith(errorMessage); + expect(err).toBe(errorMessage); + } + ); + }); + }); + + describe('clean', () => { + let shellSpy; + + beforeEach(() => { + shellSpy = jasmine.createSpyObj('shell', ['rm']); + StudioBuilder.__set__('shell', shellSpy); + spyOn(builder, 'getArgs'); + spawnSpy.and.returnValue(Promise.resolve()); + }); + + it('should get arguments for cleaning', () => { + const opts = {}; + builder.clean(opts); + + expect(builder.getArgs).toHaveBeenCalledWith('clean', opts); + }); + + it('should spawn gradle', () => { + const opts = {}; + const gradleArgs = ['test', 'args', '-f']; + builder.getArgs.and.returnValue(gradleArgs); + + return builder.clean(opts).then(() => { + expect(spawnSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), gradleArgs, jasmine.anything()); + }); + }); + + it('should remove "out" folder', () => { + return builder.clean({}).then(() => { + expect(shellSpy.rm).toHaveBeenCalledWith('-rf', path.join(rootDir, 'out')); + }); + }); + + it('should remove signing files if they are autogenerated', () => { + const debugSigningFile = path.join(rootDir, 'debug-signing.properties'); + const releaseSigningFile = path.join(rootDir, 'release-signing.properties'); + + const isAutoGeneratedSpy = jasmine.createSpy('isAutoGenerated'); + StudioBuilder.__set__('isAutoGenerated', isAutoGeneratedSpy); + isAutoGeneratedSpy.and.returnValue(true); + + return builder.clean({}).then(() => { + expect(shellSpy.rm).toHaveBeenCalledWith(jasmine.any(String), debugSigningFile); + expect(shellSpy.rm).toHaveBeenCalledWith(jasmine.any(String), releaseSigningFile); + }); + }); + + it('should not remove signing files if they are not autogenerated', () => { + const debugSigningFile = path.join(rootDir, 'debug-signing.properties'); + const releaseSigningFile = path.join(rootDir, 'release-signing.properties'); + + const isAutoGeneratedSpy = jasmine.createSpy('isAutoGenerated'); + StudioBuilder.__set__('isAutoGenerated', isAutoGeneratedSpy); + isAutoGeneratedSpy.and.returnValue(false); + + return builder.clean({}).then(() => { + expect(shellSpy.rm).not.toHaveBeenCalledWith(jasmine.any(String), debugSigningFile); + expect(shellSpy.rm).not.toHaveBeenCalledWith(jasmine.any(String), releaseSigningFile); + }); + }); + }); + + describe('isAutoGenerated', () => { + let fsSpy; + + beforeEach(() => { + fsSpy = jasmine.createSpyObj('fs', ['existsSync', 'readFileSync']); + fsSpy.existsSync.and.returnValue(true); + StudioBuilder.__set__('fs', fsSpy); + }); + + it('should return true if the file contains the autogenerated marker', () => { + const fileContents = `# DO NOT MODIFY - YOUR CHANGES WILL BE ERASED!`; + fsSpy.readFileSync.and.returnValue(fileContents); + + expect(StudioBuilder.__get__('isAutoGenerated')()).toBe(true); + }); + + it('should return false if the file does not contain the autogenerated marker', () => { + const fileContents = `# My modified file`; + fsSpy.readFileSync.and.returnValue(fileContents); + + expect(StudioBuilder.__get__('isAutoGenerated')()).toBe(false); + }); + }); +}); diff --git a/spec/unit/builders/builders.spec.js b/spec/unit/builders/builders.spec.js new file mode 100644 index 000000000..8163c6a3d --- /dev/null +++ b/spec/unit/builders/builders.spec.js @@ -0,0 +1,61 @@ +/** + 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. +*/ + +const rewire = require('rewire'); + +const CordovaError = require('cordova-common').CordovaError; +const GenericBuilder = require('../../../bin/templates/cordova/lib/builders/GenericBuilder'); +const GradleBuilder = require('../../../bin/templates/cordova/lib/builders/GradleBuilder'); +const StudioBuilder = require('../../../bin/templates/cordova/lib/builders/StudioBuilder'); + +describe('builders', () => { + let builder; + + beforeEach(() => { + builder = rewire('../../../bin/templates/cordova/lib/builders/builders'); + }); + + describe('getBuilder', () => { + it('should return an instance of GradleBuilder', () => { + const newBuilder = builder.getBuilder('gradle'); + expect(newBuilder).toEqual(jasmine.any(GradleBuilder)); + }); + + it('should return an instance of StudioBuilder', () => { + const newBuilder = builder.getBuilder('studio'); + expect(newBuilder).toEqual(jasmine.any(StudioBuilder)); + }); + + it('should return an instance of GenericBuilder', () => { + const newBuilder = builder.getBuilder('none'); + expect(newBuilder).toEqual(jasmine.any(GenericBuilder)); + }); + + it('should throw an error if the selected builder does not exist', () => { + expect(() => builder.getBuilder('NonExistentBuilder')).toThrow(jasmine.any(CordovaError)); + }); + + it('should throw an error if a builder cannot be instantiated', () => { + const requireSpy = jasmine.createSpy('require').and.throwError(); + builder.__set__('require', requireSpy); + + expect(() => builder.getBuilder('gradle')).toThrow(jasmine.any(CordovaError)); + }); + }); +}); ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
