| /** | |
| * @license | |
| * Copyright 2017 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import '../../../test/common-test-setup'; | |
| import {PLUGIN_LOADING_TIMEOUT_MS} from './gr-api-utils'; | |
| import {PluginLoader} from './gr-plugin-loader'; | |
| import {stubBaseUrl, waitEventLoop} from '../../../test/test-utils'; | |
| import {addListenerForTest, stubRestApi} from '../../../test/test-utils'; | |
| import {PluginApi} from '../../../api/plugin'; | |
| import {SinonFakeTimers} from 'sinon'; | |
| import {Timestamp} from '../../../api/rest-api'; | |
| import {assert} from '@open-wc/testing'; | |
| import {getAppContext} from '../../../services/app-context'; | |
| suite('gr-plugin-loader tests', () => { | |
| let plugin: PluginApi; | |
| let url: string; | |
| let pluginLoader: PluginLoader; | |
| let clock: SinonFakeTimers; | |
| let bodyStub: sinon.SinonStub; | |
| setup(() => { | |
| clock = sinon.useFakeTimers(); | |
| stubRestApi('getAccount').returns( | |
| Promise.resolve({name: 'Judy Hopps', registered_on: '' as Timestamp}) | |
| ); | |
| stubRestApi('send').returns( | |
| Promise.resolve({...new Response(), status: 200}) | |
| ); | |
| pluginLoader = new PluginLoader( | |
| getAppContext().reportingService, | |
| getAppContext().restApiService | |
| ); | |
| bodyStub = sinon.stub(document.body, 'appendChild'); | |
| url = window.location.origin; | |
| }); | |
| teardown(() => { | |
| clock.restore(); | |
| }); | |
| test('reuse plugin for install calls', () => { | |
| pluginLoader.install( | |
| p => { | |
| plugin = p; | |
| }, | |
| '0.1', | |
| 'http://test.com/plugins/testplugin/static/test.js' | |
| ); | |
| let otherPlugin; | |
| pluginLoader.install( | |
| p => { | |
| otherPlugin = p; | |
| }, | |
| '0.1', | |
| 'http://test.com/plugins/testplugin/static/test.js' | |
| ); | |
| assert.strictEqual(plugin, otherPlugin); | |
| }); | |
| test('versioning', () => { | |
| const callback = sinon.spy(); | |
| pluginLoader.install(callback, '0.0pre-alpha'); | |
| assert(callback.notCalled); | |
| }); | |
| test('report pluginsLoaded', async () => { | |
| const pluginsLoadedStub = sinon.stub( | |
| getAppContext().reportingService, | |
| 'pluginsLoaded' | |
| ); | |
| pluginsLoadedStub.reset(); | |
| pluginLoader.loadPlugins([]); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginsLoadedStub.called); | |
| }); | |
| test('arePluginsLoaded', async () => { | |
| assert.isFalse(pluginLoader.arePluginsLoaded()); | |
| const plugins = [ | |
| 'http://test.com/plugins/foo/static/test.js', | |
| 'http://test.com/plugins/bar/static/test.js', | |
| ]; | |
| pluginLoader.loadPlugins(plugins); | |
| assert.isFalse(pluginLoader.arePluginsLoaded()); | |
| // Timeout on loading plugins | |
| clock.tick(PLUGIN_LOADING_TIMEOUT_MS * 2); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginLoader.arePluginsLoaded()); | |
| }); | |
| test('plugins installed successfully', async () => { | |
| sinon.stub(pluginLoader, 'loadJsPlugin').callsFake(url => { | |
| pluginLoader.install(() => void 0, undefined, url); | |
| }); | |
| const pluginsLoadedStub = sinon.stub( | |
| getAppContext().reportingService, | |
| 'pluginsLoaded' | |
| ); | |
| const plugins = [ | |
| 'http://test.com/plugins/foo/static/test.js', | |
| 'http://test.com/plugins/bar/static/test.js', | |
| ]; | |
| pluginLoader.loadPlugins(plugins); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar'])); | |
| assert.isTrue(pluginLoader.arePluginsLoaded()); | |
| }); | |
| test('isPluginEnabled and isPluginLoaded', async () => { | |
| sinon.stub(pluginLoader, 'loadJsPlugin').callsFake(url => { | |
| pluginLoader.install(() => void 0, undefined, url); | |
| }); | |
| const plugins = [ | |
| 'http://test.com/plugins/foo/static/test.js', | |
| 'http://test.com/plugins/bar/static/test.js', | |
| 'bar/static/test.js', | |
| ]; | |
| pluginLoader.loadPlugins(plugins); | |
| assert.isTrue( | |
| plugins.every(plugin => pluginLoader.isPluginEnabled(plugin)) | |
| ); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginLoader.arePluginsLoaded()); | |
| assert.isTrue(plugins.every(plugin => pluginLoader.isPluginLoaded(plugin))); | |
| }); | |
| test('plugins installed mixed result, 1 fail 1 succeed', async () => { | |
| const plugins = [ | |
| 'http://test.com/plugins/foo/static/test.js', | |
| 'http://test.com/plugins/bar/static/test.js', | |
| ]; | |
| const alertStub = sinon.stub(); | |
| addListenerForTest(document, 'show-alert', alertStub); | |
| sinon.stub(pluginLoader, 'loadJsPlugin').callsFake(url => { | |
| pluginLoader.install( | |
| () => { | |
| if (url === plugins[0]) { | |
| throw new Error('failed'); | |
| } | |
| }, | |
| undefined, | |
| url | |
| ); | |
| }); | |
| const pluginsLoadedStub = sinon.stub( | |
| getAppContext().reportingService, | |
| 'pluginsLoaded' | |
| ); | |
| pluginLoader.loadPlugins(plugins); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginsLoadedStub.calledWithExactly(['bar'])); | |
| assert.isTrue(pluginLoader.arePluginsLoaded()); | |
| assert.isTrue(alertStub.calledOnce); | |
| }); | |
| test('isPluginEnabled and isPluginLoaded for mixed results', async () => { | |
| const plugins = [ | |
| 'http://test.com/plugins/foo/static/test.js', | |
| 'http://test.com/plugins/bar/static/test.js', | |
| ]; | |
| const alertStub = sinon.stub(); | |
| addListenerForTest(document, 'show-alert', alertStub); | |
| sinon.stub(pluginLoader, 'loadJsPlugin').callsFake(url => { | |
| pluginLoader.install( | |
| () => { | |
| if (url === plugins[0]) { | |
| throw new Error('failed'); | |
| } | |
| }, | |
| undefined, | |
| url | |
| ); | |
| }); | |
| const pluginsLoadedStub = sinon.stub( | |
| getAppContext().reportingService, | |
| 'pluginsLoaded' | |
| ); | |
| pluginLoader.loadPlugins(plugins); | |
| assert.isTrue( | |
| plugins.every(plugin => pluginLoader.isPluginEnabled(plugin)) | |
| ); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginsLoadedStub.calledWithExactly(['bar'])); | |
| assert.isTrue(pluginLoader.arePluginsLoaded()); | |
| assert.isTrue(alertStub.calledOnce); | |
| assert.isTrue(pluginLoader.isPluginLoaded(plugins[1])); | |
| assert.isFalse(pluginLoader.isPluginLoaded(plugins[0])); | |
| }); | |
| test('plugins installed all failed', async () => { | |
| const plugins = [ | |
| 'http://test.com/plugins/foo/static/test.js', | |
| 'http://test.com/plugins/bar/static/test.js', | |
| ]; | |
| const alertStub = sinon.stub(); | |
| addListenerForTest(document, 'show-alert', alertStub); | |
| sinon.stub(pluginLoader, 'loadJsPlugin').callsFake(url => { | |
| pluginLoader.install( | |
| () => { | |
| throw new Error('failed'); | |
| }, | |
| undefined, | |
| url | |
| ); | |
| }); | |
| const pluginsLoadedStub = sinon.stub( | |
| getAppContext().reportingService, | |
| 'pluginsLoaded' | |
| ); | |
| pluginLoader.loadPlugins(plugins); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginsLoadedStub.calledWithExactly([])); | |
| assert.isTrue(pluginLoader.arePluginsLoaded()); | |
| assert.isTrue(alertStub.calledTwice); | |
| }); | |
| test('plugins installed failed because of wrong version', async () => { | |
| const plugins = [ | |
| 'http://test.com/plugins/foo/static/test.js', | |
| 'http://test.com/plugins/bar/static/test.js', | |
| ]; | |
| const alertStub = sinon.stub(); | |
| addListenerForTest(document, 'show-alert', alertStub); | |
| sinon.stub(pluginLoader, 'loadJsPlugin').callsFake(url => { | |
| pluginLoader.install(() => {}, url === plugins[0] ? '' : 'alpha', url); | |
| }); | |
| const pluginsLoadedStub = sinon.stub( | |
| getAppContext().reportingService, | |
| 'pluginsLoaded' | |
| ); | |
| pluginLoader.loadPlugins(plugins); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo'])); | |
| assert.isTrue(pluginLoader.arePluginsLoaded()); | |
| assert.isTrue(alertStub.calledOnce); | |
| }); | |
| test('multiple assets for same plugin installed successfully', async () => { | |
| sinon.stub(pluginLoader, 'loadJsPlugin').callsFake(url => { | |
| pluginLoader.install(() => void 0, undefined, url); | |
| }); | |
| const pluginsLoadedStub = sinon.stub( | |
| getAppContext().reportingService, | |
| 'pluginsLoaded' | |
| ); | |
| const plugins = [ | |
| 'http://test.com/plugins/foo/static/test.js', | |
| 'http://test.com/plugins/foo/static/test2.js', | |
| 'http://test.com/plugins/bar/static/test.js', | |
| ]; | |
| pluginLoader.loadPlugins(plugins); | |
| await waitEventLoop(); | |
| assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar'])); | |
| assert.isTrue(pluginLoader.arePluginsLoaded()); | |
| }); | |
| suite('plugin path and url', () => { | |
| let loadJsPluginStub: sinon.SinonStub; | |
| setup(() => { | |
| loadJsPluginStub = sinon.stub(); | |
| sinon | |
| .stub(pluginLoader, 'createScriptTag') | |
| .callsFake((url: string, _onerror?: OnErrorEventHandler | undefined) => | |
| loadJsPluginStub(url) | |
| ); | |
| }); | |
| test('invalid plugin path', () => { | |
| const failToLoadStub = sinon.stub(); | |
| sinon.stub(pluginLoader, 'failToLoad').callsFake((...args) => { | |
| failToLoadStub(...args); | |
| }); | |
| pluginLoader.loadPlugins(['foo/bar']); | |
| assert.isTrue(failToLoadStub.calledOnce); | |
| assert.isTrue( | |
| failToLoadStub.calledWithExactly( | |
| 'Unrecognized plugin path foo/bar', | |
| 'foo/bar' | |
| ) | |
| ); | |
| }); | |
| test('relative path for plugins', () => { | |
| pluginLoader.loadPlugins(['foo/bar.js']); | |
| assert.isTrue(loadJsPluginStub.calledOnce); | |
| assert.isTrue(loadJsPluginStub.calledWithExactly(`${url}/foo/bar.js`)); | |
| }); | |
| test('relative path should honor getBaseUrl', () => { | |
| const testUrl = '/test'; | |
| stubBaseUrl(testUrl); | |
| pluginLoader.loadPlugins(['foo/bar.js']); | |
| assert.isTrue(loadJsPluginStub.calledOnce); | |
| assert.isTrue( | |
| loadJsPluginStub.calledWithExactly(`${url}${testUrl}/foo/bar.js`) | |
| ); | |
| }); | |
| test('absolute path for plugins', () => { | |
| pluginLoader.loadPlugins(['http://e.com/foo/bar.js']); | |
| assert.isTrue(loadJsPluginStub.calledOnce); | |
| assert.isTrue( | |
| loadJsPluginStub.calledWithExactly('http://e.com/foo/bar.js') | |
| ); | |
| }); | |
| }); | |
| suite('With ASSETS_PATH', () => { | |
| let loadJsPluginStub: sinon.SinonStub; | |
| setup(() => { | |
| window.ASSETS_PATH = 'https://cdn.com'; | |
| loadJsPluginStub = sinon.stub(); | |
| sinon | |
| .stub(pluginLoader, 'createScriptTag') | |
| .callsFake((url: string, _onerror?: OnErrorEventHandler | undefined) => | |
| loadJsPluginStub(url) | |
| ); | |
| }); | |
| teardown(() => { | |
| window.ASSETS_PATH = ''; | |
| }); | |
| test('Should try load plugins from assets path instead', () => { | |
| pluginLoader.loadPlugins(['foo/bar.js']); | |
| assert.isTrue(loadJsPluginStub.calledOnce); | |
| assert.isTrue( | |
| loadJsPluginStub.calledWithExactly('https://cdn.com/foo/bar.js') | |
| ); | |
| }); | |
| test('Should honor original path if exists', () => { | |
| pluginLoader.loadPlugins(['http://e.com/foo/bar.js']); | |
| assert.isTrue(loadJsPluginStub.calledOnce); | |
| assert.isTrue( | |
| loadJsPluginStub.calledWithExactly('http://e.com/foo/bar.js') | |
| ); | |
| }); | |
| test('Should try replace current host with assetsPath', () => { | |
| const host = window.location.origin; | |
| pluginLoader.loadPlugins([`${host}/foo/bar.js`]); | |
| assert.isTrue(loadJsPluginStub.calledOnce); | |
| assert.isTrue( | |
| loadJsPluginStub.calledWithExactly('https://cdn.com/foo/bar.js') | |
| ); | |
| }); | |
| }); | |
| test('adds js plugins will call the body', () => { | |
| pluginLoader.loadPlugins([ | |
| 'http://e.com/foo/bar.js', | |
| 'http://e.com/bar/foo.js', | |
| ]); | |
| assert.isTrue(bodyStub.calledTwice); | |
| }); | |
| test('can call awaitPluginsLoaded multiple times', async () => { | |
| const plugins = ['http://e.com/foo/bar.js', 'http://e.com/bar/foo.js']; | |
| let installed = false; | |
| function pluginCallback(url: string) { | |
| if (url === plugins[1]) { | |
| installed = true; | |
| } | |
| } | |
| sinon.stub(pluginLoader, 'loadJsPlugin').callsFake(url => { | |
| pluginLoader.install(() => pluginCallback(url), undefined, url); | |
| }); | |
| pluginLoader.loadPlugins(plugins); | |
| await pluginLoader.awaitPluginsLoaded(); | |
| assert.isTrue(installed); | |
| await pluginLoader.awaitPluginsLoaded(); | |
| }); | |
| }); |