| /** |
| 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('node:fs'); |
| const os = require('node:os'); |
| const path = require('node:path'); |
| const et = require('elementtree'); |
| |
| const configChanges = require('../../src/ConfigChanges/ConfigChanges'); |
| const mungeutil = require('../../src/ConfigChanges/munge-util'); |
| const xml_helpers = require('../../src/util/xml-helpers'); |
| const PlatformJson = require('../../src/PlatformJson'); |
| const PluginInfoProvider = require('../../src/PluginInfo/PluginInfoProvider'); |
| const PluginInfo = require('../../src/PluginInfo/PluginInfo'); |
| const ConfigParser = require('../../src/ConfigParser/ConfigParser'); |
| |
| const fixturePath = path.join(__dirname, '../fixtures'); |
| |
| // XML fixtures |
| const xml = path.join(fixturePath, 'test-config.xml'); |
| const editconfig_xml = path.join(fixturePath, 'test-editconfig.xml'); |
| const configfile_xml = path.join(fixturePath, 'test-configfile.xml'); |
| |
| // Project fixtures |
| const android_two_project = path.join(fixturePath, 'projects/android_two/'); |
| const android_two_no_perms_project = path.join(fixturePath, 'projects/android_two_no_perms'); |
| const ios_config_xml = path.join(fixturePath, 'projects/ios-config-xml/'); |
| |
| // Plugin fixtures |
| const dummyplugin = path.join(fixturePath, 'plugins/org.test.plugins.dummyplugin'); |
| const cbplugin = path.join(fixturePath, 'plugins/org.test.plugins.childbrowser'); |
| const childrenplugin = path.join(fixturePath, 'plugins/org.test.multiple-children'); |
| const shareddepsplugin = path.join(fixturePath, 'plugins/org.test.shareddeps'); |
| const configplugin = path.join(fixturePath, 'plugins/org.test.configtest'); |
| const editconfigplugin = path.join(fixturePath, 'plugins/org.test.editconfigtest'); |
| const editconfigplugin_two = path.join(fixturePath, 'plugins/org.test.editconfigtest_two'); |
| const varplugin = path.join(fixturePath, 'plugins/com.adobe.vars'); |
| const plistplugin = path.join(fixturePath, 'plugins/org.apache.plist'); |
| const bplistplugin = path.join(fixturePath, 'plugins/org.apache.bplist'); |
| |
| const temp = path.join(os.tmpdir(), 'plugman'); |
| const plugins_dir = path.join(temp, 'cordova', 'plugins'); |
| |
| const cfg = new ConfigParser(xml); |
| const pluginInfoProvider = new PluginInfoProvider(); |
| |
| // TODO: dont do fs so much |
| |
| function innerXML (xmltext) { |
| return xmltext.replace(/^<[\w\s\-=/".]+>/, '').replace(/<\/[\w\s\-=/".]+>$/, ''); |
| } |
| |
| function get_munge_change () { |
| return mungeutil.deep_find.apply(null, arguments); |
| } |
| |
| function install_plugin (pluginPath) { |
| fs.cpSync(pluginPath, path.join(plugins_dir, path.basename(pluginPath)), { recursive: true }); |
| } |
| |
| describe('config-changes module', function () { |
| beforeEach(function () { |
| fs.mkdirSync(temp, { recursive: true }); |
| fs.mkdirSync(plugins_dir, { recursive: true }); |
| }); |
| afterEach(function () { |
| fs.rmSync(temp, { recursive: true, force: true }); |
| }); |
| |
| describe('queue methods', function () { |
| describe('addInstalledPluginToPrepareQueue', function () { |
| it('Test 001 : should append specified plugin to platform.json', function () { |
| const platformJson = new PlatformJson(null, 'android', null); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {}); |
| |
| const json = platformJson.root; |
| expect(json.prepare_queue.installed[0].plugin).toEqual('org.test.plugins.dummyplugin'); |
| expect(json.prepare_queue.installed[0].vars).toEqual({}); |
| }); |
| |
| it('Test 002 : should append specified plugin with any variables to platform.json', function () { |
| const platformJson = new PlatformJson(null, 'android', null); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', { dude: 'man' }); |
| |
| const json = platformJson.root; |
| expect(json.prepare_queue.installed[0].plugin).toEqual('org.test.plugins.dummyplugin'); |
| expect(json.prepare_queue.installed[0].vars).toEqual({ dude: 'man' }); |
| }); |
| }); |
| |
| describe('addUninstalledPluginToPrepareQueue', function () { |
| it('Test 003 : should append specified plugin to platform.json', function () { |
| const platformJson = new PlatformJson(null, 'android', null); |
| platformJson.addUninstalledPluginToPrepareQueue('org.test.plugins.dummyplugin'); |
| |
| const json = platformJson.root; |
| expect(json.prepare_queue.uninstalled[0].plugin).toEqual('org.test.plugins.dummyplugin'); |
| }); |
| }); |
| }); |
| |
| describe('load method', function () { |
| it('Test 004 : should return an empty config json object if file doesn\'t exist', function () { |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| expect(platformJson.root).toBeDefined(); |
| expect(platformJson.root.prepare_queue).toBeDefined(); |
| expect(platformJson.root.config_munge).toBeDefined(); |
| expect(platformJson.root.installed_plugins).toBeDefined(); |
| }); |
| |
| it('Test 005 : should return the json file if it exists', function () { |
| const filepath = path.join(plugins_dir, 'android.json'); |
| const json = { |
| prepare_queue: { installed: [], uninstalled: [] }, |
| config_munge: { files: { some_file: { parents: { some_parent: [{ xml: 'some_change', count: 1 }] } } } }, |
| installed_plugins: {}, |
| dependent_plugins: {} |
| }; |
| fs.writeFileSync(filepath, JSON.stringify(json), 'utf8'); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| expect(JSON.stringify(json)).toEqual(JSON.stringify(platformJson.root)); |
| }); |
| }); |
| |
| describe('save method', function () { |
| it('Test 006 : should write out specified json', function () { |
| const filepath = path.join(plugins_dir, 'android.json'); |
| const platformJson = new PlatformJson(filepath, 'android', { foo: true }); |
| platformJson.save(); |
| expect(JSON.parse(fs.readFileSync(filepath, 'utf8'))).toEqual(platformJson.root); |
| }); |
| }); |
| |
| describe('generate_plugin_config_munge method', function () { |
| describe('for android projects', function () { |
| beforeEach(function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| }); |
| |
| it('Test 007 : should return a flat config hierarchy for simple, one-off config changes', function () { |
| let xml; |
| const dummy_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(dummyplugin, 'plugin.xml'), 'utf8'))); |
| const munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider); |
| const munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(dummyplugin), {}); |
| expect(munge.files['AndroidManifest.xml']).toBeDefined(); |
| expect(munge.files['AndroidManifest.xml'].parents['/manifest/application']).toBeDefined(); |
| xml = (new et.ElementTree(dummy_xml.find('./platform[@name="android"]/config-file[@target="AndroidManifest.xml"]'))).write({ xml_declaration: false }); |
| xml = innerXML(xml); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest/application', xml).count).toEqual(1); |
| expect(munge.files['res/xml/plugins.xml']).toBeDefined(); |
| expect(munge.files['res/xml/plugins.xml'].parents['/plugins']).toBeDefined(); |
| xml = (new et.ElementTree(dummy_xml.find('./platform[@name="android"]/config-file[@target="res/xml/plugins.xml"]'))).write({ xml_declaration: false }); |
| xml = innerXML(xml); |
| expect(get_munge_change(munge, 'res/xml/plugins.xml', '/plugins', xml).count).toEqual(1); |
| expect(munge.files['res/xml/config.xml']).toBeDefined(); |
| expect(munge.files['res/xml/config.xml'].parents['/cordova/plugins']).toBeDefined(); |
| xml = (new et.ElementTree(dummy_xml.find('./platform[@name="android"]/config-file[@target="res/xml/config.xml"]'))).write({ xml_declaration: false }); |
| xml = innerXML(xml); |
| expect(get_munge_change(munge, 'res/xml/config.xml', '/cordova/plugins', xml).count).toEqual(1); |
| }); |
| |
| it('Test 008 : should split out multiple children of config-file elements into individual leaves', function () { |
| const munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider); |
| const munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(childrenplugin), { PACKAGE_NAME: 'com.alunny.childapp' }); |
| expect(munge.files['AndroidManifest.xml']).toBeDefined(); |
| expect(munge.files['AndroidManifest.xml'].parents['/manifest']).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.READ_PHONE_STATE" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.INTERNET" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.GET_ACCOUNTS" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.WAKE_LOCK" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<permission android:name="com.alunny.childapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="com.alunny.childapp.permission.C2D_MESSAGE" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />')).toBeDefined(); |
| }); |
| |
| it('Test 009 : should not use xml comments as config munge leaves', function () { |
| const munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider); |
| const munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(childrenplugin), {}); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<!--library-->')).not.toBeDefined(); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<!-- GCM connects to Google Services. -->')).not.toBeDefined(); |
| }); |
| |
| it('Test 010 : should increment config hierarchy leaves if different config-file elements target the same file + selector + xml', function () { |
| const munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider); |
| const munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(configplugin), {}); |
| expect(get_munge_change(munge, 'res/xml/config.xml', '/widget', '<poop />').count).toEqual(2); |
| }); |
| |
| it('Test 011 : should take into account interpolation variables', function () { |
| const munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider); |
| const munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(childrenplugin), { PACKAGE_NAME: 'ca.filmaj.plugins' }); |
| expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="ca.filmaj.plugins.permission.C2D_MESSAGE" />')).toBeDefined(); |
| }); |
| |
| it('Test 012 : should create munges for platform-agnostic config.xml changes', function () { |
| const munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider); |
| const munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(dummyplugin), {}); |
| expect(get_munge_change(munge, 'config.xml', '/*', '<access origin="build.phonegap.com" />')).toBeDefined(); |
| expect(get_munge_change(munge, 'config.xml', '/*', '<access origin="s3.amazonaws.com" />')).toBeDefined(); |
| }); |
| }); |
| }); |
| |
| describe('processing of plugins (via process method)', function () { |
| beforeEach(function () { |
| install_plugin(dummyplugin); |
| }); |
| |
| it('Test 014 : should generate config munges for queued plugins', function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.root.prepare_queue.installed = [{ plugin: 'org.test.plugins.dummyplugin', vars: {} }]; |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| const spy = spyOn(munger, 'generate_plugin_config_munge').and.returnValue({}); |
| munger.process(plugins_dir); |
| expect(spy).toHaveBeenCalledWith(jasmine.any(PluginInfo), {}, []); |
| }); |
| |
| describe(': installation', function () { |
| describe('of xml config files', function () { |
| beforeEach(function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| }); |
| |
| it('Test 015 : should call graftXML for every new config munge it introduces (every leaf in config munge that does not exist)', function () { |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.root.prepare_queue.installed = [{ plugin: 'org.test.plugins.dummyplugin', vars: {} }]; |
| |
| const spy = spyOn(xml_helpers, 'graftXML').and.returnValue(true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| expect(spy.calls.count()).toEqual(4); |
| expect(spy.calls.argsFor(0)[2]).toEqual('/*'); |
| expect(spy.calls.argsFor(1)[2]).toEqual('/*'); |
| expect(spy.calls.argsFor(2)[2]).toEqual('/manifest/application'); |
| expect(spy.calls.argsFor(3)[2]).toEqual('/cordova/plugins'); |
| }); |
| |
| it('Test 016 : should not call graftXML for a config munge that already exists from another plugin', function () { |
| install_plugin(configplugin); |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.configtest', {}); |
| |
| const spy = spyOn(xml_helpers, 'graftXML').and.returnValue(true); |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| expect(spy.calls.count()).toEqual(1); |
| }); |
| |
| it('Test 017 : should not call graftXML for a config munge targeting a config file that does not exist', function () { |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {}); |
| |
| const spy = spyOn(fs, 'readFileSync').and.callThrough(); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'), 'utf-8'); |
| }); |
| |
| it('Test 018 : should call graftXMLMerge for every new config munge with mode \'merge\' it introduces', function () { |
| install_plugin(editconfigplugin); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {}); |
| |
| const spy = spyOn(xml_helpers, 'graftXMLMerge').and.returnValue(true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| expect(spy.calls.count()).toEqual(1); |
| expect(spy.calls.argsFor(0)[2]).toEqual('/manifest/application/activity[@android:name=\'org.test.DroidGap\']'); |
| }); |
| |
| it('Test 019 : should call graftXMLMerge with --force for every new config munge with mode \'merge\' it introduces', function () { |
| install_plugin(editconfigplugin); |
| install_plugin(editconfigplugin_two); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {}); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}, true, true); |
| |
| const spy = spyOn(xml_helpers, 'graftXMLMerge').and.returnValue(true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| expect(spy.calls.count()).toEqual(3); |
| expect(spy.calls.argsFor(0)[2]).toEqual('/manifest/application/activity[@android:name=\'org.test.DroidGap\']'); |
| expect(spy.calls.argsFor(1)[2]).toEqual('/manifest/application/activity[@android:name=\'org.test.DroidGap\']'); |
| expect(spy.calls.argsFor(2)[2]).toEqual('/manifest/uses-sdk'); |
| }); |
| |
| it('Test 020 : should call graftXMLOverwrite for every new config munge with mode \'overwrite\' it introduces', function () { |
| install_plugin(editconfigplugin); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {}); |
| |
| const spy = spyOn(xml_helpers, 'graftXMLOverwrite').and.returnValue(true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| expect(spy.calls.count()).toEqual(1); |
| expect(spy.calls.argsFor(0)[2]).toEqual('/manifest/application/activity'); |
| }); |
| |
| it('Test 021 : should call graftXMLOverwrite with --force for every new config munge with mode \'overwrite\' it introduces', function () { |
| install_plugin(editconfigplugin); |
| install_plugin(editconfigplugin_two); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {}); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}, true, true); |
| |
| const spy = spyOn(xml_helpers, 'graftXMLOverwrite').and.returnValue(true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| expect(spy.calls.count()).toEqual(2); |
| expect(spy.calls.argsFor(0)[2]).toEqual('/manifest/application/activity'); |
| expect(spy.calls.argsFor(1)[2]).toEqual('/manifest/application/activity[@android:name=\'ChildApp\']'); |
| }); |
| |
| it('Test 022 : should not install plugin when there are edit-config conflicts', function () { |
| install_plugin(editconfigplugin); |
| install_plugin(editconfigplugin_two); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {}); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| expect(function () { munger.process(plugins_dir); }).toThrow(new Error('There was a conflict trying to modify attributes with <edit-config> in plugin org.test.editconfigtest_two. The conflicting plugin, org.test.editconfigtest, already modified the same attributes. The conflict must be resolved before org.test.editconfigtest_two can be added. You may use --force to add the plugin and overwrite the conflicting attributes.')); |
| }); |
| |
| it('should call graftXMLMerge for every new config.xml config munge with mode \'merge\' it introduces', function () { |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| |
| const spy = spyOn(xml_helpers, 'graftXMLMerge').and.returnValue(true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson); |
| munger.add_config_changes(cfg, true); |
| |
| expect(spy.calls.count()).toEqual(1); |
| expect(spy.calls.argsFor(0)[2]).toEqual('/manifest/uses-sdk'); |
| }); |
| |
| it('should call graftXMLOverwrite for every new config.xml config munge with mode \'overwrite\' it introduces', function () { |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| |
| const spy = spyOn(xml_helpers, 'graftXMLOverwrite').and.returnValue(true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson); |
| munger.add_config_changes(cfg, true); |
| |
| expect(spy.calls.count()).toEqual(1); |
| expect(spy.calls.argsFor(0)[2]).toEqual('/manifest/uses-sdk'); |
| }); |
| |
| it('should call pruneXMLRemove for every new config.xml config munge with mode \'remove\' it introduces', function () { |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| |
| // var spy = spyOn(xml_helpers, 'pruneXMLRemove').andReturn(true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson); |
| munger.add_config_changes(cfg, true).save_all(); |
| |
| const am_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf8'))); |
| const sdk = am_xml.find('./uses-sdk'); |
| |
| expect(sdk).toBeDefined(); |
| expect(sdk.attrib['android:maxSdkVersion']).toBeUndefined(); |
| }); |
| |
| it('should overwrite plugin config munge for every conflicting config.xml config munge', function () { |
| install_plugin(editconfigplugin_two); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}, true, true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| munger.add_config_changes(cfg, true).save_all(); |
| |
| const am_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf8'))); |
| const sdk = am_xml.find('./uses-sdk'); |
| expect(sdk).toBeDefined(); |
| expect(sdk.attrib['android:targetSdkVersion']).toEqual('24'); |
| }); |
| |
| it('should overwrite config.xml config munge for every new config.xml config munge that has the same target', function () { |
| const editconfig_cfg = new ConfigParser(editconfig_xml); |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.add_config_changes(cfg, true).save_all(); |
| munger.add_config_changes(editconfig_cfg, true).save_all(); |
| |
| const am_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf8'))); |
| const sdk = am_xml.find('./uses-sdk'); |
| expect(sdk).toBeDefined(); |
| expect(sdk.attrib['android:targetSdkVersion']).toEqual('23'); |
| expect(sdk.attrib['android:minSdkVersion']).toEqual('5'); |
| expect(sdk.attrib['android:maxSdkVersion']).toBeUndefined(); |
| }); |
| |
| it('should append new children to XML document tree', function () { |
| const configfile_cfg = new ConfigParser(configfile_xml); |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.add_config_changes(configfile_cfg, true).save_all(); |
| const am_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf8'))); |
| const activity = am_xml.find('./application/activity[@android:name="com.foo.Bar"]'); |
| expect(activity).toBeDefined(); |
| expect(activity.attrib['android:label']).toEqual('@string/app_name'); |
| }); |
| |
| // testing the "after" attribute of the <config-file> tag in config.xml |
| it('should append new children to XML document tree in the correct order', function () { |
| const configfile_cfg = new ConfigParser(configfile_xml); |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.add_config_changes(configfile_cfg, true).save_all(); |
| const am_file = fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf8'); |
| expect(am_file.indexOf('android:name="zoo"')).toBeLessThan(am_file.indexOf('android:name="com.foo.Bar"')); |
| }); |
| |
| it('should throw error for conflicting plugin config munge with config.xml config munge', function () { |
| install_plugin(editconfigplugin_two); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}, true, true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.add_config_changes(cfg, true); |
| expect(function () { munger.process(plugins_dir); }).toThrow(new Error('org.test.editconfigtest_two cannot be added. <edit-config> changes in this plugin conflicts with <edit-config> changes in config.xml. Conflicts must be resolved before plugin can be added.')); |
| }); |
| }); |
| |
| describe('of plist config files', function () { |
| it('Test 023 : should write empty string nodes with no whitespace', function () { |
| fs.cpSync(ios_config_xml, temp, { recursive: true }); |
| install_plugin(varplugin); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'ios'); |
| platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {}); |
| configChanges.process(plugins_dir, temp, 'ios', platformJson, pluginInfoProvider); |
| expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf8')).toMatch(/<key>APluginNode<\/key>\n\t<string\/>/m); |
| }); |
| |
| it('Test 024 : should merge dictionaries and arrays, removing duplicates', function () { |
| fs.cpSync(ios_config_xml, temp, { recursive: true }); |
| install_plugin(plistplugin); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'ios'); |
| platformJson.addInstalledPluginToPrepareQueue('org.apache.plist', {}); |
| configChanges.process(plugins_dir, temp, 'ios', platformJson, pluginInfoProvider); |
| expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf8')).toMatch(/<key>UINewsstandIcon<\/key>[\s\S]*<key>CFBundlePrimaryIcon<\/key>/); |
| expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf8')).toMatch(/<string>schema-b<\/string>/); |
| expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf8')).not.toMatch(/(<string>schema-a<\/string>[^]*){2,}/); |
| }); |
| }); |
| |
| describe('of binary plist config files', function () { |
| it('should merge dictionaries and arrays, removing duplicates', function () { |
| fs.cpSync(ios_config_xml, temp, { recursive: true }); |
| install_plugin(bplistplugin); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'ios'); |
| platformJson.addInstalledPluginToPrepareQueue('org.apache.bplist', {}); |
| configChanges.process(plugins_dir, temp, 'ios', platformJson, pluginInfoProvider); |
| const edited_plist = fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-binary.plist'), 'utf8'); |
| expect(edited_plist).toMatch(/<key>UINewsstandIcon<\/key>[\s\S]*<key>CFBundlePrimaryIcon<\/key>/); |
| expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-binary.plist'), 'utf8')).toMatch(/<string>schema-b<\/string>/); |
| expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-binary.plist'), 'utf8')).not.toMatch(/(<string>schema-a<\/string>[^]*){2,}/); |
| }); |
| }); |
| |
| it('Test 025 : should resolve wildcard config-file targets to the project, if applicable', function () { |
| fs.cpSync(ios_config_xml, temp, { recursive: true }); |
| install_plugin(cbplugin); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'ios'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.childbrowser', {}); |
| const spy = spyOn(fs, 'readFileSync').and.callThrough(); |
| |
| const munger = new configChanges.PlatformMunger('ios', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| expect(spy).toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf8'); |
| }); |
| |
| it('Test 026 : should move successfully installed plugins from queue to installed plugins section, and include/retain vars if applicable', function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| install_plugin(varplugin); |
| |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', { API_KEY: 'hi' }, true); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| |
| expect(platformJson.root.prepare_queue.installed.length).toEqual(0); |
| expect(platformJson.root.installed_plugins['com.adobe.vars']).toBeDefined(); |
| expect(platformJson.root.installed_plugins['com.adobe.vars'].API_KEY).toEqual('hi'); |
| }); |
| }); |
| |
| describe(': uninstallation', function () { |
| it('Test 027 : should call pruneXML for every config munge it completely removes from the app (every leaf that is decremented to 0)', function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| |
| // Run through an "install" |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {}); |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| |
| // Now set up an uninstall and make sure prunexml is called properly |
| platformJson.addUninstalledPluginToPrepareQueue('org.test.plugins.dummyplugin'); |
| |
| const spy = spyOn(xml_helpers, 'pruneXML').and.returnValue(true); |
| munger.process(plugins_dir); |
| expect(spy.calls.count()).toEqual(4); |
| expect(spy.calls.argsFor(0)[2]).toEqual('/*'); |
| expect(spy.calls.argsFor(1)[2]).toEqual('/*'); |
| expect(spy.calls.argsFor(2)[2]).toEqual('/manifest/application'); |
| expect(spy.calls.argsFor(3)[2]).toEqual('/cordova/plugins'); |
| }); |
| |
| it('Test 028 : should generate a config munge that interpolates variables into config changes, if applicable', function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| install_plugin(varplugin); |
| |
| // Run through an "install" |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', { API_KEY: 'canucks' }); |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| |
| // Now set up an uninstall and make sure prunexml is called properly |
| platformJson.addUninstalledPluginToPrepareQueue('com.adobe.vars'); |
| |
| const spy = spyOn(munger, 'generate_plugin_config_munge').and.returnValue({}); |
| munger.process(plugins_dir); |
| |
| const munge_params = spy.calls.argsFor(0); |
| expect(munge_params[0]).toEqual(jasmine.any(PluginInfo)); |
| expect(munge_params[0].dir).toEqual(path.join(plugins_dir, 'com.adobe.vars')); |
| expect(munge_params[1].API_KEY).toEqual('canucks'); |
| }); |
| |
| it('Test 029 : should not call pruneXML for a config munge that another plugin depends on', function () { |
| fs.cpSync(android_two_no_perms_project, temp, { recursive: true }); |
| install_plugin(childrenplugin); |
| install_plugin(shareddepsplugin); |
| |
| // Run through and "install" two plugins (they share a permission for INTERNET) |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.multiple-children', {}); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.shareddeps', {}); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| |
| // Now set up an uninstall for multi-child plugin |
| platformJson.addUninstalledPluginToPrepareQueue('org.test.multiple-children'); |
| munger.process(plugins_dir); |
| munger.save_all(); |
| |
| const am_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf8'))); |
| const permission = am_xml.find('./uses-permission'); |
| expect(permission).toBeDefined(); |
| expect(permission.attrib['android:name']).toEqual('android.permission.INTERNET'); |
| }); |
| |
| it('Test 030 : should not call pruneXML for a config munge targeting a config file that does not exist', function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| // install a plugin |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {}); |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| |
| // set up an uninstall for the same plugin |
| platformJson.addUninstalledPluginToPrepareQueue('org.test.plugins.dummyplugin'); |
| |
| const spy = spyOn(fs, 'readFileSync').and.callThrough(); |
| munger.process(plugins_dir); |
| |
| expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'), 'utf-8'); |
| }); |
| |
| it('Test 031 : should remove uninstalled plugins from installed plugins list', function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| install_plugin(varplugin); |
| |
| // install the var plugin |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', { API_KEY: 'eat my shorts' }); |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| |
| // queue up an uninstall for the same plugin |
| platformJson.addUninstalledPluginToPrepareQueue('com.adobe.vars'); |
| munger.process(plugins_dir); |
| |
| expect(platformJson.root.prepare_queue.uninstalled.length).toEqual(0); |
| expect(platformJson.root.installed_plugins['com.adobe.vars']).not.toBeDefined(); |
| }); |
| |
| it('Test 032 : should call pruneXMLRestore for every config munge with mode \'merge\' or \'overwrite\' it removes from the app', function () { |
| fs.cpSync(android_two_project, temp, { recursive: true }); |
| install_plugin(editconfigplugin); |
| |
| // Run through an "install" |
| const platformJson = PlatformJson.load(plugins_dir, 'android'); |
| platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {}); |
| |
| const munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider); |
| munger.process(plugins_dir); |
| |
| // Now set up an uninstall and make sure pruneXMLMerge is called properly |
| platformJson.addUninstalledPluginToPrepareQueue('org.test.editconfigtest'); |
| |
| const spy = spyOn(xml_helpers, 'pruneXMLRestore').and.returnValue(true); |
| munger.process(plugins_dir); |
| |
| expect(spy.calls.count()).toEqual(2); |
| expect(spy.calls.argsFor(0)[1]).toEqual('/manifest/application/activity[@android:name=\'org.test.DroidGap\']'); |
| expect(spy.calls.argsFor(1)[1]).toEqual('/manifest/application/activity'); |
| }); |
| }); |
| }); |
| }); |