diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index fdb029da6c3..f4def9f69c8 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -185,8 +185,24 @@ var AbstractView = Class.extend({ */ _loadLibs: function () { var defs = []; - _.each(this.config.js_libs, function (url) { - defs.push(ajax.loadJS(url)); + var jsDefs; + _.each(this.config.js_libs, function (urls) { + if (typeof(urls) === 'string') { + // js_libs is an array of urls: those urls can be loaded in + // parallel + defs.push(ajax.loadJS(urls)); + } else { + // js_libs is an array of arrays of urls: those arrays of urls + // must be loaded sequentially, but the urls inside each + // sub-array can be loaded in parallel + defs.push($.when.apply($, jsDefs).then(function () { + jsDefs = []; + _.each(urls, function (url) { + jsDefs.push(ajax.loadJS(url)); + }); + return $.when.apply($, jsDefs); + })); + } }); _.each(this.config.css_libs, function (url) { defs.push(ajax.loadCSS(url)); diff --git a/addons/web/static/tests/views/abstract_view_tests.js b/addons/web/static/tests/views/abstract_view_tests.js new file mode 100644 index 00000000000..1c85b390cc6 --- /dev/null +++ b/addons/web/static/tests/views/abstract_view_tests.js @@ -0,0 +1,106 @@ +odoo.define('web.abstract_view_tests', function (require) { +"use strict"; + +var AbstractView = require('web.AbstractView'); +var ajax = require('web.ajax'); +var testUtils = require('web.test_utils'); + +var createAsyncView = testUtils.createAsyncView; + +QUnit.module('Views', { + beforeEach: function () { + this.data = { + fake_model: { + fields: {}, + record: [], + }, + }; + }, +}, function () { + + QUnit.module('AbstractView'); + + QUnit.test('lazy loading of js libs (in parallel)', function (assert) { + var done = assert.async(); + assert.expect(6); + + var def = $.Deferred(); + var loadJS = ajax.loadJS; + ajax.loadJS = function (url) { + assert.step(url); + return def.then(function () { + assert.step(url + ' loaded'); + }); + }; + var View = AbstractView.extend({ + config: _.extend({}, AbstractView.prototype.config, { + js_libs: ['a', 'b'], + }), + }); + createAsyncView({ + View: View, + arch: '', + data: this.data, + model: 'fake_model', + }).then(function (view) { + assert.verifySteps(['a', 'b', 'a loaded', 'b loaded'], + "should wait for both libs to be loaded"); + ajax.loadJS = loadJS; + view.destroy(); + done(); + }); + + assert.verifySteps(['a', 'b'], + "both libs should be loaded in parallel"); + def.resolve(); + }); + + QUnit.test('lazy loading of js libs (sequentially)', function (assert) { + var done = assert.async(); + assert.expect(10); + + var defs = { + a: $.Deferred(), + b: $.Deferred(), + c: $.Deferred(), + }; + var loadJS = ajax.loadJS; + ajax.loadJS = function (url) { + assert.step(url); + return defs[url].then(function () { + assert.step(url + ' loaded'); + }); + }; + var View = AbstractView.extend({ + config: _.extend({}, AbstractView.prototype.config, { + js_libs: [ + ['a', 'b'], + ['c'], + ], + }), + }); + createAsyncView({ + View: View, + arch: '', + data: this.data, + model: 'fake_model', + }).then(function (view) { + assert.verifySteps(['a', 'b', 'a loaded', 'b loaded', 'c', 'c loaded'], + "should for all libs to be loaded"); + ajax.loadJS = loadJS; + view.destroy(); + done(); + }); + + assert.verifySteps(['a', 'b'], + "libs 'a' and 'b' should be loaded in parallel"); + defs.a.resolve(); + assert.verifySteps(['a', 'b', 'a loaded'], + "should wait for 'a' and 'b' to be loaded before loading 'c'"); + defs.b.resolve(); + assert.verifySteps(['a', 'b', 'a loaded', 'b loaded', 'c'], + "should load 'c' when 'a' and 'b' are loaded"); + defs.c.resolve(); + }); +}); +}); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 8eeb2c7eba5..27831759b4b 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -434,6 +434,7 @@ +