summaryrefslogtreecommitdiff
path: root/addons/web/static
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-11-29 20:06:16 +0100
committerChristophe Simonis <chs@odoo.com>2018-11-29 20:06:16 +0100
commitce4cc24621bf6430d7d3a2d469b375b59120c200 (patch)
tree3837b5fe41ff88b24136ab53c871bc01f965cab1 /addons/web/static
parent4d7aa97da38e497fef31a06543bfa5d2c8785708 (diff)
parent82a1e1dcc33d7c123e50d3e1aacb5e127f57c521 (diff)
[MERGE] forward port branch 12.0 up to 82a1e1dcc33
Diffstat (limited to 'addons/web/static')
-rw-r--r--addons/web/static/lib/tempusdominus/tempusdominus.js6
-rw-r--r--addons/web/static/src/js/core/dom.js91
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js12
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js1
-rw-r--r--addons/web/static/src/js/views/abstract_view.js1
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js4
-rw-r--r--addons/web/static/src/js/widgets/date_picker.js13
-rw-r--r--addons/web/static/src/js/widgets/dropdown_menu.js10
-rw-r--r--addons/web/static/src/scss/ui.scss5
-rw-r--r--addons/web/static/tests/fields/basic_fields_tests.js74
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js77
-rw-r--r--addons/web/static/tests/helpers/test_utils_mock.js4
-rw-r--r--addons/web/static/tests/views/form_benchmarks.js17
-rw-r--r--addons/web/static/tests/views/search_view_tests.js61
-rw-r--r--addons/web/static/tests/views/view_dialogs_tests.js60
15 files changed, 421 insertions, 15 deletions
diff --git a/addons/web/static/lib/tempusdominus/tempusdominus.js b/addons/web/static/lib/tempusdominus/tempusdominus.js
index 63daabbd6f7..a5dac48ac3a 100644
--- a/addons/web/static/lib/tempusdominus/tempusdominus.js
+++ b/addons/web/static/lib/tempusdominus/tempusdominus.js
@@ -563,10 +563,10 @@ var DateTimePicker = function ($, moment) {
DateTimePicker.prototype._notifyEvent = function _notifyEvent(e) {
// /!\ ODOO FIX: these next conditions have been modified by odoo
// FIXME should write a test about the tricky case this handles
- if (!e.date && !e.oldDate) {
- return;
- }
if (e.type === DateTimePicker.Event.CHANGE) {
+ if (!e.date && !e.oldDate) {
+ return;
+ }
// check _isUTC flag to ensure that we are not comparing apples and oranges
var bothUTC = e.date && e.oldDate && e.date._isUTC === e.oldDate._isUTC;
if (bothUTC && e.date.isSame(e.oldDate)) {
diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js
index 2a961596862..848e406c4b2 100644
--- a/addons/web/static/src/js/core/dom.js
+++ b/addons/web/static/src/js/core/dom.js
@@ -20,6 +20,7 @@ odoo.define('web.dom', function (require) {
* something happens in the DOM.
*/
+var concurrency = require('web.concurrency');
var config = require('web.config');
var core = require('web.core');
var _t = core._t;
@@ -40,7 +41,9 @@ function _notify(content, callbacks) {
core.bus.trigger('DOM_updated', content);
}
-return {
+var dom = {
+ DEBOUNCE: 400,
+
/**
* Appends content in a jQuery object and optionnally triggers an event
*
@@ -209,6 +212,91 @@ return {
return position;
},
/**
+ * Protects a function which is to be used as a handler by preventing its
+ * execution for the duration of a previous call to it (including async
+ * parts of that call).
+ *
+ * Limitation: as the handler is ignored during async actions,
+ * the 'preventDefault' or 'stopPropagation' calls it may want to do
+ * will be ignored too. Using the 'preventDefault' and 'stopPropagation'
+ * arguments solves that problem.
+ *
+ * @param {function} fct
+ * The function which is to be used as a handler. If a promise
+ * is returned, it is used to determine when the handler's action is
+ * finished. Otherwise, the return is used as jQuery uses it.
+ * @param {function|boolean} preventDefault
+ * @param {function|boolean} stopPropagation
+ */
+ makeAsyncHandler: function (fct, preventDefault, stopPropagation) {
+ // Create a deferred indicating if a previous call to this handler is
+ // still pending.
+ var def = $.when();
+
+ return function (ev) {
+ if (preventDefault === true || preventDefault && preventDefault()) {
+ ev.preventDefault();
+ }
+ if (stopPropagation === true || stopPropagation && stopPropagation()) {
+ ev.stopPropagation();
+ }
+
+ if (def.state() === 'pending') {
+ // If a previous call to this handler is still pending, ignore
+ // the new call.
+ return;
+ }
+ var result = fct.apply(this, arguments);
+ def = $.when(result);
+ return result;
+ };
+ },
+ /**
+ * Creates a debounced version of a function to be used as a button click
+ * handler. Also improves the handler to disable the button for the time of
+ * the debounce and/or the time of the async actions it performs.
+ *
+ * Limitation: if two handlers are put on the same button, the button will
+ * become enabled again once any handler's action finishes (multiple click
+ * handlers should however not be binded to the same button).
+ *
+ * @param {function} fct
+ * The function which is to be used as a button click handler. If a
+ * promise is returned, it is used to determine when the button can be
+ * re-enabled. Otherwise, the return is used as jQuery uses it.
+ */
+ makeButtonHandler: function (fct) {
+ // Fallback: if the final handler is not binded to a button, at least
+ // make it an async handler (also handles the case where some events
+ // might ignore the disabled state of the button).
+ fct = dom.makeAsyncHandler(fct);
+
+ return function (ev) {
+ var result = fct.apply(this, arguments);
+
+ var $button = $(ev.target).closest('.btn');
+ if (!$button.length) {
+ return result;
+ }
+
+ // Disable the button for the duration of the handler's action
+ // or at least for the duration of the click debounce. This makes
+ // a 'real' debounce creation useless. Also, during the debouncing
+ // part, the button is disabled without any visual effect.
+ $button.addClass('o_debounce_disabled');
+ $.when(dom.DEBOUNCE && concurrency.delay(dom.DEBOUNCE)).then(function () {
+ $button.addClass('disabled').prop('disabled', true);
+ $button.removeClass('o_debounce_disabled');
+
+ return $.when(result).always(function () {
+ $button.removeClass('disabled').prop('disabled', false);
+ });
+ });
+
+ return result;
+ };
+ },
+ /**
* Prepends content in a jQuery object and optionnally triggers an event
*
* @param {jQuery} [$target] the node where content will be prepended
@@ -460,4 +548,5 @@ return {
}
},
};
+return dom;
});
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 53fbf99a8cd..aac91081c3c 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -2409,6 +2409,18 @@ var FieldProgressBar = AbstractField.extend({
this.$('.o_progressbar_value').focus().select();
}
},
+ /**
+ * The progress bar has more than one field/value to deal with
+ * i.e. max_value
+ *
+ * @override
+ * @private
+ */
+ _reset: function () {
+ this._super.apply(this, arguments);
+ var new_max_value = this.recordData[this.nodeOptions.max_value];
+ this.max_value = new_max_value !== undefined ? new_max_value : this.max_value;
+ },
isSet: function () {
return true;
},
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index d9e15dab302..60b0275c8c8 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -564,6 +564,7 @@ var FieldMany2One = AbstractField.extend({
initial_ids: ids ? _.map(ids, function (x) { return x[0]; }) : undefined,
initial_view: view,
disable_multiple_selection: true,
+ no_create: !self.can_create,
on_selected: function (records) {
self.reinitialize(records[0]);
self.activate();
diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js
index 8f9f167c69f..024216b5619 100644
--- a/addons/web/static/src/js/views/abstract_view.js
+++ b/addons/web/static/src/js/views/abstract_view.js
@@ -181,6 +181,7 @@ var AbstractView = Class.extend({
modelName: params.modelName,
res_id: params.currentId,
res_ids: params.ids,
+ orderedBy: params.context ? params.context.orderedBy : [],
};
if (params.modelName) {
this.loadParams.modelName = params.modelName;
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 345b76856b6..53aa2e56c5a 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -3674,7 +3674,7 @@ var BasicModel = AbstractModel.extend({
model: params.modelName,
offset: params.offset || (type === 'record' ? _.indexOf(res_ids, res_id) : 0),
openGroupByDefault: params.openGroupByDefault,
- orderedBy: params.orderedBy || (params.context && params.context.orderedBy) || [],
+ orderedBy: params.orderedBy || [],
orderedResIDs: params.orderedResIDs,
parentID: params.parentID,
rawContext: params.rawContext,
@@ -4608,7 +4608,7 @@ var BasicModel = AbstractModel.extend({
if (orderData1 > orderData2) {
return order.asc ? 1 : -1;
}
- return compareRecords(record1ID, record2ID, level + 1);
+ return compareRecords(resId1, resId2, level + 1);
};
utils.stableSort(list.res_ids, compareRecords);
}
diff --git a/addons/web/static/src/js/widgets/date_picker.js b/addons/web/static/src/js/widgets/date_picker.js
index 7ba3010a3b5..b677abe377a 100644
--- a/addons/web/static/src/js/widgets/date_picker.js
+++ b/addons/web/static/src/js/widgets/date_picker.js
@@ -15,6 +15,7 @@ var DateWidget = Widget.extend({
'change.datetimepicker': 'changeDatetime',
'change .o_datepicker_input': 'changeDatetime',
'input input': '_onInput',
+ 'show.datetimepicker': '_onDateTimePickerShow',
},
/**
* @override
@@ -226,6 +227,18 @@ var DateWidget = Widget.extend({
//--------------------------------------------------------------------------
/**
+ * Reacts to the datetimepicker being shown
+ * Could set/verify our widget value
+ * And subsequently update the datetimepicker
+ *
+ * @private
+ */
+ _onDateTimePickerShow: function () {
+ if (this.$input.val().length !== 0 && this.isValid()) {
+ this.$input.select();
+ }
+ },
+ /**
* Prevents 'input' events triggered by the library to bubble up, as they
* might have unwanted effects (like triggering 'field_changed' events in
* the context of field widgets)
diff --git a/addons/web/static/src/js/widgets/dropdown_menu.js b/addons/web/static/src/js/widgets/dropdown_menu.js
index fdab9b82ebf..e92c714d838 100644
--- a/addons/web/static/src/js/widgets/dropdown_menu.js
+++ b/addons/web/static/src/js/widgets/dropdown_menu.js
@@ -15,6 +15,7 @@ var DropdownMenu = Widget.extend({
'click .o_item_option': '_onOptionClick',
'click span.o_trash_button': '_onTrashClick',
'hidden.bs.dropdown': '_onBootstrapClose',
+ 'click .dropdown-item-text': '_onDropDownItemTextClick',
},
/**
@@ -200,6 +201,15 @@ var DropdownMenu = Widget.extend({
this._renderMenuItems();
},
/**
+ * Reacts to click on bootstrap's dropdown-item-text
+ * Protects against Bootstrap dropdown close from inside click
+ *
+ * @private
+ */
+ _onDropDownItemTextClick: function (ev) {
+ ev.stopPropagation();
+ },
+ /**
* @private
* @param {MouseEvent} event
*/
diff --git a/addons/web/static/src/scss/ui.scss b/addons/web/static/src/scss/ui.scss
index 46995aa9ba1..44d639805be 100644
--- a/addons/web/static/src/scss/ui.scss
+++ b/addons/web/static/src/scss/ui.scss
@@ -78,3 +78,8 @@
outline: 20px solid fade-out($o-brand-primary, 1.0);
}
}
+
+// Declare a similar class as 'disabled' but with no visual effect
+.o_debounce_disabled {
+ pointer-events: none;
+}
diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js
index 807b2ebadca..f176b4e0ab9 100644
--- a/addons/web/static/tests/fields/basic_fields_tests.js
+++ b/addons/web/static/tests/fields/basic_fields_tests.js
@@ -2933,6 +2933,38 @@ QUnit.module('basic_fields', {
form.destroy();
});
+ QUnit.test('field date should select its content onclick when there is one', function (assert) {
+ assert.expect(2);
+ var done = assert.async();
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch:'<form string="Partners">' +
+ '<field name="display_name" /> ' + // Do not focus on the date field right away
+ '<field name="date" />' +
+ '</form>',
+ res_id: 1,
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ form.$el.on({
+ 'show.datetimepicker': function () {
+ assert.ok($('.bootstrap-datetimepicker-widget').is(':visible'),
+ 'bootstrap-datetimepicker is visible');
+ assert.strictEqual(window.getSelection().toString(), "02/03/2017",
+ 'The whole input of the date field should have been selected');
+ done();
+ }
+ });
+ form.$('.o_input[name="date"]').mouseenter().trigger('focus');
+
+ form.destroy();
+ });
+
QUnit.module('FieldDatetime');
QUnit.test('datetime field in form view', function (assert) {
@@ -5379,6 +5411,48 @@ QUnit.module('basic_fields', {
form.destroy();
});
+
+ QUnit.module('FieldProgressBar');
+
+ QUnit.test('Field ProgressBar: max_value should update', function (assert) {
+ assert.expect(2);
+
+ this.data.partner.records = this.data.partner.records.slice(0,1);
+ this.data.partner.records[0].qux = 2;
+
+ this.data.partner.onchanges = {
+ display_name: function (obj) {
+ obj.int_field = 999;
+ obj.qux = 5;
+ }
+ };
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form>' +
+ '<field name="display_name" />' +
+ '<field name="qux" invisible="1" />' +
+ '<field name="int_field" widget="progressbar" options="{\'current_value\': \'int_field\', \'max_value\': \'qux\'}" />' +
+ '</form>',
+ res_id: 1,
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ assert.strictEqual(form.$('.o_progressbar_value').text(), '10 / 2',
+ 'The initial value of the progress bar should be correct');
+
+ // trigger the onchange
+ form.$('.o_input[name=display_name]').val('new name').trigger('input');
+
+ assert.strictEqual(form.$('.o_progressbar_value').text(), '999 / 5',
+ 'The value of the progress bar should be correct after the update');
+
+ form.destroy();
+ });
});
});
});
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 49af80784c9..8d23dafe4f4 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -150,6 +150,83 @@ QUnit.module('relational_fields', {
}
}, function () {
+ QUnit.test('x2many default_order multiple fields', function (assert) {
+ assert.expect(7);
+
+ this.data.partner.records = [
+ {int_field: 10, id: 1, display_name: "record1"},
+ {int_field: 12, id: 2, display_name: "record2"},
+ {int_field: 11, id: 3, display_name: "record3"},
+ {int_field: 12, id: 4, display_name: "record4"},
+ {int_field: 10, id: 5, display_name: "record5"},
+ {int_field: 10, id: 6, display_name: "record6"},
+ {int_field: 11, id: 7, display_name: "record7"},
+ ];
+
+ this.data.partner.records[0].p = [1, 7, 4, 5, 2, 6, 3];
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form>' +
+ '<field name="p" >' +
+ '<tree default_order="int_field,id">' +
+ '<field name="id"/>' +
+ '<field name="int_field"/>' +
+ '</tree>' +
+ '</field>' +
+ '</form>',
+ res_id: 1,
+ });
+
+ var $recordList = form.$('.o_field_x2many_list .o_data_row');
+ var expectedOrderId = ['1', '5', '6', '3', '7', '2', '4'];
+
+ _.each($recordList, function(record, index) {
+ var $record = $(record);
+ assert.strictEqual($record.find('.o_data_cell').eq(0).text(), expectedOrderId[index],
+ 'The record should be the right place. Index: ' + index);
+ });
+
+ form.destroy();
+ });
+
+
+ form.destroy();
+ });
+
+ QUnit.test('one2many from a model that has been sorted', function (assert) {
+ assert.expect(1);
+
+ /* On a standard list view, sort your records by a field
+ * Click on a record which contains a x2m with multiple records in it
+ * The x2m shouldn't take the orderedBy of the parent record (the one on the form)
+ */
+
+ this.data.partner.records[0].turtles = [3, 2];
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch:'<form string="Partners">' +
+ '<field name="turtles">' +
+ '<tree>' +
+ '<field name="turtle_foo"/>' +
+ '</tree>' +
+ '</field>' +
+ '</form>',
+ res_id: 1,
+ context: {
+ orderedBy: [{
+ name: 'foo',
+ asc: false,
+ }]
+ },
+ });
+
+ assert.strictEqual(form.$('.o_field_one2many[name=turtles] tbody').text().trim(), "kawablip",
+ 'The o2m should not have been sorted.');
QUnit.test('onchange on unloaded record clearing posterious change', function (assert) {
// when we got onchange result for fields of record that were not
// already available because they were in a inline view not already
diff --git a/addons/web/static/tests/helpers/test_utils_mock.js b/addons/web/static/tests/helpers/test_utils_mock.js
index 769c179472a..3eaaae0a46f 100644
--- a/addons/web/static/tests/helpers/test_utils_mock.js
+++ b/addons/web/static/tests/helpers/test_utils_mock.js
@@ -13,6 +13,7 @@ odoo.define('web.test_utils_mock', function (require) {
var basic_fields = require('web.basic_fields');
var config = require('web.config');
var core = require('web.core');
+var dom = require('web.dom');
var MockServer = require('web.MockServer');
var session = require('web.session');
@@ -154,6 +155,8 @@ function addMockEnvironment(widget, params) {
// make sure the debounce value for input fields is set to 0
var initialDebounceValue = DebouncedField.prototype.DEBOUNCE;
DebouncedField.prototype.DEBOUNCE = params.fieldDebounce || 0;
+ var initialDOMDebounceValue = dom.DEBOUNCE;
+ dom.DEBOUNCE = 0;
var initialSession, initialConfig, initialParameters, initialDebounce, initialThrottle;
initialSession = _.extend({}, session);
session.getTZOffset = function () {
@@ -196,6 +199,7 @@ function addMockEnvironment(widget, params) {
core.bus.trigger('clear_cache');
DebouncedField.prototype.DEBOUNCE = initialDebounceValue;
+ dom.DEBOUNCE = initialDOMDebounceValue;
if (params.debounce === false) {
_.debounce = initialDebounce;
}
diff --git a/addons/web/static/tests/views/form_benchmarks.js b/addons/web/static/tests/views/form_benchmarks.js
index c6127f847ec..efd12741d66 100644
--- a/addons/web/static/tests/views/form_benchmarks.js
+++ b/addons/web/static/tests/views/form_benchmarks.js
@@ -1,4 +1,4 @@
-odoo.define('web.list_benchmarks', function (require) {
+odoo.define('web.form_benchmarks', function (require) {
"use strict";
var FormView = require('web.FormView');
@@ -38,7 +38,6 @@ QUnit.module('Form View', {
this.run = function (assert, done, cb) {
var data = this.data;
var arch = this.arch;
- var viewOptions = this.viewOptions;
new Benchmark.Suite({})
.add('form', function () {
var list = createView({
@@ -76,13 +75,13 @@ QUnit.module('Form View', {
this.data.foo.records[0].many2many.push(i);
}
this.arch =
- '<form string="Partners">'
- '<field name="many2many">'
- '<tree editable="top" limit="250">'
- '<field name="char"/>'
- '<field name="many2many" widget="many2many_tags" attrs="{\'readonly\': [(\'char\', \'==\', \'toto\')]}"/>'
- '</tree>'
- '</field>'
+ '<form string="Partners">' +
+ '<field name="many2many">' +
+ '<tree editable="top" limit="250">' +
+ '<field name="char"/>' +
+ '<field name="many2many" widget="many2many_tags" attrs="{\'readonly\': [(\'char\', \'==\', \'toto\')]}"/>' +
+ '</tree>' +
+ '</field>' +
'</form>';
this.run(assert, done, function (form) {
testUtils.form.clickEdit(form);
diff --git a/addons/web/static/tests/views/search_view_tests.js b/addons/web/static/tests/views/search_view_tests.js
index e64ae72a9f4..0dbddbe1fae 100644
--- a/addons/web/static/tests/views/search_view_tests.js
+++ b/addons/web/static/tests/views/search_view_tests.js
@@ -975,5 +975,66 @@ QUnit.module('Search View', {
form.destroy();
});
+ QUnit.test('Customizing filter does not close the filter dropdown', function (assert) {
+ assert.expect(4);
+ var self = this;
+
+ _.each(this.data.partner.records.slice(), function (rec) {
+ var copy = _.defaults({}, rec, {id: rec.id + 10 });
+ self.data.partner.records.push(copy);
+ });
+
+ this.data.partner.fields.date_field.searchable = true;
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<field name="bar"/>' +
+ '</form>',
+ viewOptions: {
+ mode: 'edit',
+ },
+ archs: {
+ 'partner,false,list': '<tree><field name="display_name"/></tree>',
+ 'partner,false,search': '<search><field name="date_field"/></search>',
+ },
+ res_id: 1,
+ });
+
+ form.$('.o_input').click();
+ $('.ui-autocomplete .ui-menu-item:contains(Search More)').mouseenter().click();
+
+ var $modal = $('.modal-dialog.modal-lg');
+ assert.strictEqual($modal.length, 1, 'Modal Opened');
+
+ $modal.find('.o_search_options button:contains(Filters)').click();
+
+ var $filterDropDown = $modal.find('.o_dropdown_menu.show');
+
+ $filterDropDown.find('button:contains(Add Custom Filter)').click();
+
+ assert.ok($filterDropDown.find('button:contains(Add Custom Filter)').hasClass('o_open_menu'),
+ 'The right dropdown is open');
+
+ var $filterInputs = $filterDropDown.find('.o_input');
+
+ assert.strictEqual($filterInputs.length, 3,
+ 'The custom filter builder has 3 elements');
+
+ // We really are interested in the click event
+ // We do it twice on each input to make sure
+ // the parent dropdown doesn't react to any of it
+ _.each($filterInputs, function (input) {
+ var $input = $(input);
+ $input.click();
+ $input.click();
+ });
+
+ assert.ok($filterDropDown.is(':visible'));
+
+ form.destroy();
+ });
+
});
});
diff --git a/addons/web/static/tests/views/view_dialogs_tests.js b/addons/web/static/tests/views/view_dialogs_tests.js
index 8898e520618..023a977ad56 100644
--- a/addons/web/static/tests/views/view_dialogs_tests.js
+++ b/addons/web/static/tests/views/view_dialogs_tests.js
@@ -478,6 +478,66 @@ QUnit.module('Views', {
testUtils.mock.unpatch(ListController);
parent.destroy();
});
+
+ QUnit.test('propagate can_create onto the search popup o2m', function (assert) {
+ assert.expect(3);
+
+ this.data.instrument.records = [
+ {id: 1, name: 'Tromblon1'},
+ {id: 2, name: 'Tromblon2'},
+ {id: 3, name: 'Tromblon3'},
+ {id: 4, name: 'Tromblon4'},
+ {id: 5, name: 'Tromblon5'},
+ {id: 6, name: 'Tromblon6'},
+ {id: 7, name: 'Tromblon7'},
+ {id: 8, name: 'Tromblon8'},
+ ];
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form>' +
+ '<field name="name"/>' +
+ '<field name="instrument" can_create="false"/>' +
+ '</form>',
+ res_id: 1,
+ archs: {
+ 'instrument,false,list': '<tree>'+
+ '<field name="name"/>'+
+ '</tree>',
+ 'instrument,false,search': '<search>'+
+ '<field name="name"/>'+
+ '</search>',
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+
+ mockRPC: function(route, args) {
+ if (args.method === 'get_formview_id') {
+ return $.when(false);
+ }
+ return this._super(route, args);
+ },
+ });
+
+ form.$('.o_field_widget[name="instrument"] .o_input').click();
+
+ assert.notOk($('.ui-autocomplete a:contains(Create and Edit)').length,
+ 'Create and edit not present in dropdown');
+
+ $('.ui-autocomplete a:contains(Search More)').trigger('mouseenter').trigger('click');
+
+ var $modal = $('.modal-dialog.modal-lg');
+
+ assert.strictEqual($modal.length, 1, 'Modal present');
+
+ assert.strictEqual($modal.find('.modal-footer button').text(), "Cancel",
+ 'Only the cancel button is present in modal');
+
+ form.destroy();
+ });
});
});