From 268e6d95306dfe7b35b1aff84a79eeab4a4cb230 Mon Sep 17 00:00:00 2001 From: fwi-odoo Date: Wed, 6 Sep 2017 15:17:40 +0200 Subject: [PATCH 1/4] [FIX] web: calendar: set event without time as all day Before this commit, when an event had only a date, the event was set to midnight. This commit sets the event as all day event if the field used as start date is only a date and not a datetime. --- .../src/js/views/calendar/calendar_model.js | 5 +-- .../web/static/tests/views/calendar_tests.js | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js index c8b9fd574b2..eb6d7176884 100644 --- a/addons/web/static/src/js/views/calendar/calendar_model.js +++ b/addons/web/static/src/js/views/calendar/calendar_model.js @@ -614,7 +614,8 @@ return AbstractModel.extend({ if (this.mapping.all_day && evt[this.mapping.all_day]) { date_stop.add(1, 'days'); } - + var isAllDay = this.fields[this.mapping.date_start].type === 'date' || + this.mapping.all_day && evt[this.mapping.all_day] || false; var r = { 'record': evt, 'start': date_start, @@ -622,7 +623,7 @@ return AbstractModel.extend({ 'r_start': date_start, 'r_end': date_stop, 'title': the_title, - 'allDay': this.mapping.all_day && evt[this.mapping.all_day] || false, + 'allDay': isAllDay, 'id': evt.id, 'attendees':attendees, }; diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index 9f9be3a4042..f1caba9ed4a 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -1257,6 +1257,37 @@ QUnit.module('Views', { calendar.destroy(); $view.remove(); }); + + QUnit.test('set event as all day when field is date', function (assert) { + assert.expect(1); + + this.data.event.records[0].start_date = "2016-12-14"; + + var calendar = createView({ + View: CalendarView, + model: 'event', + data: this.data, + arch: + ''+ + ''+ + ''+ + ''+ + '', + archs: archs, + viewOptions: { + initialDate: initialDate, + }, + }); + assert.strictEqual(calendar.$('.fc-day-grid .fc-event-container').length, 1, + "should be one event in the all day row"); + calendar.destroy(); + }); }); }); From 68d1e6d8b1363ce551f97ab93f7a7fe5a69fdbb8 Mon Sep 17 00:00:00 2001 From: fwi-odoo Date: Thu, 7 Sep 2017 16:52:29 +0200 Subject: [PATCH 2/4] [FIX] web: evaluate the view context for the subviews not inline Before this commit, the xml attributes defined with 'context.get(key)' in the subviews not inline were not evaluated correctly. This was caused by the loss of the one context in the '_loadSubviews' method. This commit fixes this issue and modifies the mockserver in order to be able to use the parameter 'viewOptions.context'. --- .../web/static/src/js/views/abstract_view.js | 2 +- .../web/static/tests/helpers/mock_server.js | 23 +++++--- addons/web/static/tests/helpers/test_utils.js | 11 +++- addons/web/static/tests/views/form_tests.js | 59 ++++++++++++++++++- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index acb0b464785..fc3c3cb63c3 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -264,7 +264,7 @@ var AbstractView = Class.extend({ } defs.push(parent.loadViews( field.relation, - new Context(context, self.userContext), + new Context(context, self.userContext, self.loadParams.context), [[null, attrs.mode === 'tree' ? 'list' : attrs.mode]]) .then(function (views) { for (var viewName in views) { diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index 3f97df1d4bf..eb78153265c 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -57,16 +57,22 @@ var MockServer = Class.extend({ * 'field_view_get' call to the server. Calls processViews() of data_manager * to mimick the real behavior of a call to loadViews(). * - * @param {string|Object} arch a string OR a parsed xml document - * @param {string} model a model name (that should be in this.data) + * @param {Object} params + * @param {string|Object} params.arch a string OR a parsed xml document + * @param {string} params.model a model name (that should be in this.data) + * @param {Object} params.toolbar the actions possible in the toolbar + * @param {Object} [params.viewOptions] the view options set in the test (optional) * @returns {Object} an object with 2 keys: arch and fields */ - fieldsViewGet: function (arch, model, toolbar) { + fieldsViewGet: function (params) { + var model = params.model; + var toolbar = params.toolbar; + var viewOptions = params.viewOptions || {}; if (!(model in this.data)) { throw new Error('Model ' + model + ' was not defined in mock server data'); } var fields = $.extend(true, {}, this.data[model].fields); - var fvg = this._fieldsViewGet(arch, model, fields); + var fvg = this._fieldsViewGet(params.arch, model, fields, viewOptions.context); var fields_views = {}; fields_views[fvg.type] = fvg; data_manager.processViews(fields_views, fields); @@ -155,10 +161,11 @@ var MockServer = Class.extend({ * @param {string|Object} arch a string OR a parsed xml document * @param {string} model a model name (that should be in this.data) * @param {Object} fields + * @param {Object} context * @returns {Object} an object with 2 keys: arch and fields (the fields * appearing in the views) */ - _fieldsViewGet: function (arch, model, fields) { + _fieldsViewGet: function (arch, model, fields, context) { var self = this; var modifiersNames = ['invisible', 'readonly', 'required']; var onchanges = this.data[model].onchanges || {}; @@ -224,7 +231,8 @@ var MockServer = Class.extend({ } _.each(modifiersNames, function (a) { if (node.attrs[a]) { - var v = pyeval.py_eval(node.attrs[a]) ? true: false; + var pyevalContext = window.py.dict.fromJSON(context || {}); + var v = pyeval.py_eval(node.attrs[a], {context: pyevalContext}) ? true: false; if (inTreeView && a === 'invisible') { modifiers['tree_invisible'] = v; } else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) { @@ -252,7 +260,8 @@ var MockServer = Class.extend({ _.each(node.children, function (children) { relModel = field.relation; relFields = $.extend(true, {}, self.data[relModel].fields); - field.views[children.tag] = self._fieldsViewGet(children, relModel, relFields); + field.views[children.tag] = self._fieldsViewGet(children, relModel, + relFields, context); }); } diff --git a/addons/web/static/tests/helpers/test_utils.js b/addons/web/static/tests/helpers/test_utils.js index f7003faaf5c..d97b51e68c4 100644 --- a/addons/web/static/tests/helpers/test_utils.js +++ b/addons/web/static/tests/helpers/test_utils.js @@ -129,7 +129,7 @@ function createAsyncView(params) { // add mock environment: mock server, session, fieldviewget, ... var mockServer = addMockEnvironment(widget, params); - var viewInfo = mockServer.fieldsViewGet(params.arch, params.model, params.toolbar); + var viewInfo = mockServer.fieldsViewGet(params); // create the view var viewOptions = { @@ -321,10 +321,17 @@ function addMockEnvironment(widget, params) { var view_type = view_descr[1]; var key = [model, view_id, view_type].join(','); var arch = params.archs[key]; + var viewParams = { + arch: arch, + model: model, + viewOptions: { + context: event.data.context.eval(), + }, + }; if (!arch) { throw new Error('No arch found for key ' + key); } - views[view_type] = mockServer.fieldsViewGet(arch, model); + views[view_type] = mockServer.fieldsViewGet(viewParams); }); event.data.on_success(views); diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 69e9e3696f7..f47b12d6096 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -3885,7 +3885,7 @@ QUnit.module('Views', { QUnit.test('*_view_ref in context are passed correctly', function (assert) { var done = assert.async(); - assert.expect(4); + assert.expect(3); createAsyncView({ View: FormView, @@ -3902,8 +3902,6 @@ QUnit.module('Views', { var context = event.data.context.eval(); assert.strictEqual(context.tree_view_ref, 'module.tree_view_ref', "context should contain tree_view_ref"); - assert.notOk('some_context' in context, - "should not send record's context to load p's fields_view"); event.data.on_success(); } }, @@ -5789,5 +5787,60 @@ QUnit.module('Views', { form.destroy(); }); + + QUnit.test('process the context for inline subview', function (assert) { + assert.expect(1); + + this.data.partner.records[0].p = [2]; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + viewOptions: { + context: {hide_bar: true}, + }, + }); + assert.strictEqual(form.$('.o_list_view thead tr th').length, 1, + "there should be only one column"); + form.destroy(); + }); + + QUnit.test('process the context for subview not inline', function (assert) { + assert.expect(1); + + this.data.partner.records[0].p = [2]; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '', + archs: { + "partner,false,list": '' + + '' + + '' + + '', + }, + res_id: 1, + viewOptions: { + context: {hide_bar: true}, + }, + }); + assert.strictEqual(form.$('.o_list_view thead tr th').length, 1, + "there should be only one column"); + form.destroy(); + }); }); }); From 7a87484856575ccb928563c6aad5e656a59cac16 Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Mon, 11 Sep 2017 16:28:42 +0200 Subject: [PATCH 3/4] [FIX] web: remove QUnit.only occurence --- addons/web/static/tests/views/kanban_tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 1a6684ac3b4..c341aa4735d 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -1685,7 +1685,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.only('resequence a record twice', function (assert) { + QUnit.test('resequence a record twice', function (assert) { assert.expect(10); this.data.partner.records = []; From 295a377e5d4ecaa9afe66b6d40ebf3ecbfedc6ea Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 11 Sep 2017 18:14:16 +0200 Subject: [PATCH 4/4] [IMP] web: add test to ensure nobody will commit again unwanted `QUnit.only` --- addons/web/tests/test_js.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/addons/web/tests/test_js.py b/addons/web/tests/test_js.py index 1614be64c43..17eb5c8beb2 100644 --- a/addons/web/tests/test_js.py +++ b/addons/web/tests/test_js.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import re import odoo.tests @@ -11,3 +12,19 @@ class WebSuite(odoo.tests.HttpCase): def test_01_js(self): self.phantom_js('/web/tests?mod=web', "", "", login='admin', timeout=300) + + def test_check_suite(self): + # verify no js test is using `QUnit.only` as it forbid any other test to be executed + re_only = re.compile('QUnit\.only\(') + + # As we currently aren't in a request context, we can't render `web.layout`. + # redefinied it as a minimal proxy template. + self.env.ref('web.layout').write({'arch_db': ''}) + + for asset in self.env['ir.qweb']._get_asset_content('web.qunit_suite', options={})[0]: + filename = asset['filename'] + if not filename or asset['atype'] != 'text/javascript': + continue + with open(filename, 'r') as fp: + if re_only.search(fp.read()): + self.fail("`QUnit.only()` used in file %r" % asset['url'])