diff --git a/.gitignore b/.gitignore index 496fd6276..7401e5134 100644 --- a/.gitignore +++ b/.gitignore @@ -226,4 +226,5 @@ node_modules/color-support/ node_modules/fs.realpath/ node_modules/jasmine-core/ node_modules/jasmine/ -node_modules/eslint-* \ No newline at end of file +node_modules/eslint-* +.nyc_output/ diff --git a/.istanbul.yml b/.istanbul.yml deleted file mode 100644 index 9824374cb..000000000 --- a/.istanbul.yml +++ /dev/null @@ -1,5 +0,0 @@ -instrumentation: - root: "bin/templates/scripts" - include-all-sources: true -reporting: - print: "detail" diff --git a/.travis.yml b/.travis.yml index a915fe10c..cb32333bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,12 +19,13 @@ install: script: - node --version - npm --version + - npm run eslint - npm run unit-tests + - npm run test:component - npm run e2e-tests - open -b com.apple.iphonesimulator - npm run objc-tests - npm run cover - - npm run eslint after_script: - codecov diff --git a/package.json b/package.json index 74bf1bfc3..c4f4300bf 100644 --- a/package.json +++ b/package.json @@ -18,15 +18,16 @@ "cordova:platform" ], "scripts": { - "test": "npm run e2e-tests && npm run objc-tests && npm run unit-tests", + "test": "npm run unit-tests && npm run test:component && npm run objc-tests && npm run e2e-tests", + "test:component": "jasmine --config=tests/spec/component.json", "posttest": "npm run eslint", - "cover": "istanbul cover --root bin/templates/scripts/cordova --print detail jasmine -- --config=tests/spec/jasmine.json", + "cover": "nyc jasmine --config=tests/spec/coverage.json", "e2e-tests": "jasmine tests/spec/create.spec.js", "objc-tests": "npm run objc-tests-lib && npm run objc-tests-framework", "objc-tests-lib": "xcodebuild test -workspace tests/cordova-ios.xcworkspace -scheme CordovaLibTests -destination \"platform=iOS Simulator,name=iPhone 5\" CONFIGURATION_BUILD_DIR=\"`mktemp -d 2>/dev/null || mktemp -d -t 'cordova-ios'`\"", "objc-tests-framework": "xcodebuild test -workspace tests/cordova-ios.xcworkspace -scheme CordovaFrameworkApp -destination \"platform=iOS Simulator,name=iPhone 5\" CONFIGURATION_BUILD_DIR=\"`mktemp -d 2>/dev/null || mktemp -d -t 'cordova-ios'`\"", "preobjc-tests": "tests/scripts/killsim.js", - "unit-tests": "jasmine --config=tests/spec/jasmine.json", + "unit-tests": "jasmine --config=tests/spec/unit.json", "eslint": "eslint bin tests" }, "author": "Apache Software Foundation", @@ -39,10 +40,10 @@ "eslint-plugin-node": "^5.1.0", "eslint-plugin-promise": "^3.5.0", "eslint-plugin-standard": "^3.0.1", - "istanbul": "^0.4.2", - "jasmine": "~2.6.0", + "jasmine": "^3.1.0", "nodeunit": "^0.8.7", - "rewire": "^2.5.1", + "nyc": "^12.0.2", + "rewire": "^4.0.1", "tmp": "^0.0.26" }, "engines": { @@ -68,5 +69,14 @@ "shelljs", "xcode", "xml-escape" - ] + ], + "nyc": { + "include": [ + "bin/templates/scripts/**" + ], + "reporter": [ + "lcov", + "text" + ] + } } diff --git a/tests/spec/component.json b/tests/spec/component.json new file mode 100644 index 000000000..d8a708abe --- /dev/null +++ b/tests/spec/component.json @@ -0,0 +1,8 @@ +{ + "spec_dir": "tests/spec", + "spec_files": [ + "component/**/*[sS]pec.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +} diff --git a/tests/spec/component/versions.spec.js b/tests/spec/component/versions.spec.js new file mode 100644 index 000000000..fbc8dc25a --- /dev/null +++ b/tests/spec/component/versions.spec.js @@ -0,0 +1,63 @@ +/** + 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 rewire = require('rewire'); +var versions = rewire('../../../bin/templates/scripts/cordova/lib/versions'); + +// These tests can not run on windows. +if (process.platform === 'darwin') { + describe('versions', function () { + describe('get_tool_version method', () => { + it('should not have found tool by name.', (done) => { + versions.get_tool_version('unknown').catch((error) => { + expect(error).toContain('is not valid tool name'); + done(); + }); + }); + + it('should find xcodebuild version.', (done) => { + versions.get_tool_version('xcodebuild').then((version) => { + expect(version).not.toBe(undefined); + done(); + }); + }); + + it('should find ios-sim version.', (done) => { + versions.get_tool_version('ios-sim').then((version) => { + expect(version).not.toBe(undefined); + done(); + }); + }); + + it('should find ios-deploy version.', (done) => { + versions.get_tool_version('ios-deploy').then((version) => { + expect(version).not.toBe(undefined); + done(); + }); + }); + + it('should find pod version.', (done) => { + versions.get_tool_version('pod').then((version) => { + expect(version).not.toBe(undefined); + done(); + }); + }); + }); + }); +} diff --git a/tests/spec/coverage.json b/tests/spec/coverage.json new file mode 100644 index 000000000..2ec72bbfc --- /dev/null +++ b/tests/spec/coverage.json @@ -0,0 +1,9 @@ +{ + "spec_dir": "tests/spec", + "spec_files": [ + "unit/**/*[sS]pec.js", + "component/**/*[sS]pec.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +} diff --git a/tests/spec/jasmine.json b/tests/spec/unit.json similarity index 100% rename from tests/spec/jasmine.json rename to tests/spec/unit.json diff --git a/tests/spec/unit/build.spec.js b/tests/spec/unit/build.spec.js index df5304284..4e70f3e58 100644 --- a/tests/spec/unit/build.spec.js +++ b/tests/spec/unit/build.spec.js @@ -340,4 +340,174 @@ describe('build', function () { done(); }); }); + + describe('help method', () => { + it('should log a bunch of options', () => { + const logSpy = jasmine.createSpy(); + const procStub = { exit: _ => null, cwd: _ => '', argv: ['', ''] }; + build.__set__({ console: { log: logSpy }, process: procStub }); + + build.help(); + expect(logSpy).toHaveBeenCalledWith(jasmine.stringMatching(/^Usage:/)); + }); + }); + + describe('run method', () => { + let rejectSpy; + + beforeEach(() => { + rejectSpy = jasmine.createSpy('reject'); + + build.__set__('Q', { + reject: rejectSpy + }); + }); + + it('should not accept debug and release options together', () => { + build.run({ + debug: true, + release: true + }); + + expect(rejectSpy).toHaveBeenCalledWith('Cannot specify "debug" and "release" options together.'); + }); + + it('should not accept device and emulator options together', () => { + build.run({ + device: true, + emulator: true + }); + + expect(rejectSpy).toHaveBeenCalledWith('Cannot specify "device" and "emulator" options together.'); + }); + + it('should reject when build config file missing', () => { + const existsSyncSpy = jasmine.createSpy('existsSync').and.returnValue(false); + build.__set__('fs', { + existsSync: existsSyncSpy + }); + + build.run({ + buildConfig: './some/config/path' + }); + + expect(rejectSpy).toHaveBeenCalledWith(jasmine.stringMatching(/^Build config file does not exist:/)); + }); + }); + + describe('getDefaultSimulatorTarget method', () => { + it('should find iPhone X as the default simulator target.', (done) => { + const mockedEmulators = [{ + name: 'iPhone 7', + identifier: 'com.apple.CoreSimulator.SimDeviceType.iPhone-7', + simIdentifier: 'iPhone-7' + }, + { + name: 'iPhone 8', + identifier: 'com.apple.CoreSimulator.SimDeviceType.iPhone-8', + simIdentifier: 'iPhone-8' + }, + { + name: 'iPhone X', + identifier: 'com.apple.CoreSimulator.SimDeviceType.iPhone-X', + simIdentifier: 'iPhone-X' + }]; + + // This method will require a module that supports the run method. + build.__set__('require', () => { + return { + run: () => { + return new Promise((resolve, reject) => { + resolve(mockedEmulators); + }); + } + }; + }); + + const getDefaultSimulatorTarget = build.__get__('getDefaultSimulatorTarget'); + const exec = getDefaultSimulatorTarget(); + + const expected = { + name: 'iPhone X', + identifier: 'com.apple.CoreSimulator.SimDeviceType.iPhone-X', + simIdentifier: 'iPhone-X' + }; + + exec.then((actual) => { + expect(actual).toEqual(expected); + done(); + }); + }); + }); + + describe('findXCodeProjectIn method', () => { + let findXCodeProjectIn; + let shellLsSpy; + let rejectSpy; + let resolveSpy; + let emitSpy; + const fakePath = '/path/foobar'; + + beforeEach(() => { + findXCodeProjectIn = build.__get__('findXCodeProjectIn'); + + // Shell Spy + shellLsSpy = jasmine.createSpy('shellLsSpy'); + build.__set__('shell', { + ls: shellLsSpy + }); + + // Q Spy + rejectSpy = jasmine.createSpy('rejectSpy'); + resolveSpy = jasmine.createSpy('resolveSpy'); + build.__set__('Q', { + reject: rejectSpy, + resolve: resolveSpy + }); + + // Events spy + emitSpy = jasmine.createSpy('emitSpy'); + build.__set__('events', { + emit: emitSpy + }); + }); + + it('should find not find Xcode project', () => { + shellLsSpy.and.returnValue(['README.md']); + + findXCodeProjectIn(fakePath); + + expect(rejectSpy).toHaveBeenCalledWith('No Xcode project found in ' + fakePath); + }); + + it('should emit finding multiple Xcode projects', () => { + shellLsSpy.and.returnValue(['Test1.xcodeproj', 'Test2.xcodeproj']); + + findXCodeProjectIn(fakePath); + + // Emit + let actualEmit = emitSpy.calls.argsFor(0)[1]; + expect(emitSpy).toHaveBeenCalled(); + expect(actualEmit).toContain('Found multiple .xcodeproj directories in'); + + // Resolve + let actualResolve = resolveSpy.calls.argsFor(0)[0]; + expect(resolveSpy).toHaveBeenCalled(); + expect(actualResolve).toContain('Test1'); + }); + + it('should detect and return only one projects', () => { + shellLsSpy.and.returnValue(['Test1.xcodeproj']); + + findXCodeProjectIn(fakePath); + + // Emit + expect(emitSpy).not.toHaveBeenCalled(); + + // Resolve + let actualResolve = resolveSpy.calls.argsFor(0)[0]; + expect(resolveSpy).toHaveBeenCalled(); + expect(actualResolve).toContain('Test1'); + }); + }); }); diff --git a/tests/spec/unit/lib/check_reqs.spec.js b/tests/spec/unit/lib/check_reqs.spec.js new file mode 100644 index 000000000..5b78ceb35 --- /dev/null +++ b/tests/spec/unit/lib/check_reqs.spec.js @@ -0,0 +1,113 @@ +/** + 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 rewire = require('rewire'); +var checkReqs = rewire('../../../../bin/templates/scripts/cordova/lib/check_reqs'); + +describe('check_reqs', function () { + describe('checkTool method', () => { + const originalVersion = checkReqs.__get__('versions'); + let shellWhichSpy; + let rejectSpy; + let resolveSpy; + let getToolVersionSpy; + + beforeEach(() => { + // Shell Spy + shellWhichSpy = jasmine.createSpy('shellWhichSpy'); + checkReqs.__set__('shell', { + which: shellWhichSpy + }); + + // Q Spy + rejectSpy = jasmine.createSpy('rejectSpy'); + resolveSpy = jasmine.createSpy('resolveSpy'); + checkReqs.__set__('Q', { + reject: rejectSpy, + resolve: resolveSpy + }); + + // Versions Spy + getToolVersionSpy = jasmine.createSpy('rejectSpy'); + }); + + it('should not have found tool.', () => { + shellWhichSpy.and.returnValue(false); + const checkTool = checkReqs.__get__('checkTool'); + + checkTool('node', '1.0.0'); + + expect(rejectSpy).toHaveBeenCalledWith(jasmine.stringMatching(/^node was not found./)); + }); + + it('should throw error because version is not following semver-notated.', (done) => { + shellWhichSpy.and.returnValue('/bin/node'); + const checkTool = checkReqs.__get__('checkTool'); + + checkReqs.__set__('versions', { + get_tool_version: getToolVersionSpy.and.returnValue(new Promise((resolve) => { + return resolve('1.0.0'); + }).catch((error) => { console.log(error); })), + compareVersions: originalVersion.compareVersions + }); + + checkTool('node', 'v1.0.0').catch((error) => { + expect(error).toEqual('Version should contain only numbers and dots'); + done(); + }); + }); + + it('should resolve passing back tool version.', (done) => { + shellWhichSpy.and.returnValue('/bin/node'); + const checkTool = checkReqs.__get__('checkTool'); + + checkReqs.__set__('versions', { + get_tool_version: getToolVersionSpy.and.returnValue(new Promise((resolve) => { + return resolve('1.0.0'); + })), + compareVersions: originalVersion.compareVersions + }); + + checkTool('node', '1.0.0').then(() => { + let actual = resolveSpy.calls.argsFor(0)[0]; + expect(actual).toEqual({version: '1.0.0'}); + done(); + }); + }); + + it('should reject because tool does not meet minimum requirement.', (done) => { + shellWhichSpy.and.returnValue('/bin/node'); + const checkTool = checkReqs.__get__('checkTool'); + + checkReqs.__set__('versions', { + get_tool_version: getToolVersionSpy.and.returnValue(new Promise((resolve) => { + return resolve('1.0.0'); + })), + compareVersions: originalVersion.compareVersions + }); + + checkTool('node', '1.0.1').then(() => { + let actual = rejectSpy.calls.argsFor(0)[0]; + expect(actual).toContain('version 1.0.1 or greater'); + expect(actual).toContain('you have version 1.0.0'); + done(); + }); + }); + }); +}); diff --git a/tests/spec/unit/versions.spec.js b/tests/spec/unit/versions.spec.js index 983b839dc..e921820f6 100644 --- a/tests/spec/unit/versions.spec.js +++ b/tests/spec/unit/versions.spec.js @@ -25,8 +25,8 @@ if (process.platform === 'darwin') { describe('versions', function () { describe('get_apple_ios_version method', () => { it('should have found ios version.', (done) => { - var _console = versions.__get__('console'); - var logSpy = jasmine.createSpy('logSpy'); + let _console = versions.__get__('console'); + let logSpy = jasmine.createSpy('logSpy'); versions.__set__('console', {log: logSpy}); versions.get_apple_ios_version().then(() => { @@ -39,8 +39,8 @@ if (process.platform === 'darwin') { describe('get_apple_osx_version method', () => { it('should have found osx version.', (done) => { - var _console = versions.__get__('console'); - var logSpy = jasmine.createSpy('logSpy'); + let _console = versions.__get__('console'); + let logSpy = jasmine.createSpy('logSpy'); versions.__set__('console', {log: logSpy}); versions.get_apple_osx_version().then(() => {