diff options
| author | Christophe Simonis <chs@odoo.com> | 2017-06-15 18:46:47 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2017-06-15 18:46:47 +0200 |
| commit | ed2ddeccdfaac46acb4d4bfbdac5f1b570f0829c (patch) | |
| tree | 5d0c41f089c8deddd753a7ec61217ee33d007451 /addons/web/static/tests | |
| parent | 156e07f6387b3e2b8915b1bd8624195d7110c3f5 (diff) | |
| parent | 1b50c829ef383705647d0c2df7c89e9051ec40fb (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.js | 81 | ||||
| -rw-r--r-- | addons/web/static/tests/views/basic_model_tests.js | 96 | ||||
| -rw-r--r-- | addons/web/static/tests/views/form_tests.js | 142 | ||||
| -rw-r--r-- | addons/web/static/tests/views/kanban_model_tests.js | 1 | ||||
| -rw-r--r-- | addons/web/static/tests/views/kanban_tests.js | 37 | ||||
| -rw-r--r-- | addons/web/static/tests/views/pivot_tests.js | 76 | ||||
| -rw-r--r-- | addons/web/static/tests/views/view_dialogs_tests.js | 79 |
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 |
