| /* |
| 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('fs-extra'); |
| const path = require('path'); |
| const rewire = require('rewire'); |
| const { events, PluginInfo, ConfigParser, CordovaError } = require('cordova-common'); |
| |
| const rootDir = path.resolve(__dirname, '../../../..'); |
| const fixturesDir = path.join(rootDir, 'tests/spec/fixtures'); |
| const tmpDir = path.join(rootDir, 'temp'); |
| const testProjectDir = path.join(tmpDir, 'testapp'); |
| |
| const create = require(path.join(rootDir, 'lib/create')); |
| const Api = rewire(path.join(rootDir, 'lib/Api')); |
| |
| const apiRequire = Api.__get__('require'); |
| const pluginFixture = path.join(fixturesDir, 'testplugin'); |
| const pluginFixtureEmptyJSModule = path.join(fixturesDir, 'testplugin-empty-jsmodule'); |
| const pluginNotElectronFixture = path.join(fixturesDir, 'test-non-electron-plugin'); |
| const pluginBrowserFixture = path.join(fixturesDir, 'test-browser-plugin'); |
| |
| function dirExists (dir) { |
| return fs.existsSync(dir) && fs.statSync(dir).isDirectory(); |
| } |
| |
| function fileExists (file) { |
| return fs.existsSync(file) && fs.statSync(file).isFile(); |
| } |
| |
| const mockExpectedLocations = { |
| platformRootDir: testProjectDir, |
| root: testProjectDir, |
| www: path.join(testProjectDir, 'www'), |
| res: path.join(testProjectDir, 'res'), |
| platformWww: path.join(testProjectDir, 'platform_www'), |
| configXml: path.join(testProjectDir, 'config.xml'), |
| defaultConfigXml: path.join(testProjectDir, 'cordova/defaults.xml'), |
| build: path.join(testProjectDir, 'build'), |
| buildRes: path.join(testProjectDir, 'build-res'), |
| cache: path.join(testProjectDir, 'cache'), |
| cordovaJs: 'bin/templates/project/assets/www/cordova.js', |
| cordovaJsSrc: 'cordova-js-src' |
| }; |
| |
| describe('Api class', () => { |
| let api; |
| let apiEvents; |
| |
| beforeAll(() => { |
| fs.ensureDirSync(tmpDir); |
| fs.copySync(path.resolve(fixturesDir, 'testapp'), testProjectDir); |
| |
| apiEvents = Api.__get__('selfEvents'); |
| apiEvents.addListener('verbose', (data) => { }); |
| }); |
| |
| afterAll(() => { |
| fs.removeSync(tmpDir); |
| apiEvents.removeAllListeners(); |
| }); |
| |
| beforeEach(() => { |
| api = new Api(null, testProjectDir); |
| }); |
| |
| describe('constructor', () => { |
| it('should have been constructed with initial values.', () => { |
| expect(api).toBeDefined(); |
| /** |
| * In Unit Testing: |
| * The API file path is located in "cordova-electron/bin/templates/cordova". |
| * The expected path is the "cordova-electron/bin/templates" dir. |
| * |
| * In production: |
| * The API file path is actually located in "<project_dir>/platforms/electron/cordova". |
| * The expected path is "<project_dir>/platforms/electron" which is the electron's platform root dir |
| */ |
| expect(api.root).toEqual(testProjectDir); |
| expect(api.locations).toEqual(jasmine.objectContaining(mockExpectedLocations)); |
| }); |
| |
| it('should throw error when the constructor is missing platformRootDir argument.', () => { |
| expect(() => new Api()).toThrowError( |
| CordovaError, |
| 'The path to the platform root directory was undefined or invalid.' |
| ); |
| }); |
| |
| it('should throw error when the constructor contains an invalid platformRootDir path.', () => { |
| const fakePath = path.join(__dirname, '/some-fake-path'); |
| expect(() => new Api(undefined, fakePath)).toThrowError( |
| CordovaError, |
| 'The path to the platform root directory was undefined or invalid.' |
| ); |
| }); |
| }); |
| |
| describe('getPlatformInfo method', () => { |
| it('should return object containing platform information', () => { |
| spyOn(Api, 'version').and.returnValue('1.0.0'); |
| |
| expect(api.getPlatformInfo()).toEqual({ |
| locations: mockExpectedLocations, |
| root: testProjectDir, |
| name: 'electron', |
| version: '1.0.0', |
| projectConfig: undefined |
| }); |
| }); |
| }); |
| |
| describe('prepare method', () => { |
| it('should return object containing platform information', () => { |
| const prepare = jasmine.createSpy('prepare'); |
| |
| Api.__set__('require', () => ({ prepare })); |
| |
| // Mock project configs coming from lib. |
| const appendProjectPath = (dirFile) => path.join(api.root, dirFile); |
| const project = { |
| root: api.root, |
| projectConfig: new ConfigParser(appendProjectPath('config.xml')), |
| locations: { |
| plugins: appendProjectPath('plugins'), |
| www: appendProjectPath('www'), |
| rootConfigXml: appendProjectPath('config.xml') |
| } |
| }; |
| |
| api.prepare(project, {}); |
| expect(prepare).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Object)); |
| Api.__set__('require', apiRequire); |
| }); |
| }); |
| |
| describe('addPlugin method', () => { |
| beforeEach(() => { |
| spyOn(events, 'emit'); |
| |
| fs.removeSync(path.resolve(testProjectDir, 'electron.json')); |
| fs.removeSync(path.resolve(testProjectDir, 'www')); |
| }); |
| |
| afterEach(() => { |
| fs.removeSync(path.resolve(testProjectDir, 'electron.json')); |
| fs.removeSync(path.resolve(testProjectDir, 'www')); |
| |
| apiEvents.removeAllListeners(); |
| }); |
| |
| it('should Promise reject when missing PluginInfo parameter.', () => { |
| return api.addPlugin().then( |
| () => {}, |
| error => { |
| expect(error).toEqual(new Error('Missing plugin info parameter. The first parameter should contain a valid PluginInfo instance.')); |
| } |
| ); |
| }); |
| |
| it('should error out when a PluginInfo parameter is not valid', () => { |
| class FakePluginInfo {} |
| return expect(() => { |
| api.addPlugin(new FakePluginInfo()); |
| }).toThrowError(); |
| }); |
| |
| describe('Use Electron Plugin with Default Install Options', () => { |
| beforeEach(() => { |
| const pluginInfo = new PluginInfo(pluginFixture); |
| return api.addPlugin(pluginInfo); |
| }); |
| |
| it('should add the plugins assets and meta data.', () => { |
| expect(dirExists(path.resolve(testProjectDir, 'www'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'electron.json'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'www/cordova_plugins.js'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'www/js/electron.json'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'www/plugins/org.apache.testplugin/www/plugin.js'))).toBeTruthy(); |
| }); |
| |
| it('should emit that source-file is not supported.', () => { |
| expect(events.emit).toHaveBeenCalledWith( |
| 'verbose', |
| jasmine.stringMatching(/not supported/) |
| ); |
| }); |
| |
| it('should add a second plugins assets', () => { |
| const pluginInfo2 = new PluginInfo(pluginBrowserFixture); |
| return api.addPlugin(pluginInfo2).then(() => { |
| expect(fileExists(path.resolve(testProjectDir, 'www/plugins/org.apache.testbrowserplugin/www/plugin.js'))).toBeTruthy(); |
| }); |
| }); |
| |
| it('should have "clobber", "merge", and "run" set when defined in "js-module".', () => { |
| const { modules } = fs.readJsonSync(path.resolve(testProjectDir, 'electron.json')); |
| expect(modules[0].clobbers).toBeDefined(); |
| expect(modules[0].merges).toBeDefined(); |
| expect(modules[0].runs).toBeDefined(); |
| }); |
| |
| it('should have module id containing the name attribute value.', () => { |
| const { modules } = fs.readJsonSync(path.resolve(testProjectDir, 'electron.json')); |
| expect(modules[0].id).toBe('org.apache.testplugin.TestPlugin'); |
| }); |
| }); |
| |
| // usePlatformWww |
| describe('Use Empty JS-Module Plugin', () => { |
| beforeEach(() => { |
| const pluginInfo = new PluginInfo(pluginFixtureEmptyJSModule); |
| return api.addPlugin(pluginInfo); |
| }); |
| |
| it('should not have "clobber", "merge", and "run" set when not defined in "js-module".', () => { |
| const { modules } = fs.readJsonSync(path.resolve(testProjectDir, 'electron.json')); |
| expect(modules[0].clobbers).not.toBeDefined(); |
| expect(modules[0].merges).not.toBeDefined(); |
| expect(modules[0].runs).not.toBeDefined(); |
| }); |
| |
| it('should use js filename for plugin id if name is missing.', () => { |
| const { modules } = fs.readJsonSync(path.resolve(testProjectDir, 'electron.json')); |
| expect(modules[0].id).toBe('org.apache.testplugin2.MyTestPlugin2'); |
| }); |
| }); |
| |
| describe('Use Electron Plugin with Custom Install Options', () => { |
| beforeEach(() => { |
| const pluginInfo = new PluginInfo(pluginFixture); |
| return api.addPlugin(pluginInfo, { |
| variables: { PACKAGE_NAME: 'com.foobar.newpackagename' }, |
| usePlatformWww: true |
| }); |
| }); |
| |
| it('should use custom package name.', () => { |
| const { installed_plugins } = fs.readJsonSync(path.resolve(testProjectDir, 'electron.json')); |
| expect(installed_plugins['org.apache.testplugin'].PACKAGE_NAME).toEqual('com.foobar.newpackagename'); |
| }); |
| |
| it('should use platform www instead of www.', () => { |
| expect(fileExists(path.resolve(testProjectDir, 'www/cordova_plugins.js'))).toBeFalsy(); |
| expect(fileExists(path.resolve(testProjectDir, 'platform_www/cordova_plugins.js'))).toBeTruthy(); |
| }); |
| }); |
| |
| /** |
| * @todo verfiy validity of an "unknown" itemType acgtually being able to be set. |
| */ |
| it('should warn when unknown itemType is added.', () => { |
| const pluginInfo = new PluginInfo(pluginFixture); |
| pluginInfo.getAssets = () => [{ itemType: 'unknown' }]; |
| |
| return api.addPlugin(pluginInfo).then( |
| () => { |
| expect(events.emit).toHaveBeenCalledWith( |
| 'warn', |
| jasmine.stringMatching(/Unrecognized type/) |
| ); |
| } |
| ); |
| }); |
| |
| it('should add browser plugins as well.', () => { |
| const pluginInfo = new PluginInfo(pluginBrowserFixture); |
| return api.addPlugin(pluginInfo).then( |
| () => { |
| expect(dirExists(path.resolve(testProjectDir, 'www'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'electron.json'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'www', 'cordova_plugins.js'))).toBeTruthy(); |
| } |
| ); |
| }); |
| }); |
| |
| describe('removePlugin method', () => { |
| beforeEach(() => { |
| spyOn(events, 'emit'); |
| |
| fs.removeSync(path.resolve(testProjectDir, 'electron.json')); |
| fs.removeSync(path.resolve(testProjectDir, 'www')); |
| }); |
| |
| afterEach(() => { |
| fs.removeSync(path.resolve(testProjectDir, 'electron.json')); |
| fs.removeSync(path.resolve(testProjectDir, 'www')); |
| |
| apiEvents.removeAllListeners(); |
| }); |
| |
| it('should Promise reject when missing PluginInfo parameter.', () => { |
| return api.removePlugin().then( |
| () => {}, |
| error => { |
| expect(error).toEqual(new Error('Missing plugin info parameter. The first parameter should contain a valid PluginInfo instance.')); |
| } |
| ); |
| }); |
| |
| it('should error out when a PluginInfo parameter is not valid.', () => { |
| class FakePluginInfo {} |
| return expect(() => { |
| api.removePlugin(new FakePluginInfo()); |
| }).toThrowError(); |
| }); |
| |
| describe('Use Electron Plugin with Default Install Options', () => { |
| let pluginInfo; |
| |
| beforeEach(() => { |
| pluginInfo = new PluginInfo(pluginFixture); |
| return api.addPlugin(pluginInfo); |
| }); |
| |
| it('should remove the empty plugin data from electron.json.', () => { |
| return api.removePlugin(pluginInfo).then( |
| () => { |
| const { plugin_metadata, modules, installed_plugins } = fs.readJsonSync(path.resolve(testProjectDir, 'electron.json')); |
| expect(plugin_metadata).toEqual({}); |
| expect(modules).toEqual([]); |
| expect(installed_plugins).toEqual({}); |
| } |
| ); |
| }); |
| |
| it('should remove the added plugin assets, source files, and meta data.', () => { |
| return api.removePlugin(pluginInfo).then(() => { |
| expect(dirExists(path.resolve(testProjectDir, 'www'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'electron.json'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'www/cordova_plugins.js'))).toBeTruthy(); |
| expect(fileExists(path.resolve(testProjectDir, 'www/js/electron.json'))).toBeFalsy(); |
| expect(fileExists(path.resolve(testProjectDir, 'www/plugins/org.apache.testplugin/www/plugin.js'))).toBeFalsy(); |
| |
| const cordovaPluginContent = fs.readFileSync(path.resolve(testProjectDir, 'www/cordova_plugins.js'), 'utf8'); |
| expect(cordovaPluginContent).not.toContain(/org.apache.testplugin/); |
| }); |
| }); |
| }); |
| |
| it('should remove the added plugin assets, from the platform www.', () => { |
| const pluginInfo = new PluginInfo(pluginFixture); |
| return api.addPlugin(pluginInfo, { usePlatformWww: true }) |
| .then(() => api.removePlugin(pluginInfo, { usePlatformWww: true })) |
| .then(() => { |
| expect(fileExists(path.resolve(testProjectDir, 'www/cordova_plugins.js'))).toBeFalsy(); |
| expect(fileExists(path.resolve(testProjectDir, 'platform_www/cordova_plugins.js'))).toBeTruthy(); |
| }); |
| }); |
| |
| /** |
| * @todo verfiy validity of an "unknown" itemType acgtually being able to be set. |
| */ |
| it('should remove the plugin assets and source files.', () => { |
| const pluginInfo = new PluginInfo(pluginFixture); |
| pluginInfo.getAssets = () => [{ itemType: 'unknown' }]; |
| |
| return api.removePlugin(pluginInfo).then(() => { |
| expect(events.emit).toHaveBeenCalledWith( |
| 'warn', |
| jasmine.stringMatching(/unrecognized type/) |
| ); |
| }); |
| }); |
| |
| it('should remove the empty non-electron plugin using platform www as target.', () => { |
| const pluginInfo = new PluginInfo(pluginNotElectronFixture); |
| return api.addPlugin(pluginInfo, { usePlatformWww: true }) |
| .then(() => api.removePlugin(pluginInfo, { usePlatformWww: true })) |
| .then(() => { |
| const cordovaPluginContent = fs.readFileSync(path.resolve(testProjectDir, 'platform_www/cordova_plugins.js'), 'utf8'); |
| expect(cordovaPluginContent).not.toContain(/org.apache.testnonelectronplugin/); |
| }); |
| }); |
| }); |
| |
| describe('build method', () => { |
| it('should execute build', () => { |
| const call = jasmine.createSpy('run'); |
| const mockBuildOptions = { foo: 'bar' }; |
| |
| Api.__set__('require', () => ({ run: { call: call } })); |
| api.build(mockBuildOptions); |
| expect(call).toHaveBeenCalledWith(api, mockBuildOptions, api); |
| Api.__set__('require', apiRequire); |
| }); |
| }); |
| |
| describe('run method', () => { |
| it('should execute run', () => { |
| const run = jasmine.createSpy('run'); |
| const mockRunOptions = { foo: 'bar' }; |
| |
| Api.__set__('require', () => ({ run })); |
| api.run(mockRunOptions); |
| expect(run).toHaveBeenCalledWith(mockRunOptions); |
| Api.__set__('require', apiRequire); |
| }); |
| }); |
| |
| describe('clean method', () => { |
| it('should execute clean', () => { |
| const run = jasmine.createSpy('clean'); |
| const mockCleanOptions = { foo: 'bar' }; |
| |
| Api.__set__('require', () => ({ run })); |
| api.clean(mockCleanOptions); |
| expect(run).toHaveBeenCalledWith(mockCleanOptions); |
| Api.__set__('require', apiRequire); |
| }); |
| }); |
| |
| describe('requirements method', () => { |
| it('should execute requirements', () => { |
| const run = jasmine.createSpy('requirements'); |
| |
| Api.__set__('require', () => ({ run })); |
| api.requirements(); |
| expect(run).toHaveBeenCalled(); |
| Api.__set__('require', apiRequire); |
| }); |
| }); |
| }); |
| |
| describe('Api prototype methods', () => { |
| describe('updatePlatform method', () => { |
| it('should return a resolved promise.', () => { |
| Api.updatePlatform().then( |
| result => { |
| expect(result).toBeUndefined(); |
| } |
| ); |
| }); |
| }); |
| |
| describe('createPlatform method', () => { |
| let config; |
| |
| beforeEach(() => { |
| fs.removeSync(tmpDir); |
| config = new ConfigParser(path.join(fixturesDir, 'test-config-empty.xml')); |
| }); |
| |
| afterEach(() => { |
| fs.removeSync(tmpDir); |
| }); |
| |
| it('should create cordova project at the provided destination', () => { |
| spyOn(events, 'emit'); |
| |
| return Api.createPlatform(tmpDir, config) |
| .then((results) => { |
| expect(events.emit).toHaveBeenCalledWith( |
| 'log', |
| jasmine.stringMatching(/Creating Cordova project/) |
| ); |
| |
| expect(results.constructor.name).toBe('Api'); |
| }); |
| }); |
| |
| it('should emit createPlatform not callable when error occurs.', () => { |
| spyOn(create, 'createProject').and.returnValue(new Error('Some Random Error')); |
| expect(() => Api.createPlatform(tmpDir, config)).toThrowError(); |
| }); |
| |
| it('should throw error when config argument is missing.', () => { |
| expect(() => Api.createPlatform(tmpDir)).toThrowError(/An Electron platform can not be created with a missing config argument./); |
| }); |
| }); |
| |
| describe('version method', () => { |
| it('should get version from cordova-electron package.', () => { |
| const dummyRequire = path => { |
| expect(path).toEqual('cordova-elecrtron-resolved-package-path'); |
| return { version: '1.0.0' }; |
| }; |
| |
| dummyRequire.resolve = path => { |
| return 'cordova-elecrtron-resolved-package-path'; |
| }; |
| |
| Api.__set__('require', dummyRequire); |
| |
| expect(Api.version()).toEqual('1.0.0'); |
| }); |
| |
| it('should get version from package.json.', () => { |
| const dummyRequire = path => { |
| expect(path).toEqual('../package.json'); |
| return { version: '1.0.0' }; |
| }; |
| |
| dummyRequire.resolve = path => { |
| throw Error('random error'); |
| }; |
| |
| Api.__set__('require', dummyRequire); |
| |
| expect(Api.version()).toEqual('1.0.0'); |
| }); |
| }); |
| }); |