Merge "Move plugin loading related methods to PluginLoader." into stable-3.1
This commit is contained in:
@@ -89,6 +89,7 @@ limitations under the License.
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
Gerrit._testOnly_resetPlugins();
|
||||
});
|
||||
|
||||
suite('by default', () => {
|
||||
@@ -141,7 +142,7 @@ limitations under the License.
|
||||
new URL('test/plugin.html?' + Math.random(),
|
||||
window.location.href).toString());
|
||||
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
element = createElement();
|
||||
});
|
||||
|
||||
|
@@ -725,7 +725,7 @@ limitations under the License.
|
||||
},
|
||||
'0.1',
|
||||
'http://some/plugins/url.html');
|
||||
Gerrit._setPluginsCount(0);
|
||||
Gerrit._loadPlugins([]);
|
||||
flush(() => {
|
||||
assert.strictEqual(hookEl.plugin, plugin);
|
||||
assert.strictEqual(hookEl.change, element.change);
|
||||
|
@@ -81,7 +81,7 @@ limitations under the License.
|
||||
});
|
||||
element = fixture('basic');
|
||||
sandbox.stub(element.$.actions, 'reload').returns(Promise.resolve());
|
||||
Gerrit._setPluginsCount(0);
|
||||
Gerrit._loadPlugins([]);
|
||||
});
|
||||
|
||||
teardown(done => {
|
||||
|
@@ -426,7 +426,8 @@
|
||||
},
|
||||
|
||||
_computePluginScreenName({plugin, screen}) {
|
||||
return Gerrit._getPluginScreenName(plugin, screen);
|
||||
if (!plugin || !screen) return '';
|
||||
return `${plugin}-screen-${screen}`;
|
||||
},
|
||||
|
||||
_logWelcome() {
|
||||
|
@@ -39,7 +39,7 @@ limitations under the License.
|
||||
let plugin;
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||
Gerrit._loadPlugins([]);
|
||||
adminApi = plugin.admin();
|
||||
});
|
||||
|
||||
|
@@ -67,7 +67,7 @@ limitations under the License.
|
||||
replacementHook = plugin.registerCustomComponent(
|
||||
'second', 'other-module', {replace: true});
|
||||
// Mimic all plugins loaded.
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
flush(done);
|
||||
});
|
||||
|
||||
|
@@ -27,33 +27,29 @@
|
||||
},
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.BaseUrlBehavior,
|
||||
],
|
||||
|
||||
_configChanged(config) {
|
||||
const plugins = config.plugin;
|
||||
const htmlPlugins = (plugins.html_resource_paths || [])
|
||||
.map(p => this._urlFor(p))
|
||||
.filter(p => !Gerrit._isPluginPreloaded(p));
|
||||
const htmlPlugins = (plugins.html_resource_paths || []);
|
||||
const jsPlugins =
|
||||
this._handleMigrations(plugins.js_resource_paths || [], htmlPlugins)
|
||||
.map(p => this._urlFor(p))
|
||||
.filter(p => !Gerrit._isPluginPreloaded(p));
|
||||
this._handleMigrations(plugins.js_resource_paths || [], htmlPlugins);
|
||||
|
||||
const shouldLoadTheme = config.default_theme &&
|
||||
!Gerrit._isPluginPreloaded('preloaded:gerrit-theme');
|
||||
const defaultTheme =
|
||||
shouldLoadTheme ? this._urlFor(config.default_theme) : null;
|
||||
const themeToLoad =
|
||||
shouldLoadTheme ? [config.default_theme] : [];
|
||||
|
||||
// Theme should be loaded first if has one to have better UX
|
||||
const pluginsPending =
|
||||
[].concat(jsPlugins, htmlPlugins, defaultTheme || []);
|
||||
Gerrit._setPluginsPending(pluginsPending);
|
||||
if (defaultTheme) {
|
||||
// Make theme first to be first to load.
|
||||
// Load sync to work around rare theme loading race condition.
|
||||
this._importHtmlPlugins([defaultTheme], true);
|
||||
themeToLoad.concat(jsPlugins, htmlPlugins);
|
||||
|
||||
const pluginOpts = {};
|
||||
|
||||
if (shouldLoadTheme) {
|
||||
// Theme needs to be loaded synchronous.
|
||||
pluginOpts[config.default_theme] = {sync: true};
|
||||
}
|
||||
this._loadJsPlugins(jsPlugins);
|
||||
this._importHtmlPlugins(htmlPlugins);
|
||||
|
||||
Gerrit._loadPlugins(pluginsPending, pluginOpts);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -66,53 +62,5 @@
|
||||
return !htmlPlugins.includes(counterpart);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @suppress {checkTypes}
|
||||
* States that it expects no more than 3 parameters, but that's not true.
|
||||
* @todo (beckysiegel) check Polymer annotations and submit change.
|
||||
* @param {Array} plugins
|
||||
* @param {boolean=} opt_sync
|
||||
*/
|
||||
_importHtmlPlugins(plugins, opt_sync) {
|
||||
const async = !opt_sync;
|
||||
for (const url of plugins) {
|
||||
// onload (second param) needs to be a function. When null or undefined
|
||||
// were passed, plugins were not loaded correctly.
|
||||
(this.importHref || Polymer.importHref)(
|
||||
this._urlFor(url), () => {},
|
||||
Gerrit._pluginInstallError.bind(null, `${url} import error`),
|
||||
async);
|
||||
}
|
||||
},
|
||||
|
||||
_loadJsPlugins(plugins) {
|
||||
for (const url of plugins) {
|
||||
this._createScriptTag(this._urlFor(url));
|
||||
}
|
||||
},
|
||||
|
||||
_createScriptTag(url) {
|
||||
const el = document.createElement('script');
|
||||
el.defer = true;
|
||||
el.src = url;
|
||||
el.onerror = Gerrit._pluginInstallError.bind(null, `${url} load error`);
|
||||
return document.body.appendChild(el);
|
||||
},
|
||||
|
||||
_urlFor(pathOrUrl) {
|
||||
if (!pathOrUrl) {
|
||||
return pathOrUrl;
|
||||
}
|
||||
if (pathOrUrl.startsWith('preloaded:') ||
|
||||
pathOrUrl.startsWith('http')) {
|
||||
// Plugins are loaded from another domain or preloaded.
|
||||
return pathOrUrl;
|
||||
}
|
||||
if (!pathOrUrl.startsWith('/')) {
|
||||
pathOrUrl = '/' + pathOrUrl;
|
||||
}
|
||||
return window.location.origin + this.getBaseUrl() + pathOrUrl;
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
@@ -38,195 +38,57 @@ limitations under the License.
|
||||
suite('gr-plugin-host tests', () => {
|
||||
let element;
|
||||
let sandbox;
|
||||
let url;
|
||||
|
||||
setup(() => {
|
||||
element = fixture('basic');
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(document.body, 'appendChild');
|
||||
sandbox.stub(element, 'importHref');
|
||||
url = window.location.origin;
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
test('counts plugins', () => {
|
||||
sandbox.stub(Gerrit, '_setPluginsCount');
|
||||
test('load plugins should be called', () => {
|
||||
sandbox.stub(Gerrit, '_loadPlugins');
|
||||
element.config = {
|
||||
plugin: {
|
||||
html_resource_paths: ['plugins/foo/bar', 'plugins/baz'],
|
||||
js_resource_paths: ['plugins/42'],
|
||||
},
|
||||
};
|
||||
assert.isTrue(Gerrit._setPluginsCount.calledWith(3));
|
||||
assert.isTrue(Gerrit._loadPlugins.calledOnce);
|
||||
assert.isTrue(Gerrit._loadPlugins.calledWith([
|
||||
'plugins/42', 'plugins/foo/bar', 'plugins/baz',
|
||||
], {}));
|
||||
});
|
||||
|
||||
test('imports relative html plugins from config', () => {
|
||||
sandbox.stub(Gerrit, '_pluginInstallError');
|
||||
element.config = {
|
||||
plugin: {html_resource_paths: ['foo/bar', 'baz']},
|
||||
};
|
||||
assert.equal(element.importHref.firstCall.args[0], url + '/foo/bar');
|
||||
assert.isTrue(element.importHref.firstCall.args[3]);
|
||||
|
||||
assert.equal(element.importHref.secondCall.args[0], url + '/baz');
|
||||
assert.isTrue(element.importHref.secondCall.args[3]);
|
||||
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 0);
|
||||
element.importHref.firstCall.args[2]();
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 1);
|
||||
element.importHref.secondCall.args[2]();
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 2);
|
||||
});
|
||||
|
||||
test('imports relative html plugins from config with a base url', () => {
|
||||
sandbox.stub(Gerrit, '_pluginInstallError');
|
||||
sandbox.stub(element, 'getBaseUrl').returns('/the-base');
|
||||
element.config = {
|
||||
plugin: {html_resource_paths: ['foo/bar', 'baz']}};
|
||||
assert.equal(element.importHref.firstCall.args[0],
|
||||
url + '/the-base/foo/bar');
|
||||
assert.isTrue(element.importHref.firstCall.args[3]);
|
||||
|
||||
assert.equal(element.importHref.secondCall.args[0],
|
||||
url + '/the-base/baz');
|
||||
assert.isTrue(element.importHref.secondCall.args[3]);
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 0);
|
||||
element.importHref.firstCall.args[2]();
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 1);
|
||||
element.importHref.secondCall.args[2]();
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 2);
|
||||
});
|
||||
|
||||
test('importHref is not called with null callback functions', () => {
|
||||
const plugins = ['path/to/plugin'];
|
||||
element._importHtmlPlugins(plugins);
|
||||
assert.isTrue(element.importHref.calledOnce);
|
||||
assert.isFunction(element.importHref.lastCall.args[1]);
|
||||
assert.isFunction(element.importHref.lastCall.args[2]);
|
||||
});
|
||||
|
||||
test('imports absolute html plugins from config', () => {
|
||||
sandbox.stub(Gerrit, '_pluginInstallError');
|
||||
test('theme plugins should be loaded if enabled', () => {
|
||||
sandbox.stub(Gerrit, '_loadPlugins');
|
||||
element.config = {
|
||||
default_theme: 'gerrit-theme.html',
|
||||
plugin: {
|
||||
html_resource_paths: [
|
||||
'http://example.com/foo/bar',
|
||||
'https://example.com/baz',
|
||||
],
|
||||
html_resource_paths: ['plugins/foo/bar', 'plugins/baz'],
|
||||
js_resource_paths: ['plugins/42'],
|
||||
},
|
||||
};
|
||||
assert.equal(element.importHref.firstCall.args[0],
|
||||
'http://example.com/foo/bar');
|
||||
assert.isTrue(element.importHref.firstCall.args[3]);
|
||||
|
||||
assert.equal(element.importHref.secondCall.args[0],
|
||||
'https://example.com/baz');
|
||||
assert.isTrue(element.importHref.secondCall.args[3]);
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 0);
|
||||
element.importHref.firstCall.args[2]();
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 1);
|
||||
element.importHref.secondCall.args[2]();
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 2);
|
||||
assert.isTrue(Gerrit._loadPlugins.calledOnce);
|
||||
assert.isTrue(Gerrit._loadPlugins.calledWith([
|
||||
'gerrit-theme.html', 'plugins/42', 'plugins/foo/bar', 'plugins/baz',
|
||||
], {'gerrit-theme.html': {sync: true}}));
|
||||
});
|
||||
|
||||
test('adds js plugins from config to the body', () => {
|
||||
element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}};
|
||||
assert.isTrue(document.body.appendChild.calledTwice);
|
||||
});
|
||||
|
||||
test('imports relative js plugins from config', () => {
|
||||
sandbox.stub(element, '_createScriptTag');
|
||||
element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}};
|
||||
assert.isTrue(element._createScriptTag.calledWith(url + '/foo/bar'));
|
||||
assert.isTrue(element._createScriptTag.calledWith(url + '/baz'));
|
||||
});
|
||||
|
||||
test('imports relative html plugins from config with a base url', () => {
|
||||
sandbox.stub(element, '_createScriptTag');
|
||||
sandbox.stub(element, 'getBaseUrl').returns('/the-base');
|
||||
element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}};
|
||||
assert.isTrue(element._createScriptTag.calledWith(
|
||||
url + '/the-base/foo/bar'));
|
||||
assert.isTrue(element._createScriptTag.calledWith(
|
||||
url + '/the-base/baz'));
|
||||
});
|
||||
|
||||
test('imports absolute html plugins from config', () => {
|
||||
sandbox.stub(element, '_createScriptTag');
|
||||
element.config = {
|
||||
plugin: {
|
||||
js_resource_paths: [
|
||||
'http://example.com/foo/bar',
|
||||
'https://example.com/baz',
|
||||
],
|
||||
},
|
||||
};
|
||||
assert.isTrue(element._createScriptTag.calledWith(
|
||||
'http://example.com/foo/bar'));
|
||||
assert.isTrue(element._createScriptTag.calledWith(
|
||||
'https://example.com/baz'));
|
||||
});
|
||||
|
||||
test('default theme is loaded with html plugins', () => {
|
||||
sandbox.stub(Gerrit, '_pluginInstallError');
|
||||
element.config = {
|
||||
default_theme: '/oof',
|
||||
plugin: {
|
||||
html_resource_paths: ['some'],
|
||||
},
|
||||
};
|
||||
assert.equal(element.importHref.firstCall.args[0], url + '/oof');
|
||||
assert.isFalse(element.importHref.firstCall.args[3]);
|
||||
|
||||
assert.equal(element.importHref.secondCall.args[0], url + '/some');
|
||||
assert.isTrue(element.importHref.secondCall.args[3]);
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 0);
|
||||
element.importHref.firstCall.args[2]();
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 1);
|
||||
element.importHref.secondCall.args[2]();
|
||||
assert.equal(Gerrit._pluginInstallError.callCount, 2);
|
||||
});
|
||||
|
||||
test('default theme is loaded with html plugins', () => {
|
||||
sandbox.stub(Gerrit, '_setPluginsPending');
|
||||
element.config = {
|
||||
default_theme: '/oof',
|
||||
plugin: {},
|
||||
};
|
||||
assert.isTrue(Gerrit._setPluginsPending.calledWith([url + '/oof']));
|
||||
});
|
||||
|
||||
test('skips default theme loading if preloaded', () => {
|
||||
test('skip theme if preloaded', () => {
|
||||
sandbox.stub(Gerrit, '_isPluginPreloaded')
|
||||
.withArgs('preloaded:gerrit-theme').returns(true);
|
||||
sandbox.stub(Gerrit, '_setPluginsPending');
|
||||
sandbox.stub(Gerrit, '_loadPlugins');
|
||||
element.config = {
|
||||
default_theme: '/oof',
|
||||
plugin: {},
|
||||
};
|
||||
assert.isFalse(element.importHref.calledWith(url + '/oof'));
|
||||
});
|
||||
|
||||
test('skips preloaded plugins', () => {
|
||||
sandbox.stub(Gerrit, '_isPluginPreloaded')
|
||||
.withArgs(url + '/plugins/foo/bar').returns(true)
|
||||
.withArgs(url + '/plugins/42').returns(true);
|
||||
sandbox.stub(Gerrit, '_setPluginsCount');
|
||||
sandbox.stub(Gerrit, '_setPluginsPending');
|
||||
sandbox.stub(element, '_createScriptTag');
|
||||
element.config = {
|
||||
plugin: {
|
||||
html_resource_paths: ['plugins/foo/bar', 'plugins/baz'],
|
||||
js_resource_paths: ['plugins/42'],
|
||||
},
|
||||
};
|
||||
assert.isTrue(
|
||||
Gerrit._setPluginsPending.calledWith([url + '/plugins/baz']));
|
||||
assert.equal(element._createScriptTag.callCount, 0);
|
||||
assert.isTrue(element.importHref.calledWith(url + '/plugins/baz'));
|
||||
assert.isTrue(Gerrit._loadPlugins.calledOnce);
|
||||
assert.isTrue(Gerrit._loadPlugins.calledWith([], {}));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@@ -46,7 +46,7 @@ limitations under the License.
|
||||
let plugin;
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||
Gerrit._loadPlugins([]);
|
||||
repoApi = plugin.project();
|
||||
});
|
||||
|
||||
|
@@ -48,7 +48,7 @@ limitations under the License.
|
||||
let plugin;
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||
Gerrit._loadPlugins([]);
|
||||
settingsApi = plugin.settings();
|
||||
});
|
||||
|
||||
|
@@ -46,7 +46,7 @@ limitations under the License.
|
||||
let plugin;
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||
Gerrit._loadPlugins([]);
|
||||
stylesApi = plugin.styles();
|
||||
});
|
||||
|
||||
@@ -76,7 +76,7 @@ limitations under the License.
|
||||
let plugin;
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||
Gerrit._loadPlugins([]);
|
||||
stylesApi = plugin.styles();
|
||||
displayInlineStyle = stylesApi.css('display: inline');
|
||||
displayNoneStyle = stylesApi.css('display: none');
|
||||
|
@@ -67,7 +67,7 @@ limitations under the License.
|
||||
stub('gr-custom-plugin-header', {
|
||||
ready() { customHeader = this; },
|
||||
});
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
});
|
||||
|
||||
test('sets logo and title', done => {
|
||||
|
@@ -117,7 +117,7 @@ limitations under the License.
|
||||
assert.strictEqual(element.style.backgroundImage, '');
|
||||
|
||||
// Emulate plugins loaded.
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
|
||||
Promise.all([
|
||||
element.$.restAPI.getConfig(),
|
||||
@@ -155,7 +155,7 @@ limitations under the License.
|
||||
assert.isFalse(element.hasAttribute('hidden'));
|
||||
|
||||
// Emulate plugins loaded.
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
|
||||
return Promise.all([
|
||||
element.$.restAPI.getConfig(),
|
||||
@@ -197,7 +197,7 @@ limitations under the License.
|
||||
_account_id: 123,
|
||||
};
|
||||
// Emulate plugins loaded.
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
|
||||
return Promise.all([
|
||||
element.$.restAPI.getConfig(),
|
||||
|
@@ -19,6 +19,7 @@
|
||||
'use strict';
|
||||
|
||||
const PRELOADED_PROTOCOL = 'preloaded:';
|
||||
const PLUGIN_LOADING_TIMEOUT_MS = 10000;
|
||||
|
||||
let _restAPI;
|
||||
function getRestAPI() {
|
||||
@@ -28,6 +29,10 @@
|
||||
return _restAPI;
|
||||
}
|
||||
|
||||
function getBaseUrl() {
|
||||
return Gerrit.BaseUrlBehavior.getBaseUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the plugin base on the url.
|
||||
* @param {string|URL} url
|
||||
@@ -96,6 +101,9 @@
|
||||
getPluginNameFromUrl,
|
||||
send,
|
||||
getRestAPI,
|
||||
getBaseUrl,
|
||||
PRELOADED_PROTOCOL,
|
||||
PLUGIN_LOADING_TIMEOUT_MS,
|
||||
|
||||
// TEST only methods
|
||||
testOnly_resetInternalState,
|
||||
|
@@ -58,13 +58,14 @@ breaking changes to gr-change-actions won’t be noticed.
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
// Mimic all plugins loaded.
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
changeActions = plugin.changeActions();
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
changeActions = null;
|
||||
Gerrit._testOnly_resetPlugins();
|
||||
});
|
||||
|
||||
test('does not throw', ()=> {
|
||||
@@ -85,11 +86,12 @@ breaking changes to gr-change-actions won’t be noticed.
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
changeActions = plugin.changeActions();
|
||||
// Mimic all plugins loaded.
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
changeActions = null;
|
||||
Gerrit._testOnly_resetPlugins();
|
||||
});
|
||||
|
||||
test('property existence', () => {
|
||||
|
@@ -23,42 +23,12 @@
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Hash of loaded and installed plugins, name to Plugin object.
|
||||
*/
|
||||
const _plugins = {};
|
||||
|
||||
/**
|
||||
* Array of plugin URLs to be loaded, name to url.
|
||||
*/
|
||||
let _pluginsPending = {};
|
||||
|
||||
let _pluginsInstalled = [];
|
||||
|
||||
let _pluginsPendingCount = -1;
|
||||
|
||||
const UNKNOWN_PLUGIN = 'unknown';
|
||||
const PRELOADED_PROTOCOL = 'preloaded:';
|
||||
|
||||
const PLUGIN_LOADING_TIMEOUT_MS = 10000;
|
||||
|
||||
let _reporting;
|
||||
const getReporting = () => {
|
||||
if (!_reporting) {
|
||||
_reporting = document.createElement('gr-reporting');
|
||||
}
|
||||
return _reporting;
|
||||
};
|
||||
|
||||
// Import utils methods
|
||||
const {
|
||||
getPluginNameFromUrl,
|
||||
send,
|
||||
getRestAPI,
|
||||
} = window._apiUtils;
|
||||
|
||||
const API_VERSION = '0.1';
|
||||
|
||||
/**
|
||||
* Trigger the preinstalls for bundled plugins.
|
||||
* This needs to happen before Gerrit as plugin bundle overrides the Gerrit.
|
||||
@@ -72,9 +42,7 @@
|
||||
|
||||
window.Gerrit = window.Gerrit || {};
|
||||
const Gerrit = window.Gerrit;
|
||||
|
||||
let _resolveAllPluginsLoaded = null;
|
||||
let _allPluginsPromise = null;
|
||||
Gerrit._pluginLoader = new PluginLoader();
|
||||
|
||||
Gerrit._endpoints = new GrPluginEndpoints();
|
||||
|
||||
@@ -85,20 +53,13 @@
|
||||
const {
|
||||
testOnly_resetInternalState,
|
||||
} = window._apiUtils;
|
||||
Gerrit._testOnly_installPreloadedPlugins = installPreloadedPlugins;
|
||||
Gerrit._testOnly_installPreloadedPlugins = (...args) => Gerrit._pluginLoader
|
||||
.installPreloadedPlugins(...args);
|
||||
Gerrit._testOnly_flushPreinstalls = flushPreinstalls;
|
||||
Gerrit._testOnly_resetPlugins = () => {
|
||||
_allPluginsPromise = null;
|
||||
_pluginsInstalled = [];
|
||||
_pluginsPending = {};
|
||||
_pluginsPendingCount = -1;
|
||||
_reporting = null;
|
||||
_resolveAllPluginsLoaded = null;
|
||||
testOnly_resetInternalState();
|
||||
Gerrit._endpoints = new GrPluginEndpoints();
|
||||
for (const k of Object.keys(_plugins)) {
|
||||
delete _plugins[k];
|
||||
}
|
||||
Gerrit._pluginLoader = new PluginLoader();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -122,36 +83,7 @@
|
||||
};
|
||||
|
||||
Gerrit.install = function(callback, opt_version, opt_src) {
|
||||
// HTML import polyfill adds __importElement pointing to the import tag.
|
||||
const script = document.currentScript &&
|
||||
(document.currentScript.__importElement || document.currentScript);
|
||||
|
||||
let src = opt_src || (script && script.src);
|
||||
if (!src || src.startsWith('data:')) {
|
||||
src = script && script.baseURI;
|
||||
}
|
||||
const name = getPluginNameFromUrl(src);
|
||||
|
||||
if (opt_version && opt_version !== API_VERSION) {
|
||||
Gerrit._pluginInstallError(`Plugin ${name} install error: only version ` +
|
||||
API_VERSION + ' is supported in PolyGerrit. ' + opt_version +
|
||||
' was given.');
|
||||
return;
|
||||
}
|
||||
|
||||
const existingPlugin = _plugins[name];
|
||||
const plugin = existingPlugin || new Plugin(src);
|
||||
try {
|
||||
callback(plugin);
|
||||
if (name) {
|
||||
_plugins[name] = plugin;
|
||||
}
|
||||
if (!existingPlugin) {
|
||||
Gerrit._pluginInstalled(src);
|
||||
}
|
||||
} catch (e) {
|
||||
Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
|
||||
}
|
||||
Gerrit._pluginLoader.install(callback, opt_version, opt_src);
|
||||
};
|
||||
|
||||
Gerrit.getLoggedIn = function() {
|
||||
@@ -195,96 +127,33 @@
|
||||
};
|
||||
|
||||
Gerrit.awaitPluginsLoaded = function() {
|
||||
if (!_allPluginsPromise) {
|
||||
if (Gerrit._arePluginsLoaded()) {
|
||||
_allPluginsPromise = Promise.resolve();
|
||||
} else {
|
||||
let timeoutId;
|
||||
_allPluginsPromise =
|
||||
Promise.race([
|
||||
new Promise(resolve => _resolveAllPluginsLoaded = resolve),
|
||||
new Promise(resolve => timeoutId = setTimeout(
|
||||
Gerrit._pluginLoadingTimeout, PLUGIN_LOADING_TIMEOUT_MS)),
|
||||
]).then(() => clearTimeout(timeoutId));
|
||||
}
|
||||
}
|
||||
return _allPluginsPromise;
|
||||
return Gerrit._pluginLoader.awaitPluginsLoaded();
|
||||
};
|
||||
|
||||
Gerrit._pluginLoadingTimeout = function() {
|
||||
console.error(`Failed to load plugins: ${Object.keys(_pluginsPending)}`);
|
||||
Gerrit._setPluginsPending([]);
|
||||
};
|
||||
// TODO(taoalpha): consider removing these proxy methods
|
||||
// and using _pluginLoader directly
|
||||
|
||||
Gerrit._setPluginsPending = function(plugins) {
|
||||
_pluginsPending = plugins.reduce((o, url) => {
|
||||
// TODO(viktard): Remove guard (@see Issue 8962)
|
||||
o[getPluginNameFromUrl(url) || UNKNOWN_PLUGIN] = url;
|
||||
return o;
|
||||
}, {});
|
||||
Gerrit._setPluginsCount(Object.keys(_pluginsPending).length);
|
||||
};
|
||||
|
||||
Gerrit._setPluginsCount = function(count) {
|
||||
_pluginsPendingCount = count;
|
||||
if (Gerrit._arePluginsLoaded()) {
|
||||
getReporting().pluginsLoaded(_pluginsInstalled);
|
||||
if (_resolveAllPluginsLoaded) {
|
||||
_resolveAllPluginsLoaded();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Gerrit._pluginInstallError = function(message) {
|
||||
document.dispatchEvent(new CustomEvent('show-alert', {
|
||||
detail: {
|
||||
message: `Plugin install error: ${message}`,
|
||||
},
|
||||
}));
|
||||
console.info(`Plugin install error: ${message}`);
|
||||
Gerrit._setPluginsCount(_pluginsPendingCount - 1);
|
||||
};
|
||||
|
||||
Gerrit._pluginInstalled = function(url) {
|
||||
const name = getPluginNameFromUrl(url) || UNKNOWN_PLUGIN;
|
||||
if (!_pluginsPending[name]) {
|
||||
console.warn(`Unexpected plugin ${name} installed from ${url}.`);
|
||||
} else {
|
||||
delete _pluginsPending[name];
|
||||
_pluginsInstalled.push(name);
|
||||
Gerrit._setPluginsCount(_pluginsPendingCount - 1);
|
||||
getReporting().pluginLoaded(name);
|
||||
console.log(`Plugin ${name} installed.`);
|
||||
}
|
||||
Gerrit._loadPlugins = function(plugins, opt_option) {
|
||||
Gerrit._pluginLoader.loadPlugins(plugins, opt_option);
|
||||
};
|
||||
|
||||
Gerrit._arePluginsLoaded = function() {
|
||||
return _pluginsPendingCount === 0;
|
||||
};
|
||||
|
||||
Gerrit._getPluginScreenName = function(pluginName, screenName) {
|
||||
return `${pluginName}-screen-${screenName}`;
|
||||
return Gerrit._pluginLoader.arePluginsLoaded;
|
||||
};
|
||||
|
||||
Gerrit._isPluginPreloaded = function(url) {
|
||||
const name = getPluginNameFromUrl(url);
|
||||
if (name && Gerrit._preloadedPlugins) {
|
||||
return name in Gerrit._preloadedPlugins;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return Gerrit._pluginLoader.isPluginPreloaded(url);
|
||||
};
|
||||
|
||||
function installPreloadedPlugins() {
|
||||
if (!Gerrit._preloadedPlugins) { return; }
|
||||
for (const name in Gerrit._preloadedPlugins) {
|
||||
if (!Gerrit._preloadedPlugins.hasOwnProperty(name)) { continue; }
|
||||
const callback = Gerrit._preloadedPlugins[name];
|
||||
Gerrit.install(callback, API_VERSION, PRELOADED_PROTOCOL + name);
|
||||
}
|
||||
}
|
||||
Gerrit._isPluginEnabled = function(pathOrUrl) {
|
||||
return Gerrit._pluginLoader.isPluginEnabled(pathOrUrl);
|
||||
};
|
||||
|
||||
Gerrit._isPluginLoaded = function(pathOrUrl) {
|
||||
return Gerrit._pluginLoader.isPluginLoaded(pathOrUrl);
|
||||
};
|
||||
|
||||
// Preloaded plugins should be installed after Gerrit.install() is set,
|
||||
// since plugin preloader substitutes Gerrit.install() temporarily.
|
||||
installPreloadedPlugins();
|
||||
Gerrit._pluginLoader.installPreloadedPlugins();
|
||||
})(window);
|
@@ -37,11 +37,11 @@ limitations under the License.
|
||||
<script>
|
||||
suite('gr-gerrit tests', () => {
|
||||
let element;
|
||||
let plugin;
|
||||
let sandbox;
|
||||
let sendStub;
|
||||
|
||||
setup(() => {
|
||||
this.clock = sinon.useFakeTimers();
|
||||
sandbox = sinon.sandbox.create();
|
||||
sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
|
||||
stub('gr-rest-api-interface', {
|
||||
@@ -53,136 +53,48 @@ limitations under the License.
|
||||
},
|
||||
});
|
||||
element = fixture('basic');
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
Gerrit._setPluginsPending([]);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
this.clock.restore();
|
||||
sandbox.restore();
|
||||
element._removeEventCallbacks();
|
||||
plugin = null;
|
||||
Gerrit._testOnly_resetPlugins();
|
||||
});
|
||||
|
||||
test('reuse plugin for install calls', () => {
|
||||
let otherPlugin;
|
||||
Gerrit.install(p => { otherPlugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
assert.strictEqual(plugin, otherPlugin);
|
||||
});
|
||||
|
||||
test('flushes preinstalls if provided', () => {
|
||||
assert.doesNotThrow(() => {
|
||||
Gerrit._testOnly_flushPreinstalls();
|
||||
suite('proxy methods', () => {
|
||||
test('Gerrit._isPluginEnabled proxy to pluginLoader', () => {
|
||||
const stubFn = sandbox.stub();
|
||||
sandbox.stub(
|
||||
Gerrit._pluginLoader,
|
||||
'isPluginEnabled',
|
||||
(...args) => stubFn(...args)
|
||||
);
|
||||
Gerrit._isPluginEnabled('test_plugin');
|
||||
assert.isTrue(stubFn.calledWith('test_plugin'));
|
||||
});
|
||||
window.Gerrit.flushPreinstalls = sandbox.stub();
|
||||
Gerrit._testOnly_flushPreinstalls();
|
||||
assert.isTrue(window.Gerrit.flushPreinstalls.calledOnce);
|
||||
delete window.Gerrit.flushPreinstalls;
|
||||
});
|
||||
|
||||
test('versioning', () => {
|
||||
const callback = sandbox.spy();
|
||||
Gerrit.install(callback, '0.0pre-alpha');
|
||||
assert(callback.notCalled);
|
||||
});
|
||||
|
||||
test('_setPluginsCount', done => {
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded() {
|
||||
done();
|
||||
},
|
||||
test('Gerrit._isPluginLoaded proxy to pluginLoader', () => {
|
||||
const stubFn = sandbox.stub();
|
||||
sandbox.stub(
|
||||
Gerrit._pluginLoader,
|
||||
'isPluginLoaded',
|
||||
(...args) => stubFn(...args)
|
||||
);
|
||||
Gerrit._isPluginLoaded('test_plugin');
|
||||
assert.isTrue(stubFn.calledWith('test_plugin'));
|
||||
});
|
||||
Gerrit._setPluginsCount(0);
|
||||
});
|
||||
|
||||
test('_arePluginsLoaded', () => {
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
Gerrit._setPluginsCount(1);
|
||||
assert.isFalse(Gerrit._arePluginsLoaded());
|
||||
Gerrit._setPluginsCount(0);
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
});
|
||||
|
||||
test('_pluginInstalled', () => {
|
||||
const pluginsLoadedStub = sandbox.stub();
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded: (...args) => pluginsLoadedStub(...args),
|
||||
test('Gerrit._isPluginPreloaded proxy to pluginLoader', () => {
|
||||
const stubFn = sandbox.stub();
|
||||
sandbox.stub(
|
||||
Gerrit._pluginLoader,
|
||||
'isPluginPreloaded',
|
||||
(...args) => stubFn(...args)
|
||||
);
|
||||
Gerrit._isPluginPreloaded('test_plugin');
|
||||
assert.isTrue(stubFn.calledWith('test_plugin'));
|
||||
});
|
||||
const plugins = [
|
||||
'http://test.com/plugins/foo/static/test.js',
|
||||
'http://test.com/plugins/bar/static/test.js',
|
||||
];
|
||||
Gerrit._setPluginsPending(plugins);
|
||||
Gerrit._pluginInstalled(plugins[0]);
|
||||
Gerrit._pluginInstalled(plugins[1]);
|
||||
assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar']));
|
||||
});
|
||||
|
||||
test('install calls _pluginInstalled', () => {
|
||||
sandbox.stub(Gerrit, '_pluginInstalled');
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
|
||||
// testplugin has already been installed once (in setup).
|
||||
assert.isFalse(Gerrit._pluginInstalled.called);
|
||||
|
||||
// testplugin2 plugin has not yet been installed.
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin2/static/test.js');
|
||||
assert.isTrue(Gerrit._pluginInstalled.calledOnce);
|
||||
});
|
||||
|
||||
test('plugin install errors mark plugins as loaded', () => {
|
||||
Gerrit._setPluginsCount(1);
|
||||
Gerrit.install(() => {}, '0.0pre-alpha');
|
||||
return Gerrit.awaitPluginsLoaded();
|
||||
});
|
||||
|
||||
test('multiple ui plugins per java plugin', () => {
|
||||
const file1 = 'http://test.com/plugins/qaz/static/foo.nocache.js';
|
||||
const file2 = 'http://test.com/plugins/qaz/static/bar.js';
|
||||
Gerrit._setPluginsPending([file1, file2]);
|
||||
Gerrit.install(() => {}, '0.1', file1);
|
||||
Gerrit.install(() => {}, '0.1', file2);
|
||||
return Gerrit.awaitPluginsLoaded();
|
||||
});
|
||||
|
||||
test('plugin install errors shows toasts', () => {
|
||||
const alertStub = sandbox.stub();
|
||||
document.addEventListener('show-alert', alertStub);
|
||||
Gerrit._setPluginsCount(1);
|
||||
Gerrit.install(() => {}, '0.0pre-alpha');
|
||||
return Gerrit.awaitPluginsLoaded().then(() => {
|
||||
assert.isTrue(alertStub.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
test('Gerrit._isPluginPreloaded', () => {
|
||||
Gerrit._preloadedPlugins = {baz: ()=>{}};
|
||||
assert.isFalse(Gerrit._isPluginPreloaded('plugins/foo/bar'));
|
||||
assert.isFalse(Gerrit._isPluginPreloaded('http://a.com/42'));
|
||||
assert.isTrue(Gerrit._isPluginPreloaded('preloaded:baz'));
|
||||
Gerrit._preloadedPlugins = null;
|
||||
});
|
||||
|
||||
test('preloaded plugins are installed', () => {
|
||||
const installStub = sandbox.stub();
|
||||
Gerrit._preloadedPlugins = {foo: installStub};
|
||||
Gerrit._testOnly_installPreloadedPlugins();
|
||||
assert.isTrue(installStub.called);
|
||||
const pluginApi = installStub.lastCall.args[0];
|
||||
assert.strictEqual(pluginApi.getPluginName(), 'foo');
|
||||
});
|
||||
|
||||
test('installing preloaded plugin', () => {
|
||||
let plugin;
|
||||
window.ASSETS_PATH = 'http://blips.com/chitz';
|
||||
Gerrit.install(p => { plugin = p; }, '0.1', 'preloaded:foo');
|
||||
assert.strictEqual(plugin.getPluginName(), 'foo');
|
||||
assert.strictEqual(plugin.url('/some/thing.html'),
|
||||
'http://blips.com/chitz/plugins/foo/some/thing.html');
|
||||
delete window.ASSETS_PATH;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@@ -31,6 +31,13 @@ limitations under the License.
|
||||
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
|
||||
<dom-module id="gr-js-api-interface">
|
||||
<!--
|
||||
Note: the order matters as files depend on each other.
|
||||
1. gr-api-utils will be used in multiple files below.
|
||||
2. gr-gerrit depends on gr-plugin-loader, gr-public-js-api and
|
||||
also gr-plugin-endpoints
|
||||
3. gr-public-js-api depends on gr-plugin-rest-api
|
||||
-->
|
||||
<script src="gr-api-utils.js"></script>
|
||||
<script src="gr-annotation-actions-context.js"></script>
|
||||
<script src="gr-annotation-actions-js-api.js"></script>
|
||||
@@ -41,5 +48,6 @@ limitations under the License.
|
||||
<script src="gr-plugin-action-context.js"></script>
|
||||
<script src="gr-plugin-rest-api.js"></script>
|
||||
<script src="gr-public-js-api.js"></script>
|
||||
<script src="gr-plugin-loader.js"></script>
|
||||
<script src="gr-gerrit.js"></script>
|
||||
</dom-module>
|
||||
|
@@ -35,6 +35,7 @@ limitations under the License.
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
const {PLUGIN_LOADING_TIMEOUT_MS} = window._apiUtils;
|
||||
suite('gr-js-api-interface tests', () => {
|
||||
let element;
|
||||
let plugin;
|
||||
@@ -48,6 +49,7 @@ limitations under the License.
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
this.clock = sinon.useFakeTimers();
|
||||
sandbox = sinon.sandbox.create();
|
||||
getResponseObjectStub = sandbox.stub().returns(Promise.resolve());
|
||||
sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
|
||||
@@ -64,10 +66,11 @@ limitations under the License.
|
||||
errorStub = sandbox.stub(console, 'error');
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
Gerrit._setPluginsPending([]);
|
||||
Gerrit._loadPlugins([]);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
this.clock.restore();
|
||||
sandbox.restore();
|
||||
element._removeEventCallbacks();
|
||||
plugin = null;
|
||||
@@ -194,12 +197,15 @@ limitations under the License.
|
||||
revisions: {def: {_number: 2}, abc: {_number: 1}},
|
||||
};
|
||||
const spy = sandbox.spy();
|
||||
Gerrit._setPluginsCount(1);
|
||||
Gerrit._loadPlugins(['plugins/test.html']);
|
||||
plugin.on(element.EventType.SHOW_CHANGE, spy);
|
||||
element.handleEvent(element.EventType.SHOW_CHANGE,
|
||||
{change: testChange, patchNum: 1});
|
||||
assert.isFalse(spy.called);
|
||||
Gerrit._setPluginsCount(0);
|
||||
|
||||
// Timeout on loading plugins
|
||||
this.clock.tick(PLUGIN_LOADING_TIMEOUT_MS * 2);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(spy.called);
|
||||
done();
|
||||
@@ -334,7 +340,6 @@ limitations under the License.
|
||||
setup(() => {
|
||||
sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl').returns('/r');
|
||||
|
||||
Gerrit._setPluginsCount(1);
|
||||
Gerrit.install(p => { baseUrlPlugin = p; }, '0.1',
|
||||
'http://test.com/r/plugins/baseurlplugin/static/test.js');
|
||||
});
|
||||
|
@@ -42,7 +42,6 @@ limitations under the License.
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
Gerrit._setPluginsCount(1);
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
instance = new GrPluginActionContext(plugin);
|
||||
|
@@ -0,0 +1,393 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
// Import utils methods
|
||||
const {
|
||||
PLUGIN_LOADING_TIMEOUT_MS,
|
||||
PRELOADED_PROTOCOL,
|
||||
getPluginNameFromUrl,
|
||||
getBaseUrl,
|
||||
} = window._apiUtils;
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
const PluginState = {
|
||||
/**
|
||||
* State that indicates the plugin is pending to be loaded.
|
||||
*/
|
||||
PENDING: 'PENDING',
|
||||
|
||||
/**
|
||||
* State that indicates the plugin is already loaded.
|
||||
*/
|
||||
LOADED: 'LOADED',
|
||||
|
||||
/**
|
||||
* State that indicates the plugin is already loaded.
|
||||
*/
|
||||
PRE_LOADED: 'PRE_LOADED',
|
||||
|
||||
/**
|
||||
* State that indicates the plugin failed to load.
|
||||
*/
|
||||
LOAD_FAILED: 'LOAD_FAILED',
|
||||
};
|
||||
|
||||
// Prefix for any unrecognized plugin urls.
|
||||
// Url should match following patterns:
|
||||
// /plugins/PLUGINNAME/static/SCRIPTNAME.(html|js)
|
||||
// /plugins/PLUGINNAME.(js|html)
|
||||
const UNKNOWN_PLUGIN_PREFIX = '__$$__';
|
||||
|
||||
// Current API version for Plugin,
|
||||
// plugins with incompatible version will not be laoded.
|
||||
const API_VERSION = '0.1';
|
||||
|
||||
/**
|
||||
* PluginLoader, responsible for:
|
||||
*
|
||||
* Loading all plugins and handling errors etc.
|
||||
* Recording plugin state.
|
||||
* Reporting on plugin loading status.
|
||||
* Retrieve plugin.
|
||||
* Check plugin status and if all plugins loaded.
|
||||
*/
|
||||
class PluginLoader {
|
||||
constructor() {
|
||||
this._pluginListLoaded = false;
|
||||
|
||||
/** @type {Map<string,PluginLoader.PluginObject>} */
|
||||
this._plugins = new Map();
|
||||
|
||||
this._reporting = null;
|
||||
|
||||
// Promise that resolves when all plugins loaded
|
||||
this._loadingPromise = null;
|
||||
|
||||
// Resolver to resolve _loadingPromise once all plugins loaded
|
||||
this._loadingResolver = null;
|
||||
}
|
||||
|
||||
_getReporting() {
|
||||
if (!this._reporting) {
|
||||
this._reporting = document.createElement('gr-reporting');
|
||||
}
|
||||
return this._reporting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the plugin name or use the full url if not recognized.
|
||||
* @see gr-api-utils#getPluginNameFromUrl
|
||||
* @param {string|URL} url
|
||||
*/
|
||||
_getPluginKeyFromUrl(url) {
|
||||
return getPluginNameFromUrl(url) ||
|
||||
`${UNKNOWN_PLUGIN_PREFIX}${url}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load multiple plugins with certain options.
|
||||
*
|
||||
* @param {Array<string>} plugins
|
||||
* @param {Object<string, PluginLoader.PluginOption>} opts
|
||||
*/
|
||||
loadPlugins(plugins = [], opts = {}) {
|
||||
this._pluginListLoaded = true;
|
||||
|
||||
plugins.forEach(path => {
|
||||
const url = this._urlFor(path);
|
||||
// Skip if preloaded, for bundling.
|
||||
if (this.isPluginPreloaded(url)) return;
|
||||
|
||||
const pluginKey = this._getPluginKeyFromUrl(url);
|
||||
// Skip if already installed.
|
||||
if (this._plugins.has(pluginKey)) return;
|
||||
this._plugins.set(pluginKey, {
|
||||
name: pluginKey,
|
||||
url,
|
||||
state: PluginState.PENDING,
|
||||
plugin: null,
|
||||
});
|
||||
|
||||
if (this._isPathEndsWith(url, '.html')) {
|
||||
this._importHtmlPlugin(url, opts && opts[path]);
|
||||
} else if (this._isPathEndsWith(url, '.js')) {
|
||||
this._loadJsPlugin(url);
|
||||
} else {
|
||||
this._failToLoad(`Unrecognized plugin url ${url}`, url);
|
||||
}
|
||||
});
|
||||
|
||||
this.awaitPluginsLoaded().then(() => {
|
||||
console.info('Plugins loaded');
|
||||
this._getReporting().pluginsLoaded(this._getAllInstalledPluginNames());
|
||||
});
|
||||
}
|
||||
|
||||
_isPathEndsWith(url, suffix) {
|
||||
if (!(url instanceof URL)) {
|
||||
try {
|
||||
url = new URL(url);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return url.pathname && url.pathname.endsWith(suffix);
|
||||
}
|
||||
|
||||
_getAllInstalledPluginNames() {
|
||||
const installedPlugins = [];
|
||||
for (const plugin of this._plugins.values()) {
|
||||
if (plugin.state === PluginState.LOADED) {
|
||||
installedPlugins.push(plugin.name);
|
||||
}
|
||||
}
|
||||
return installedPlugins;
|
||||
}
|
||||
|
||||
install(callback, opt_version, opt_src) {
|
||||
// HTML import polyfill adds __importElement pointing to the import tag.
|
||||
const script = document.currentScript &&
|
||||
(document.currentScript.__importElement || document.currentScript);
|
||||
let src = opt_src || (script && script.src);
|
||||
if (!src || src.startsWith('data:')) {
|
||||
src = script && script.baseURI;
|
||||
}
|
||||
|
||||
if (opt_version && opt_version !== API_VERSION) {
|
||||
this._failToLoad(`Plugin ${src} install error: only version ` +
|
||||
API_VERSION + ' is supported in PolyGerrit. ' + opt_version +
|
||||
' was given.', src);
|
||||
return;
|
||||
}
|
||||
|
||||
const pluginObject = this.getPlugin(src);
|
||||
let plugin = pluginObject && pluginObject.plugin;
|
||||
if (!plugin) {
|
||||
plugin = new Plugin(src);
|
||||
}
|
||||
try {
|
||||
callback(plugin);
|
||||
this._pluginInstalled(src, plugin);
|
||||
} catch (e) {
|
||||
this._failToLoad(`${e.name}: ${e.message}`, src);
|
||||
}
|
||||
}
|
||||
|
||||
get arePluginsLoaded() {
|
||||
// As the size of plugins is relatively small,
|
||||
// so the performance of this check should be reasonable
|
||||
if (!this._pluginListLoaded) return false;
|
||||
for (const plugin of this._plugins.values()) {
|
||||
if (plugin.state === PluginState.PENDING) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_checkIfCompleted() {
|
||||
if (this.arePluginsLoaded && this._loadingResolver) {
|
||||
this._loadingResolver();
|
||||
this._loadingResolver = null;
|
||||
this._loadingPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
_timeout() {
|
||||
const pendingPlugins = [];
|
||||
for (const plugin of this._plugins.values()) {
|
||||
if (plugin.state === PluginState.PENDING) {
|
||||
this._updatePluginState(plugin.url, PluginState.LOAD_FAILED);
|
||||
this._checkIfCompleted();
|
||||
pendingPlugins.push(plugin.url);
|
||||
}
|
||||
}
|
||||
return `Timeout when loading plugins: ${pendingPlugins.join(',')}`;
|
||||
}
|
||||
|
||||
_failToLoad(message, pluginUrl) {
|
||||
// Show an alert with the error
|
||||
document.dispatchEvent(new CustomEvent('show-alert', {
|
||||
detail: {
|
||||
message: `Plugin install error: ${message} from ${pluginUrl}`,
|
||||
},
|
||||
}));
|
||||
this._updatePluginState(pluginUrl, PluginState.LOAD_FAILED);
|
||||
this._checkIfCompleted();
|
||||
}
|
||||
|
||||
_updatePluginState(pluginUrl, state) {
|
||||
const key = this._getPluginKeyFromUrl(pluginUrl);
|
||||
if (this._plugins.has(key)) {
|
||||
this._plugins.get(key).state = state;
|
||||
} else {
|
||||
// Plugin is not recorded for some reason.
|
||||
console.warn(`Plugin loaded separately: ${pluginUrl}`);
|
||||
this._plugins.set(key, {
|
||||
name: key,
|
||||
url: pluginUrl,
|
||||
state,
|
||||
plugin: null,
|
||||
});
|
||||
}
|
||||
return this._plugins.get(key);
|
||||
}
|
||||
|
||||
_pluginInstalled(url, plugin) {
|
||||
const pluginObj = this._updatePluginState(url, PluginState.LOADED);
|
||||
pluginObj.plugin = plugin;
|
||||
this._getReporting().pluginLoaded(plugin.getPluginName() || url);
|
||||
console.log(`Plugin ${plugin.getPluginName() || url} installed.`);
|
||||
this._checkIfCompleted();
|
||||
}
|
||||
|
||||
installPreloadedPlugins() {
|
||||
if (!window.Gerrit || !window.Gerrit._preloadedPlugins) { return; }
|
||||
const Gerrit = window.Gerrit;
|
||||
for (const name in Gerrit._preloadedPlugins) {
|
||||
if (!Gerrit._preloadedPlugins.hasOwnProperty(name)) { continue; }
|
||||
const callback = Gerrit._preloadedPlugins[name];
|
||||
this.install(callback, API_VERSION, PRELOADED_PROTOCOL + name);
|
||||
}
|
||||
}
|
||||
|
||||
isPluginPreloaded(pathOrUrl) {
|
||||
const url = this._urlFor(pathOrUrl);
|
||||
const name = getPluginNameFromUrl(url);
|
||||
if (name && window.Gerrit._preloadedPlugins) {
|
||||
return window.Gerrit._preloadedPlugins.hasOwnProperty(name);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given plugin path/url is enabled or not.
|
||||
* @param {string} pathOrUrl
|
||||
*/
|
||||
isPluginEnabled(pathOrUrl) {
|
||||
const url = this._urlFor(pathOrUrl);
|
||||
if (this.isPluginPreloaded(url)) return true;
|
||||
const key = this._getPluginKeyFromUrl(url);
|
||||
return this._plugins.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin object with a given url.
|
||||
* @param {string} pathOrUrl
|
||||
*/
|
||||
getPlugin(pathOrUrl) {
|
||||
const key = this._getPluginKeyFromUrl(this._urlFor(pathOrUrl));
|
||||
return this._plugins.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given plugin path/url is loaded or not.
|
||||
* @param {string} pathOrUrl
|
||||
*/
|
||||
isPluginLoaded(pathOrUrl) {
|
||||
const url = this._urlFor(pathOrUrl);
|
||||
const key = this._getPluginKeyFromUrl(url);
|
||||
return this._plugins.has(key) ?
|
||||
this._plugins.get(key).state === PluginState.LOADED :
|
||||
false;
|
||||
}
|
||||
|
||||
_importHtmlPlugin(pluginUrl, opts = {}) {
|
||||
// onload (second param) needs to be a function. When null or undefined
|
||||
// were passed, plugins were not loaded correctly.
|
||||
(Polymer.importHref || Polymer.Base.importHref)(
|
||||
this._urlFor(pluginUrl), () => {},
|
||||
() => this._failToLoad(`${pluginUrl} import error`, pluginUrl),
|
||||
!opts.sync);
|
||||
}
|
||||
|
||||
_loadJsPlugin(pluginUrl) {
|
||||
this._createScriptTag(this._urlFor(pluginUrl));
|
||||
}
|
||||
|
||||
_createScriptTag(url) {
|
||||
const el = document.createElement('script');
|
||||
el.defer = true;
|
||||
el.src = url;
|
||||
el.onerror = () => this._failToLoad(`${url} load error`, url);
|
||||
return document.body.appendChild(el);
|
||||
}
|
||||
|
||||
_urlFor(pathOrUrl) {
|
||||
if (!pathOrUrl) {
|
||||
return pathOrUrl;
|
||||
}
|
||||
if (pathOrUrl.startsWith(PRELOADED_PROTOCOL) ||
|
||||
pathOrUrl.startsWith('http')) {
|
||||
// Plugins are loaded from another domain or preloaded.
|
||||
return pathOrUrl;
|
||||
}
|
||||
if (!pathOrUrl.startsWith('/')) {
|
||||
pathOrUrl = '/' + pathOrUrl;
|
||||
}
|
||||
return window.location.origin + getBaseUrl() + pathOrUrl;
|
||||
}
|
||||
|
||||
awaitPluginsLoaded() {
|
||||
// Resolve if completed.
|
||||
this._checkIfCompleted();
|
||||
|
||||
if (this.arePluginsLoaded) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!this._loadingPromise) {
|
||||
let timerId;
|
||||
this._loadingPromise =
|
||||
Promise.race([
|
||||
new Promise(resolve => this._loadingResolver = resolve),
|
||||
new Promise((_, reject) => timerId = setTimeout(
|
||||
() => {
|
||||
reject(this._timeout());
|
||||
}, PLUGIN_LOADING_TIMEOUT_MS)),
|
||||
]).then(() => {
|
||||
if (timerId) clearTimeout(timerId);
|
||||
});
|
||||
}
|
||||
return this._loadingPromise;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* name:string,
|
||||
* url:string,
|
||||
* state:PluginState,
|
||||
* plugin:Object
|
||||
* }}
|
||||
*/
|
||||
PluginLoader.PluginObject;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* sync:boolean,
|
||||
* }}
|
||||
*/
|
||||
PluginLoader.PluginOption;
|
||||
|
||||
window.PluginLoader = PluginLoader;
|
||||
})(window);
|
@@ -0,0 +1,502 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
@license
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed 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.
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||
<title>gr-plugin-host</title>
|
||||
<script src="/test/common-test-setup.js"></script>
|
||||
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
||||
|
||||
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
|
||||
<script src="/bower_components/web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||
<link rel="import" href="gr-js-api-interface.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-js-api-interface></gr-js-api-interface>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
const {PRELOADED_PROTOCOL, PLUGIN_LOADING_TIMEOUT_MS} = window._apiUtils;
|
||||
suite('gr-plugin-loader tests', () => {
|
||||
let plugin;
|
||||
let sandbox;
|
||||
let url;
|
||||
let sendStub;
|
||||
|
||||
setup(() => {
|
||||
this.clock = sinon.useFakeTimers();
|
||||
sandbox = sinon.sandbox.create();
|
||||
sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
|
||||
stub('gr-rest-api-interface', {
|
||||
getAccount() {
|
||||
return Promise.resolve({name: 'Judy Hopps'});
|
||||
},
|
||||
send(...args) {
|
||||
return sendStub(...args);
|
||||
},
|
||||
});
|
||||
sandbox.stub(document.body, 'appendChild');
|
||||
fixture('basic');
|
||||
url = window.location.origin;
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
this.clock.restore();
|
||||
Gerrit._testOnly_resetPlugins();
|
||||
});
|
||||
|
||||
test('reuse plugin for install calls', () => {
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
|
||||
let otherPlugin;
|
||||
Gerrit.install(p => { otherPlugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
assert.strictEqual(plugin, otherPlugin);
|
||||
});
|
||||
|
||||
test('flushes preinstalls if provided', () => {
|
||||
assert.doesNotThrow(() => {
|
||||
Gerrit._testOnly_flushPreinstalls();
|
||||
});
|
||||
window.Gerrit.flushPreinstalls = sandbox.stub();
|
||||
Gerrit._testOnly_flushPreinstalls();
|
||||
assert.isTrue(window.Gerrit.flushPreinstalls.calledOnce);
|
||||
delete window.Gerrit.flushPreinstalls;
|
||||
});
|
||||
|
||||
test('versioning', () => {
|
||||
const callback = sandbox.spy();
|
||||
Gerrit.install(callback, '0.0pre-alpha');
|
||||
assert(callback.notCalled);
|
||||
});
|
||||
|
||||
test('report pluginsLoaded', done => {
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded() {
|
||||
done();
|
||||
},
|
||||
});
|
||||
Gerrit._loadPlugins([]);
|
||||
});
|
||||
|
||||
test('arePluginsLoaded', done => {
|
||||
assert.isFalse(Gerrit._arePluginsLoaded());
|
||||
const plugins = [
|
||||
'http://test.com/plugins/foo/static/test.js',
|
||||
'http://test.com/plugins/bar/static/test.js',
|
||||
];
|
||||
|
||||
Gerrit._loadPlugins(plugins);
|
||||
assert.isFalse(Gerrit._arePluginsLoaded());
|
||||
// Timeout on loading plugins
|
||||
this.clock.tick(PLUGIN_LOADING_TIMEOUT_MS * 2);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('plugins installed successfully', done => {
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
Gerrit.install(() => void 0, undefined, url);
|
||||
});
|
||||
const pluginsLoadedStub = sandbox.stub();
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded: (...args) => pluginsLoadedStub(...args),
|
||||
});
|
||||
|
||||
const plugins = [
|
||||
'http://test.com/plugins/foo/static/test.js',
|
||||
'http://test.com/plugins/bar/static/test.js',
|
||||
];
|
||||
Gerrit._loadPlugins(plugins);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar']));
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('isPluginEnabled and isPluginLoaded', done => {
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
Gerrit.install(() => void 0, undefined, url);
|
||||
});
|
||||
const pluginsLoadedStub = sandbox.stub();
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded: (...args) => pluginsLoadedStub(...args),
|
||||
});
|
||||
|
||||
const plugins = [
|
||||
'http://test.com/plugins/foo/static/test.js',
|
||||
'http://test.com/plugins/bar/static/test.js',
|
||||
'bar/static/test.js',
|
||||
];
|
||||
Gerrit._loadPlugins(plugins);
|
||||
assert.isTrue(
|
||||
plugins.every(plugin => Gerrit._pluginLoader.isPluginEnabled(plugin))
|
||||
);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
assert.isTrue(
|
||||
plugins.every(plugin => Gerrit._pluginLoader.isPluginLoaded(plugin))
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('plugins installed mixed result, 1 fail 1 succeed', done => {
|
||||
const plugins = [
|
||||
'http://test.com/plugins/foo/static/test.js',
|
||||
'http://test.com/plugins/bar/static/test.js',
|
||||
];
|
||||
|
||||
const alertStub = sandbox.stub();
|
||||
document.addEventListener('show-alert', alertStub);
|
||||
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
Gerrit.install(() => {
|
||||
if (url === plugins[0]) {
|
||||
throw new Error('failed');
|
||||
}
|
||||
}, undefined, url);
|
||||
});
|
||||
|
||||
const pluginsLoadedStub = sandbox.stub();
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded: (...args) => pluginsLoadedStub(...args),
|
||||
});
|
||||
|
||||
Gerrit._loadPlugins(plugins);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(pluginsLoadedStub.calledWithExactly(['bar']));
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
assert.isTrue(alertStub.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('isPluginEnabled and isPluginLoaded for mixed results', done => {
|
||||
const plugins = [
|
||||
'http://test.com/plugins/foo/static/test.js',
|
||||
'http://test.com/plugins/bar/static/test.js',
|
||||
];
|
||||
|
||||
const alertStub = sandbox.stub();
|
||||
document.addEventListener('show-alert', alertStub);
|
||||
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
Gerrit.install(() => {
|
||||
if (url === plugins[0]) {
|
||||
throw new Error('failed');
|
||||
}
|
||||
}, undefined, url);
|
||||
});
|
||||
|
||||
const pluginsLoadedStub = sandbox.stub();
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded: (...args) => pluginsLoadedStub(...args),
|
||||
});
|
||||
|
||||
Gerrit._loadPlugins(plugins);
|
||||
assert.isTrue(
|
||||
plugins.every(plugin => Gerrit._pluginLoader.isPluginEnabled(plugin))
|
||||
);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(pluginsLoadedStub.calledWithExactly(['bar']));
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
assert.isTrue(alertStub.calledOnce);
|
||||
assert.isTrue(Gerrit._pluginLoader.isPluginLoaded(plugins[1]));
|
||||
assert.isFalse(Gerrit._pluginLoader.isPluginLoaded(plugins[0]));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('plugins installed all failed', done => {
|
||||
const plugins = [
|
||||
'http://test.com/plugins/foo/static/test.js',
|
||||
'http://test.com/plugins/bar/static/test.js',
|
||||
];
|
||||
|
||||
const alertStub = sandbox.stub();
|
||||
document.addEventListener('show-alert', alertStub);
|
||||
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
Gerrit.install(() => {
|
||||
throw new Error('failed');
|
||||
}, undefined, url);
|
||||
});
|
||||
|
||||
const pluginsLoadedStub = sandbox.stub();
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded: (...args) => pluginsLoadedStub(...args),
|
||||
});
|
||||
|
||||
Gerrit._loadPlugins(plugins);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(pluginsLoadedStub.calledWithExactly([]));
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
assert.isTrue(alertStub.calledTwice);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('plugins installed failed becasue of wrong version', done => {
|
||||
const plugins = [
|
||||
'http://test.com/plugins/foo/static/test.js',
|
||||
'http://test.com/plugins/bar/static/test.js',
|
||||
];
|
||||
|
||||
const alertStub = sandbox.stub();
|
||||
document.addEventListener('show-alert', alertStub);
|
||||
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
Gerrit.install(() => {
|
||||
}, url === plugins[0] ? '' : 'alpha', url);
|
||||
});
|
||||
|
||||
const pluginsLoadedStub = sandbox.stub();
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded: (...args) => pluginsLoadedStub(...args),
|
||||
});
|
||||
|
||||
Gerrit._loadPlugins(plugins);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo']));
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
assert.isTrue(alertStub.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('multiple assets for same plugin installed successfully', done => {
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
Gerrit.install(() => void 0, undefined, url);
|
||||
});
|
||||
const pluginsLoadedStub = sandbox.stub();
|
||||
stub('gr-reporting', {
|
||||
pluginsLoaded: (...args) => pluginsLoadedStub(...args),
|
||||
});
|
||||
|
||||
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',
|
||||
];
|
||||
Gerrit._loadPlugins(plugins);
|
||||
|
||||
flush(() => {
|
||||
assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar']));
|
||||
assert.isTrue(Gerrit._arePluginsLoaded());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suite('plugin path and url', () => {
|
||||
let importHtmlPluginStub;
|
||||
let loadJsPluginStub;
|
||||
setup(() => {
|
||||
importHtmlPluginStub = sandbox.stub();
|
||||
sandbox.stub(Gerrit._pluginLoader, '_importHtmlPlugin', url => {
|
||||
importHtmlPluginStub(url);
|
||||
});
|
||||
loadJsPluginStub = sandbox.stub();
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
loadJsPluginStub(url);
|
||||
});
|
||||
});
|
||||
|
||||
test('invalid plugin path', () => {
|
||||
const failToLoadStub = sandbox.stub();
|
||||
sandbox.stub(Gerrit._pluginLoader, '_failToLoad', (...args) => {
|
||||
failToLoadStub(...args);
|
||||
});
|
||||
|
||||
Gerrit._loadPlugins([
|
||||
'foo/bar',
|
||||
]);
|
||||
|
||||
assert.isTrue(failToLoadStub.calledOnce);
|
||||
assert.isTrue(failToLoadStub.calledWithExactly(
|
||||
`Unrecognized plugin url ${url}/foo/bar`,
|
||||
`${url}/foo/bar`
|
||||
));
|
||||
});
|
||||
|
||||
test('relative path for plugins', () => {
|
||||
Gerrit._loadPlugins([
|
||||
'foo/bar.js',
|
||||
'foo/bar.html',
|
||||
]);
|
||||
|
||||
assert.isTrue(importHtmlPluginStub.calledOnce);
|
||||
assert.isTrue(
|
||||
importHtmlPluginStub.calledWithExactly(`${url}/foo/bar.html`)
|
||||
);
|
||||
assert.isTrue(loadJsPluginStub.calledOnce);
|
||||
assert.isTrue(
|
||||
loadJsPluginStub.calledWithExactly(`${url}/foo/bar.js`)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
test('relative path should honor getBaseUrl', () => {
|
||||
const testUrl = '/test';
|
||||
sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl', () => {
|
||||
return testUrl;
|
||||
});
|
||||
|
||||
Gerrit._loadPlugins([
|
||||
'foo/bar.js',
|
||||
'foo/bar.html',
|
||||
]);
|
||||
|
||||
assert.isTrue(importHtmlPluginStub.calledOnce);
|
||||
assert.isTrue(loadJsPluginStub.calledOnce);
|
||||
assert.isTrue(
|
||||
importHtmlPluginStub.calledWithExactly(
|
||||
`${url}${testUrl}/foo/bar.html`
|
||||
)
|
||||
);
|
||||
assert.isTrue(
|
||||
loadJsPluginStub.calledWithExactly(`${url}${testUrl}/foo/bar.js`)
|
||||
);
|
||||
});
|
||||
|
||||
test('absolute path for plugins', () => {
|
||||
Gerrit._loadPlugins([
|
||||
'http://e.com/foo/bar.js',
|
||||
'http://e.com/foo/bar.html',
|
||||
]);
|
||||
|
||||
assert.isTrue(importHtmlPluginStub.calledOnce);
|
||||
assert.isTrue(
|
||||
importHtmlPluginStub.calledWithExactly(`http://e.com/foo/bar.html`)
|
||||
);
|
||||
assert.isTrue(loadJsPluginStub.calledOnce);
|
||||
assert.isTrue(
|
||||
loadJsPluginStub.calledWithExactly(`http://e.com/foo/bar.js`)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('adds js plugins will call the body', () => {
|
||||
Gerrit._loadPlugins([
|
||||
'http://e.com/foo/bar.js',
|
||||
'http://e.com/bar/foo.js',
|
||||
]);
|
||||
assert.isTrue(document.body.appendChild.calledTwice);
|
||||
});
|
||||
|
||||
test('can call awaitPluginsLoaded multiple times', done => {
|
||||
const plugins = [
|
||||
'http://e.com/foo/bar.js',
|
||||
'http://e.com/bar/foo.js',
|
||||
];
|
||||
|
||||
let installed = false;
|
||||
function pluginCallback(url) {
|
||||
if (url === plugins[1]) {
|
||||
installed = true;
|
||||
}
|
||||
}
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
Gerrit.install(() => pluginCallback(url), undefined, url);
|
||||
});
|
||||
|
||||
Gerrit._loadPlugins(plugins);
|
||||
|
||||
Gerrit.awaitPluginsLoaded().then(() => {
|
||||
assert.isTrue(installed);
|
||||
|
||||
Gerrit.awaitPluginsLoaded().then(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('preloaded plugins', () => {
|
||||
test('skips preloaded plugins when load plugins', () => {
|
||||
const importHtmlPluginStub = sandbox.stub();
|
||||
sandbox.stub(Gerrit._pluginLoader, '_importHtmlPlugin', url => {
|
||||
importHtmlPluginStub(url);
|
||||
});
|
||||
const loadJsPluginStub = sandbox.stub();
|
||||
sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
|
||||
loadJsPluginStub(url);
|
||||
});
|
||||
|
||||
Gerrit._preloadedPlugins = {
|
||||
foo: () => void 0,
|
||||
bar: () => void 0,
|
||||
};
|
||||
|
||||
Gerrit._loadPlugins([
|
||||
'http://e.com/plugins/foo.js',
|
||||
'plugins/bar.html',
|
||||
'http://e.com/plugins/test/foo.js',
|
||||
]);
|
||||
|
||||
assert.isTrue(importHtmlPluginStub.notCalled);
|
||||
assert.isTrue(loadJsPluginStub.calledOnce);
|
||||
});
|
||||
|
||||
test('isPluginPreloaded', () => {
|
||||
Gerrit._preloadedPlugins = {baz: ()=>{}};
|
||||
assert.isFalse(Gerrit._pluginLoader.isPluginPreloaded('plugins/foo/bar'));
|
||||
assert.isFalse(Gerrit._pluginLoader.isPluginPreloaded('http://a.com/42'));
|
||||
assert.isTrue(
|
||||
Gerrit._pluginLoader.isPluginPreloaded(PRELOADED_PROTOCOL + 'baz')
|
||||
);
|
||||
Gerrit._preloadedPlugins = null;
|
||||
});
|
||||
|
||||
test('preloaded plugins are installed', () => {
|
||||
const installStub = sandbox.stub();
|
||||
Gerrit._preloadedPlugins = {foo: installStub};
|
||||
Gerrit._pluginLoader.installPreloadedPlugins();
|
||||
assert.isTrue(installStub.called);
|
||||
const pluginApi = installStub.lastCall.args[0];
|
||||
assert.strictEqual(pluginApi.getPluginName(), 'foo');
|
||||
});
|
||||
|
||||
test('installing preloaded plugin', () => {
|
||||
let plugin;
|
||||
window.ASSETS_PATH = 'http://blips.com/chitz';
|
||||
Gerrit.install(p => { plugin = p; }, '0.1', 'preloaded:foo');
|
||||
assert.strictEqual(plugin.getPluginName(), 'foo');
|
||||
assert.strictEqual(plugin.url('/some/thing.html'),
|
||||
'http://blips.com/chitz/plugins/foo/some/thing.html');
|
||||
delete window.ASSETS_PATH;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
@@ -50,7 +50,6 @@ limitations under the License.
|
||||
a[k] = (...args) => restApiStub[k](...args);
|
||||
return a;
|
||||
}, {}));
|
||||
Gerrit._setPluginsCount(1);
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
instance = new GrPluginRestApi();
|
||||
|
@@ -267,10 +267,14 @@
|
||||
return;
|
||||
}
|
||||
return this.registerCustomComponent(
|
||||
Gerrit._getPluginScreenName(this.getPluginName(), screenName),
|
||||
this._getScreenName(screenName),
|
||||
opt_moduleName);
|
||||
};
|
||||
|
||||
Plugin.prototype._getScreenName = function(screenName) {
|
||||
return `${this.getPluginName()}-screen-${screenName}`;
|
||||
};
|
||||
|
||||
const deprecatedAPI = {
|
||||
_loadedGwt: ()=> {},
|
||||
|
||||
@@ -321,7 +325,7 @@
|
||||
'Please use strings for patterns.');
|
||||
return;
|
||||
}
|
||||
this.hook(Gerrit._getPluginScreenName(this.getPluginName(), pattern))
|
||||
this.hook(this._getScreenName(pattern))
|
||||
.onAttached(el => {
|
||||
el.style.display = 'none';
|
||||
callback({
|
||||
|
@@ -188,6 +188,7 @@ limitations under the License.
|
||||
'shared/gr-js-api-interface/gr-js-api-interface_test.html',
|
||||
'shared/gr-js-api-interface/gr-gerrit_test.html',
|
||||
'shared/gr-js-api-interface/gr-plugin-action-context_test.html',
|
||||
'shared/gr-js-api-interface/gr-plugin-loader_test.html',
|
||||
'shared/gr-js-api-interface/gr-plugin-endpoints_test.html',
|
||||
'shared/gr-js-api-interface/gr-plugin-rest-api_test.html',
|
||||
'shared/gr-fixed-panel/gr-fixed-panel_test.html',
|
||||
|
Reference in New Issue
Block a user