summaryrefslogtreecommitdiff
path: root/addons/web/static
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-01-28 13:58:11 +0000
committerChristophe Simonis <chs@odoo.com>2019-01-28 13:58:11 +0000
commitcf52a04979d7634908c2f0d8eb6a83b7dd614f64 (patch)
tree2f8c25f3fab45268400c4ac6addfa15f1f2d37f0 /addons/web/static
parentcd1f57e9ded2e8ca81d7290e4b5b95a8776045c6 (diff)
parentd3b8422c9c44557256836bdc0da5480f553eec4d (diff)
[MERGE] forward port branch saas-12.1 up to d3b8422c9c4
closes odoo/odoo#30614
Diffstat (limited to 'addons/web/static')
-rw-r--r--addons/web/static/src/js/chrome/abstract_web_client.js4
-rw-r--r--addons/web/static/src/js/chrome/apps_menu.js3
-rw-r--r--addons/web/static/src/js/fields/field_utils.js2
-rw-r--r--addons/web/static/src/js/fields/special_fields.js3
-rw-r--r--addons/web/static/src/js/services/crash_manager.js35
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js12
-rw-r--r--addons/web/static/src/js/views/control_panel/control_panel_model.js14
-rw-r--r--addons/web/static/src/js/views/field_manager_mixin.js6
-rw-r--r--addons/web/static/src/js/views/graph/graph_renderer.js4
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_controller.js29
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js3
-rw-r--r--addons/web/static/src/js/views/list/list_controller.js10
-rw-r--r--addons/web/static/src/js/views/pivot/pivot_model.js39
-rw-r--r--addons/web/static/src/js/widgets/pie_chart.js1
-rw-r--r--addons/web/static/src/scss/bootstrap_overridden.scss10
-rw-r--r--addons/web/static/src/scss/kanban_view_mobile.scss3
-rw-r--r--addons/web/static/src/xml/pie_chart.xml2
-rw-r--r--addons/web/static/tests/fields/field_utils_tests.js30
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js97
-rw-r--r--addons/web/static/tests/views/form_tests.js108
-rw-r--r--addons/web/static/tests/views/kanban_mobile_tests.js6
-rw-r--r--addons/web/static/tests/views/kanban_tests.js100
-rw-r--r--addons/web/static/tests/views/pivot_tests.js84
-rw-r--r--addons/web/static/tests/views/search_view_tests.js62
-rw-r--r--addons/web/static/tests/widgets/data_export_tests.js28
25 files changed, 551 insertions, 144 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 ea8fdc24a18..7082f26e69d 100644
--- a/addons/web/static/src/js/chrome/abstract_web_client.js
+++ b/addons/web/static/src/js/chrome/abstract_web_client.js
@@ -205,6 +205,10 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
});
}
} else {
+ // ignore Chrome video internal error: https://crbug.com/809574
+ if (!error && message === 'ResizeObserver loop limit exceeded') {
+ return;
+ }
var traceback = error ? error.stack : '';
crash_manager.show_error({
type: _t("Odoo Client Error"),
diff --git a/addons/web/static/src/js/chrome/apps_menu.js b/addons/web/static/src/js/chrome/apps_menu.js
index f6535ed764c..eb7faaa971b 100644
--- a/addons/web/static/src/js/chrome/apps_menu.js
+++ b/addons/web/static/src/js/chrome/apps_menu.js
@@ -41,6 +41,9 @@ var AppsMenu = Widget.extend({
* Open the first app in the list of apps
*/
openFirstApp: function () {
+ if (!this._apps.length) {
+ return
+ }
var firstApp = this._apps[0];
this._openApp(firstApp);
},
diff --git a/addons/web/static/src/js/fields/field_utils.js b/addons/web/static/src/js/fields/field_utils.js
index bbdee83c4f9..ec99f4fa0f6 100644
--- a/addons/web/static/src/js/fields/field_utils.js
+++ b/addons/web/static/src/js/fields/field_utils.js
@@ -363,7 +363,7 @@ function formatPercentage(value, field, options) {
if (options.humanReadable && options.humanReadable(value * 100)) {
return result + "%";
}
- return parseFloat(result) + "%";
+ return (parseFloat(result) + "%").replace('.', _t.database.parameters.decimal_point);
}
/**
* Returns a string representing the value of the selection.
diff --git a/addons/web/static/src/js/fields/special_fields.js b/addons/web/static/src/js/fields/special_fields.js
index 5057b9de33f..e9dcbc592a3 100644
--- a/addons/web/static/src/js/fields/special_fields.js
+++ b/addons/web/static/src/js/fields/special_fields.js
@@ -122,7 +122,8 @@ var FieldTimezoneMismatch = FieldSelection.extend({
});
var FieldReportLayout = relational_fields.FieldMany2One.extend({
- supportedFieldTypes: ['many2one', 'selection'],
+ // this widget is not generic, so we disable its studio use
+ // supportedFieldTypes: ['many2one', 'selection'],
events: _.extend({}, relational_fields.FieldMany2One.prototype.events, {
'click img': '_onImgClicked',
}),
diff --git a/addons/web/static/src/js/services/crash_manager.js b/addons/web/static/src/js/services/crash_manager.js
index e1da816c8e5..31aa178dc1b 100644
--- a/addons/web/static/src/js/services/crash_manager.js
+++ b/addons/web/static/src/js/services/crash_manager.js
@@ -22,6 +22,7 @@ var map_title ={
var CrashManager = core.Class.extend({
init: function() {
this.active = true;
+ this.isConnected = true;
},
enable: function () {
this.active = true;
@@ -29,8 +30,29 @@ var CrashManager = core.Class.extend({
disable: function () {
this.active = false;
},
- rpc_error: function(error) {
+ handleLostConnection: function () {
var self = this;
+ if (!this.isConnected) {
+ // already handled, nothing to do. This can happen when several
+ // rpcs are done in parallel and fail because of a lost connection.
+ return;
+ }
+ this.isConnected = false;
+ var delay = 2000;
+ core.bus.trigger('connection_lost');
+
+ setTimeout(function checkConnection() {
+ ajax.jsonRpc('/web/webclient/version_info', 'call', {}, {shadow:true}).then(function () {
+ core.bus.trigger('connection_restored');
+ self.isConnected = true;
+ }).fail(function () {
+ // exponential backoff, with some jitter
+ delay = (delay * 1.5) + 500*Math.random();
+ setTimeout(checkConnection, delay);
+ });
+ }, delay);
+ },
+ rpc_error: function(error) {
if (!this.active) {
return;
}
@@ -38,16 +60,7 @@ var CrashManager = core.Class.extend({
return;
}
if (error.code === -32098) {
- core.bus.trigger('connection_lost');
- this.connection_lost = true;
- var timeinterval = setInterval(function() {
- var options = {shadow: true};
- ajax.jsonRpc('/web/webclient/version_info', 'call', {}, options).then(function () {
- clearInterval(timeinterval);
- core.bus.trigger('connection_restored');
- self.connection_lost = false;
- });
- }, 2000);
+ this.handleLostConnection();
return;
}
var handler = core.crash_registry.get(error.data.name, true);
diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js
index 7f578da077d..f26a9cbe737 100644
--- a/addons/web/static/src/js/views/basic/basic_controller.js
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -38,6 +38,10 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
FieldManagerMixin.init.call(this, this.model);
this.mode = params.mode || 'readonly';
this.handle = this.initialState.id;
+ // savingDef is used to ensure that we always wait for pending save
+ // operations to complete before checking if there are changes to
+ // discard when discardChanges is called
+ this.savingDef = $.when();
},
/**
* @override
@@ -102,14 +106,15 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
return true;
},
/**
- * Waits for the mutex to be unlocked and then calls _.discardChanges.
+ * Waits for the mutex to be unlocked and for changes to be saved, then
+ * calls _.discardChanges.
* This ensures that the confirm dialog isn't displayed directly if there is
* a pending 'write' rpc.
*
* @see _.discardChanges
*/
discardChanges: function (recordID, options) {
- return this.mutex.exec(function () {})
+ return $.when(this.mutex.getUnlockedDef(), this.savingDef)
.then(this._discardChanges.bind(this, recordID || this.handle, options));
},
/**
@@ -183,11 +188,12 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
// mutex-protected as commitChanges function of x2m has to be aware of
// all final changes made to a row.
var self = this;
- return this.mutex.getUnlockedDef().then(function () {
+ this.savingDef = this.mutex.getUnlockedDef().then(function () {
return self.renderer.commitChanges(recordID || self.handle).then(function () {
return self.mutex.exec(self._saveRecord.bind(self, recordID, options));
});
});
+ return this.savingDef;
},
/**
* @override
diff --git a/addons/web/static/src/js/views/control_panel/control_panel_model.js b/addons/web/static/src/js/views/control_panel/control_panel_model.js
index 67b1a4c64f8..809fb0608b8 100644
--- a/addons/web/static/src/js/views/control_panel/control_panel_model.js
+++ b/addons/web/static/src/js/views/control_panel/control_panel_model.js
@@ -646,8 +646,18 @@ var ControlPanelModel = mvc.Model.extend({
*/
_getFilterContext: function (filter) {
var context = filter.context || {};
+
+ // for <field> nodes, a dynamic context (like context="{'field1': self}")
+ // should set {'field1': [value1, value2]} in the context
+ if (filter.type === 'field' && filter.attrs.context) {
+ context = pyUtils.eval('context', filter.attrs.context, {
+ self: _.map(filter.autoCompleteValues, function (autoCompleteValue) {
+ return autoCompleteValue.value;
+ }),
+ });
+ }
// the following code aims to restore this:
- // https://github.com/odoo/odoo/blob/master/addons/web/static/src/js/views/search/search_inputs.js#L498
+ // https://github.com/odoo/odoo/blob/12.0/addons/web/static/src/js/views/search/search_inputs.js#L498
// this is required for the helpdesk tour to pass
// this seems weird to only do that for m2o fields, but a test fails if
// we do it for other fields (my guess being that the test should simply
@@ -656,7 +666,7 @@ var ControlPanelModel = mvc.Model.extend({
if (this.fields[filter.attrs.name].type === 'many2one') {
var value = filter.defaultValue;
// the following if required to make the main_flow_tour pass (see
- // https://github.com/odoo/odoo/blob/master/addons/web/static/src/js/views/search/search_inputs.js#L461)
+ // https://github.com/odoo/odoo/blob/12.0/addons/web/static/src/js/views/search/search_inputs.js#L461)
if (_.isArray(filter.defaultValue)) {
value = filter.defaultValue[0];
}
diff --git a/addons/web/static/src/js/views/field_manager_mixin.js b/addons/web/static/src/js/views/field_manager_mixin.js
index 343730cc7a4..bc61e92a89a 100644
--- a/addons/web/static/src/js/views/field_manager_mixin.js
+++ b/addons/web/static/src/js/views/field_manager_mixin.js
@@ -130,8 +130,10 @@ var FieldManagerMixin = {
if ('offset' in data) {
params.offset = data.offset;
}
- this.model.reload(data.id, params).then(function (db_id) {
- data.on_success(self.model.get(db_id));
+ this.mutex.exec(function () {
+ return self.model.reload(data.id, params).then(function (db_id) {
+ data.on_success(self.model.get(db_id));
+ });
});
},
/**
diff --git a/addons/web/static/src/js/views/graph/graph_renderer.js b/addons/web/static/src/js/views/graph/graph_renderer.js
index 38c2f02351e..856882a303b 100644
--- a/addons/web/static/src/js/views/graph/graph_renderer.js
+++ b/addons/web/static/src/js/views/graph/graph_renderer.js
@@ -215,6 +215,10 @@ return AbstractRenderer.extend({
margin: {left: 80, bottom: 100, top: 80, right: 0},
delay: 100,
transition: 10,
+ controlLabels: {
+ 'grouped': _t('Grouped'),
+ 'stacked': _t('Stacked'),
+ },
showLegend: _.size(data) <= MAX_LEGEND_LENGTH,
showXAxis: true,
showYAxis: true,
diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js
index 15c804f5a8e..b51c32aa770 100644
--- a/addons/web/static/src/js/views/kanban/kanban_controller.js
+++ b/addons/web/static/src/js/views/kanban/kanban_controller.js
@@ -213,15 +213,17 @@ var KanbanController = BasicController.extend({
*/
_onAddColumn: function (ev) {
var self = this;
- this.model.createGroup(ev.data.value, this.handle).then(function () {
- var state = self.model.get(self.handle, {raw: true});
- var ids = _.pluck(state.data, 'res_id').filter(_.isNumber);
- return self._resequenceColumns(ids);
- }).then(function () {
- return self.update({}, {reload: false});
- }).then(function () {
- self._updateButtons();
- self.renderer.quickCreateToggleFold();
+ this.mutex.exec(function () {
+ return self.model.createGroup(ev.data.value, self.handle).then(function () {
+ var state = self.model.get(self.handle, {raw: true});
+ var ids = _.pluck(state.data, 'res_id').filter(_.isNumber);
+ return self._resequenceColumns(ids);
+ }).then(function () {
+ return self.update({}, {reload: false});
+ }).then(function () {
+ self._updateButtons();
+ self.renderer.quickCreateToggleFold();
+ });
});
},
/**
@@ -274,11 +276,16 @@ var KanbanController = BasicController.extend({
* @private
*/
_onButtonNew: function () {
+ var self = this;
var state = this.model.get(this.handle, {raw: true});
var quickCreateEnabled = this.quickCreateEnabled && viewUtils.isQuickCreateEnabled(state);
if (this.on_create === 'quick_create' && quickCreateEnabled && state.data.length) {
- // Activate the quick create in the first column
- this.renderer.addQuickCreate();
+ // activate the quick create in the first column when the mutex is
+ // unlocked, to ensure that there is no pending re-rendering that
+ // would remove it (e.g. if we are currently adding a new column)
+ this.mutex.getUnlockedDef().then(function () {
+ self.renderer.addQuickCreate();
+ });
} else if (this.on_create && this.on_create !== 'quick_create') {
// Execute the given action
this.do_action(this.on_create, {
diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js b/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
index b7f2dd62b28..8619448e793 100644
--- a/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
+++ b/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
@@ -143,7 +143,7 @@ KanbanRenderer.include({
onSuccess: function () {
// update the columns and tabs positions (optionally with an animation)
var updateFunc = animate ? 'animate' : 'css';
- self.$('.o_kanban_mobile_tab, .o_kanban_group').removeClass('o_current');
+ self.$('.o_kanban_mobile_tab').removeClass('o_current');
_.each(self.widgets, function (column, index) {
var columnID = column.id || column.db_id;
var $column = self.$('.o_kanban_group[data-id="' + columnID + '"]');
@@ -157,7 +157,6 @@ KanbanRenderer.include({
} else if (index === moveToIndex) {
$column[updateFunc]({left: '0%'});
$tab[updateFunc]({left: '50%'});
- $column.addClass('o_current');
$tab.addClass('o_current');
} else if (index < moveToIndex) {
$column.css({left: '-100%'});
diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js
index fd5429d7c3e..dfa7d272a49 100644
--- a/addons/web/static/src/js/views/list/list_controller.js
+++ b/addons/web/static/src/js/views/list/list_controller.js
@@ -71,15 +71,9 @@ var ListController = BasicController.extend({
// TODO: this method should be synchronous...
var self = this;
if (this.$('thead .o_list_record_selector input').prop('checked')) {
- var searchData = this.searchView.build_search_data();
- var userContext = this.getSession().user_context;
- var results = pyUtils.eval_domains_and_contexts({
- domains: searchData.domains,
- contexts: [userContext].concat(searchData.contexts),
- group_by_seq: searchData.groupbys || []
- });
+ var searchQuery = this._controlPanel ? this._controlPanel.getSearchQuery() : {};
var record = self.model.get(self.handle, {raw: true});
- return $.when(record.getDomain().concat(results.domain || []));
+ return $.when(record.getDomain().concat(searchQuery.domain || []));
} else {
return $.Deferred().resolve();
}
diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js
index 5a6423fbe34..4c37b823639 100644
--- a/addons/web/static/src/js/views/pivot/pivot_model.js
+++ b/addons/web/static/src/js/views/pivot/pivot_model.js
@@ -315,13 +315,13 @@ var PivotModel = AbstractModel.extend({
if (!this.data.has_data) {
return {
has_data: false,
- colGroupBys: this.data.main_col.groupbys,
- rowGroupBys: this.data.main_row.groupbys,
+ colGroupBys: this.data.main_col.root.groupbys,
+ rowGroupBys: this.data.main_row.root.groupbys,
measures: this.data.measures,
};
}
return {
- colGroupBys: this.data.main_col.groupbys,
+ colGroupBys: this.data.main_col.root.groupbys,
context: this.data.context,
domain: this.data.domain,
compare: this.data.compare,
@@ -331,7 +331,7 @@ var PivotModel = AbstractModel.extend({
mainColWidth: this.data.main_col.width,
measures: this.data.measures,
rows: !isRaw && this._computeRows(),
- rowGroupBys: this.data.main_row.groupbys,
+ rowGroupBys: this.data.main_row.root.groupbys,
sortedColumn: this.data.sorted_column,
};
},
@@ -432,22 +432,19 @@ var PivotModel = AbstractModel.extend({
return this._loadData();
}
- var old_row_root = this.data.main_row.root;
- var old_col_root = this.data.main_col.root;
+ var old_row = this.data.main_row;
+ var old_col = this.data.main_col;
return this._loadData().then(function () {
- var new_groupby_length;
if (!('groupBy' in params) && !('pivot_row_groupby' in (params.context || {}))) {
// we only update the row groupbys according to the old groupbys
// if we don't have the key 'groupBy' in params. In that case,
// we want to have the full open state for the groupbys.
- self._updateTree(old_row_root, self.data.main_row.root);
- new_groupby_length = self._getHeaderDepth(self.data.main_row.root) - 1;
- self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length);
+ self._updateTree(old_row.root, self.data.main_row.root);
+ self._updateMainGroupBys(old_row, self.data.main_row);
}
- self._updateTree(old_col_root, self.data.main_col.root);
- new_groupby_length = self._getHeaderDepth(self.data.main_col.root) - 1;
- self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length);
+ self._updateTree(old_col.root, self.data.main_col.root);
+ self._updateMainGroupBys(old_col, self.data.main_col);
});
},
/**
@@ -1052,9 +1049,6 @@ var PivotModel = AbstractModel.extend({
}
}
- this.data.main_row.groupbys = rowGroupBys;
- this.data.main_col.groupbys = colGroupBys;
-
main_row_header.other_root = main_col_header;
main_col_header.other_root = main_row_header;
@@ -1124,6 +1118,19 @@ var PivotModel = AbstractModel.extend({
}
},
/**
+ * Updates the groupBys that the main[row | col] holds
+ *
+ * @private
+ * @param {Object} old: The main abstract header before reload
+ * @param {Object} main: The main abstract header after reload
+ *
+ */
+ _updateMainGroupBys: function (old, main) {
+ var new_groupby_length = this._getHeaderDepth(main.root) - 1;
+ var new_groupby_list = old.root.groupbys.slice(0, new_groupby_length);
+ main.root.groupbys = new_groupby_list;
+ },
+ /**
* @param {Object} old_tree
* @param {Object} new_tree
*/
diff --git a/addons/web/static/src/js/widgets/pie_chart.js b/addons/web/static/src/js/widgets/pie_chart.js
index 30944d69584..1054360b3fd 100644
--- a/addons/web/static/src/js/widgets/pie_chart.js
+++ b/addons/web/static/src/js/widgets/pie_chart.js
@@ -31,6 +31,7 @@ var PieChart = Widget.extend({
Domain.prototype.stringToArray(modifiers.domain || '[]'));
var arch = qweb.render('web.PieChart', {
modifiers: modifiers,
+ title: node.attrs.title || modifiers.title || modifiers.measure,
});
this.subViewParams = {
diff --git a/addons/web/static/src/scss/bootstrap_overridden.scss b/addons/web/static/src/scss/bootstrap_overridden.scss
index b81146a811f..c1f8cbb44d3 100644
--- a/addons/web/static/src/scss/bootstrap_overridden.scss
+++ b/addons/web/static/src/scss/bootstrap_overridden.scss
@@ -66,11 +66,11 @@ $font-family-sans-serif: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
$font-size-base: $o-font-size-base !default;
$line-height-base: $o-line-height-base !default;
-$h1-font-size: $o-font-size-base * 2.0 !default;
-$h2-font-size: $o-font-size-base * 1.5 !default;
-$h3-font-size: $o-font-size-base * 1.3 !default;
-$h4-font-size: $o-font-size-base * 1.2 !default;
-$h5-font-size: $o-font-size-base * 1.1 !default;
+$h1-font-size: $font-size-base * 2.0 !default;
+$h2-font-size: $font-size-base * 1.5 !default;
+$h3-font-size: $font-size-base * 1.3 !default;
+$h4-font-size: $font-size-base * 1.2 !default;
+$h5-font-size: $font-size-base * 1.1 !default;
$headings-font-weight: bold !default;
diff --git a/addons/web/static/src/scss/kanban_view_mobile.scss b/addons/web/static/src/scss/kanban_view_mobile.scss
index 1c5a9eab531..b74ae8aaea5 100644
--- a/addons/web/static/src/scss/kanban_view_mobile.scss
+++ b/addons/web/static/src/scss/kanban_view_mobile.scss
@@ -36,9 +36,6 @@
margin-left: 0; // override the margin-left: -1px of the desktop mode
border: none;
- &.o_current {
- position: initial;
- }
.o_kanban_header {
display: none;
}
diff --git a/addons/web/static/src/xml/pie_chart.xml b/addons/web/static/src/xml/pie_chart.xml
index 5c3411bc882..d45bef7af6f 100644
--- a/addons/web/static/src/xml/pie_chart.xml
+++ b/addons/web/static/src/xml/pie_chart.xml
@@ -1,7 +1,7 @@
<template>
<t t-name="web.PieChart">
- <graph type="pie" t-att-title="modifiers.title || modifiers.measure">
+ <graph type="pie" t-att-title="title">
<field t-att-name="modifiers.groupby.split(':')[0]" t-att-interval="modifiers.groupby.split(':')[1]"/>
<field t-if="modifiers.measure" t-att-name="modifiers.measure" type="measure"/>
</graph>
diff --git a/addons/web/static/tests/fields/field_utils_tests.js b/addons/web/static/tests/fields/field_utils_tests.js
index 6b7cda13264..940f5674780 100644
--- a/addons/web/static/tests/fields/field_utils_tests.js
+++ b/addons/web/static/tests/fields/field_utils_tests.js
@@ -135,7 +135,9 @@ QUnit.test('format binary', function (assert) {
});
QUnit.test('format percentage', function (assert) {
- assert.expect(9);
+ assert.expect(11);
+
+ var originalParameters = _.clone(core._t.database.parameters);
assert.strictEqual(fieldUtils.format.percentage(0), '0%');
assert.strictEqual(fieldUtils.format.percentage(0.5), '50%');
@@ -151,6 +153,16 @@ QUnit.test('format percentage', function (assert) {
assert.strictEqual(fieldUtils.format.percentage(50, null,
{humanReadable: function (val) {return true;}}), '5k%'
);
+
+ _.extend(core._t.database.parameters, {
+ grouping: [3, 0],
+ decimal_point: ',',
+ thousands_sep: '.'
+ });
+ assert.strictEqual(fieldUtils.format.percentage(0.125), '12,5%');
+ assert.strictEqual(fieldUtils.format.percentage(0.666666), '66,67%');
+
+ core._t.database.parameters = originalParameters;
});
QUnit.test('parse float', function(assert) {
@@ -262,7 +274,9 @@ QUnit.test('parse monetary', function(assert) {
});
QUnit.test('parse percentage', function(assert) {
- assert.expect(7);
+ assert.expect(9);
+
+ var originalParameters = _.clone(core._t.database.parameters);
assert.strictEqual(fieldUtils.parse.percentage(""), 0);
assert.strictEqual(fieldUtils.parse.percentage("0"), 0);
@@ -271,6 +285,18 @@ QUnit.test('parse percentage', function(assert) {
assert.strictEqual(fieldUtils.parse.percentage("1"), 1);
assert.strictEqual(fieldUtils.parse.percentage("2%"), 0.02);
assert.strictEqual(fieldUtils.parse.percentage("100%"), 1);
+
+ _.extend(core._t.database.parameters, {
+ grouping: [3, 0],
+ decimal_point: ',',
+ thousands_sep: '.'
+ });
+
+ assert.strictEqual(fieldUtils.parse.percentage("0,04"), 0.04);
+ assert.strictEqual(fieldUtils.parse.percentage("6,02%"), 0.0602);
+
+ core._t.database.parameters = originalParameters;
+
})
});
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 15999ca952e..463effad124 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -150,6 +150,61 @@ QUnit.module('relational_fields', {
}
}, function () {
+ QUnit.test('search more pager is reset when doing a new search', function (assert) {
+ assert.expect(6);
+ for(var i = 10; i < 180; i++) {
+ this.data.partner.records.push({
+ id: i,
+ display_name: "Partner " + i,
+ });
+ }
+
+ function stringToEvent ($element, string) {
+ for (var i = 0; i < string.length; i++) {
+ var keyAscii = string.charCodeAt(i);
+ $element.val($element.val()+string[i]);
+ $element.trigger($.Event('keyup', {which: keyAscii, keyCode:keyAscii}));
+ }
+ $element.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER, keyCode:$.ui.keyCode.ENTER}));
+ }
+
+ this.data.partner.fields.datetime.searchable = true;
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<sheet>' +
+ '<group>' +
+ '<field name="trululu"/>' +
+ '</group>' +
+ '</sheet>' +
+ '</form>',
+ archs: {
+ 'partner,false,list': '<tree><field name="display_name"/></tree>',
+ 'partner,false,search': '<search><field name="datetime"/><field name="display_name"/></search>',
+ },
+ res_id: 1,
+ });
+
+ form.$buttons.find('.o_form_button_edit').click();
+ var $dropdown = form.$('.o_field_many2one input').autocomplete('widget');
+ form.$('.o_field_many2one input').click();
+ $dropdown.find('.o_m2o_dropdown_option:contains(Search)').mouseenter().click();
+ $('.modal .o_pager_next').click();
+
+ assert.strictEqual($('.o_pager_limit').text(), "1173", "there should be 173 records");
+ assert.strictEqual($('.o_pager_value').text(), "181-160", "should display the second page");
+ assert.strictEqual($('tr.o_data_row').length, 80, "should display 80 record");
+
+ stringToEvent($('.modal .o_searchview_input'), 'first');
+
+ assert.strictEqual($('.o_pager_limit').text(), "11", "there should be 1 record");
+ assert.strictEqual($('.o_pager_value').text(), "11-1", "should display the first page");
+ assert.strictEqual($('tr.o_data_row').length, 1, "should display 1 record");
+ form.destroy();
+ });
+
QUnit.test('x2many default_order multiple fields', function (assert) {
assert.expect(7);
@@ -446,6 +501,48 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('quickly switch between pages in one2many list', function (assert) {
+ assert.expect(2);
+
+ this.data.partner.records[0].turtles = [1, 2, 3];
+
+ var readDefs = [$.when(), $.Deferred(), $.Deferred()];
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<field name="turtles">' +
+ '<tree limit="1">' +
+ '<field name="display_name"/>' +
+ '</tree>' +
+ '</field>' +
+ '</form>',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'read') {
+ var recordID = args.args[0][0];
+ return $.when(readDefs[recordID - 1]).then(_.constant(result));
+ }
+ return result;
+ },
+ res_id: 1,
+ });
+
+ form.$('.o_field_widget[name=turtles] .o_pager_next').click();
+ form.$('.o_field_widget[name=turtles] .o_pager_next').click();
+
+ readDefs[1].resolve();
+
+ assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_data_cell').text(), 'donatello');
+
+ readDefs[2].resolve();
+
+ assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_data_cell').text(), 'raphael');
+
+ form.destroy();
+ });
+
QUnit.test('many2many read, field context is properly sent', function (assert) {
assert.expect(4);
diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js
index e1cb3277dba..e7d425b997e 100644
--- a/addons/web/static/tests/views/form_tests.js
+++ b/addons/web/static/tests/views/form_tests.js
@@ -1634,12 +1634,6 @@ QUnit.module('Views', {
'</form>',
viewOptions: {hasSidebar: true},
res_id: 1,
- mockRPC: function (route, args) {
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
- return this._super.apply(this, arguments);
- },
});
assert.isVisible(form.sidebar);
@@ -1782,12 +1776,6 @@ QUnit.module('Views', {
'</form>',
res_id: 1,
viewOptions: {hasSidebar: true},
- mockRPC: function (route, args) {
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
- return this._super.apply(this, arguments);
- },
});
assert.strictEqual(form.$('.o_control_panel .breadcrumb').text(), 'first record',
@@ -1822,9 +1810,6 @@ QUnit.module('Views', {
assert.strictEqual(args.kwargs.context.hey, 'hoy',
"should have send the correct context");
}
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
return this._super.apply(this, arguments);
},
});
@@ -1847,12 +1832,6 @@ QUnit.module('Views', {
'</form>',
res_id: 1,
viewOptions: {hasSidebar: true},
- mockRPC: function (route, args) {
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
- return this._super.apply(this, arguments);
- },
});
assert.strictEqual(form.$('.o_control_panel .breadcrumb').text(), 'first record',
@@ -2394,12 +2373,6 @@ QUnit.module('Views', {
arch: '<form string="Partners"><field name="foo"></field></form>',
res_id: 1,
viewOptions: {hasSidebar: true},
- mockRPC: function (route, args) {
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
- return this._super.apply(this, arguments);
- },
});
testUtils.form.clickEdit(form);
testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala');
@@ -2736,12 +2709,6 @@ QUnit.module('Views', {
hasSidebar: true,
},
res_id: 1,
- mockRPC: function (route, args) {
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
- return this._super.apply(this, arguments);
- },
});
assert.strictEqual(form.pager.$('.o_pager_value').text(), "1", 'pager value should be 1');
@@ -2781,9 +2748,6 @@ QUnit.module('Views', {
},
res_id: 1,
mockRPC: function (route, args) {
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
assert.step(args.method);
return this._super.apply(this, arguments);
}
@@ -4945,9 +4909,6 @@ QUnit.module('Views', {
"the active_ids should be an array with 1 inside.");
return $.when({});
}
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
return this._super.apply(this, arguments);
},
});
@@ -6224,9 +6185,6 @@ QUnit.module('Views', {
res_id: 1,
viewOptions: {hasSidebar: true},
mockRPC: function (route, args) {
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
var result = this._super.apply(this, arguments);
if (args.method === 'copy') {
return result.then(function (id) {
@@ -6878,9 +6836,6 @@ QUnit.module('Views', {
if (args.method === 'unlink') {
assert.step('unlink');
}
- if (args.method === 'search_read' && args.model === 'ir.attachment') {
- return $.when([]);
- }
return this._super.apply(this, arguments);
},
res_id: 1,
@@ -7189,6 +7144,69 @@ QUnit.module('Views', {
form.destroy();
});
+ QUnit.test('call canBeRemoved while saving', function (assert) {
+ assert.expect(10);
+
+ this.data.partner.onchanges = {
+ foo: function (obj) {
+ obj.display_name = obj.foo === 'trigger onchange' ? 'changed' : 'default';
+ },
+ };
+
+ var onchangeDef;
+ var createDef = $.Deferred();
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form><field name="display_name"/><field name="foo"/></form>',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'onchange') {
+ return $.when(onchangeDef).then(_.constant(result));
+ }
+ if (args.method === 'create') {
+ return $.when(createDef).then(_.constant(result));
+ }
+ return result;
+ },
+ });
+
+ // edit foo to trigger a delayed onchange
+ onchangeDef = $.Deferred();
+ testUtils.fields.editInput(form.$('.o_field_widget[name=foo]'), 'trigger onchange');
+
+ assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), 'default');
+
+ // save (will wait for the onchange to return), and will be delayed as well
+ testUtils.dom.click(form.$buttons.find('.o_form_button_save'));
+
+ assert.hasClass(form.$('.o_form_view'), 'o_form_editable');
+ assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), 'default');
+
+ // simulate a click on the breadcrumbs to leave the form view
+ form.canBeRemoved();
+
+ assert.hasClass(form.$('.o_form_view'), 'o_form_editable');
+ assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), 'default');
+
+ // unlock the onchange
+ onchangeDef.resolve();
+
+ assert.hasClass(form.$('.o_form_view'), 'o_form_editable');
+ assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), 'changed');
+
+ // unlock the create
+ createDef.resolve();
+
+ assert.hasClass(form.$('.o_form_view'), 'o_form_readonly');
+ assert.strictEqual(form.$('.o_field_widget[name=display_name]').text(), 'changed');
+ assert.containsNone(document.body, '.modal',
+ "should not display the 'Changes will be discarded' dialog");
+
+ form.destroy();
+ });
+
QUnit.module('FormViewTABMainButtons');
QUnit.test('using tab in an empty required string field should not move to the next field',function(assert) {
diff --git a/addons/web/static/tests/views/kanban_mobile_tests.js b/addons/web/static/tests/views/kanban_mobile_tests.js
index a9eec223143..98e65d1ea9c 100644
--- a/addons/web/static/tests/views/kanban_mobile_tests.js
+++ b/addons/web/static/tests/views/kanban_mobile_tests.js
@@ -56,7 +56,7 @@ QUnit.module('Views', {
QUnit.module('KanbanView Mobile');
QUnit.test('mobile grouped rendering', function (assert) {
- assert.expect(11);
+ assert.expect(9);
var kanban = createView({
View: KanbanView,
@@ -75,8 +75,6 @@ QUnit.module('Views', {
assert.containsN(kanban, '.o_kanban_group', 2, "should have 2 columns" );
assert.hasClass(kanban.$('.o_kanban_mobile_tab:first'),'o_current',
"first tab is the active tab with class 'o_current'");
- assert.hasClass(kanban.$('.o_kanban_group:first'),'o_current',
- "first column is the active column with class 'o_current'");
assert.containsN(kanban, '.o_kanban_group:first > div.o_kanban_record', 2,
"there are 2 records in active tab");
assert.strictEqual(kanban.$('.o_kanban_group:nth(1) > div.o_kanban_record').length, 0,
@@ -91,8 +89,6 @@ QUnit.module('Views', {
kanban.$('.o_kanban_mobile_tab:nth(1)').trigger('click');
assert.hasClass(kanban.$('.o_kanban_mobile_tab:nth(1)'),'o_current',
"second tab is now active with class 'o_current'");
- assert.hasClass(kanban.$('.o_kanban_group:nth(1)'),'o_current',
- "second column is now active with class 'o_current'");
assert.strictEqual(kanban.$('.o_kanban_group:nth(1) > div.o_kanban_record').length, 2,
"the 2 records of the second group have now been loaded");
diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js
index 6702a9bee37..47af910083d 100644
--- a/addons/web/static/tests/views/kanban_tests.js
+++ b/addons/web/static/tests/views/kanban_tests.js
@@ -2110,6 +2110,67 @@ QUnit.module('Views', {
kanban.destroy();
});
+ QUnit.test('quick create record while adding a new column', function (assert) {
+ assert.expect(10);
+
+ var def = $.Deferred();
+ var kanban = createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: '<kanban 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) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'name_create' && args.model === 'product') {
+ return def.then(_.constant(result));
+ }
+ return result;
+ },
+ });
+
+ assert.containsN(kanban, '.o_kanban_group', 2);
+ assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2);
+
+ // add a new column
+ assert.containsOnce(kanban, '.o_column_quick_create');
+ assert.isNotVisible(kanban.$('.o_column_quick_create input'));
+
+ testUtils.dom.click(kanban.$('.o_quick_create_folded'));
+
+ assert.isVisible(kanban.$('.o_column_quick_create input'));
+
+ testUtils.fields.editInput(kanban.$('.o_column_quick_create input'), 'new column');
+ testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add'));
+
+ assert.containsN(kanban, '.o_kanban_group', 2);
+
+ // click to add a new record
+ testUtils.dom.click(kanban.$buttons.find('.o-kanban-button-new'));
+
+ // should wait for the column to be created (and view to be re-rendered
+ // before opening the quick create
+ assert.containsNone(kanban, '.o_kanban_quick_create');
+
+ // unlock column creation
+ def.resolve();
+
+ assert.containsN(kanban, '.o_kanban_group', 3);
+ assert.containsOnce(kanban, '.o_kanban_quick_create');
+
+ // quick create record in first column
+ testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record');
+ testUtils.dom.click(kanban.$('.o_kanban_quick_create .o_kanban_add'));
+
+ assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 3);
+
+ kanban.destroy();
+ });
+
QUnit.test('many2many_tags in kanban views', function (assert) {
assert.expect(12);
@@ -2580,6 +2641,45 @@ QUnit.module('Views', {
kanban.destroy();
});
+ QUnit.test('prevent drag and drop if grouped by date/datetime field', function (assert) {
+ assert.expect(5);
+
+ this.data.partner.records[0].date = '2017-01-08';
+ this.data.partner.records[1].date = '2017-01-09';
+ this.data.partner.records[2].date = '2017-02-08';
+ this.data.partner.records[3].date = '2017-02-10';
+
+ var kanban = createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: '<kanban class="o_kanban_test">' +
+ '<field name="bar"/>' +
+ '<templates><t t-name="kanban-box">' +
+ '<div><field name="foo"/></div>' +
+ '</t></templates></kanban>',
+ groupBy: ['date:month'],
+ });
+
+ assert.strictEqual(kanban.$('.o_kanban_group').length, 2, "should have 2 columns");
+ assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 2,
+ "1st column should contain 2 records of January month");
+ assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length , 2,
+ "2nd column should contain 2 records of February month");
+
+ // drag&drop a record in another column
+ var $record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first');
+ var $group = kanban.$('.o_kanban_group:nth-child(2)');
+ testUtils.dragAndDrop($record, $group);
+
+ // should not drag&drop record
+ assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length , 2,
+ "Should remain same records in first column(2 records)");
+ assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length , 2,
+ "Should remain same records in 2nd column(2 record)");
+ kanban.destroy();
+ });
+
QUnit.test('kanban view with default_group_by', function (assert) {
assert.expect(7);
this.data.partner.records.product_id = 1;
diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js
index 930fadbbb7b..7af2892ac33 100644
--- a/addons/web/static/tests/views/pivot_tests.js
+++ b/addons/web/static/tests/views/pivot_tests.js
@@ -923,6 +923,90 @@ QUnit.module('Views', {
pivot.destroy();
});
+ QUnit.test('correctly remove pivot_ keys from the context', function (assert) {
+ assert.expect(5);
+
+ this.data.partner.fields.amount = {string: "Amount", type: "float"};
+
+ // Equivalent to loading with default filter
+ var pivot = createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="day" type="col"/>' +
+ '<field name="amount" type="measure"/>' +
+ '</pivot>',
+ viewOptions: {
+ context: {
+ pivot_measures: ['foo'],
+ pivot_column_groupby: ['customer'],
+ pivot_row_groupby: ['product_id'],
+ },
+ },
+ });
+
+ // Equivalent to unload the filter
+ var reloadParams = {
+ context: {},
+ };
+ pivot.reload(reloadParams);
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id'],
+ },
+ }, "context should be correct");
+
+ // Let's get rid of the rows groupBy
+ pivot.$('tbody .o_pivot_header_cell_opened').click();
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: [],
+ },
+ }, "context should be correct");
+
+ // And now, get rid of the col groupby
+ pivot.$('thead .o_pivot_header_cell_opened').click();
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: [],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: [],
+ },
+ }, "context should be correct");
+
+ pivot.$('tbody .o_pivot_header_cell_closed').click();
+ pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first').click();
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: [],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id'],
+ },
+ }, "context should be correct");
+
+ pivot.$('thead .o_pivot_header_cell_closed').click();
+ pivot.$('.o_pivot_field_menu .dropdown-item[data-field="customer"]:first').click();
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id'],
+ },
+ }, "context should be correct");
+
+ pivot.destroy();
+ });
+
QUnit.test('correctly uses pivot_ keys from the context', function (assert) {
assert.expect(7);
diff --git a/addons/web/static/tests/views/search_view_tests.js b/addons/web/static/tests/views/search_view_tests.js
index f4b0163ce07..0714a5157e5 100644
--- a/addons/web/static/tests/views/search_view_tests.js
+++ b/addons/web/static/tests/views/search_view_tests.js
@@ -113,6 +113,13 @@ QUnit.module('Search View', {
type: 'ir.actions.act_window',
views: [[false, 'pivot']],
search_view_id: [8, 'search'],
+ }, {
+ id: 11,
+ name: 'Partners Action 11',
+ res_model: 'partner',
+ type: 'ir.actions.act_window',
+ views: [[2, 'list']],
+ search_view_id: [8, 'search'],
}
];
@@ -187,7 +194,7 @@ QUnit.module('Search View', {
'<field name="foo"/>' +
'<field name="date_field"/>' +
'<field name="birthday"/>' +
- '<field name="bar"/>' +
+ '<field name="bar" context="{\'bar\': self}"/>' +
'<field name="float_field"/>' +
'<filter string="Date Field Filter" name="positive" date="date_field"/>' +
'<filter string="Date Field Groupby" name="coolName" context="{\'group_by\': \'date_field:day\'}"/>' +
@@ -992,6 +999,59 @@ QUnit.module('Search View', {
actionManager.destroy();
});
+ QUnit.test('select an autocomplete field with `context` key', function (assert) {
+ assert.expect(9);
+
+ var searchRead = 0;
+ var actionManager = createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ debug: 1,
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ if (searchRead === 1) {
+ assert.deepEqual(args.domain, [["bar", "=", 1]]);
+ assert.deepEqual(args.context, {'bar': [1]});
+ } else if (searchRead === 2) {
+ assert.deepEqual(args.domain, ["|", ["bar", "=", 1], ["bar", "=", 2]]);
+ assert.deepEqual(args.context, { 'bar': [1, 2] });
+ }
+ searchRead++;
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ actionManager.doAction(11);
+ assert.strictEqual(searchRead, 1, "there should be 1 search_read");
+
+ // 'r' key to filter on bar "First Record"
+ $('.o_searchview_input').trigger($.Event('keypress', { which: 82, keyCode: 82 }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.RIGHT, keyCode: $.ui.keyCode.RIGHT }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.ENTER, keyCode: $.ui.keyCode.ENTER }));
+
+ assert.strictEqual($('.o_searchview_input_container .o_facet_values').eq(0).text().trim(), "First record",
+ "the autocompletion facet should be correct");
+ assert.strictEqual(searchRead, 2, "there should be 2 search_read");
+
+ // 'r' key to filter on bar "Second Record"
+ $('.o_searchview_input').trigger($.Event('keypress', { which: 82, keyCode: 82 }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.RIGHT, keyCode: $.ui.keyCode.RIGHT }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN }));
+ $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.ENTER, keyCode: $.ui.keyCode.ENTER }));
+
+ assert.strictEqual($('.o_searchview_input_container .o_facet_values').eq(0).text().trim(), "First record or Second record",
+ "the autocompletion facet should be correct");
+ assert.strictEqual(searchRead, 3, "there should be 3 search_read");
+
+ actionManager.destroy();
+ });
+
QUnit.module('TimeRangeMenu');
QUnit.test('time range menu stays hidden', function (assert) {
diff --git a/addons/web/static/tests/widgets/data_export_tests.js b/addons/web/static/tests/widgets/data_export_tests.js
index 40b59157b02..51c82ef1ded 100644
--- a/addons/web/static/tests/widgets/data_export_tests.js
+++ b/addons/web/static/tests/widgets/data_export_tests.js
@@ -36,7 +36,7 @@ QUnit.module('widgets', {
QUnit.test('exporting all data in list view', function (assert) {
- assert.expect(7);
+ assert.expect(6);
var blockUI = framework.blockUI;
var unblockUI = framework.unblockUI;
@@ -94,16 +94,6 @@ QUnit.module('widgets', {
},
});
- list.searchView = {
- build_search_data: function () {
- assert.step('build_search_data');
- return {
- contexts: [],
- domains: [],
- groupbys: [],
- };
- },
- };
testUtils.dom.click(list.$('thead th.o_list_record_selector input'));
testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)'));
testUtils.dom.click(list.sidebar.$('a:contains(Export)'));
@@ -122,7 +112,6 @@ QUnit.module('widgets', {
framework.blockUI = blockUI;
framework.unblockUI = unblockUI;
assert.verifySteps([
- 'build_search_data',
'block UI',
'/web/export/csv',
'unblock UI',
@@ -130,7 +119,7 @@ QUnit.module('widgets', {
});
QUnit.test('saving fields list when exporting data', function (assert) {
- assert.expect(6);
+ assert.expect(5);
var create = data.DataSet.prototype.create;
@@ -172,17 +161,6 @@ QUnit.module('widgets', {
},
});
- list.searchView = {
- build_search_data: function () {
- assert.step('build_search_data');
- return {
- contexts: [],
- domains: [],
- groupbys: [],
- };
- },
- };
-
// Open the export modal
testUtils.dom.click(list.$('thead th.o_list_record_selector input'));
testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)'));
@@ -202,7 +180,7 @@ QUnit.module('widgets', {
testUtils.dom.click($('.modal a:contains(Save fields list)'));
testUtils.fields.editInput($('.modal .o_save_list > input'), 'fields list');
testUtils.dom.click($('.modal .o_save_list > button'));
- assert.verifySteps(['build_search_data', 'create'],
+ assert.verifySteps(['create'],
"create should have been called");
// Close the modal and destroy list