diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-09-14 16:00:17 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-09-14 16:00:17 +0200 |
| commit | 9c77066f345360429a0920ca4d17af1f84a7ebea (patch) | |
| tree | 469494018b2da134a8ddb5f51930566c1d39bf8e /addons/web/static | |
| parent | 5c11e0d6f9dcc2161735ce3fb93430b767d54cf9 (diff) | |
| parent | 6c103589c15dcf97603f3e2909871cb1fefd6484 (diff) | |
[MERGE] forward port branch saas-11.4 up to 6c103589c15
Diffstat (limited to 'addons/web/static')
| -rw-r--r-- | addons/web/static/src/js/chrome/abstract_web_client.js | 6 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/ajax.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/session.js | 7 | ||||
| -rw-r--r-- | addons/web/static/src/js/services/ajax_service.js | 25 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/kanban/kanban_model.js | 6 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/list/list_renderer.js | 3 | ||||
| -rw-r--r-- | addons/web/static/tests/chrome/action_manager_tests.js | 6 | ||||
| -rw-r--r-- | addons/web/static/tests/core/widget_tests.js | 37 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/mock_server.js | 17 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils.js | 10 | ||||
| -rw-r--r-- | addons/web/static/tests/views/kanban_tests.js | 37 | ||||
| -rw-r--r-- | addons/web/static/tests/views/list_tests.js | 70 |
12 files changed, 83 insertions, 143 deletions
diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js index 7a8b3ce8b01..68f35f272da 100644 --- a/addons/web/static/src/js/chrome/abstract_web_client.js +++ b/addons/web/static/src/js/chrome/abstract_web_client.js @@ -187,7 +187,9 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi // If it is not handled, we should display something clearer than the common crash_manager error dialog // since it won't show anything except "Script error." // This link will probably explain it better: https://blog.sentry.io/2016/05/17/what-is-script-error.html - if (message === "Script error." && !file && !line && !col && !error) { + if (!file && !line && !col) { + // Chrome and Opera set "Script error." on the `message` and hide the `error` + // Firefox handles the "Script error." directly. It sets the error thrown by the CORS file into `error` if (window.onOriginError) { window.onOriginError(); delete window.onOriginError; @@ -195,7 +197,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi crash_manager.show_error({ type: _t("Odoo Client Error"), message: _t("Unknown CORS error"), - data: {debug: _t("An unknown CORS error occured. The error probably originates from a JavaScript file served from a different origin.")}, + data: {debug: _t("An unknown CORS error occured. The error probably originates from a JavaScript file served from a different origin. (Opening your browser console might give you a hint on the error.)")}, }); } } else { diff --git a/addons/web/static/src/js/core/ajax.js b/addons/web/static/src/js/core/ajax.js index 45313b99428..f844cbdaed4 100644 --- a/addons/web/static/src/js/core/ajax.js +++ b/addons/web/static/src/js/core/ajax.js @@ -46,7 +46,7 @@ function genericJsonRpc (fct_name, params, settings, fct) { }); // FIXME: jsonp? deferred.abort = function () { - deferred.reject('communication', $.Event(), 'abort', 'abort'); + deferred.reject({message: "XmlHttpRequestError abort"}, $.Event('abort')); if (xhr.abort) { xhr.abort(); } diff --git a/addons/web/static/src/js/core/session.js b/addons/web/static/src/js/core/session.js index fcbc5872be0..694285cccdc 100644 --- a/addons/web/static/src/js/core/session.js +++ b/addons/web/static/src/js/core/session.js @@ -344,9 +344,9 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { } }; - return deferred.then(function () { + var promise = deferred.then(function () { if (aborted) { - var def = $.Deferred().reject('communication', $.Event(), 'abort', 'abort'); + var def = $.Deferred().reject({message: "XmlHttpRequestError abort"}, $.Event('abort')); def.abort = function () {}; return def; } @@ -376,6 +376,9 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { xhrDef = fct(url, "call", params, options); return xhrDef; }); + + promise.abort = deferred.abort; + return promise; }, url: function (path, params) { params = _.extend(params || {}); diff --git a/addons/web/static/src/js/services/ajax_service.js b/addons/web/static/src/js/services/ajax_service.js index bf754792932..0cecaae9da3 100644 --- a/addons/web/static/src/js/services/ajax_service.js +++ b/addons/web/static/src/js/services/ajax_service.js @@ -7,17 +7,20 @@ var session = require('web.session'); var AjaxService = AbstractService.extend({ rpc: function (route, args, options, target) { - return $.Deferred(function (def) { - session.rpc(route, args, options).then(function () { - if (!target.isDestroyed()) { - def.resolve.apply(def, arguments); - } - }, function () { - if (!target.isDestroyed()) { - def.reject.apply(def, arguments); - } - }); - }).promise(); + var def = $.Deferred(); + var promise = def.promise(); + var xhrDef = session.rpc(route, args, options); + promise.abort = xhrDef.abort.bind(xhrDef); + xhrDef.then(function () { + if (!target.isDestroyed()) { + def.resolve.apply(def, arguments); + } + }).fail(function () { + if (!target.isDestroyed()) { + def.reject.apply(def, arguments); + } + }); + return promise; }, }); diff --git a/addons/web/static/src/js/views/kanban/kanban_model.js b/addons/web/static/src/js/views/kanban/kanban_model.js index 9c0bcdf37fe..905ea05ac1e 100644 --- a/addons/web/static/src/js/views/kanban/kanban_model.js +++ b/addons/web/static/src/js/views/kanban/kanban_model.js @@ -61,15 +61,11 @@ var KanbanModel = BasicModel.extend({ if (!groupByField || groupByField.type !== 'many2one') { return $.Deferred().reject(); // only supported when grouped on m2o } - // ignore action default which may be for another model - var context = _.pick(parent.context, function (value, key) { - return key.slice(0,8) !== 'default_'; - }); return this._rpc({ model: groupByField.relation, method: 'name_create', args: [name], - context: context, // todo: combine with view context + context: parent.context, // todo: combine with view context }) .then(function (result) { var newGroup = self._makeDataPoint({ diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js index b343f044bc2..15c3e24116f 100644 --- a/addons/web/static/src/js/views/list/list_renderer.js +++ b/addons/web/static/src/js/views/list/list_renderer.js @@ -200,6 +200,7 @@ var ListRenderer = BasicRenderer.extend({ reject = columnInvisibleFields[c.attrs.name]; } if (!reject && c.attrs.widget === 'handle') { + self.hasHandle = true; self.handleField = c.attrs.name; } return reject; @@ -663,8 +664,6 @@ var ListRenderer = BasicRenderer.extend({ this._computeAggregates(); $table.toggleClass('o_list_view_grouped', this.isGrouped); $table.toggleClass('o_list_view_ungrouped', !this.isGrouped); - this.hasHandle = this.state.orderedBy.length === 0 || - this.state.orderedBy[0].name === this.handleField; if (this.isGrouped) { $table .append(this._renderHeader(true)) diff --git a/addons/web/static/tests/chrome/action_manager_tests.js b/addons/web/static/tests/chrome/action_manager_tests.js index 491d7d0f382..5cd9a216abf 100644 --- a/addons/web/static/tests/chrome/action_manager_tests.js +++ b/addons/web/static/tests/chrome/action_manager_tests.js @@ -1980,7 +1980,7 @@ QUnit.module('ActionManager', { start: function () { var self = this; return this._super.apply(this, arguments).then(function () { - self.iframe.src = 'test: ' + self.iframe.getAttribute('src'); + self.iframe.src = 'test ' + self.iframe.getAttribute('src'); }); } }); @@ -2002,7 +2002,7 @@ QUnit.module('ActionManager', { if (route === '/report/check_wkhtmltopdf') { return $.when('broken'); } - if (route === 'test: /report/html/some_report') { + if (route === 'test /report/html/some_report') { return $.when(); } return this._super.apply(this, arguments); @@ -2023,7 +2023,7 @@ QUnit.module('ActionManager', { '/web/action/load', '/report/check_wkhtmltopdf', 'notification', - 'test: /report/html/some_report', // report client action's iframe + 'test /report/html/some_report', // report client action's iframe ]); actionManager.destroy(); diff --git a/addons/web/static/tests/core/widget_tests.js b/addons/web/static/tests/core/widget_tests.js index 19e72cb97b6..9510079e380 100644 --- a/addons/web/static/tests/core/widget_tests.js +++ b/addons/web/static/tests/core/widget_tests.js @@ -1,10 +1,12 @@ odoo.define('web.widget_tests', function (require) { "use strict"; +var AjaxService = require('web.AjaxService'); var concurrency = require('web.concurrency'); var core = require('web.core'); var QWeb = require('web.QWeb'); var Widget = require('web.Widget'); +var testUtils = require('web.test_utils'); QUnit.module('core', {}, function () { @@ -377,16 +379,47 @@ QUnit.module('core', {}, function () { }); QUnit.test("calling _rpc on destroyed widgets", function (assert) { - assert.expect(1); + assert.expect(3); + + var def; + var parent = new Widget(); + testUtils.addMockEnvironment(parent, { + session: { + rpc: function () { + def = $.Deferred(); + def.abort = def.reject; + return def; + }, + }, + services: [AjaxService], + }); + var widget = new Widget(parent); + + widget._rpc({route: '/a/route'}).then(function () { + assert.ok(true, "The ajax call should be resolve"); + }); + def.resolve(); + def = null; - var widget = new Widget(); + widget._rpc({route: '/a/route'}).always(function () { + throw Error("Calling _rpc on a destroyed widget should return a " + + "deferred that is never resolved nor rejected"); + }); widget.destroy(); + def.resolve(); + def = null; + widget._rpc({route: '/a/route'}).always(function () { throw Error("Calling _rpc on a destroyed widget should return a " + "deferred that is never resolved nor rejected"); }); + assert.ok(!def, + "The trigger_up is not performed and the call returns a deferred "+ + "never resolved nor rejected"); + assert.ok(true, "there should be no crash when calling _rpc on a destroyed widget"); + parent.destroy(); }); QUnit.test('start is not called when widget is destroyed', function (assert) { diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index f5ddc0982d0..83bd275d9e0 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -111,7 +111,16 @@ var MockServer = Class.extend({ args = JSON.parse(JSON.stringify(args)); } var def = this._performRpc(route, args); - var abort = def.abort; + + var abort = def.abort || def.reject; + if (abort) { + abort = abort.bind(def); + } else { + abort = function () { + throw new Error("Can't abort this request"); + }; + } + def = def.then(function (result) { var resultString = JSON.stringify(result || false); if (debug) { @@ -125,8 +134,10 @@ var MockServer = Class.extend({ } return $.Deferred().reject(errorString, event || $.Event()); }); - def.abort = abort; - return def; + + var promise = def.promise(); + promise.abort = abort; + return promise; }, //-------------------------------------------------------------------------- diff --git a/addons/web/static/tests/helpers/test_utils.js b/addons/web/static/tests/helpers/test_utils.js index b1a0467254a..7c47bf5574a 100644 --- a/addons/web/static/tests/helpers/test_utils.js +++ b/addons/web/static/tests/helpers/test_utils.js @@ -497,15 +497,15 @@ function addMockEnvironment(widget, params) { var services = {ajax: null}; // mocked ajax service already loaded intercept(widget, 'call_service', function (ev) { var args, result; - if (ev.data.service === 'ajax') { + if (services[ev.data.service]) { + var service = services[ev.data.service]; + args = (ev.data.args || []); + result = service[ev.data.method].apply(service, args); + } else if (ev.data.service === 'ajax') { // ajax service is already mocked by the server var route = ev.data.args[0]; args = ev.data.args[1]; result = mockServer.performRpc(route, args); - } else if (services[ev.data.service]) { - var service = services[ev.data.service]; - args = (ev.data.args || []); - result = service[ev.data.method].apply(service, args); } ev.data.callback(result); }); diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 399095f9427..c967b42a332 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -1490,43 +1490,6 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('m2o group record created does not use action defaults', function (assert) { - assert.expect(1); - - var kanban = createView({ - View: KanbanView, - model: 'partner', - data: this.data, - arch: '<kanban class="o_kanban_test" on_create="quick_create">' + - '<templates><t t-name="kanban-box">' + - '<div>' + - '<field name="foo"/>' + - '</div>' + - '</t></templates>' + - '</kanban>', - groupBy: ['product_id'], - mockRPC: function (route, args) { - if (args.method === 'name_create') { - assert.notOk(_.has(args.kwargs.context, 'default_name'), - "default_* should be removed from context"); - } - return this._super.apply(this, arguments); - }, - viewOptions: { - context: { - default_name: 'default name partner', - }, - }, - }); - kanban.renderButtons(); - - kanban.$('.o_column_quick_create input').val('new product'); - kanban.$('.o_column_quick_create button.o_kanban_add').click(); - - kanban.destroy(); - }); - - QUnit.test('quick create record fail in grouped', function (assert) { assert.expect(8); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index cafd529b47c..567e722c135 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -3312,76 +3312,6 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list, handle widget locks and unlocks on sort', function (assert) { - assert.expect(6); - - // we need another sortable field to lock/unlock the handle - this.data.foo.fields.amount.sortable = true; - // resequence makes sense on a sequence field, not on arbitrary fields - this.data.foo.records[0].int_field = 0; - this.data.foo.records[1].int_field = 1; - this.data.foo.records[2].int_field = 2; - this.data.foo.records[3].int_field = 3; - - var list = createView({ - View: ListView, - model: 'foo', - data: this.data, - arch: '<tree editable="top" default_order="int_field">' + - '<field name="int_field" widget="handle"/>' + - '<field name="amount" widget="float"/>' + - '</tree>', - }); - - assert.strictEqual(list.$('tbody span[name="amount"]').text(), '1200.00500.00300.000.00', - "default should be sorted by int_field"); - - // Drag and drop the fourth line in second position - testUtils.dragAndDrop( - list.$('.ui-sortable-handle').eq(3), - list.$('tbody tr').first(), - {position: 'bottom'} - ); - - // Handle should be unlocked at this point - assert.strictEqual(list.$('tbody span[name="amount"]').text(), '1200.000.00500.00300.00', - "drag and drop should have succeeded, as the handle is unlocked"); - - // Sorting by a field different for int_field should lock the handle - list.$('.o_column_sortable').eq(1).click(); - - assert.strictEqual(list.$('tbody span[name="amount"]').text(), '0.00300.00500.001200.00', - "should have been sorted by amount"); - - // Drag and drop the fourth line in second position (not) - testUtils.dragAndDrop( - list.$('.ui-sortable-handle').eq(3), - list.$('tbody tr').first(), - {position: 'bottom'} - ); - - assert.strictEqual(list.$('tbody span[name="amount"]').text(), '0.00300.00500.001200.00', - "drag and drop should have failed as the handle is locked"); - - // Sorting by int_field should unlock the handle - list.$('.o_column_sortable').eq(0).click(); - - assert.strictEqual(list.$('tbody span[name="amount"]').text(), '1200.000.00500.00300.00', - "records should be ordered as per the previous resequence"); - - // Drag and drop the fourth line in second position - testUtils.dragAndDrop( - list.$('.ui-sortable-handle').eq(3), - list.$('tbody tr').first(), - {position: 'bottom'} - ); - - assert.strictEqual(list.$('tbody span[name="amount"]').text(), '1200.00300.000.00500.00', - "drag and drop should have worked as the handle is unlocked"); - - list.destroy(); - }); - QUnit.test('editable list with handle widget with slow network', function (assert) { assert.expect(15); |
