[FIX] web: Views: sequential lazy loading of libs

The views can specify js (and css) libraries to lazy load when
they are instantiated for the first time. The previous
implementation was too naive as it always loaded the specified
libraries in parallel. However, it may happen (e.g. gantt view)
that some of those libraries depend on other ones.

This commit implements a mechanism to handle libs dependencies
between each other. For instance, specifying:
    js_libs: [
        ['a', 'b'],
        ['c'],
    ],
will load 'a' and 'b' in parallel, but wait for them to be
loaded before loading 'c'.

The new implementation still supports the old syntax. So,
    js_libs: ['a', 'b', 'c'],
will load 'a', 'b' and 'c' in parallel.
This commit is contained in:
Aaron Bohy
2017-04-26 11:19:20 +02:00
parent 8f9362809e
commit bead8452c2
3 changed files with 125 additions and 2 deletions
@@ -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));
@@ -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: '<fake/>',
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: '<fake/>',
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();
});
});
});
+1
View File
@@ -434,6 +434,7 @@
<script type="text/javascript" src="/web/static/tests/fields/relational_fields_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/fields/special_fields.js"></script>
<script type="text/javascript" src="/web/static/tests/views/abstract_view_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/form_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/graph_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/list_tests.js"></script>