blob: 4023ea190c756cfacd3e9fa0ed22e19545f01f40 [file] [log] [blame]
/**
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 Q = require('q');
var fs = require('fs');
var path = require('path');
var rewire = require('rewire');
var platformRoot = '../../template';
var testPath = 'testpath';
var buildPath = path.join(platformRoot, 'cordova', 'build');
var prepare = require(platformRoot + '/cordova/lib/prepare.js');
var build = rewire(platformRoot + '/cordova/lib/build.js');
var utils = require(platformRoot + '/cordova/lib/utils');
var pkg = require(platformRoot + '/cordova/lib/package');
var AppxManifest = require(platformRoot + '/cordova/lib/AppxManifest');
var MSBuildTools = require(platformRoot + '/cordova/lib/MSBuildTools');
function createFindAvailableVersionMock (version, path, buildSpy) {
build.__set__('MSBuildTools.findAvailableVersion', function () {
return Q.resolve({
version: version,
path: path,
buildProject: function (solutionFile, buildType, buildArch) {
if (typeof buildSpy === 'function') {
buildSpy(solutionFile, buildType, buildArch);
}
return Q.reject(); // rejecting here to stop build process
}
});
});
}
function createFindAllAvailableVersionsMock (versionSet) {
build.__set__('MSBuildTools.findAllAvailableVersions', function () {
return Q.resolve(versionSet);
});
}
function createConfigParserMock (winVersion, phoneVersion) {
build.__set__('ConfigParser', function () {
return {
getPreference: function (prefName) {
switch (prefName) {
case 'windows-target-version':
return winVersion;
case 'windows-phone-target-version':
return phoneVersion;
}
},
getWindowsTargetVersion: function () {
return winVersion;
},
getWindowsPhoneTargetVersion: function () {
return phoneVersion;
}
};
});
}
describe('run method', function () {
var findAvailableVersionOriginal,
findAllAvailableVersionsOriginal,
configParserOriginal;
beforeEach(function () {
findAvailableVersionOriginal = build.__get__('MSBuildTools.findAvailableVersion');
findAllAvailableVersionsOriginal = build.__get__('MSBuildTools.findAllAvailableVersions');
configParserOriginal = build.__get__('ConfigParser');
var originalBuildMethod = build.run;
spyOn(build, 'run').and.callFake(function () {
// Bind original build to custom 'this' object to mock platform's locations property
return originalBuildMethod.apply({ locations: { www: 'some/path' } }, arguments);
});
spyOn(utils, 'isCordovaProject').and.returnValue(true);
spyOn(prepare, 'applyPlatformConfig');
spyOn(prepare, 'updateBuildConfig');
spyOn(pkg, 'getPackage').and.returnValue(Q({}));
spyOn(AppxManifest, 'get').and.returnValue({
getIdentity: function () {
return { setPublisher: function () {} };
},
write: function () {}
});
});
afterEach(function () {
build.__set__('MSBuildTools.findAvailableVersion', findAvailableVersionOriginal);
build.__set__('MSBuildTools.findAllAvailableVersions', findAllAvailableVersionsOriginal);
build.__set__('ConfigParser', configParserOriginal);
});
it('spec.1 should reject if not launched from project directory', function () {
var buildSpy = jasmine.createSpy();
// utils.isCordovaProject is a spy, so we can call andReturn directly on it
utils.isCordovaProject.and.returnValue(false);
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: buildSpy, path: testPath }]);
return build.run([ 'node', buildPath, '--release', '--debug' ]).then(
() => fail('Expected promise to be rejected'),
() => expect(buildSpy).not.toHaveBeenCalled()
);
});
it('spec.2 should throw if both debug and release args specified', function () {
var buildSpy = jasmine.createSpy();
createFindAvailableVersionMock('14.0', testPath, buildSpy);
expect(function () {
build.run({ release: true, debug: true });
}).toThrow();
});
it('spec.3 should throw if both phone and win args specified', function () {
var buildSpy = jasmine.createSpy();
createFindAvailableVersionMock('14.0', testPath, buildSpy);
expect(function () {
build.run({ argv: [ '--phone', '--win' ] });
}).toThrow();
});
it('should respect build configuration from \'buildConfig\' option', function () {
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: jasmine.createSpy(), path: testPath }]);
var buildConfigPath = path.resolve(__dirname, 'fixtures/fakeBuildConfig.json');
return build.run({ buildConfig: buildConfigPath })
.finally(function () {
expect(prepare.updateBuildConfig).toHaveBeenCalled();
var buildOpts = prepare.updateBuildConfig.calls.argsFor(0)[0];
var buildConfig = require(buildConfigPath).windows.debug;
expect(buildOpts.packageCertificateKeyFile).toBeDefined();
expect(buildOpts.packageCertificateKeyFile)
.toEqual(path.resolve(path.dirname(buildConfigPath), buildConfig.packageCertificateKeyFile));
['packageThumbprint', 'publisherId'].forEach(function (key) {
expect(buildOpts[key]).toBeDefined();
expect(buildOpts[key]).toEqual(buildConfig[key]);
});
});
}, 20000);
it('spec.4 should call buildProject of MSBuildTools with buildType = "release" if called with --release argument', function () {
var buildSpy = jasmine.createSpy().and.callFake(function (solutionFile, buildType, buildArch) {
expect(buildType).toBe('release');
});
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: buildSpy, path: testPath }]);
return build.run({ release: true })
.finally(function () {
expect(buildSpy).toHaveBeenCalled();
});
});
it('spec.5 should call buildProject of MSBuildTools with buildType = "debug" if called without arguments', function () {
var buildSpy = jasmine.createSpy().and.callFake(function (solutionFile, buildType, buildArch) {
expect(buildType).toBe('debug');
});
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: buildSpy, path: testPath }]);
return build.run([ 'node', buildPath ])
.finally(function () {
expect(buildSpy).toHaveBeenCalled();
});
});
it('spec.6 should call buildProject of MSBuildTools with buildArch = "arm" if called with --archs="arm" argument', function () {
var buildSpy = jasmine.createSpy().and.callFake(function (solutionFile, buildType, buildArch) {
expect(buildArch).toBe('arm');
});
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: buildSpy, path: testPath }]);
return build.run({ archs: 'arm' })
.finally(function () {
expect(buildSpy).toHaveBeenCalled();
});
});
it('spec.7 should call buildProject of MSBuildTools once for all architectures if called with --archs="arm x86 x64 anycpu" argument', function () {
var armBuild = jasmine.createSpy();
var x86Build = jasmine.createSpy();
var x64Build = jasmine.createSpy();
var anyCpuBuild = jasmine.createSpy();
createFindAllAvailableVersionsMock([
{
version: '14.0',
path: testPath,
buildProject: function (solutionFile, buildType, buildArch) {
expect(buildArch).toMatch(/^arm$|^any\s?cpu$|^x86$|^x64$/);
switch (buildArch) {
case 'arm':
armBuild();
return Q();
case 'x86':
x86Build();
return Q();
case 'anycpu':
case 'any cpu':
anyCpuBuild();
return Q();
case 'x64':
x64Build();
return Q();
default:
return Q.reject();
}
}
}]);
return build.run({ archs: 'arm x86 x64 anycpu', argv: ['--phone'] })
.finally(function () {
expect(armBuild).toHaveBeenCalled();
expect(x86Build).toHaveBeenCalled();
expect(x64Build).toHaveBeenCalled();
expect(anyCpuBuild).toHaveBeenCalled();
});
});
xit('spec.8 should fail buildProject if built with MSBuildTools version 4.0', function () {
var buildSpy = jasmine.createSpy();
createFindAllAvailableVersionsMock([{ version: '4.0', buildProject: buildSpy, path: testPath }]);
createConfigParserMock('8.0');
return build.run({ argv: ['--win'] }).then(
() => fail('Expected promise to be rejected'),
error => {
expect(error).toBeDefined();
expect(buildSpy).not.toHaveBeenCalled();
}
);
});
it('spec.9 should call buildProject of MSBuildTools if built for windows 8.1', function () {
var buildSpy = jasmine.createSpy();
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: buildSpy, path: testPath }]);
createConfigParserMock('8.1');
return build.run({ argv: ['--win'] })
.finally(function () {
expect(buildSpy).toHaveBeenCalled();
});
});
xit('spec.10 should throw an error if windows-target-version has unsupported value', function () {
var buildSpy = jasmine.createSpy();
createFindAvailableVersionMock('14.0', testPath, buildSpy);
createConfigParserMock('unsupported value here');
return build.run({ argv: ['--win'] }).then(
() => fail('Expected promise to be rejected'),
error => {
expect(error).toBeDefined();
expect(buildSpy).not.toHaveBeenCalled();
}
);
});
it('spec.11 should call buildProject of MSBuildTools if built for windows phone 8.1', function () {
var buildSpy = jasmine.createSpy();
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: buildSpy, path: testPath }]);
createConfigParserMock(null, '8.1');
return build.run({ argv: ['--phone'] })
.finally(function () {
expect(buildSpy).toHaveBeenCalled();
});
});
xit('spec.12 should throw an error if windows-phone-target-version has unsupported value', function () {
var buildSpy = jasmine.createSpy();
createFindAvailableVersionMock('14.0', testPath, buildSpy);
createConfigParserMock(null, 'unsupported value here');
return build.run({ argv: ['--phone'] }).then(
() => fail('Expected promise to be rejected'),
error => {
expect(error).toBeDefined();
expect(buildSpy).not.toHaveBeenCalled();
}
);
});
it('spec.13a should be able to override target via --appx parameter', function () {
var buildSpy = jasmine.createSpy().and.callFake(function (solutionFile, buildType, buildArch) {
// check that we build Windows 10 and not Windows 8.1
expect(solutionFile.toLowerCase()).toMatch('cordovaapp.windows10.jsproj');
});
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: buildSpy, path: testPath }]);
// provision config to target Windows 8.1
createConfigParserMock('8.1', '8.1');
// explicitly specify Windows 10 as target
return build.run({ argv: ['--appx=uap'] })
.finally(function () {
expect(buildSpy).toHaveBeenCalled();
});
});
it('spec.13b should be able to override target via --appx parameter', function () {
var buildSpy = jasmine.createSpy().and.callFake(function (solutionFile, buildType, buildArch) {
// check that we build Windows 10 and not Windows 8.1
expect(solutionFile.toLowerCase()).toMatch('cordovaapp.windows10.jsproj');
});
createFindAllAvailableVersionsMock([{ version: '14.0', buildProject: buildSpy, path: testPath }]);
// provision config to target Windows 8.1
createConfigParserMock('8.1', '8.1');
// explicitly specify Windows 10 as target
return build.run({ argv: ['--appx=uwp'] })
.finally(function () {
expect(buildSpy).toHaveBeenCalled();
});
});
it('spec.14a should use user-specified msbuild if VSINSTALLDIR variable is set', function () {
var customMSBuildPath = '/some/path';
var msBuildBinPath = path.join(customMSBuildPath, 'MSBuild/15.0/Bin');
var customMSBuildVersion = '15.0';
process.env.VSINSTALLDIR = customMSBuildPath;
// avoid crosspollution with MSBUILDDIR
var backupMSBUILDDIR = process.env.MSBUILDDIR;
delete process.env.MSBUILDDIR;
spyOn(MSBuildTools, 'getMSBuildToolsAt')
.and.returnValue(Q({
path: customMSBuildPath,
version: customMSBuildVersion,
buildProject: jasmine.createSpy('buildProject').and.returnValue(Q())
}));
return build.run({})
.then(() => {
expect(MSBuildTools.getMSBuildToolsAt).toHaveBeenCalledWith(msBuildBinPath);
})
.finally(function () {
delete process.env.VSINSTALLDIR;
process.env.MSBUILDDIR = backupMSBUILDDIR;
});
});
it('spec.14b should use user-specified msbuild if MSBUILDDIR variable is set', function () {
var msBuildBinPath = path.join('/some/path', 'MSBuild/15.0/Bin');
var customMSBuildVersion = '15.0';
process.env.MSBUILDDIR = msBuildBinPath;
// avoid crosspollution with VSINSTALLDIR
var backupVSINSTALLDIR = process.env.VSINSTALLDIR;
delete process.env.VSINSTALLDIR;
spyOn(MSBuildTools, 'getMSBuildToolsAt')
.and.returnValue(Q({
path: msBuildBinPath,
version: customMSBuildVersion,
buildProject: jasmine.createSpy('buildProject').and.returnValue(Q())
}));
return build.run({})
.then(() => {
expect(MSBuildTools.getMSBuildToolsAt).toHaveBeenCalledWith(msBuildBinPath);
})
.finally(function () {
delete process.env.MSBUILDDIR;
process.env.VSINSTALLDIR = backupVSINSTALLDIR;
});
});
it('spec.15a should choose latest version if there are multiple versions available with minor version difference', function () {
var buildTools14 = { version: '14.0', buildProject: jasmine.createSpy('buildTools14'), path: testPath };
var buildTools15 = { version: '15.0', buildProject: jasmine.createSpy('buildTools15'), path: testPath };
var buildTools151 = { version: '15.1', buildProject: jasmine.createSpy('buildTools151'), path: testPath };
createFindAllAvailableVersionsMock([buildTools14, buildTools15, buildTools151]);
// explicitly specify Windows 10 as target
return build.run({ argv: ['--appx=uap'] })
.then(() => {
expect(buildTools151.buildProject).toHaveBeenCalled();
});
});
it('spec.15b should choose latest version if there are multiple versions available with minor version difference', function () {
var buildTools14 = { version: '14.0', buildProject: jasmine.createSpy('buildTools14'), path: testPath };
var buildTools15 = { version: '15.0', buildProject: jasmine.createSpy('buildTools15'), path: testPath };
var buildTools151 = { version: '15.1', buildProject: jasmine.createSpy('buildTools151'), path: testPath };
createFindAllAvailableVersionsMock([buildTools14, buildTools15, buildTools151]);
// explicitly specify Windows 10 as target
return build.run({ argv: ['--appx=uap'] })
.then(() => {
expect(buildTools151.buildProject).toHaveBeenCalled();
});
});
});
describe('buildFlags', function () {
describe('parseAndValidateArgs method', function () {
var parseAndValidateArgs;
var readFileSync;
beforeEach(function () {
parseAndValidateArgs = build.__get__('parseAndValidateArgs');
readFileSync = spyOn(fs, 'readFileSync');
});
it('should handle build flags from both CLI and buildConfig.json', function () {
readFileSync.and.returnValue(JSON.stringify({
windows: { debug: { buildFlag: 'baz="quux"' } }
}));
var buildOptions = {
argv: ['--buildFlag', 'foo=bar', '--buildFlag', 'bar=baz', '--buildConfig', 'buildConfig.json']
};
expect(parseAndValidateArgs(buildOptions).buildFlags).toEqual([ 'baz="quux"', 'foo=bar', 'bar=baz' ]);
});
});
describe('build', function () {
beforeEach(function () {
spyOn(utils, 'isCordovaProject').and.returnValue(true);
spyOn(prepare, 'applyPlatformConfig');
spyOn(prepare, 'updateBuildConfig');
spyOn(pkg, 'getPackage').and.returnValue(Q({}));
spyOn(AppxManifest, 'get').and.returnValue({
getIdentity: function () {
return { setPublisher: function () {} };
},
write: function () {}
});
});
it('should pass buildFlags directly to MSBuild', function () {
var buildTools = { version: '14.0', buildProject: jasmine.createSpy('buildProject').and.returnValue(Q()), path: testPath };
var buildOptions = {
argv: ['--buildFlag', 'foo=bar']
};
createFindAllAvailableVersionsMock([buildTools]);
return build.run(buildOptions)
.then(() => {
// CB-12416 AppxBundle=Never is present because we are not building a bundle
expect(buildTools.buildProject).toHaveBeenCalledWith(jasmine.any(String),
jasmine.any(String), jasmine.any(String), [ 'foo=bar', '/p:AppxBundle=Never' ]);
});
});
});
});