Repository: cordova-windows Updated Branches: refs/heads/master 49812d448 -> 60cca717a
http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/60cca717/spec/unit/pluginHandler/windows.spec.js ---------------------------------------------------------------------- diff --git a/spec/unit/pluginHandler/windows.spec.js b/spec/unit/pluginHandler/windows.spec.js new file mode 100644 index 0000000..cf9d956 --- /dev/null +++ b/spec/unit/pluginHandler/windows.spec.js @@ -0,0 +1,510 @@ +/** + 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 fs = require('fs'); +var os = require('os'); +var et = require('elementtree'); +var path = require('path'); +var shell = require('shelljs'); +var rewire = require('rewire'); + +var PluginHandler = rewire('../../../template/cordova/lib/PluginHandler'); +var JsprojManager = require('../../../template/cordova/lib/JsprojManager'); +var cordovaProjectDir = path.join(os.tmpdir(), 'plugman'); + +var cordovaProjectWindowsPlatformDir = path.join(cordovaProjectDir, 'platforms', 'windows'); +var cordovaProjectPluginsDir = path.join(cordovaProjectDir, 'plugins'); +var PluginInfo = require('cordova-common').PluginInfo; + +var dummyplugin = path.join(__dirname, '../fixtures/org.test.plugins.dummyplugin'); +var dummyPluginInfo = new PluginInfo(dummyplugin); +var valid_source = dummyPluginInfo.getSourceFiles('windows'); +var valid_resourceFiles = dummyPluginInfo.getResourceFiles('windows'); +var valid_libfiles = dummyPluginInfo.getLibFiles('windows'); +var valid_frameworks = dummyPluginInfo.getFrameworks('windows'); + +var faultyplugin = path.join(__dirname, '../fixtures/org.test.plugins.faultyplugin'); +var faultyPluginInfo = new PluginInfo(faultyplugin); +var invalid_source = faultyPluginInfo.getSourceFiles('windows'); +var invalid_resourceFiles = faultyPluginInfo.getResourceFiles('windows'); +var invalid_libfiles = faultyPluginInfo.getLibFiles('windows'); + +function copyArray(arr) { + return Array.prototype.slice.call(arr, 0); +} + +function winJoin() { + // use Node API when possible + if (path.win32) return path.win32.join.apply(path, arguments); + return copyArray(arguments).join('\\').replace(/\//g, '\\'); +} + +beforeEach(function () { + this.addMatchers({ + toContainXmlPath: function (xpath) { + var xml = this.actual; + var notText = this.isNot ? 'not ' : ''; + this.message = function () { + return 'Expected xml \'' + et.tostring(xml) + '\' ' + notText + 'to contain elements matching \'' + xpath + '\'.'; + }; + + return xml.find(xpath) !== null; + } }); +}); + +var getPluginFilePath = PluginHandler.__get__('getPluginFilePath'); +var computeResourcePath = function(resourceFile) { + return getPluginFilePath(dummyPluginInfo, resourceFile.src, cordovaProjectWindowsPlatformDir); +}; + +var PLATFORM_PROJECTS = { + all: 'CordovaApp.projitems', + phone: 'CordovaApp.Phone.jsproj', + windows: 'CordovaApp.Windows.jsproj', + windows8: 'CordovaApp.Windows80.jsproj', + windows10: 'CordovaApp.Windows10.jsproj' +}; + +describe('windows project handler', function () { + var dummyProject; + beforeEach(function () { + shell.mkdir('-p', cordovaProjectWindowsPlatformDir); + shell.cp('-rf', path.join(__dirname, '../fixtures/DummyProject/*'), cordovaProjectWindowsPlatformDir); + dummyProject = JsprojManager.getProject(cordovaProjectWindowsPlatformDir); + shell.mkdir('-p', cordovaProjectPluginsDir); + }); + + afterEach(function () { + shell.rm('-rf', cordovaProjectDir); + }); + + describe('installation', function () { + var copyFileOrig = PluginHandler.__get__('copyFile'); + var copyFileSpy = jasmine.createSpy('copyFile'); + + beforeEach(function () { + PluginHandler.__set__('copyFile', copyFileSpy.andCallFake(copyFileOrig)); + }); + + afterEach(function() { + PluginHandler.__set__('copyFile', copyFileOrig); + }); + + function validateInstalledProjects(tag, elementToInstall, xpath, supportedPlatforms) { + jasmine.getEnv().currentSpec.removeAllSpies(); + + var projects = copyArray(dummyProject.projects); + projects.push(dummyProject.master); + + // Check that installed framework reference is properly added to project. + var checkInstalledFrameworkReference = function (tag, elementToInstall, xml) { + var frameworkCustomPathElement = xml.find(xpath); + expect(frameworkCustomPathElement).not.toBe(null); + var frameworkCustomPath = frameworkCustomPathElement.text; + expect(frameworkCustomPath).not.toBe(null); + var targetDir = elementToInstall.targetDir || ''; + var frameworkCustomExpectedPath = path.join('plugins', dummyPluginInfo.id, targetDir, + path.basename(elementToInstall.src)); + expect(frameworkCustomPath).toEqual(frameworkCustomExpectedPath); + }; + + // Check that framework file was copied to correct path + var checkInstalledFrameworkPath = function (framework) { + var targetDir = framework.targetDir || ''; + var dest = path.join(cordovaProjectWindowsPlatformDir, 'plugins', dummyPluginInfo.id, targetDir, path.basename(framework.src)); + var copiedSuccessfully = fs.existsSync(path.resolve(dest)); + expect(copiedSuccessfully).toBe(true); + }; + + var appendToRootFake = function (itemGroup) { + // In case we install framework with 'custom' attribute set to 'true' + // we verify that file is copied to correct dir and reference is added properly. + // This is not required in case of 'projectReference' attribute is used. + if (tag === 'framework' && elementToInstall.type !== 'projectReference') { + checkInstalledFrameworkReference(tag, elementToInstall, itemGroup); + checkInstalledFrameworkPath(elementToInstall); + return; + } + + expect(itemGroup).toContainXmlPath(xpath); + }; + + var projectsAddedToSpies = []; + var projectsNotAddedToSpies = []; + + var projectsAddedTo = []; + supportedPlatforms.forEach(function (platform) { + var platformProject = PLATFORM_PROJECTS[platform]; + if (platformProject) { + projectsAddedTo.push(PLATFORM_PROJECTS[platform]); + } + }); + + projects.forEach(function (project) { + if (projectsAddedTo.indexOf(path.basename(project.location)) > -1) { + projectsAddedToSpies.push(spyOn(project, 'appendToRoot').andCallFake(appendToRootFake)); + } else { + projectsNotAddedToSpies.push(spyOn(project, 'appendToRoot')); + } + }); + + PluginHandler.getInstaller(tag)(elementToInstall, dummyPluginInfo, dummyProject); + + projectsAddedToSpies.forEach(function (spy) { + expect(spy).toHaveBeenCalled(); + }); + + projectsNotAddedToSpies.forEach(function (spy) { + expect(spy).not.toHaveBeenCalled(); + }); + } + + describe('of <source-file> elements', function () { + + var install = PluginHandler.getInstaller('source-file'); + + it('should copy stuff from one location to another by calling common.copyFile', function () { + var source = copyArray(valid_source); + install(source[0], dummyPluginInfo, dummyProject); + expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin, 'src/windows/dummer.js', cordovaProjectWindowsPlatformDir, path.join('plugins', 'org.test.plugins.dummyplugin', 'dummer.js'), false); + }); + it('should throw if source-file src cannot be found', function () { + var source = copyArray(invalid_source); + copyFileSpy.andCallFake(copyFileOrig); + expect(function () { + install(source[1], faultyPluginInfo, dummyProject); + }).toThrow('"' + path.resolve(faultyplugin, 'src/windows/NotHere.js') + '" not found!'); + }); + it('should throw if source-file target already exists', function () { + var source = copyArray(valid_source); + var target = path.join(cordovaProjectWindowsPlatformDir, 'plugins', dummyPluginInfo.id, 'dummer.js'); + shell.mkdir('-p', path.dirname(target)); + fs.writeFileSync(target, 'some bs', 'utf-8'); + expect(function () { + install(source[0], dummyPluginInfo, dummyProject); + }).toThrow('"' + target + '" already exists!'); + }); + }); + + describe('of <resource-file> elements', function () { + var resourceFiles = copyArray(valid_resourceFiles); + var invalidResourceFiles = copyArray(invalid_resourceFiles); + var install = PluginHandler.getInstaller('resource-file'); + + // This could be separated into individual specs, but that results in a lot of copying and deleting the + // project files, which is not needed. + it('should write to correct project files when conditions are specified', function () { + + var xpath = 'Content[@Include="' + computeResourcePath(resourceFiles[0]) + '"][@Condition="\'$(Platform)\'==\'x86\'"]'; + validateInstalledProjects('resource-file', resourceFiles[0], xpath, ['all']); + + xpath = 'Content[@Include="' + computeResourcePath(resourceFiles[1]) + '"]'; + validateInstalledProjects('resource-file', resourceFiles[1], xpath, ['windows', 'phone', 'windows10']); + + xpath = 'Content[@Include="' + computeResourcePath(resourceFiles[2]) + '"]'; + validateInstalledProjects('resource-file', resourceFiles[2], xpath, ['phone']); + + xpath = 'Content[@Include="' + computeResourcePath(resourceFiles[3]) + '"][@Condition="\'$(Platform)\'==\'x64\'"]'; + validateInstalledProjects('resource-file', resourceFiles[3], xpath, ['windows8']); + }); + + it('should throw if conditions are invalid', function () { + expect(function () { + install(invalidResourceFiles[0], faultyPluginInfo, dummyProject); + }).toThrow('Invalid arch attribute (must be "x86", "x64" or "ARM"): x85'); + + expect(function () { + install(invalidResourceFiles[1], faultyPluginInfo, dummyProject); + }).toThrow('Invalid versions attribute (must be a valid semantic version range): 8.0a'); + + expect(function () { + install(invalidResourceFiles[2], faultyPluginInfo, dummyProject); + }).toThrow('Invalid device-target attribute (must be "all", "phone", "windows" or "win"): daphne'); + }); + }); + + describe('of <lib-file> elements', function () { + var libfiles = copyArray(valid_libfiles); + var invalidLibFiles = copyArray(invalid_libfiles); + var install = PluginHandler.getInstaller('lib-file'); + + // This could be separated into individual specs, but that results in a lot of copying and deleting the + // project files, which is not needed. + it('should write to correct project files when conditions are specified', function () { + var xpath = 'SDKReference[@Include="TestSDK1, Version=1.0"][@Condition="\'$(Platform)\'==\'x86\'"]'; + validateInstalledProjects('lib-file', libfiles[0], xpath, ['all']); + + xpath = 'SDKReference[@Include="TestSDK2, Version=1.0"]'; + validateInstalledProjects('lib-file', libfiles[1], xpath, ['windows', 'phone', 'windows10']); + + xpath = 'SDKReference[@Include="TestSDK3, Version=1.0"]'; + validateInstalledProjects('lib-file', libfiles[2], xpath, ['phone']); + + xpath = 'SDKReference[@Include="TestSDK4, Version=1.0"]'; + validateInstalledProjects('lib-file', libfiles[3], xpath, ['windows8']); + }); + + it('should throw if conditions are invalid', function () { + expect(function () { + install(invalidLibFiles[0], faultyPluginInfo, dummyProject); + }).toThrow('Invalid arch attribute (must be "x86", "x64" or "ARM"): x85'); + + expect(function () { + install(invalidLibFiles[1], faultyPluginInfo, dummyProject); + }).toThrow('Invalid versions attribute (must be a valid semantic version range): 8.0a'); + + expect(function () { + install(invalidLibFiles[2], faultyPluginInfo, dummyProject); + }).toThrow('Invalid device-target attribute (must be "all", "phone", "windows" or "win"): daphne'); + }); + }); + + describe('of <framework> elements', function () { + var frameworks = copyArray(valid_frameworks); + + // This could be separated into individual specs, but that results in a lot of copying and deleting the + // project files, which is not needed. + it('should write to correct project files when conditions are specified', function () { + var xpath = 'Reference[@Include="dummy1"][@Condition="\'$(Platform)\'==\'x64\'"]/HintPath'; + validateInstalledProjects('framework', frameworks[0], xpath, ['all']); + + xpath = 'Reference[@Include="dummy2"]/HintPath'; + validateInstalledProjects('framework', frameworks[1], xpath, ['all']); + + xpath = 'Reference[@Include="dummy3"]/HintPath'; + validateInstalledProjects('framework', frameworks[2], xpath, ['windows', 'windows8', 'windows10']); + + xpath = 'Reference[@Include="dummy4"][@Condition="\'$(Platform)\'==\'ARM\'"]/HintPath'; + validateInstalledProjects('framework', frameworks[3], xpath, ['phone']); + + xpath = 'Reference[@Include="dummy5"]/HintPath'; + validateInstalledProjects('framework', frameworks[4], xpath, ['phone']); + + xpath = 'Reference[@Include="dummy6"]/HintPath'; + validateInstalledProjects('framework', frameworks[5], xpath, ['windows', 'windows10', 'phone']); + }); + }); + + describe('of <framework> elements of type \'projectReference\'', function () { + var frameworks = copyArray(valid_frameworks); + + it('should write to correct project files when conditions are specified', function () { + var xpath = 'ProjectReference[@Include="' + winJoin(dummyplugin, 'src', 'windows', 'dummy1.vcxproj') + '"][@Condition="\'$(Platform)\'==\'x64\'"]'; + validateInstalledProjects('framework', frameworks[6], xpath, ['all']); + + xpath = 'ProjectReference[@Include="' + winJoin(dummyplugin, 'src', 'windows', 'dummy2.vcxproj') + '"]'; + validateInstalledProjects('framework', frameworks[7], xpath, ['windows8']); + + xpath = 'ProjectReference[@Include="' + winJoin(dummyplugin, 'src', 'windows', 'dummy3.vcxproj') + '"]'; + validateInstalledProjects('framework', frameworks[8], xpath, ['windows', 'windows8', 'windows10']); + + xpath = 'ProjectReference[@Include="' + winJoin(dummyplugin, 'src', 'windows', 'dummy4.vcxproj') + '"][@Condition="\'$(Platform)\'==\'x86\'"]'; + validateInstalledProjects('framework', frameworks[9], xpath, ['windows', 'phone']); + }); + }); + }); + + describe('uninstallation', function () { + var removeFileOrig = PluginHandler.__get__('removeFile'); + var removeFileSpy = jasmine.createSpy('removeFile'); + + beforeEach(function () { + PluginHandler.__set__('removeFile', removeFileSpy.andCallFake(removeFileOrig)); + }); + + afterEach(function () { + PluginHandler.__set__('removeFile', removeFileOrig); + }); + + function validateUninstalledProjects(tag, elementToUninstall, xmlPath, incText, targetConditions, supportedPlatforms) { + jasmine.getEnv().currentSpec.removeAllSpies(); + + var projects = copyArray(dummyProject.projects); + projects.push(dummyProject.master); + + var projectsAddedToSpies = []; + var projectsNotAddedToSpies = []; + + var projectsAddedTo = []; + supportedPlatforms.forEach(function (platform) { + var platformProject = PLATFORM_PROJECTS[platform]; + if (platformProject) { + projectsAddedTo.push(PLATFORM_PROJECTS[platform]); + } + }); + + projects.forEach(function (project) { + var spy = spyOn(project, 'removeItemGroupElement'); + if (projectsAddedTo.indexOf(path.basename(project.location)) > -1) { + projectsAddedToSpies.push(spy); + } else { + projectsNotAddedToSpies.push(spy); + } + }); + + PluginHandler.getUninstaller(tag)(elementToUninstall, dummyPluginInfo, dummyProject); + + projectsAddedToSpies.forEach(function (spy) { + expect(spy).toHaveBeenCalledWith(xmlPath, incText, targetConditions); + }); + + projectsNotAddedToSpies.forEach(function (spy) { + expect(spy).not.toHaveBeenCalled(); + }); + } + + describe('of <source-file> elements', function () { + var install = PluginHandler.getInstaller('source-file'); + var uninstall = PluginHandler.getUninstaller('source-file'); + + it('should remove stuff by calling common.removeFile', function () { + var source = copyArray(valid_source); + install(source[0], dummyPluginInfo, dummyProject); + uninstall(source[0], dummyPluginInfo, dummyProject); + expect(removeFileSpy).toHaveBeenCalledWith(cordovaProjectWindowsPlatformDir, path.join('plugins', 'org.test.plugins.dummyplugin', 'dummer.js')); + }); + }); + + describe('of <resource-file> elements', function () { + // This could be separated into individual specs, but that results in a lot of copying and deleting the + // project files, which is not needed. + var install = PluginHandler.getInstaller('resource-file'); + + it('should remove from correct project files when conditions specified', function () { + var resourcefiles = copyArray(valid_resourceFiles); + + resourcefiles.forEach(function(resourceFile) { + install(resourceFile, dummyPluginInfo, dummyProject); + }); + + var path = 'ItemGroup/Content'; + var incText = computeResourcePath(resourcefiles[0]); + var targetConditions = {versions: undefined, deviceTarget: undefined, arch: 'x86'}; + validateUninstalledProjects('resource-file', resourcefiles[0], path, incText, targetConditions, ['all']); + + incText = computeResourcePath(resourcefiles[1]); + targetConditions = {versions: '>=8.1', deviceTarget: undefined, arch: undefined}; + validateUninstalledProjects('resource-file', resourcefiles[1], path, incText, targetConditions, ['windows', 'phone', 'windows10']); + + incText = computeResourcePath(resourcefiles[2]); + targetConditions = {versions: undefined, deviceTarget: 'phone', arch: undefined}; + validateUninstalledProjects('resource-file', resourcefiles[2], path, incText, targetConditions, ['phone']); + + incText = computeResourcePath(resourcefiles[3]); + targetConditions = {versions: '8.0', deviceTarget: 'windows', arch: 'x64'}; + validateUninstalledProjects('resource-file', resourcefiles[3], path, incText, targetConditions, ['windows8']); + }); + }); + + describe('of <lib-file> elements', function () { + // This could be separated into individual specs, but that results in a lot of copying and deleting the + // project files, which is not needed. + it('should remove from correct project files when conditions specified', function () { + var libfiles = copyArray(valid_libfiles); + + libfiles.forEach(function(libfile) { + PluginHandler.getInstaller('lib-file')(libfile, dummyPluginInfo, dummyProject); + }); + + var path = 'ItemGroup/SDKReference'; + var incText = 'TestSDK1, Version=1.0'; + var targetConditions = {versions: undefined, deviceTarget: undefined, arch: 'x86'}; + validateUninstalledProjects('lib-file', libfiles[0], path, incText, targetConditions, ['all']); + + incText = 'TestSDK2, Version=1.0'; + targetConditions = {versions: '>=8.1', deviceTarget: undefined, arch: undefined}; + validateUninstalledProjects('lib-file', libfiles[1], path, incText, targetConditions, ['windows', 'phone', 'windows10']); + + incText = 'TestSDK3, Version=1.0'; + targetConditions = {versions: undefined, deviceTarget: 'phone', arch: undefined}; + validateUninstalledProjects('lib-file', libfiles[2], path, incText, targetConditions, ['phone']); + + incText = 'TestSDK4, Version=1.0'; + targetConditions = {versions: '8.0', deviceTarget: 'windows', arch: 'x86'}; + validateUninstalledProjects('lib-file', libfiles[3], path, incText, targetConditions, ['windows8']); + }); + }); + + describe('of <framework> elements', function () { + // This could be separated into individual specs, but that results in a lot of copying and deleting the + // project files, which is not needed. + it('should remove from correct project files when conditions specified', function () { + var frameworks = copyArray(valid_frameworks); + + frameworks.forEach(function(framework) { + PluginHandler.getInstaller('framework')(framework, dummyPluginInfo, dummyProject); + }); + + var path = 'ItemGroup/Reference'; + var incText = 'dummy1'; + var targetConditions = {versions: undefined, deviceTarget: undefined, arch: 'x64'}; + validateUninstalledProjects('framework', frameworks[0], path, incText, targetConditions, ['all']); + + incText = 'dummy2'; + targetConditions = {versions: '>=8.0', deviceTarget: undefined, arch: undefined}; + validateUninstalledProjects('framework', frameworks[1], path, incText, targetConditions, ['all']); + + incText = 'dummy3'; + targetConditions = {versions: undefined, deviceTarget: 'windows', arch: undefined}; + validateUninstalledProjects('framework', frameworks[2], path, incText, targetConditions, ['windows', 'windows8', 'windows10']); + + incText = 'dummy4'; + targetConditions = {versions: '8.1', deviceTarget: 'phone', arch: 'ARM'}; + validateUninstalledProjects('framework', frameworks[3], path, incText, targetConditions, ['phone']); + + incText = 'dummy5'; + targetConditions = {versions: undefined, deviceTarget: 'phone', arch: undefined}; + validateUninstalledProjects('framework', frameworks[4], path, incText, targetConditions, ['phone']); + + incText = 'dummy6'; + targetConditions = {versions: '>=8.1', deviceTarget: undefined, arch: undefined}; + validateUninstalledProjects('framework', frameworks[5], path, incText, targetConditions, ['windows', 'windows10', 'phone']); + }); + }); + + describe('of <framework> elements of type \'projectReference\'', function () { + // This could be separated into individual specs, but that results in a lot of copying and deleting the + // project files, which is not needed. + it('should remove from correct project files when conditions specified', function () { + var frameworks = copyArray(valid_frameworks); + + frameworks.forEach(function(framework) { + PluginHandler.getInstaller('framework')(framework, dummyPluginInfo, dummyProject); + }); + + var xmlPath = 'ItemGroup/ProjectReference'; + var incText = winJoin(dummyPluginInfo.dir, frameworks[6].src); + var targetConditions = {versions: undefined, deviceTarget: undefined, arch: 'x64'}; + validateUninstalledProjects('framework', frameworks[6], xmlPath, incText, targetConditions, ['all']); + + incText = winJoin(dummyPluginInfo.dir, frameworks[7].src); + targetConditions = {versions: '<8.1', deviceTarget: undefined, arch: undefined}; + validateUninstalledProjects('framework', frameworks[7], xmlPath, incText, targetConditions, ['windows8']); + + incText = winJoin(dummyPluginInfo.dir, frameworks[8].src); + targetConditions = {versions: undefined, deviceTarget: 'win', arch: undefined}; + validateUninstalledProjects('framework', frameworks[8], xmlPath, incText, targetConditions, ['windows', 'windows8', 'windows10']); + + incText = winJoin(dummyPluginInfo.dir, frameworks[9].src); + targetConditions = {versions: '8.1', deviceTarget: 'all', arch: 'x86'}; + validateUninstalledProjects('framework', frameworks[9], xmlPath, incText, targetConditions, ['windows', 'phone']); + }); + }); + }); +}); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
