blob: acce236b08a5cad63056cc1d30affe52fe83a5c1 [file] [log] [blame]
/**
* @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 {EventType} from '../../../types/events';
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, EventType.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, EventType.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, EventType.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, EventType.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();
});
});