You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by er...@apache.org on 2019/03/12 01:31:14 UTC

[cordova-electron] branch master updated: Support User Defined Electron Settings (#38)

This is an automated email from the ASF dual-hosted git repository.

erisu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-electron.git


The following commit(s) were added to refs/heads/master by this push:
     new 4007c3b  Support User Defined Electron Settings (#38)
4007c3b is described below

commit 4007c3b4df93b1a2aa6e611855b042d1f97ea37a
Author: エリス <er...@users.noreply.github.com>
AuthorDate: Tue Mar 12 10:31:10 2019 +0900

    Support User Defined Electron Settings (#38)
    
    * Support User Defined Electron Settings
    * Move dev option to the appropriate location (#11)
    * Updated Docs
---
 DOCUMENTATION.md                                   | 116 +++++--
 bin/templates/cordova/lib/SettingJsonParser.js     |  11 +-
 bin/templates/cordova/lib/build.js                 |  15 +-
 bin/templates/cordova/lib/prepare.js               |   7 +-
 bin/templates/cordova/lib/util.js                  |  13 +
 bin/templates/project/cdv-electron-main.js         |  10 +-
 bin/templates/project/cdv-electron-settings.json   |  10 +-
 .../cordova/lib/SettingJsonParser.spec.js          | 174 ++++++-----
 .../spec/unit/templates/cordova/lib/build.spec.js  |  18 --
 .../unit/templates/cordova/lib/prepare.spec.js     | 346 ++++++++++++++-------
 .../spec/unit/templates/cordova/lib/util.spec.js   |  42 ++-
 11 files changed, 478 insertions(+), 284 deletions(-)

diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index bd6279c..eb9ee0f 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -31,12 +31,13 @@ Electron is a framework that uses web technologies (HTML, CSS, and JS) to build
     - [Preview a Project](#preview-a-project)
     - [Build a Project](#build-a-project)
   - [Customizing the Application's Icon](#customizing-the-applications-icon)
-  - [Customizing the Application's Main Process](#customizing-the-applications-main-process)
-    - [Window Appearance (BrowserWindow)](#window-appearance-browserwindow)
-      - [How to set the window default size?](#how-to-set-the-window-default-size)
-      - [How to make the window not resizable?](#how-to-make-the-window-not-resizable)
-      - [How to make my window fullscreen?](#how-to-make-my-window-fullscreen)
-    - [DevTools](#devtools)
+  - [Customizing the Application's Window Options](#customizing-the-applications-window-options)
+    - [How to set the window's default size?](#how-to-set-the-windows-default-size)
+    - [How to make the window not resizable?](#how-to-make-the-window-not-resizable)
+    - [How to make my window fullscreen?](#how-to-make-my-window-fullscreen)
+    - [How to support Node.js and Electron APIs?](#how-to-support-nodejs-and-electron-apis)
+  - [Customizing the Electron's Main Process](#customizing-the-electrons-main-process)
+  - [DevTools](#devtools)
   - [Build Configurations](#build-configurations)
     - [Default Build Configurations](#default-build-configurations)
     - [Customizing Build Configurations](#customizing-build-configurations)
@@ -94,10 +95,10 @@ $ cordova run cordova-electron
 
 ### Preview a Project
 
-It is not necessary to build the Electron application for previewing. Since the building process can be slow, it is recommended to pass in the `--no-build` flag to disable the build process when previewing.
+It is not necessary to build the Electron application for previewing. Since the building process can be slow, it is recommended to pass in the `--nobuild` flag to disable the build process when previewing.
 
 ```
-$ cordova run electron --no-build
+$ cordova run electron --nobuild
 ```
 
 ### Build a Project
@@ -158,48 +159,101 @@ If you want to support displays with different DPI densities at the same time, y
 </platform>
 ```
 
-## Customizing the Application's Main Process
+## Customizing the Application's Window Options
 
-In the `{PROJECT_ROOT_DIR}/platform/electron/platform_www/` directory, the file `cdv-electron-main.js` defines the application's main process. We can customize the application's window appearance as well as defining or enabling additional features in this file.
+Electron provides many options to manipulate the [`BrowserWindow`](https://electronjs.org/docs/api/browser-window). This section will cover how to configure a few basic options. For a full list of options, please see the [Electron's Docs - BrowserWindow Options](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions).
 
-### Window Appearance (BrowserWindow)
+Working with a Cordova project, it is recommended to create an Electron settings file within the project's root directory, and set its the relative path in the preference option `ElectronSettingsFilePath`, in the `config.xml` file.
 
-Electron provides many options to manipulate the BrowserWindow. This section covers only a few basic options. For a full list of options, please see the [Electron's Docs - BrowserWindow Options](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions).
+**Example `config.xml`:**
 
-Electron provides many optional options that can manipulate the BrowserWindow. This section will cover a few of these options that many uses. A full list of these options can be found on the 
+```xml
+<platform name="electron">
+    <preference name="ElectronSettingsFilePath" value="res/electron/settings.json" />
+</platform>
+```
 
-#### How to set the window default size?
+To override or set any BrowserWindow options, in this file the options are added to the  `browserWindow` property.
 
-Using the `width` and `height` option, you can define starting default window size.
+**Example `res/electron/settings.json`:**
 
-```
-mainWindow = new BrowserWindow({
-  width: 800,
-  height: 600
-});
+```json
+{
+    "browserWindow": { ... }
+}
 ```
 
-#### How to make the window not resizable?
+### How to set the window's default size?
 
-Using the `resizable` flag option, you can disable the user's ability to resize your application's window.
+By default, the `width` is set to **800** and the `height` set to **600**. This can be overridden by setting the `width` and `height` property.
 
+**Example:**
+
+```json
+{
+    "browserWindow": {
+        "width": 1024,
+        "height": 768
+    }
+}
 ```
-mainWindow = new BrowserWindow({ resizable: false });
+
+### How to make the window not resizable?
+
+Setting the `resizable` flag property, you can disable the user's ability to resize your application's window.
+
+**Example:**
+
+```json
+{
+    "browserWindow": {
+        "resizable": false
+    }
+}
 ```
 
-#### How to make my window fullscreen?
+### How to make my window fullscreen?
+
+Using the `fullscreen` flag property, you can force the application to launch in fullscreen.
 
-Using the `fullscreen` flag option, you can force the application to launch in fullscreen.
+**Example:**
 
+```json
+{
+    "browserWindow": {
+        "fullscreen": true
+    }
+}
 ```
-mainWindow = new BrowserWindow({ fullscreen: true });
+
+### How to support Node.js and Electron APIs?
+
+Set the `nodeIntegration` flag property to true.  By default, this property flag is set to false to support popular libraries that insert symbols with the same names that Node.js and Electron already uses.
+
+> You can read more about this at Electron docs: [I can not use jQuery/RequireJS/Meteor/AngularJS in Electron
+](https://electronjs.org/docs/faq#i-can-not-use-jqueryrequirejsmeteorangularjs-in-electron).
+
+**Example:**
+
+```json
+{
+    "browserWindow": {
+        "nodeIntegration": true
+    }
+}
 ```
 
-### DevTools
+## Customizing the Electron's Main Process
+
+If it is necessary to customize the Electron's main process beyond the scope of the Electron's configuration settings, chances can be added directly to the `cdv-electron-main.js` file located in `{PROJECT_ROOT_DIR}/platform/electron/platform_www/`. This is the application's main process.
+
+> :warning:  However, it is not recommended to modify this file as the upgrade process for `cordova-electron` is to remove the old platform before adding the new version.
+
+## DevTools
 
 The `--release` and `--debug` flags control the visibility of the DevTools. DevTools are shown by default on **Debug Builds** (`without a flag` or with `--debug`). If you want to hide the DevTools pass in the `--release` flag when building or running the application.
 
-_Note: DevTools still can be closed or opened manually._
+> Note: DevTools can be closed or opened manually with the debug build.
 
 ## Build Configurations
 
@@ -326,7 +380,7 @@ The `arch` property is an array list of architectures that each package is built
 * arm64
 
 
-The example above will generate a `x64` `dmg` package.
+The example above will generate an `x64` `dmg` package.
 
 ```
 {
@@ -477,6 +531,6 @@ There are not signing requirements for Linux builds.
 
 All browser-based plugins are usable with the Electron platform.
 
-When adding a plugin, if the plugin supports both the `electron` and `browser` platform, the `electron` portion will be used. If the plugin misses `electron`, but contains the `browser` implementation, it will fall back on the `browser` implementation.
+When adding a plugin, if the plugin supports both the `electron` and `browser` platform, the `electron` portion will be used. If the plugin misses `electron` but contains the `browser` implementation, it will fall back on the `browser` implementation.
 
-Internally, Electron is using Chromium (Chrome) as its web view. Some plugins may have conditions written specifically for each different browser. In this case, it may affect the behavior from what is intended. Since Electron may support feature that the browser does not, these plugins would possibly need to be updated for the `electron` platform.
+Internally, Electron is using Chromium (Chrome) as its web view. Some plugins may have conditions written specifically for each different browser. In this case, it may affect the behavior of what is intended. Since Electron may support feature that the browser does not, these plugins would possibly need to be updated for the `electron` platform.
diff --git a/bin/templates/cordova/lib/SettingJsonParser.js b/bin/templates/cordova/lib/SettingJsonParser.js
index 3d78374..05ce9c5 100644
--- a/bin/templates/cordova/lib/SettingJsonParser.js
+++ b/bin/templates/cordova/lib/SettingJsonParser.js
@@ -19,6 +19,7 @@
 
 const fs = require('fs-extra');
 const path = require('path');
+const { deepMerge } = require('./util');
 
 class SettingJsonParser {
     constructor (wwwDir) {
@@ -26,9 +27,15 @@ class SettingJsonParser {
         this.package = require(this.path);
     }
 
-    configure (config) {
+    configure (config, userElectronSettingsPath) {
+        // Apply user settings ontop of defaults.
+        if (userElectronSettingsPath) {
+            const userElectronSettings = require(userElectronSettingsPath);
+            this.package = deepMerge(this.package, userElectronSettings);
+        }
+
         if (config) {
-            this.package.isRelease = (typeof (config.release) !== 'undefined') ? config.release : false;
+            this.package.browserWindow.webPreferences.devTools = !config.release;
         }
 
         return this;
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 7b8e146..277930b 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -20,20 +20,7 @@
 const fs = require('fs-extra');
 const path = require('path');
 const events = require('cordova-common').events;
-
-function deepMerge (mergeTo, mergeWith) {
-    for (const property in mergeWith) {
-        if (Object.prototype.toString.call(mergeWith[property]) === '[object Object]') {
-            mergeTo[property] = deepMerge((mergeTo[property] || {}), mergeWith[property]);
-        } else if (Object.prototype.toString.call(mergeWith[property]) === '[object Array]') {
-            mergeTo[property] = [].concat((mergeTo[property] || []), mergeWith[property]);
-        } else {
-            mergeTo[property] = mergeWith[property];
-        }
-    }
-
-    return mergeTo;
-}
+const { deepMerge } = require('./util');
 
 const PLATFORM_MAPPING = {
     linux: 'linux',
diff --git a/bin/templates/cordova/lib/prepare.js b/bin/templates/cordova/lib/prepare.js
index f690519..754a95c 100644
--- a/bin/templates/cordova/lib/prepare.js
+++ b/bin/templates/cordova/lib/prepare.js
@@ -75,9 +75,14 @@ module.exports.prepare = function (cordovaProject, options) {
         .configure(this.config)
         .write();
 
+    const userElectronSettings = cordovaProject.projectConfig.getPlatformPreference('ElectronSettingsFilePath', 'electron');
+    const userElectronSettingsPath = userElectronSettings && fs.existsSync(path.resolve(cordovaProject.root, userElectronSettings))
+        ? path.resolve(cordovaProject.root, userElectronSettings)
+        : undefined;
+
     // update Electron settings in .json file
     (new SettingJsonParser(this.locations.www))
-        .configure(options.options)
+        .configure(options.options, userElectronSettingsPath)
         .write();
 
     // update project according to config.xml changes.
diff --git a/bin/templates/cordova/lib/util.js b/bin/templates/cordova/lib/util.js
new file mode 100644
index 0000000..9a48c1e
--- /dev/null
+++ b/bin/templates/cordova/lib/util.js
@@ -0,0 +1,13 @@
+module.exports.deepMerge = function (mergeTo, mergeWith) {
+    for (const property in mergeWith) {
+        if (Object.prototype.toString.call(mergeWith[property]) === '[object Object]') {
+            mergeTo[property] = module.exports.deepMerge((mergeTo[property] || {}), mergeWith[property]);
+        } else if (Object.prototype.toString.call(mergeWith[property]) === '[object Array]') {
+            mergeTo[property] = [].concat((mergeTo[property] || []), mergeWith[property]);
+        } else {
+            mergeTo[property] = mergeWith[property];
+        }
+    }
+
+    return mergeTo;
+};
diff --git a/bin/templates/project/cdv-electron-main.js b/bin/templates/project/cdv-electron-main.js
index ae4945b..88c337e 100644
--- a/bin/templates/project/cdv-electron-main.js
+++ b/bin/templates/project/cdv-electron-main.js
@@ -38,12 +38,8 @@ function createWindow () {
         appIcon = `${__dirname}/img/logo.png`;
     }
 
-    mainWindow = new BrowserWindow({
-        width: 800,
-        height: 600,
-        icon: appIcon,
-        webPreferences: cdvElectronSettings.webPreferences
-    });
+    const browserWindowOpts = Object.assign({}, cdvElectronSettings.browserWindow, { icon: appIcon });
+    mainWindow = new BrowserWindow(browserWindowOpts);
 
     // and load the index.html of the app.
     // TODO: possibly get this data from config.xml
@@ -53,7 +49,7 @@ function createWindow () {
     });
 
     // Open the DevTools.
-    if (!cdvElectronSettings.isRelease) {
+    if (cdvElectronSettings.browserWindow.webPreferences.devTools) {
         mainWindow.webContents.openDevTools();
     }
 
diff --git a/bin/templates/project/cdv-electron-settings.json b/bin/templates/project/cdv-electron-settings.json
index ac65b67..99c0bb9 100644
--- a/bin/templates/project/cdv-electron-settings.json
+++ b/bin/templates/project/cdv-electron-settings.json
@@ -1,6 +1,10 @@
 {
-    "isRelease": false,
-    "webPreferences": {
-        "nodeIntegration": false
+    "browserWindow": {
+        "height": 600,
+        "webPreferences":{
+            "devTools": true,
+            "nodeIntegration": false
+        },
+        "width": 800
     }
 }
\ No newline at end of file
diff --git a/tests/spec/unit/templates/cordova/lib/SettingJsonParser.spec.js b/tests/spec/unit/templates/cordova/lib/SettingJsonParser.spec.js
index bdf0210..9a64da2 100644
--- a/tests/spec/unit/templates/cordova/lib/SettingJsonParser.spec.js
+++ b/tests/spec/unit/templates/cordova/lib/SettingJsonParser.spec.js
@@ -67,108 +67,124 @@ describe('Testing SettingJsonParser.js:', () => {
             expect(settingJsonParser.package).toEqual({});
         });
 
-        it('should be equal to undefined, when config file is undefined.', () => {
-            // mock options data
-            options = { options: { argv: [] } };
-
-            settingJsonParser = new SettingJsonParser(locations.www).configure(undefined);
+        it('should use default when users settings files does not exist, but set devTools value from options', () => {
+            options = { options: { release: true, argv: [] } };
 
-            expect(requireSpy).toHaveBeenCalled();
-            expect(settingJsonParser.package.isRelease).toEqual(undefined);
+            SettingJsonParser.__set__('require', (file) => {
+                // return defaults
+                if (file.includes('cdv-electron-settings.json')) {
+                    return {
+                        browserWindow: {
+                            webPreferences: {
+                                devTools: true,
+                                nodeIntegration: true
+                            }
+                        }
+                    };
+                }
+
+                return require(file);
+            });
+
+            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options, false);
+
+            expect(settingJsonParser.package.browserWindow.webPreferences.devTools).toBe(false);
+            expect(settingJsonParser.package.browserWindow.webPreferences.nodeIntegration).toBe(true);
         });
 
-        it('should be equal to false, when no flag is set.', () => {
-            // mock options data
-            options = { options: { argv: [] } };
-
-            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options);
-
-            expect(requireSpy).toHaveBeenCalled();
-            expect(settingJsonParser.package.isRelease).toEqual(false);
+        it('should use default when users settings files does not exist.', () => {
+            options = {};
+
+            SettingJsonParser.__set__('require', (file) => {
+                // return defaults
+                if (file.includes('cdv-electron-settings.json')) {
+                    return {
+                        browserWindow: {
+                            webPreferences: {
+                                devTools: true,
+                                nodeIntegration: true
+                            }
+                        }
+                    };
+                }
+
+                return require(file);
+            });
+
+            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options, false);
+
+            expect(settingJsonParser.package.browserWindow.webPreferences.devTools).toBe(true);
+            expect(settingJsonParser.package.browserWindow.webPreferences.nodeIntegration).toBe(true);
         });
 
-        it('should be equal to false, when debug flag is set.', () => {
-            // mock options data
+        it('should load users override settings and merge on default, but set devTools value from options.', () => {
             options = { options: { debug: true, argv: [] } };
 
-            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options);
-
-            expect(requireSpy).toHaveBeenCalled();
-            expect(settingJsonParser.package.isRelease).toEqual(false);
-        });
-
-        it('should be equal to true, when release flag is set.', () => {
-            // mock options data
-            options = { options: { release: true, argv: [] } };
-
-            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options);
-
-            expect(requireSpy).toHaveBeenCalled();
-            expect(settingJsonParser.package.isRelease).toEqual(true);
-        });
-
-        it('should write provided data, when no flag is set.', () => {
-            let writeFileSyncSpy;
-            writeFileSyncSpy = jasmine.createSpy('writeFileSync');
-            settingJsonParser.__set__('fs', { writeFileSync: writeFileSyncSpy });
-
-            options = { options: { argv: [] } };
-
-            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options).write();
-
-            expect(writeFileSyncSpy).toHaveBeenCalled();
-
-            const settingsPath = writeFileSyncSpy.calls.argsFor(0)[0];
-            expect(settingsPath).toEqual(path.join('mock', 'www', 'cdv-electron-settings.json'));
-
-            // get settings json file content and remove white spaces
-            let settingsFile = writeFileSyncSpy.calls.argsFor(0)[1];
-            settingsFile = settingsFile.replace(/\s+/g, '');
-            expect(settingsFile).toEqual('{"isRelease":false}');
+            SettingJsonParser.__set__('require', (file) => {
+                if (file === 'LOAD_MY_FAKE_DATA') {
+                    return {
+                        browserWindow: {
+                            webPreferences: {
+                                devTools: false,
+                                nodeIntegration: false
+                            }
+                        }
+                    };
+                }
+
+                // return defaults
+                if (file.includes('cdv-electron-settings.json')) {
+                    return {
+                        browserWindow: {
+                            webPreferences: {
+                                devTools: true,
+                                nodeIntegration: true
+                            }
+                        }
+                    };
+                }
+
+                return require(file);
+            });
+
+            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options, 'LOAD_MY_FAKE_DATA');
+
+            expect(settingJsonParser.package.browserWindow.webPreferences.devTools).toBe(true);
+            expect(settingJsonParser.package.browserWindow.webPreferences.nodeIntegration).toBe(false);
 
-            const settingsFormat = writeFileSyncSpy.calls.argsFor(0)[2];
-            expect(settingsFormat).toEqual('utf8');
         });
 
-        it('should write provided data, when debug flag is set.', () => {
+        it('should write provided data.', () => {
             const writeFileSyncSpy = jasmine.createSpy('writeFileSync');
             settingJsonParser.__set__('fs', { writeFileSync: writeFileSyncSpy });
-
             options = { options: { debug: true, argv: [] } };
 
-            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options).write();
+            SettingJsonParser.__set__('require', (file) => {
+                if (file === 'LOAD_MY_FAKE_DATA') return {};
 
-            expect(writeFileSyncSpy).toHaveBeenCalled();
+                // return defaults
+                if (file.includes('cdv-electron-settings.json')) {
+                    return {
+                        browserWindow: {
+                            webPreferences: {
+                                devTools: true,
+                                nodeIntegration: true
+                            }
+                        }
+                    };
+                }
 
-            const settingsPath = writeFileSyncSpy.calls.argsFor(0)[0];
-            expect(settingsPath).toEqual(path.join('mock', 'www', 'cdv-electron-settings.json'));
+                return require(file);
+            });
 
-            // get settings json file content and remove white spaces
-            let settingsFile = writeFileSyncSpy.calls.argsFor(0)[1];
-            settingsFile = settingsFile.replace(/\s+/g, '');
-            expect(settingsFile).toEqual('{"isRelease":false}');
-
-            const settingsFormat = writeFileSyncSpy.calls.argsFor(0)[2];
-            expect(settingsFormat).toEqual('utf8');
-        });
-
-        it('should write provided data.', () => {
-            const writeFileSyncSpy = jasmine.createSpy('writeFileSync');
-            settingJsonParser.__set__('fs', { writeFileSync: writeFileSyncSpy });
-
-            options = { options: { release: true, argv: [] } };
-
-            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options).write();
+            settingJsonParser = new SettingJsonParser(locations.www).configure(options.options, 'LOAD_MY_FAKE_DATA').write();
 
             expect(writeFileSyncSpy).toHaveBeenCalled();
 
-            const settingsPath = writeFileSyncSpy.calls.argsFor(0)[0];
-            expect(settingsPath).toEqual(path.join('mock', 'www', 'cdv-electron-settings.json'));
-
             // get settings json file content and remove white spaces
             let settingsFile = writeFileSyncSpy.calls.argsFor(0)[1];
             settingsFile = settingsFile.replace(/\s+/g, '');
-            expect(settingsFile).toEqual('{"isRelease":true}');
+            expect(settingsFile).toEqual(`{"browserWindow":{"webPreferences":{"devTools":true,"nodeIntegration":true}}}`);
 
             const settingsFormat = writeFileSyncSpy.calls.argsFor(0)[2];
             expect(settingsFormat).toEqual('utf8');
diff --git a/tests/spec/unit/templates/cordova/lib/build.spec.js b/tests/spec/unit/templates/cordova/lib/build.spec.js
index 5b0b51f..b8d61d6 100644
--- a/tests/spec/unit/templates/cordova/lib/build.spec.js
+++ b/tests/spec/unit/templates/cordova/lib/build.spec.js
@@ -29,24 +29,6 @@ describe('Testing build.js:', () => {
         build = rewire('../../../../../../bin/templates/cordova/lib/build');
     });
 
-    describe('deepMerge method', () => {
-        it('should deep merge objects and arrays.', () => {
-            const deepMerge = build.__get__('deepMerge');
-
-            const mergeTo = { foo: 'bar', abc: [ 1, 2, 3 ] };
-            const mergeWith = { food: 'candy', abc: [ 5 ] };
-
-            const actual = deepMerge(mergeTo, mergeWith);
-            const expected = {
-                foo: 'bar',
-                food: 'candy',
-                abc: [ 1, 2, 3, 5 ]
-            };
-
-            expect(actual).toEqual(expected);
-        });
-    });
-
     describe('Build class', () => {
         let ElectronBuilder;
         let electronBuilder;
diff --git a/tests/spec/unit/templates/cordova/lib/prepare.spec.js b/tests/spec/unit/templates/cordova/lib/prepare.spec.js
index 64d412b..2a722a5 100644
--- a/tests/spec/unit/templates/cordova/lib/prepare.spec.js
+++ b/tests/spec/unit/templates/cordova/lib/prepare.spec.js
@@ -21,6 +21,7 @@ const rewire = require('rewire');
 const path = require('path');
 const CordovaError = require('cordova-common').CordovaError;
 const Api = require(path.resolve(__dirname, '..', '..', '..', '..', '..', '..', 'bin', 'templates', 'cordova', 'Api'));
+let prepare;
 
 /**
  * Create a mock item from the getIcon collection with the supplied updated data.
@@ -40,104 +41,118 @@ function mockGetIconItem (data) {
     }, data);
 }
 
-describe('Testing prepare.js:', () => {
-    let prepare;
-
-    // Spies
-    let emitSpy;
-    let updatePathsSpy;
+const cordovaProjectDefault = {
+    root: 'mock',
+    projectConfig: {
+        path: path.join('mock', 'config.xml'),
+        cdvNamespacePrefix: 'cdv',
+        doc: {
+            getroot: function () {
+                return this;
+            }
+        }
+    },
+    locations: {
+        buildRes: path.join('mock', 'build-res'),
+        www: path.join('mock', 'www'),
+        configXml: path.join('mock', 'config.xml'),
+        platformRootDir: path.join('mock', 'platform_www')
+    }
+};
+
+const locationsDefault = cordovaProjectDefault.locations;
+
+let fakeParserConstructorSpy;
+let fakeParserConfigureSpy;
+let fakeParserWriteSpy;
+let fakeConfigParserConstructorSpy;
+let fakeConfigParserWriteSpy;
+let mergeXmlSpy;
+let updateIconsSpy;
+let emitSpy;
+let xmlHelpersMock;
+let updateIconsFake;
+
+function createSpies () {
+    fakeParserConstructorSpy = jasmine.createSpy('fakeParserConstructorSpy');
+    fakeParserConfigureSpy = jasmine.createSpy('fakeParserConfigureSpy');
+    fakeParserWriteSpy = jasmine.createSpy('fakeParserWriteSpy');
+
+    fakeConfigParserConstructorSpy = jasmine.createSpy('fakeConfigParserConstructorSpy');
+    fakeConfigParserWriteSpy = jasmine.createSpy('fakeConfigParserWriteSpy');
+    mergeXmlSpy = jasmine.createSpy('mergeXmlSpy');
+    updateIconsSpy = jasmine.createSpy('updateIconsSpy');
+    emitSpy = jasmine.createSpy('emitSpy');
+
+    prepare = rewire(path.resolve(__dirname, '..', '..', '..', '..', '..', '..', 'bin', 'templates', 'cordova', 'lib', 'prepare'));
+
+    prepare.__set__('events', {
+        emit: emitSpy
+    });
 
-    let cordovaProject;
-    let locations;
+    xmlHelpersMock = {
+        mergeXml: function () {
+            mergeXmlSpy();
+            return this;
+        }
+    };
 
-    beforeEach(() => {
-        prepare = rewire(path.resolve(__dirname, '..', '..', '..', '..', '..', '..', 'bin', 'templates', 'cordova', 'lib', 'prepare'));
+    updateIconsFake = () => {
+        updateIconsSpy();
+        return this;
+    };
+}
 
-        locations = {
-            buildRes: path.join('mock', 'build-res'),
-            www: path.join('mock', 'www'),
-            configXml: path.join('mock', 'config.xml'),
-            platformRootDir: path.join('mock', 'platform_www')
-        };
+// define fake classses, methods and variables
+class FakeParser {
+    constructor () {
+        fakeParserConstructorSpy();
+    }
+    configure (options, userElectronFile) {
+        fakeParserConfigureSpy(options, userElectronFile);
+        return this;
+    }
+    write () {
+        fakeParserWriteSpy();
+        return this;
+    }
+}
 
-        cordovaProject = {
-            root: 'mock',
-            projectConfig: {
-                path: path.join('mock', 'config.xml'),
-                cdvNamespacePrefix: 'cdv',
-                doc: {
-                    getroot: function () {
-                        return this;
-                    }
-                }
-            },
-            locations: locations
+class FakeConfigParser {
+    constructor () {
+        this.doc = {
+            getroot: function () {
+                return this;
+            }
         };
+        fakeConfigParserConstructorSpy();
+    }
 
-        emitSpy = jasmine.createSpy('emit');
-        prepare.__set__('events', {
-            emit: emitSpy
-        });
-
-        updatePathsSpy = jasmine.createSpy('updatePaths');
-        prepare.__set__('FileUpdater', {
-            updatePaths: updatePathsSpy
-        });
-    });
+    write () {
+        fakeConfigParserWriteSpy();
+        return this;
+    }
+}
 
+describe('Testing prepare.js:', () => {
     describe('module.exports.prepare method', () => {
         // define spies
-        let constructorSpy = jasmine.createSpy('constructor');
-        let configureSpy = jasmine.createSpy('configure');
-        let writeSpy = jasmine.createSpy('write');
-        let mergeXmlSpy = jasmine.createSpy('mergeXml');
-        let updateIconsSpy = jasmine.createSpy('updateIcons');
-
-        // define fake classses, methods and variables
-        class FakeParser {
-            constructor () {
-                constructorSpy();
-            }
-            configure () {
-                configureSpy();
-                return this;
-            }
-            write () {
-                writeSpy();
-                return this;
-            }
-        }
+        let updatePathsSpy;
+        let cordovaProject;
 
-        class FakeConfigParser {
-            constructor () {
-                this.doc = {
-                    getroot: function () {
-                        return this;
-                    }
-                };
-                constructorSpy();
-            }
-            write () {
-                writeSpy();
-                return this;
-            }
-        }
-
-        const xmlHelpersMock = {
-            mergeXml: function () {
-                mergeXmlSpy();
-                return this;
-            }
-        };
+        beforeEach(() => {
+            createSpies();
+            cordovaProject = Object.assign({}, cordovaProjectDefault);
 
-        const updateIconsFake = () => {
-            updateIconsSpy();
-            return this;
-        };
+            updatePathsSpy = jasmine.createSpy('updatePaths');
+            prepare.__set__('FileUpdater', {
+                updatePaths: updatePathsSpy
+            });
+        });
 
         it('should generate config.xml from defaults for platform.', () => {
             // Mocking the scope with dummy API;
-            Promise.resolve().then(function () {
+            return Promise.resolve().then(function () {
                 // Create API instance and mock for test case.
                 const api = new Api(null, '', '');
                 api.events = { emit: emitSpy };
@@ -163,15 +178,19 @@ describe('Testing prepare.js:', () => {
                 prepare.__set__('PackageJsonParser', FakeParser);
                 prepare.__set__('SettingJsonParser', FakeParser);
 
+                cordovaProject.projectConfig.getPlatformPreference = () => undefined;
+
                 prepare.prepare.call(api, cordovaProject, {}, api);
 
                 expect(copySyncSpy).toHaveBeenCalledWith(defaultConfigPathMock, ownConfigPathMock);
                 expect(mergeXmlSpy).toHaveBeenCalled();
                 expect(updateIconsSpy).toHaveBeenCalled();
                 expect(updateIconsSpy).toHaveBeenCalled();
-                expect(constructorSpy).toHaveBeenCalled();
-                expect(configureSpy).toHaveBeenCalled();
-                expect(writeSpy).toHaveBeenCalled();
+                expect(fakeParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeConfigParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeParserConfigureSpy).toHaveBeenCalled();
+                expect(fakeParserWriteSpy).toHaveBeenCalled();
+                expect(fakeConfigParserWriteSpy).toHaveBeenCalled();
 
                 const actual = emitSpy.calls.argsFor(0)[1];
                 const expected = 'Generating config.xml';
@@ -179,9 +198,84 @@ describe('Testing prepare.js:', () => {
             });
         });
 
+        it('should get user supplied Electron settings overide path from config.xml but iggnore for incorrect path.', () => {
+            // Mocking the scope with dummy API;
+            return Promise.resolve().then(function () {
+                // Create API instance and mock for test case.
+                const api = new Api(null, '', '');
+                api.events = { emit: emitSpy };
+                api.parser.update_www = () => { return this; };
+                api.parser.update_project = () => { return this; };
+
+                const defaultConfigPathMock = path.join(api.locations.platformRootDir, 'cordova', 'defaults.xml');
+
+                const copySyncSpy = jasmine.createSpy('copySync');
+                prepare.__set__('fs', {
+                    existsSync: function (configPath) {
+                        if (configPath === defaultConfigPathMock) return true;
+                        if (configPath.includes('fail_test_path')) return false;
+                    },
+                    copySync: copySyncSpy
+                });
+
+                // override classes and methods called in modules.export.prepare
+                prepare.__set__('ConfigParser', FakeConfigParser);
+                prepare.__set__('xmlHelpers', xmlHelpersMock);
+                prepare.__set__('updateIcons', updateIconsFake);
+                prepare.__set__('ManifestJsonParser', FakeParser);
+                prepare.__set__('PackageJsonParser', FakeParser);
+                prepare.__set__('SettingJsonParser', FakeParser);
+
+                cordovaProject.projectConfig.getPlatformPreference = (name, platform) => 'fail_test_path';
+
+                prepare.prepare.call(api, cordovaProject, {}, api);
+
+                expect(fakeParserConfigureSpy).toHaveBeenCalled();
+                const actual = fakeParserConfigureSpy.calls.argsFor(2)[1];
+                expect(actual).toEqual(undefined);
+            });
+        });
+
+        it('should get valid user supplied Electron settings overide path from config.xml.', () => {
+            // Mocking the scope with dummy API;
+            return Promise.resolve().then(function () {
+                // Create API instance and mock for test case.
+                const api = new Api(null, '', '');
+                api.events = { emit: emitSpy };
+                api.parser.update_www = () => { return this; };
+                api.parser.update_project = () => { return this; };
+
+                const defaultConfigPathMock = path.join(api.locations.platformRootDir, 'cordova', 'defaults.xml');
+                const copySyncSpy = jasmine.createSpy('copySync');
+                prepare.__set__('fs', {
+                    existsSync: function (configPath) {
+                        if (configPath === defaultConfigPathMock) return true;
+                        if (configPath.includes('pass_test_path')) return true;
+                    },
+                    copySync: copySyncSpy
+                });
+
+                // override classes and methods called in modules.export.prepare
+                prepare.__set__('ConfigParser', FakeConfigParser);
+                prepare.__set__('xmlHelpers', xmlHelpersMock);
+                prepare.__set__('updateIcons', updateIconsFake);
+                prepare.__set__('ManifestJsonParser', FakeParser);
+                prepare.__set__('PackageJsonParser', FakeParser);
+                prepare.__set__('SettingJsonParser', FakeParser);
+
+                cordovaProject.projectConfig.getPlatformPreference = (name, platform) => 'pass_test_path';
+
+                prepare.prepare.call(api, cordovaProject, {}, api);
+
+                expect(fakeParserConfigureSpy).toHaveBeenCalled();
+                const actual = fakeParserConfigureSpy.calls.argsFor(2)[1];
+                expect(actual).toContain('pass_test_path');
+            });
+        });
+
         it('should generate defaults.xml from own config.xml for platform.', () => {
             // Mocking the scope with dummy API;
-            Promise.resolve().then(function () {
+            return Promise.resolve().then(function () {
                 // Create API instance and mock for test case.
                 const api = new Api(null, '', '');
                 api.events = { emit: emitSpy };
@@ -207,14 +301,18 @@ describe('Testing prepare.js:', () => {
                 prepare.__set__('PackageJsonParser', FakeParser);
                 prepare.__set__('SettingJsonParser', FakeParser);
 
+                cordovaProject.projectConfig.getPlatformPreference = () => undefined;
+
                 prepare.prepare.call(api, cordovaProject, {}, api);
 
                 expect(copySyncSpy).toHaveBeenCalledWith(ownConfigPathMock, defaultConfigPathMock);
                 expect(mergeXmlSpy).toHaveBeenCalled();
                 expect(updateIconsSpy).toHaveBeenCalled();
-                expect(constructorSpy).toHaveBeenCalled();
-                expect(configureSpy).toHaveBeenCalled();
-                expect(writeSpy).toHaveBeenCalled();
+                expect(fakeParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeConfigParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeParserConfigureSpy).toHaveBeenCalled();
+                expect(fakeParserWriteSpy).toHaveBeenCalled();
+                expect(fakeConfigParserWriteSpy).toHaveBeenCalled();
 
                 const actual = emitSpy.calls.argsFor(0)[1];
                 const expected = 'Generating defaults.xml';
@@ -224,7 +322,7 @@ describe('Testing prepare.js:', () => {
 
         it('should hit case 3.', () => {
             // Mocking the scope with dummy API;
-            Promise.resolve().then(function () {
+            return Promise.resolve().then(function () {
                 // Create API instance and mock for test case.
                 const api = new Api(null, '', '');
                 api.events = { emit: emitSpy };
@@ -251,14 +349,18 @@ describe('Testing prepare.js:', () => {
                 prepare.__set__('PackageJsonParser', FakeParser);
                 prepare.__set__('SettingJsonParser', FakeParser);
 
+                cordovaProject.projectConfig.getPlatformPreference = () => undefined;
+
                 prepare.prepare.call(api, cordovaProject, {}, api);
 
                 expect(copySyncSpy).toHaveBeenCalledWith(sourceCfgMock.path, ownConfigPathMock);
                 expect(mergeXmlSpy).toHaveBeenCalled();
                 expect(updateIconsSpy).toHaveBeenCalled();
-                expect(constructorSpy).toHaveBeenCalled();
-                expect(configureSpy).toHaveBeenCalled();
-                expect(writeSpy).toHaveBeenCalled();
+                expect(fakeParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeConfigParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeParserConfigureSpy).toHaveBeenCalled();
+                expect(fakeParserWriteSpy).toHaveBeenCalled();
+                expect(fakeConfigParserWriteSpy).toHaveBeenCalled();
 
                 const actual = emitSpy.calls.argsFor(0)[1];
                 const expected = 'case 3';
@@ -268,7 +370,7 @@ describe('Testing prepare.js:', () => {
 
         it('should copy manifest.', () => {
             // Mocking the scope with dummy API;
-            Promise.resolve().then(function () {
+            return Promise.resolve().then(function () {
                 // Create API instance and mock for test case.
                 const api = new Api(null, '', '');
                 api.events = { emit: emitSpy };
@@ -294,14 +396,18 @@ describe('Testing prepare.js:', () => {
                 prepare.__set__('PackageJsonParser', FakeParser);
                 prepare.__set__('SettingJsonParser', FakeParser);
 
+                cordovaProject.projectConfig.getPlatformPreference = () => undefined;
+
                 prepare.prepare.call(api, cordovaProject, {}, api);
 
                 expect(copySyncSpy).toHaveBeenCalledWith(srcManifestPathMock, manifestPathMock);
                 expect(mergeXmlSpy).toHaveBeenCalled();
                 expect(updateIconsSpy).toHaveBeenCalled();
-                expect(constructorSpy).toHaveBeenCalled();
-                expect(configureSpy).toHaveBeenCalled();
-                expect(writeSpy).toHaveBeenCalled();
+                expect(fakeParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeConfigParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeParserConfigureSpy).toHaveBeenCalled();
+                expect(fakeParserWriteSpy).toHaveBeenCalled();
+                expect(fakeConfigParserWriteSpy).toHaveBeenCalled();
 
                 const actual = emitSpy.calls.argsFor(1)[1];
                 const expected = 'Copying';
@@ -311,7 +417,7 @@ describe('Testing prepare.js:', () => {
 
         it('should create new manifest file.', () => {
             // Mocking the scope with dummy API;
-            Promise.resolve().then(function () {
+            return Promise.resolve().then(function () {
                 // Create API instance and mock for test case.
                 const api = new Api(null, '', '');
                 api.events = { emit: emitSpy };
@@ -336,13 +442,17 @@ describe('Testing prepare.js:', () => {
                 prepare.__set__('PackageJsonParser', FakeParser);
                 prepare.__set__('SettingJsonParser', FakeParser);
 
+                cordovaProject.projectConfig.getPlatformPreference = () => undefined;
+
                 prepare.prepare.call(api, cordovaProject, {}, api);
 
                 expect(mergeXmlSpy).toHaveBeenCalled();
                 expect(updateIconsSpy).toHaveBeenCalled();
-                expect(constructorSpy).toHaveBeenCalled();
-                expect(configureSpy).toHaveBeenCalled();
-                expect(writeSpy).toHaveBeenCalled();
+                expect(fakeParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeConfigParserConstructorSpy).toHaveBeenCalled();
+                expect(fakeParserConfigureSpy).toHaveBeenCalled();
+                expect(fakeParserWriteSpy).toHaveBeenCalled();
+                expect(fakeConfigParserWriteSpy).toHaveBeenCalled();
 
                 const actual = emitSpy.calls.argsFor(1)[1];
                 const expected = 'Creating';
@@ -352,13 +462,19 @@ describe('Testing prepare.js:', () => {
     });
 
     describe('updateIcons method', () => {
+        let cordovaProject;
+        let locations;
+
+        beforeEach(() => {
+            createSpies();
+            cordovaProject = Object.assign({}, cordovaProjectDefault);
+            locations = Object.assign({}, locationsDefault);
+        });
 
         it('should detect no defined icons.', () => {
             const updateIcons = prepare.__get__('updateIcons');
 
-            cordovaProject.projectConfig.getIcons = () => {
-                return [];
-            };
+            cordovaProject.projectConfig.getIcons = () => [];
 
             updateIcons(cordovaProject, locations);
 
@@ -407,8 +523,10 @@ describe('Testing prepare.js:', () => {
 
     describe('checkIconsAttributes method', () => {
         let checkIconsAttributes;
+        // let emitSpy;
 
         beforeEach(() => {
+            createSpies();
             checkIconsAttributes = prepare.__get__('checkIconsAttributes');
         });
 
@@ -511,6 +629,7 @@ describe('Testing prepare.js:', () => {
         let prepareIcons;
 
         beforeEach(() => {
+            createSpies();
             prepareIcons = prepare.__get__('prepareIcons');
         });
 
@@ -771,6 +890,7 @@ describe('Testing prepare.js:', () => {
         let findHighResIcons;
 
         beforeEach(() => {
+            createSpies();
             findHighResIcons = prepare.__get__('findHighResIcons');
         });
 
@@ -986,8 +1106,14 @@ describe('Testing prepare.js:', () => {
     describe('createResourceMap method', () => {
         let createResourceMap;
         let shellLsSpy;
+        let cordovaProject;
+        let locations;
 
         beforeEach(() => {
+            prepare = rewire(path.resolve(__dirname, '..', '..', '..', '..', '..', '..', 'bin', 'templates', 'cordova', 'lib', 'prepare'));
+
+            cordovaProject = Object.assign({}, cordovaProjectDefault);
+            locations = Object.assign({}, locationsDefault);
             createResourceMap = prepare.__get__('createResourceMap');
             shellLsSpy = prepare.__get__('mapIconResources');
 
@@ -1158,8 +1284,12 @@ describe('Testing prepare.js:', () => {
     describe('mapIconResources method', () => {
         let mapIconResources;
         let shellLsSpy;
+        let cordovaProject;
 
         beforeEach(() => {
+            prepare = rewire(path.resolve(__dirname, '..', '..', '..', '..', '..', '..', 'bin', 'templates', 'cordova', 'lib', 'prepare'));
+
+            cordovaProject = Object.assign({}, cordovaProjectDefault);
             mapIconResources = prepare.__get__('mapIconResources');
             shellLsSpy = prepare.__get__('mapIconResources');
 
@@ -1205,8 +1335,12 @@ describe('Testing prepare.js:', () => {
     describe('copyIcons method', () => {
         let copyIcons;
         let fsCopySyncSpy;
+        let cordovaProject;
 
         beforeEach(() => {
+            prepare = rewire(path.resolve(__dirname, '..', '..', '..', '..', '..', '..', 'bin', 'templates', 'cordova', 'lib', 'prepare'));
+
+            cordovaProject = Object.assign({}, cordovaProjectDefault);
             copyIcons = prepare.__get__('copyIcons');
 
             fsCopySyncSpy = jasmine.createSpy('copySync');
diff --git a/bin/templates/cordova/lib/SettingJsonParser.js b/tests/spec/unit/templates/cordova/lib/util.spec.js
similarity index 57%
copy from bin/templates/cordova/lib/SettingJsonParser.js
copy to tests/spec/unit/templates/cordova/lib/util.spec.js
index 3d78374..2f4fa2f 100644
--- a/bin/templates/cordova/lib/SettingJsonParser.js
+++ b/tests/spec/unit/templates/cordova/lib/util.spec.js
@@ -17,26 +17,22 @@
     under the License.
 */
 
-const fs = require('fs-extra');
-const path = require('path');
-
-class SettingJsonParser {
-    constructor (wwwDir) {
-        this.path = path.join(wwwDir, 'cdv-electron-settings.json');
-        this.package = require(this.path);
-    }
-
-    configure (config) {
-        if (config) {
-            this.package.isRelease = (typeof (config.release) !== 'undefined') ? config.release : false;
-        }
-
-        return this;
-    }
-
-    write () {
-        fs.writeFileSync(this.path, JSON.stringify(this.package, null, 2), 'utf8');
-    }
-}
-
-module.exports = SettingJsonParser;
+const util = require('../../../../../../bin/templates/cordova/lib/util');
+
+describe('Testing util.js:', () => {
+    describe('deepMerge method', () => {
+        it('should deep merge objects and arrays.', () => {
+            const mergeTo = { foo: 'bar', abc: [ 1, 2, 3 ] };
+            const mergeWith = { food: 'candy', abc: [ 5 ] };
+
+            const actual = util.deepMerge(mergeTo, mergeWith);
+            const expected = {
+                foo: 'bar',
+                food: 'candy',
+                abc: [ 1, 2, 3, 5 ]
+            };
+
+            expect(actual).toEqual(expected);
+        });
+    });
+});


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org