summaryrefslogtreecommitdiff
path: root/addons/web/static/tests
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2017-06-15 18:46:47 +0200
committerChristophe Simonis <chs@odoo.com>2017-06-15 18:46:47 +0200
commited2ddeccdfaac46acb4d4bfbdac5f1b570f0829c (patch)
tree5d0c41f089c8deddd753a7ec61217ee33d007451 /addons/web/static/tests
parent156e07f6387b3e2b8915b1bd8624195d7110c3f5 (diff)
parent1b50c829ef383705647d0c2df7c89e9051ec40fb (diff)
[MERGE] forward port branch saas-16 up to 1b50c829ef3
Diffstat (limited to 'addons/web/static/tests')
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js81
-rw-r--r--addons/web/static/tests/views/basic_model_tests.js96
-rw-r--r--addons/web/static/tests/views/form_tests.js142
-rw-r--r--addons/web/static/tests/views/kanban_model_tests.js1
-rw-r--r--addons/web/static/tests/views/kanban_tests.js37
-rw-r--r--addons/web/static/tests/views/pivot_tests.js76
-rw-r--r--addons/web/static/tests/views/view_dialogs_tests.js79
7 files changed, 510 insertions, 2 deletions
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 559a9387baa..a1f851eb91f 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -659,6 +659,87 @@ QUnit.module('relational_fields', {
});
});
+ QUnit.test('list in form: quick create then add a new line directly', function (assert) {
+ // required many2one inside a one2many list: directly after quick creating
+ // a new many2one value (before the name_create returns), click on add an item:
+ // at this moment, the many2one has still no value, and as it is required,
+ // the row is discarded if a saveLine is requested. However, it should
+ // wait for the name_create to return before trying to save the line.
+ var done = assert.async();
+ assert.expect(8);
+
+ this.data.partner.onchanges = {
+ trululu: function () {},
+ };
+
+ var M2O_DELAY = relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY;
+ relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0;
+
+ var def = $.Deferred();
+ var newRecordID;
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form>' +
+ '<sheet>' +
+ '<field name="p">' +
+ '<tree editable="bottom">' +
+ '<field name="trululu" required="1"/>' +
+ '</tree>' +
+ '</field>' +
+ '</sheet>' +
+ '</form>',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'name_create') {
+ return def.then(_.constant(result)).then(function (nameGet) {
+ newRecordID = nameGet[0];
+ return nameGet;
+ });
+ }
+ if (args.method === 'create') {
+ assert.deepEqual(args.args[0].p[0][2].trululu, newRecordID);
+ }
+ return result;
+ },
+ });
+
+ form.$('.o_field_x2many_list_row_add a').click();
+
+ var $dropdown = form.$('.o_field_many2one input').autocomplete('widget');
+ form.$('.o_field_many2one input').val('b').trigger('keydown');
+ concurrency.delay(0).then(function () {
+ $dropdown.find('li:first()').click(); // quick create 'b'
+ form.$('.o_field_x2many_list_row_add a').click();
+
+ assert.strictEqual(form.$('.o_data_row').length, 1,
+ "there should still be only one row");
+ assert.ok(form.$('.o_data_row').hasClass('o_selected_row'),
+ "the row should still be in edition");
+
+ def.resolve();
+
+ assert.strictEqual(form.$('.o_data_row:first .o_data_cell').text(), 'b',
+ "first row should have the correct m2o value");
+ assert.strictEqual(form.$('.o_data_row').length, 2,
+ "there should now be 2 rows");
+ assert.ok(form.$('.o_data_row:nth(1)').hasClass('o_selected_row'),
+ "the second row should be in edition");
+
+ form.$buttons.find('.o_form_button_save').click();
+
+ assert.strictEqual(form.$('.o_data_row').length, 1,
+ "there should be 1 row saved (the second one was empty and invalid)");
+ assert.strictEqual(form.$('.o_data_row .o_data_cell').text(), 'b',
+ "should have the correct m2o value");
+
+ relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY;
+ form.destroy();
+ done();
+ });
+ });
+
QUnit.test('autocompletion in a many2one, in form view with a domain', function (assert) {
assert.expect(1);
diff --git a/addons/web/static/tests/views/basic_model_tests.js b/addons/web/static/tests/views/basic_model_tests.js
index fefc7231e5b..ca2f6f37f3e 100644
--- a/addons/web/static/tests/views/basic_model_tests.js
+++ b/addons/web/static/tests/views/basic_model_tests.js
@@ -572,6 +572,20 @@ QUnit.module('Views', {
this.params.fieldNames = ['product_id', 'category', 'product_ids'];
this.params.res_id = undefined;
this.params.type = 'record';
+ this.params.fieldsInfo = {
+ form: {
+ category: {},
+ product_id: {},
+ product_ids: {
+ fieldsInfo: {
+ default: { name: {} },
+ },
+ relatedFields: this.data.product.fields,
+ viewType: 'default',
+ },
+ },
+ };
+ this.params.viewType = 'form';
var model = createModel({
Model: BasicModel,
@@ -1015,6 +1029,19 @@ QUnit.module('Views', {
this.params.fieldNames = ['total', 'product_ids'];
this.params.res_id = undefined;
this.params.type = 'record';
+ this.params.fieldsInfo = {
+ form: {
+ product_ids: {
+ fieldsInfo: {
+ default: { name: {} },
+ },
+ relatedFields: this.data.product.fields,
+ viewType: 'default',
+ },
+ total: {},
+ },
+ };
+ this.params.viewType = 'form';
var o2mRecordParams = {
fields: this.data.product.fields,
@@ -1277,6 +1304,63 @@ QUnit.module('Views', {
model.destroy();
});
+ QUnit.test('dont write on readonly fields (write and create)', function (assert) {
+ assert.expect(6);
+
+ this.params.fieldNames = ['foo', 'bar'];
+ this.data.partner.onchanges.foo = function (obj) {
+ obj.bar = obj.foo.length;
+ };
+ this.data.partner.fields.bar.readonly = true;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'write') {
+ assert.deepEqual(args.args[1], {foo: "verylongstring"},
+ "should only save foo field");
+ }
+ if (args.method === 'create') {
+ assert.deepEqual(args.args[0], {foo: "anotherverylongstring"},
+ "should only save foo field");
+ }
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.bar, 2,
+ "should be initialized with correct value");
+
+ model.notifyChanges(resultID, {foo: "verylongstring"});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.bar, 14,
+ "should be changed with correct value");
+
+ model.save(resultID);
+ });
+
+ // start again, but with a new record
+ delete this.params.res_id;
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.bar, false,
+ "should be initialized with correct value");
+
+ model.notifyChanges(resultID, {foo: "anotherverylongstring"});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.bar, 21,
+ "should be changed with correct value");
+
+ model.save(resultID);
+ });
+ model.destroy();
+ });
+
QUnit.test('default_get with one2many values', function (assert) {
assert.expect(1);
@@ -1297,6 +1381,18 @@ QUnit.module('Views', {
fields: this.data.partner.fields,
modelName: 'partner',
type: 'record',
+ fieldsInfo: {
+ form: {
+ product_ids: {
+ fieldsInfo: {
+ default: { name: {} },
+ },
+ relatedFields: this.data.product.fields,
+ viewType: 'default',
+ },
+ },
+ },
+ viewType: 'form',
};
model.load(params).then(function (resultID) {
assert.strictEqual(typeof resultID, 'string', "result should be a valid id");
diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js
index 7ed63d3bece..ab304e62ebc 100644
--- a/addons/web/static/tests/views/form_tests.js
+++ b/addons/web/static/tests/views/form_tests.js
@@ -5013,5 +5013,147 @@ QUnit.module('Views', {
form.destroy();
});
+
+ QUnit.test('readonly fields are not sent when saving', function (assert) {
+ assert.expect(6);
+
+ // define an onchange on display_name to check that the value of readonly
+ // fields is correctly sent for onchanges
+ this.data.partner.onchanges = {
+ display_name: function () {},
+ p: function () {},
+ };
+ var checkOnchange = false;
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<field name="p">' +
+ '<tree>' +
+ '<field name="display_name"/>' +
+ '</tree>' +
+ '<form string="Partners">' +
+ '<field name="display_name"/>' +
+ '<field name="foo" attrs="{\'readonly\': [[\'display_name\', \'=\', \'readonly\']]}"/>' +
+ '</form>' +
+ '</field>' +
+ '</form>',
+ mockRPC: function (route, args) {
+ if (checkOnchange && args.method === 'onchange') {
+ if (args.args[2] === 'display_name') { // onchange on field display_name
+ assert.strictEqual(args.args[1].foo, 'foo value',
+ "readonly fields value should be sent for onchanges");
+ } else { // onchange on field p
+ assert.deepEqual(args.args[1].p, [
+ [0, false, {display_name: 'readonly', foo: 'foo value'}]
+ ], "readonly fields value should be sent for onchanges");
+ }
+ }
+ if (args.method === 'create') {
+ assert.deepEqual(args.args[0], {
+ p: [[0, false, {display_name: 'readonly'}]]
+ }, "should not have sent the value of the readonly field");
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ form.$('.o_field_x2many_list_row_add a').click();
+ assert.strictEqual($('.modal input.o_field_widget[name=foo]').length, 1,
+ 'foo should be editable');
+ checkOnchange = true;
+ $('.modal .o_field_widget[name=foo]').val('foo value').trigger('input');
+ $('.modal .o_field_widget[name=display_name]').val('readonly').trigger('input');
+ assert.strictEqual($('.modal span.o_field_widget[name=foo]').length, 1,
+ 'foo should be readonly');
+ $('.modal .modal-footer .btn-primary').click(); // close the modal
+ checkOnchange = false;
+
+ form.$('.o_data_row').click(); // re-open previous record
+ assert.strictEqual($('.modal .o_field_widget[name=foo]').text(), 'foo value',
+ "the edited value should have been kept");
+ $('.modal .modal-footer .btn-primary').click(); // close the modal
+
+ form.$buttons.find('.o_form_button_save').click(); // save the record
+
+ form.destroy();
+ });
+
+ QUnit.test('id is False in evalContext for new records', function (assert) {
+ assert.expect(2);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<field name="id"/>' +
+ '<field name="foo" attrs="{\'readonly\': [[\'id\', \'=\', False]]}"/>' +
+ '</form>',
+ });
+
+ assert.ok(form.$('.o_field_widget[name=foo]').hasClass('o_readonly_modifier'),
+ "foo should be readonly in 'Create' mode");
+
+ form.$buttons.find('.o_form_button_save').click();
+ form.$buttons.find('.o_form_button_edit').click();
+
+ assert.notOk(form.$('.o_field_widget[name=foo]').hasClass('o_readonly_modifier'),
+ "foo should not be readonly anymore");
+
+ form.destroy();
+ });
+
+ QUnit.test('delete a duplicated record', function (assert) {
+ assert.expect(5);
+
+ var newRecordID;
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<field name="display_name"/>' +
+ '</form>',
+ res_id: 1,
+ viewOptions: {sidebar: true},
+ mockRPC: function (route, args) {
+ if (args.method === 'search_read' && args.model === 'ir.attachment') {
+ // rpcs done by the sidebar
+ return $.when([]);
+ }
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'copy') {
+ return result.then(function (id) {
+ newRecordID = id;
+ return id;
+ });
+ }
+ if (args.method === 'unlink') {
+ assert.deepEqual(args.args[0], [newRecordID],
+ "should delete the newly created record");
+ }
+ return result;
+ },
+ });
+
+ form.sidebar.$('a:contains(Duplicate)').click(); // duplicate record 1
+ assert.strictEqual(form.$('.o_form_editable').length, 1,
+ "form should be in edit mode");
+ assert.strictEqual(form.$('.o_field_widget').val(), 'first record (copy)',
+ "duplicated record should have correct name");
+ form.$buttons.find('.o_form_button_save').click(); // save duplicated record
+
+ form.sidebar.$('a:contains(Delete)').click(); // delete duplicated record
+ assert.strictEqual($('.modal').length, 1, "should have opened a confirm dialog");
+ $('.modal .modal-footer .btn-primary').click(); // confirm
+
+ assert.strictEqual(form.$('.o_field_widget').text(), 'first record',
+ "should have come back to previous record");
+
+ form.destroy();
+ });
});
});
diff --git a/addons/web/static/tests/views/kanban_model_tests.js b/addons/web/static/tests/views/kanban_model_tests.js
index eeb07c17358..1a3391c720d 100644
--- a/addons/web/static/tests/views/kanban_model_tests.js
+++ b/addons/web/static/tests/views/kanban_model_tests.js
@@ -55,6 +55,7 @@ QUnit.module('Views', {
limit: 40,
modelName: 'partner',
openGroupByDefault: true,
+ viewType: 'kanban',
};
}
}, function () {
diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js
index a6c0e049a17..3d375e7be0a 100644
--- a/addons/web/static/tests/views/kanban_tests.js
+++ b/addons/web/static/tests/views/kanban_tests.js
@@ -1503,6 +1503,43 @@ QUnit.module('Views', {
"Create button should now be highlighted");
kanban.destroy();
});
+
+ QUnit.test('group_by_tooltip option when grouping on a many2one', function (assert) {
+ assert.expect(5);
+ var kanban = createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: '<kanban default_group_by="bar">' +
+ '<field name="bar"/>' +
+ '<field name="product_id" '+
+ 'options=\'{"group_by_tooltip": {"name": "Kikou"}}\'/>' +
+ '<templates><t t-name="kanban-box">' +
+ '<div><field name="foo"/></div>' +
+ '</t></templates></kanban>',
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/call_kw/product/read') {
+ assert.strictEqual(args.args[0].length, 2,
+ "read on two groups");
+ assert.deepEqual(args.args[1], ['display_name', 'name'],
+ "should read on specified fields on the group by relation");
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ assert.strictEqual(kanban.$('.o_kanban_group').length, 2, "should have " + 2 + " columns");
+
+ // simulate an update coming from the searchview, with another groupby given
+ kanban.update({groupBy: ['product_id']});
+ assert.ok(kanban.$('.o_kanban_group:first span.o_column_title:contains(hello)').length,
+ "first column should have a title with a value from the many2one");
+ assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_header').data('original-title'),
+ "<p>2 records</p><div>Kikou<br>hello</div>",
+ "first column should have a tooltip with the number of records, the group_by_tooltip title and many2one field value");
+
+ kanban.destroy();
+ });
});
});
diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js
index d74e29204d7..c3acdb52399 100644
--- a/addons/web/static/tests/views/pivot_tests.js
+++ b/addons/web/static/tests/views/pivot_tests.js
@@ -1,6 +1,7 @@
odoo.define('web.pivot_tests', function (require) {
"use strict";
+var Context = require('web.Context');
var core = require('web.core');
var PivotView = require('web.PivotView');
var testUtils = require('web.test_utils');
@@ -81,7 +82,7 @@ QUnit.module('Views', {
QUnit.module('PivotView');
QUnit.test('simple pivot rendering', function (assert) {
- assert.expect(2);
+ assert.expect(3);
var pivot = createView({
View: PivotView,
@@ -97,6 +98,8 @@ QUnit.module('Views', {
},
});
+ assert.ok(pivot.$el.hasClass('o_enable_linking'),
+ "root node should have classname 'o_enable_linking'");
assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(32)').length, 1,
"should contain a pivot cell with the sum of all records");
pivot.destroy();
@@ -119,6 +122,77 @@ QUnit.module('Views', {
pivot.destroy();
});
+ QUnit.test('clicking on a cell triggers a do_action', function (assert) {
+ assert.expect(2);
+
+ var pivot = createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ intercepts: {
+ do_action: function (ev) {
+ assert.deepEqual(ev.data.action, {
+ context: {someKey: true, userContextKey: true},
+ domain: [['product_id', '=', 37]],
+ name: 'Partners',
+ res_model: 'partner',
+ target: 'current',
+ type: 'ir.actions.act_window',
+ view_mode: 'list',
+ view_type: 'list',
+ views: [[false, 'list'], [2, 'form']],
+ }, "should trigger do_action with the correct args");
+ },
+ },
+ session: {
+ user_context: {userContextKey: true},
+ },
+ viewOptions: {
+ action: {
+ views: [[2, 'form'], [5, 'kanban'], [false, 'list'], [false, 'pivot']],
+ },
+ context: {someKey: true, search_default_test: 3},
+ title: 'Partners',
+ }
+ });
+
+ assert.ok(pivot.$el.hasClass('o_enable_linking'),
+ "root node should have classname 'o_enable_linking'");
+ pivot.$('.o_pivot_cell_value:contains(12)').click(); // should trigger a do_action
+
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view with disable_linking="True"', function (assert) {
+ assert.expect(2);
+
+ var pivot = createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot disable_linking="True">' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ intercepts: {
+ do_action: function () {
+ assert.ok(false, "should not trigger do_action");
+ },
+ },
+ });
+
+ assert.notOk(pivot.$el.hasClass('o_enable_linking'),
+ "root node should not have classname 'o_enable_linking'");
+ assert.strictEqual(pivot.$('.o_pivot_cell_value').length, 1,
+ "should have one cell");
+ pivot.$('.o_pivot_cell_value').click(); // should not trigger a do_action
+
+ pivot.destroy();
+ });
+
QUnit.test('pivot view grouped by date field', function (assert) {
assert.expect(2);
diff --git a/addons/web/static/tests/views/view_dialogs_tests.js b/addons/web/static/tests/views/view_dialogs_tests.js
index 963b3c1552e..8c46d8fff46 100644
--- a/addons/web/static/tests/views/view_dialogs_tests.js
+++ b/addons/web/static/tests/views/view_dialogs_tests.js
@@ -10,10 +10,14 @@ QUnit.module('Views', {
this.data = {
partner: {
fields: {
+ display_name: { string: "Displayed name", type: "char" },
foo: {string: "Foo", type: 'char'},
+ bar: {string: "Bar", type: "boolean"},
},
records: [
- {id: 1, foo: 'blip',},
+ {id: 1, foo: 'blip', display_name: 'blipblip', bar: true},
+ {id: 2, foo: 'ta tata ta ta', display_name: 'macgyver', bar: false},
+ {id: 3, foo: 'piou piou', display_name: "Jack O'Neill", bar: true},
],
},
};
@@ -63,6 +67,79 @@ QUnit.module('Views', {
dialog.destroy();
});
+ QUnit.test('SelectCreateDialog use domain, group_by and search default', function (assert) {
+ assert.expect(3);
+
+ var search = 0;
+ var parent = createParent({
+ data: this.data,
+ archs: {
+ 'partner,false,list':
+ '<tree string="Partner">' +
+ '<field name="display_name"/>' +
+ '<field name="foo"/>' +
+ '</tree>',
+ 'partner,false,search':
+ '<search>' +
+ '<field name="foo" filter_domain="[(\'display_name\',\'ilike\',self), (\'foo\',\'ilike\',self)]"/>' +
+ '<group expand="0" string="Group By">' +
+ '<filter name="groupby_bar" context="{\'group_by\' : \'bar\'}"/>' +
+ '</group>' +
+ '</search>',
+ },
+ mockRPC: function (route, args) {
+ search++;
+ if (search === 1 && args.method === 'read_group') {
+ assert.deepEqual(args.kwargs, {
+ context: {group_by: "bar"},
+ domain: [["display_name","like","a"], ["display_name","ilike","piou"], ["foo","ilike","piou"]],
+ fields:["display_name","foo","bar"],
+ groupby:["bar"],
+ lazy: true
+ }, "should search with the complete domain (domain + search), and group by 'bar'");
+ }
+ if (search === 2 && route === '/web/dataset/search_read') {
+ assert.deepEqual(args, {
+ context: {},
+ domain: [["display_name","like","a"], ["display_name","ilike","piou"], ["foo","ilike","piou"]],
+ fields:["display_name","foo"],
+ model: "partner",
+ limit: 80,
+ sort: ""
+ }, "should search with the complete domain (domain + search)");
+ }
+ if (search === 3 && route === '/web/dataset/search_read') {
+ assert.deepEqual(args, {
+ context: {},
+ domain: [["display_name","like","a"]],
+ fields:["display_name","foo"],
+ model: "partner",
+ limit: 80,
+ sort: ""
+ }, "should search with the domain");
+ }
+
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ var dialog = new dialogs.SelectCreateDialog(parent, {
+ no_create: true,
+ readonly: true,
+ res_model: 'partner',
+ domain: [['display_name', 'like', 'a']],
+ context: {
+ search_default_groupby_bar: true,
+ search_default_foo: 'piou',
+ },
+ }).open();
+
+ dialog.$('.o_searchview_facet:contains(groupby_bar) .o_facet_remove').click();
+ dialog.$('.o_searchview_facet .o_facet_remove').click();
+
+ dialog.destroy();
+ });
+
});
}); \ No newline at end of file