diff options
| author | Vincent Schippefilt <vsc@odoo.com> | 2019-03-06 14:02:32 +0100 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2019-03-06 20:07:17 +0100 |
| commit | ab56e637b7f97ddcdbcc9baea99ae097e342409c (patch) | |
| tree | 4cb47da9f7ef89b19d18429220a7507470ec12a9 /addons/web/static/tests/views | |
| parent | eeed894fd50e477135e1ac9a82f6e98ce063ab90 (diff) | |
[REF] web: adapt code after jQuery update
Part of task 1896658
Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Christophe Matthieu <chm@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
Co-authored-by: David Monjoie <dmo@odoo.com>
Co-authored-by: Martin Geubelle <mge@odoo.com>
Co-authored-by: svs-odoo <svs@odoo.com>
Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
Diffstat (limited to 'addons/web/static/tests/views')
21 files changed, 4974 insertions, 4832 deletions
diff --git a/addons/web/static/tests/views/abstract_controller_tests.js b/addons/web/static/tests/views/abstract_controller_tests.js index f9045645bc6..e0fa433b13c 100644 --- a/addons/web/static/tests/views/abstract_controller_tests.js +++ b/addons/web/static/tests/views/abstract_controller_tests.js @@ -1,89 +1,82 @@ -odoo.define("base.abstract_controller_tests", function(require) { - "use strict"; +odoo.define("base.abstract_controller_tests", function (require) { +"use strict"; - var testUtils = require("web.test_utils"); - var createView = testUtils.createView; - var BasicView = require("web.BasicView"); - var BasicRenderer = require("web.BasicRenderer"); +var testUtils = require("web.test_utils"); +var createView = testUtils.createView; +var BasicView = require("web.BasicView"); +var BasicRenderer = require("web.BasicRenderer"); - function getHtmlRenderer(html) { - return BasicRenderer.extend({ - start: function() { - this.$el.html(html); - return this._super.apply(this, arguments); - } - }); - } - function getHtmlView(html, viewType) { - viewType = viewType || "test"; - return BasicView.extend({ - viewType: viewType, - config: _.extend({}, BasicView.prototype.config, { - Renderer: getHtmlRenderer(html) - }) - }); - } +function getHtmlRenderer(html) { + return BasicRenderer.extend({ + start: function () { + this.$el.html(html); + return this._super.apply(this, arguments); + } + }); +} +function getHtmlView(html, viewType) { + viewType = viewType || "test"; + return BasicView.extend({ + viewType: viewType, + config: _.extend({}, BasicView.prototype.config, { + Renderer: getHtmlRenderer(html) + }) + }); +} - QUnit.module( - "Views", - { - beforeEach: function() { - this.data = { - test_model: { - fields: {}, - records: [] - } - }; +QUnit.module("Views", { + beforeEach: function () { + this.data = { + test_model: { + fields: {}, + records: [] } - }, - function() { - - QUnit.module('AbstractController'); - - QUnit.test( - 'click on a a[type="action"] child triggers the correct action', - function(assert) { - assert.expect(7); + }; + } +}, function () { + QUnit.module('AbstractController'); - var html = - "<div>" + - '<a name="a1" type="action" class="simple">simple</a>' + - '<a name="a2" type="action" class="with-child">' + - "<span>child</input>" + - "</a>" + - '<a type="action" data-model="foo" data-method="bar" class="method">method</a>' + - '<a type="action" data-model="foo" data-res-id="42" class="descr">descr</a>' + - '<a type="action" data-model="foo" class="descr2">descr2</a>' + - "</div>"; + QUnit.test('click on a a[type="action"] child triggers the correct action', async function (assert) { + assert.expect(7); - var view = createView({ - View: getHtmlView(html, "test"), - data: this.data, - model: "test_model", - arch: "<test/>", - intercepts: { - do_action: function (event) { - assert.step(event.data.action.name || event.data.action); - } - }, - mockRPC: function (route, args) { - if (args.model === 'foo' && args.method === 'bar') { - assert.step("method"); - return $.when({name: 'method'}); - } - return this._super.apply(this, arguments); - } - }); - testUtils.dom.click(view.$(".simple")); - testUtils.dom.click(view.$(".with-child span")); - testUtils.dom.click(view.$(".method")); - testUtils.dom.click(view.$(".descr")); - testUtils.dom.click(view.$(".descr2")); - assert.verifySteps(["a1", "a2", "method", "method", "descr", "descr2"]); + var html = + "<div>" + + '<a name="a1" type="action" class="simple">simple</a>' + + '<a name="a2" type="action" class="with-child">' + + "<span>child</input>" + + "</a>" + + '<a type="action" data-model="foo" data-method="bar" class="method">method</a>' + + '<a type="action" data-model="foo" data-res-id="42" class="descr">descr</a>' + + '<a type="action" data-model="foo" class="descr2">descr2</a>' + + "</div>"; - view.destroy(); + var view = await createView({ + View: getHtmlView(html, "test"), + data: this.data, + model: "test_model", + arch: "<test/>", + intercepts: { + do_action: function (event) { + assert.step(event.data.action.name || event.data.action); } - ); - } + }, + mockRPC: function (route, args) { + if (args.model === 'foo' && args.method === 'bar') { + assert.step("method"); + return Promise.resolve({name: 'method'}); + } + return this._super.apply(this, arguments); + } + }); + await testUtils.dom.click(view.$(".simple")); + await testUtils.dom.click(view.$(".with-child span")); + await testUtils.dom.click(view.$(".method")); + await testUtils.dom.click(view.$(".descr")); + await testUtils.dom.click(view.$(".descr2")); + assert.verifySteps(["a1", "a2", "method", "method", "descr", "descr2"]); + + view.destroy(); + } ); }); +}); diff --git a/addons/web/static/tests/views/abstract_view_banner_tests.js b/addons/web/static/tests/views/abstract_view_banner_tests.js index 21532ed60d9..ab668dd8c0d 100644 --- a/addons/web/static/tests/views/abstract_view_banner_tests.js +++ b/addons/web/static/tests/views/abstract_view_banner_tests.js @@ -5,7 +5,7 @@ var AbstractRenderer = require('web.AbstractRenderer'); var AbstractView = require('web.AbstractView'); var testUtils = require('web.test_utils'); -var createAsyncView = testUtils.createAsyncView; +var createView = testUtils.createView; var TestRenderer = AbstractRenderer.extend({ _renderView: function () { @@ -48,7 +48,7 @@ QUnit.module('Views', { '<div class="hello_banner"/>' + '</div>'; - createAsyncView({ + createView({ View: TestView, model: 'test_model', data: this.data, @@ -56,7 +56,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { if (route === '/module/hello_banner') { assert.step(route); - return $.when({html: banner_html}); + return Promise.resolve({html: banner_html}); } return this._super(route, args); }, diff --git a/addons/web/static/tests/views/abstract_view_tests.js b/addons/web/static/tests/views/abstract_view_tests.js index 3d4cc423ddd..ba819379279 100644 --- a/addons/web/static/tests/views/abstract_view_tests.js +++ b/addons/web/static/tests/views/abstract_view_tests.js @@ -6,7 +6,7 @@ var ajax = require('web.ajax'); var testUtils = require('web.test_utils'); var createActionManager = testUtils.createActionManager; -var createAsyncView = testUtils.createAsyncView; +var createView = testUtils.createView; QUnit.module('Views', { beforeEach: function () { @@ -31,15 +31,15 @@ QUnit.module('Views', { QUnit.module('AbstractView'); - QUnit.test('lazy loading of js libs (in parallel)', function (assert) { + QUnit.test('lazy loading of js libs (in parallel)', async function (assert) { var done = assert.async(); assert.expect(6); - var def = $.Deferred(); + var prom = testUtils.makeTestPromise(); var loadJS = ajax.loadJS; ajax.loadJS = function (url) { assert.step(url); - return def.then(function () { + return prom.then(function () { assert.step(url + ' loaded'); }); }; @@ -47,37 +47,37 @@ QUnit.module('Views', { var View = AbstractView.extend({ jsLibs: [['a', 'b']], }); - createAsyncView({ + createView({ View: View, arch: '<fake/>', data: this.data, model: 'fake_model', }).then(function (view) { - assert.verifySteps(['a', 'b', 'a loaded', 'b loaded'], + assert.verifySteps(['a loaded', 'b loaded'], "should wait for both libs to be loaded"); ajax.loadJS = loadJS; view.destroy(); done(); }); - assert.verifySteps(['a', 'b'], - "both libs should be loaded in parallel"); - def.resolve(); + await testUtils.nextTick(); + assert.verifySteps(['a', 'b'], "both libs should be loaded in parallel"); + prom.resolve(); }); - QUnit.test('lazy loading of js libs (sequentially)', function (assert) { + QUnit.test('lazy loading of js libs (sequentially)', async function (assert) { var done = assert.async(); assert.expect(10); - var defs = { - a: $.Deferred(), - b: $.Deferred(), - c: $.Deferred(), + var proms = { + a: testUtils.makeTestPromise(), + b: testUtils.makeTestPromise(), + c: testUtils.makeTestPromise(), }; var loadJS = ajax.loadJS; ajax.loadJS = function (url) { assert.step(url); - return defs[url].then(function () { + return proms[url].then(function () { assert.step(url + ' loaded'); }); }; @@ -88,34 +88,32 @@ QUnit.module('Views', { 'c', ], }); - createAsyncView({ + createView({ View: View, arch: '<fake/>', data: this.data, model: 'fake_model', }).then(function (view) { - assert.verifySteps(['a', 'b', 'b loaded', 'a loaded', 'c', 'c loaded'], - "should for all libs to be loaded"); + assert.verifySteps(['c loaded'], "should wait for all libs to be loaded"); ajax.loadJS = loadJS; view.destroy(); done(); }); - - assert.verifySteps(['a', 'b'], - "libs 'a' and 'b' should be loaded in parallel"); - defs.b.resolve(); - assert.verifySteps(['a', 'b', 'b loaded'], - "should wait for 'a' and 'b' to be loaded before loading 'c'"); - defs.a.resolve(); - assert.verifySteps(['a', 'b', 'b loaded', 'a loaded', 'c'], - "should load 'c' when 'a' and 'b' are loaded"); - defs.c.resolve(); + await testUtils.nextTick(); + assert.verifySteps(['a', 'b'], "libs 'a' and 'b' should be loaded in parallel"); + await proms.b.resolve(); + await testUtils.nextTick(); + assert.verifySteps(['b loaded'], "should wait for 'a' and 'b' to be loaded before loading 'c'"); + await proms.a.resolve(); + await testUtils.nextTick(); + assert.verifySteps(['a loaded', 'c'], "should load 'c' when 'a' and 'b' are loaded"); + await proms.c.resolve(); }); - QUnit.test('group_by from context can be a string, instead of a list of strings', function (assert) { + QUnit.test('group_by from context can be a string, instead of a list of strings', async function (assert) { assert.expect(1); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: [{ id: 1, name: 'Foo', @@ -139,7 +137,7 @@ QUnit.module('Views', { }, }); - actionManager.doAction(1); + await actionManager.doAction(1); actionManager.destroy(); }); diff --git a/addons/web/static/tests/views/basic_model_tests.js b/addons/web/static/tests/views/basic_model_tests.js index bf5261c5b6e..e19cadea2cf 100644 --- a/addons/web/static/tests/views/basic_model_tests.js +++ b/addons/web/static/tests/views/basic_model_tests.js @@ -1,92 +1,92 @@ odoo.define('web.basic_model_tests', function (require) { -"use strict"; - -var BasicModel = require('web.BasicModel'); -var testUtils = require('web.test_utils'); - -var createModel = testUtils.createModel; - -QUnit.module('Views', { - beforeEach: function () { - this.data = { - partner: { - fields: { - display_name: {string: "STRING", type: 'char'}, - total: {string: "Total", type: 'integer'}, - foo: {string: "Foo", type: 'char'}, - bar: {string: "Bar", type: 'integer'}, - qux: {string: "Qux", type: 'many2one', relation: 'partner'}, - product_id: {string: "Favorite product", type: 'many2one', relation: 'product'}, - product_ids: {string: "Favorite products", type: 'one2many', relation: 'product'}, - category: {string: "Category M2M", type: 'many2many', relation: 'partner_type'}, - date: {string: "Date Field", type: 'date'}, - reference: {string: "Reference Field", type: 'reference', selection: [["product", "Product"], ["partner_type", "Partner Type"], ["partner", "Partner"]]}, + "use strict"; + + var BasicModel = require('web.BasicModel'); + var testUtils = require('web.test_utils'); + + var createModel = testUtils.createModel; + + QUnit.module('Views', { + beforeEach: function () { + this.data = { + partner: { + fields: { + display_name: { string: "STRING", type: 'char' }, + total: { string: "Total", type: 'integer' }, + foo: { string: "Foo", type: 'char' }, + bar: { string: "Bar", type: 'integer' }, + qux: { string: "Qux", type: 'many2one', relation: 'partner' }, + product_id: { string: "Favorite product", type: 'many2one', relation: 'product' }, + product_ids: { string: "Favorite products", type: 'one2many', relation: 'product' }, + category: { string: "Category M2M", type: 'many2many', relation: 'partner_type' }, + date: { string: "Date Field", type: 'date' }, + reference: { string: "Reference Field", type: 'reference', selection: [["product", "Product"], ["partner_type", "Partner Type"], ["partner", "Partner"]] }, + }, + records: [ + { id: 1, foo: 'blip', bar: 1, product_id: 37, category: [12], display_name: "first partner", date: "2017-01-25" }, + { id: 2, foo: 'gnap', bar: 2, product_id: 41, display_name: "second partner" }, + ], + onchanges: {}, }, - records: [ - {id: 1, foo: 'blip', bar: 1, product_id: 37, category: [12], display_name: "first partner", date: "2017-01-25"}, - {id: 2, foo: 'gnap', bar: 2, product_id: 41, display_name: "second partner"}, - ], - onchanges: {}, - }, - product: { - fields: { - name: {string: "Product Name", type: "char"} + product: { + fields: { + name: { string: "Product Name", type: "char" } + }, + records: [ + { id: 37, display_name: "xphone" }, + { id: 41, display_name: "xpad" } + ] }, - records: [ - {id: 37, display_name: "xphone"}, - {id: 41, display_name: "xpad"} - ] - }, - partner_type: { - fields: { - display_name: {string: "Partner Type", type: "char"}, - date: {string: "Date Field", type: 'date'}, + partner_type: { + fields: { + display_name: { string: "Partner Type", type: "char" }, + date: { string: "Date Field", type: 'date' }, + }, + records: [ + { id: 12, display_name: "gold", date: "2017-01-25" }, + { id: 14, display_name: "silver" }, + { id: 15, display_name: "bronze" } + ] }, - records: [ - {id: 12, display_name: "gold", date: "2017-01-25"}, - {id: 14, display_name: "silver"}, - {id: 15, display_name: "bronze"} - ] - }, - }; - - // add related fields to category. - this.data.partner.fields.category.relatedFields = - $.extend(true, {}, this.data.partner_type.fields); - this.params = { - res_id: 2, - modelName: 'partner', - fields: this.data.partner.fields, - }; - } -}, function () { - QUnit.module('BasicModel'); - - QUnit.test('can load a record', function (assert) { - assert.expect(7); - - this.params.fieldNames = ['foo']; - this.params.context = {active_field: 2}; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - assert.deepEqual(args.kwargs.context, { - active_field: 2, - bin_size: true, - someKey: 'some value', - }, "should have sent the correct context"); - return this._super.apply(this, arguments); - }, - session: { - user_context: {someKey: 'some value'}, - } - }); + }; - assert.strictEqual(model.get(1), null, "should return null for non existing key"); + // add related fields to category. + this.data.partner.fields.category.relatedFields = + $.extend(true, {}, this.data.partner_type.fields); + this.params = { + res_id: 2, + modelName: 'partner', + fields: this.data.partner.fields, + }; + }, + }, function () { + QUnit.module('BasicModel'); + + QUnit.test('can load a record', async function (assert) { + assert.expect(7); + + this.params.fieldNames = ['foo']; + this.params.context = { active_field: 2 }; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + assert.deepEqual(args.kwargs.context, { + active_field: 2, + bin_size: true, + someKey: 'some value', + }, "should have sent the correct context"); + return this._super.apply(this, arguments); + }, + session: { + user_context: { someKey: 'some value' }, + } + }); - model.load(this.params).then(function (resultID) { + assert.strictEqual(model.get(1), null, "should return null for non existing key"); + + var resultID = await model.load(this.params); // it is a string, because it is used as a key in an object assert.strictEqual(typeof resultID, 'string', "result should be a valid id"); @@ -95,656 +95,637 @@ QUnit.module('Views', { assert.strictEqual(record.type, 'record', "should be of type 'record'"); assert.strictEqual(record.data.foo, "gnap", "should correctly read value"); assert.strictEqual(record.data.bar, undefined, "should not fetch the field 'bar'"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('rejects loading a record with invalid id', function (assert) { - assert.expect(1); + QUnit.test('rejects loading a record with invalid id', async function (assert) { + assert.expect(1); - this.params.res_id = 99; + this.params.res_id = 99; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); + try { + await model.load(this.params); + } + catch (e) { + assert.ok("load should return a rejected deferred for an invalid id"); + } - model.load(this.params).always(function (error) { - assert.strictEqual(this.state(), 'rejected', - "load should return a rejected deferred for an invalid id") + model.destroy(); }); - model.destroy(); - }); - QUnit.test('notify change with many2one', function (assert) { - assert.expect(2); + QUnit.test('notify change with many2one', async function (assert) { + assert.expect(2); - this.params.fieldNames = ['foo', 'qux']; + this.params.fieldNames = ['foo', 'qux']; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.qux, false, "qux field should be false"); - model.notifyChanges(resultID, {qux: {id: 1, display_name: "hello"}}); + await model.notifyChanges(resultID, { qux: { id: 1, display_name: "hello" } }); record = model.get(resultID); assert.strictEqual(record.data.qux.data.id, 1, "qux field should be 1"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('notify change on many2one: unset and reset same value', function (assert) { - assert.expect(3); + QUnit.test('notify change on many2one: unset and reset same value', async function (assert) { + assert.expect(3); - this.data.partner.records[1].qux = 1; + this.data.partner.records[1].qux = 1; - this.params.fieldNames = ['qux']; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + this.params.fieldNames = ['qux']; + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.qux.data.id, 1, "qux value should be 1"); - model.notifyChanges(resultID, {qux: false}); + await model.notifyChanges(resultID, { qux: false }); record = model.get(resultID); assert.strictEqual(record.data.qux, false, "qux should be unset"); - model.notifyChanges(resultID, {qux: {id: 1, display_name: 'second_partner'}}); + await model.notifyChanges(resultID, { qux: { id: 1, display_name: 'second_partner' } }); record = model.get(resultID); assert.strictEqual(record.data.qux.data.id, 1, "qux value should be 1 again"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('write on a many2one', function (assert) { - assert.expect(4); - var self = this; + QUnit.test('write on a many2one', async function (assert) { + assert.expect(4); + var self = this; - this.params.fieldNames = ['product_id']; + this.params.fieldNames = ['product_id']; - var rpcCount = 0; + var rpcCount = 0; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - rpcCount++; - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + rpcCount++; + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.product_id.data.display_name, 'xpad', "should be initialized with correct value"); - model.notifyChanges(resultID, {product_id: {id: 37, display_name: 'xphone'}}); + await model.notifyChanges(resultID, { product_id: { id: 37, display_name: 'xphone' } }); record = model.get(resultID); assert.strictEqual(record.data.product_id.data.display_name, 'xphone', "should be changed with correct value"); - model.save(resultID); + await model.save(resultID); assert.strictEqual(self.data.partner.records[1].product_id, 37, "should have really saved the data"); assert.strictEqual(rpcCount, 3, "should have done 3 rpc: 1 read, 1 write, 1 read"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('basic onchange', function (assert) { - assert.expect(5); - - this.data.partner.fields.foo.onChange = true; - this.data.partner.onchanges.foo = function (obj) { - obj.bar = obj.foo.length; - }; - - this.params.fieldNames = ['foo', 'bar']; - this.params.context = {hello: 'world'}; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'onchange') { - var context = args.args[4]; - assert.deepEqual(context, {hello: 'world'}, - "context should be sent by the onchange"); - } - return this._super(route, args); - }, - }); + QUnit.test('basic onchange', async function (assert) { + assert.expect(5); + + this.data.partner.fields.foo.onChange = true; + this.data.partner.onchanges.foo = function (obj) { + obj.bar = obj.foo.length; + }; - model.load(this.params).then(function (resultID) { + this.params.fieldNames = ['foo', 'bar']; + this.params.context = { hello: 'world' }; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'onchange') { + var context = args.args[4]; + assert.deepEqual(context, { hello: 'world' }, + "context should be sent by the onchange"); + } + return this._super(route, args); + }, + }); + + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.foo, 'gnap', "foo field is properly initialized"); assert.strictEqual(record.data.bar, 2, "bar field is properly initialized"); - model.notifyChanges(resultID, {foo: 'mary poppins'}); + await model.notifyChanges(resultID, { foo: 'mary poppins' }); record = model.get(resultID); assert.strictEqual(record.data.foo, 'mary poppins', "onchange has been applied"); assert.strictEqual(record.data.bar, 12, "onchange has been applied"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('onchange with a many2one', function (assert) { - assert.expect(5); + QUnit.test('onchange with a many2one', async function (assert) { + assert.expect(5); - this.data.partner.fields.product_id.onChange = true; - this.data.partner.onchanges.product_id = function (obj) { - if (obj.product_id === 37) { - obj.foo = "space lollipop"; - } - }; + this.data.partner.fields.product_id.onChange = true; + this.data.partner.onchanges.product_id = function (obj) { + if (obj.product_id === 37) { + obj.foo = "space lollipop"; + } + }; - this.params.fieldNames = ['foo', 'product_id']; + this.params.fieldNames = ['foo', 'product_id']; - var rpcCount = 0; + var rpcCount = 0; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'onchange') { - assert.strictEqual(args.args[2], "product_id", - "should send the only changed field as a string, not a list"); - } - rpcCount++; - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'onchange') { + assert.strictEqual(args.args[2], "product_id", + "should send the only changed field as a string, not a list"); + } + rpcCount++; + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.foo, 'gnap', "foo field is properly initialized"); assert.strictEqual(record.data.product_id.data.id, 41, "product_id field is properly initialized"); - model.notifyChanges(resultID, {product_id: {id: 37, display_name: 'xphone'}}); + await model.notifyChanges(resultID, { product_id: { id: 37, display_name: 'xphone' } }); record = model.get(resultID); assert.strictEqual(record.data.foo, 'space lollipop', "onchange has been applied"); assert.strictEqual(rpcCount, 2, "should have done 2 rpc: 1 read and 1 onchange"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('onchange on a one2many not in view (fieldNames)', function (assert) { - assert.expect(6); + QUnit.test('onchange on a one2many not in view (fieldNames)', async function (assert) { + assert.expect(6); - this.data.partner.fields.foo.onChange = true; - this.data.partner.onchanges.foo = function (obj) { - obj.bar = obj.foo.length; - obj.product_ids = []; - }; + this.data.partner.fields.foo.onChange = true; + this.data.partner.onchanges.foo = function (obj) { + obj.bar = obj.foo.length; + obj.product_ids = []; + }; - this.params.fieldNames = ['foo']; + this.params.fieldNames = ['foo']; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.foo, 'gnap', "foo field is properly initialized"); assert.strictEqual(record.data.bar, undefined, "bar field is not loaded"); assert.strictEqual(record.data.product_ids, undefined, "product_ids field is not loaded"); - model.notifyChanges(resultID, {foo: 'mary poppins'}); + await model.notifyChanges(resultID, { foo: 'mary poppins' }); record = model.get(resultID); assert.strictEqual(record.data.foo, 'mary poppins', "onchange has been applied"); assert.strictEqual(record.data.bar, 12, "onchange has been applied"); assert.strictEqual(record.data.product_ids, undefined, "onchange on product_ids (one2many) has not been applied"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('notifyChange on a one2many', function (assert) { - assert.expect(9); + QUnit.test('notifyChange on a one2many', async function (assert) { + assert.expect(9); - this.data.partner.records[1].product_ids = [37]; - this.params.fieldNames = ['product_ids']; + this.data.partner.records[1].product_ids = [37]; + this.params.fieldNames = ['product_ids']; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'name_get') { - assert.strictEqual(args.model, 'product'); - } - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'name_get') { + assert.strictEqual(args.model, 'product'); + } + return this._super(route, args); + }, + }); - var o2mParams = { - modelName: 'product', - fields: this.data.product.fields, - fieldNames: ['display_name'], - }; - model.load(this.params).then(function (resultID) { - model.load(o2mParams).then(function (newRecordID) { - var record = model.get(resultID); - var x2mListID = record.data.product_ids.id; + var o2mParams = { + modelName: 'product', + fields: this.data.product.fields, + fieldNames: ['display_name'], + }; + var resultID = await model.load(this.params); + var newRecordID = await model.load(o2mParams); + var record = model.get(resultID); + var x2mListID = record.data.product_ids.id; - assert.strictEqual(record.data.product_ids.count, 1, - "there should be one record in the relation"); + assert.strictEqual(record.data.product_ids.count, 1, + "there should be one record in the relation"); - // trigger a 'ADD' command - model.notifyChanges(resultID, {product_ids: {operation: 'ADD', id: newRecordID}}); + // trigger a 'ADD' command + await model.notifyChanges(resultID, { product_ids: { operation: 'ADD', id: newRecordID } }); - assert.deepEqual(model.localData[x2mListID]._changes, [{ - operation: 'ADD', id: newRecordID, - }], "_changes should be correct"); - record = model.get(resultID); - assert.strictEqual(record.data.product_ids.count, 2, - "there should be two records in the relation"); + assert.deepEqual(model.localData[x2mListID]._changes, [{ + operation: 'ADD', id: newRecordID, + }], "_changes should be correct"); + record = model.get(resultID); + assert.strictEqual(record.data.product_ids.count, 2, + "there should be two records in the relation"); - // trigger a 'UPDATE' command - model.notifyChanges(resultID, {product_ids: {operation: 'UPDATE', id: newRecordID}}); + // trigger a 'UPDATE' command + await model.notifyChanges(resultID, { product_ids: { operation: 'UPDATE', id: newRecordID } }); - assert.deepEqual(model.localData[x2mListID]._changes, [{ - operation: 'ADD', id: newRecordID, - }, { - operation: 'UPDATE', id: newRecordID, - }], "_changes should be correct"); - record = model.get(resultID); - assert.strictEqual(record.data.product_ids.count, 2, - "there should be two records in the relation"); - - // trigger a 'DELETE' command on the existing record - var existingRecordID = record.data.product_ids.data[0].id; - model.notifyChanges(resultID, {product_ids: {operation: 'DELETE', ids: [existingRecordID]}}); - - assert.deepEqual(model.localData[x2mListID]._changes, [{ - operation: 'ADD', id: newRecordID, - }, { - operation: 'UPDATE', id: newRecordID, - }, { - operation: 'DELETE', id: existingRecordID, - }], - "_changes should be correct"); - record = model.get(resultID); - assert.strictEqual(record.data.product_ids.count, 1, - "there should be one record in the relation"); + assert.deepEqual(model.localData[x2mListID]._changes, [{ + operation: 'ADD', id: newRecordID, + }, { + operation: 'UPDATE', id: newRecordID, + }], "_changes should be correct"); + record = model.get(resultID); + assert.strictEqual(record.data.product_ids.count, 2, + "there should be two records in the relation"); - // trigger a 'DELETE' command on the new record - model.notifyChanges(resultID, {product_ids: {operation: 'DELETE', ids: [newRecordID]}}); + // trigger a 'DELETE' command on the existing record + var existingRecordID = record.data.product_ids.data[0].id; + await model.notifyChanges(resultID, { product_ids: { operation: 'DELETE', ids: [existingRecordID] } }); - assert.deepEqual(model.localData[x2mListID]._changes, [{ - operation: 'DELETE', id: existingRecordID, - }], "_changes should be correct"); - record = model.get(resultID); - assert.strictEqual(record.data.product_ids.count, 0, - "there should be no record in the relation"); - }); + assert.deepEqual(model.localData[x2mListID]._changes, [{ + operation: 'ADD', id: newRecordID, + }, { + operation: 'UPDATE', id: newRecordID, + }, { + operation: 'DELETE', id: existingRecordID, + }], + "_changes should be correct"); + record = model.get(resultID); + assert.strictEqual(record.data.product_ids.count, 1, + "there should be one record in the relation"); + + // trigger a 'DELETE' command on the new record + await model.notifyChanges(resultID, { product_ids: { operation: 'DELETE', ids: [newRecordID] } }); + + assert.deepEqual(model.localData[x2mListID]._changes, [{ + operation: 'DELETE', id: existingRecordID, + }], "_changes should be correct"); + record = model.get(resultID); + assert.strictEqual(record.data.product_ids.count, 0, + "there should be no record in the relation"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('notifyChange on a many2one, without display_name', function (assert) { - assert.expect(3); + QUnit.test('notifyChange on a many2one, without display_name', async function (assert) { + assert.expect(3); - this.params.fieldNames = ['product_id']; + this.params.fieldNames = ['product_id']; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'name_get') { - assert.strictEqual(args.model, 'product'); - } - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'name_get') { + assert.strictEqual(args.model, 'product'); + } + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.product_id.data.display_name, 'xpad', "product_id field is set to xpad"); - model.notifyChanges(resultID, {product_id: {id: 37}}); + await model.notifyChanges(resultID, { product_id: { id: 37 } }); record = model.get(resultID); assert.strictEqual(record.data.product_id.data.display_name, 'xphone', "display_name should have been fetched"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('onchange on a char with an unchanged many2one', function (assert) { - assert.expect(2); + QUnit.test('onchange on a char with an unchanged many2one', async function (assert) { + assert.expect(2); - this.data.partner.fields.foo.onChange = true; - this.data.partner.onchanges.foo = function (obj) { - obj.foo = obj.foo + " alligator"; - }; + this.data.partner.fields.foo.onChange = true; + this.data.partner.onchanges.foo = function (obj) { + obj.foo = obj.foo + " alligator"; + }; - this.params.fieldNames = ['foo', 'product_id']; + this.params.fieldNames = ['foo', 'product_id']; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'onchange') { - assert.strictEqual(args.args[1].product_id, 41, "should send correct value"); - } - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'onchange') { + assert.strictEqual(args.args[1].product_id, 41, "should send correct value"); + } + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { - model.notifyChanges(resultID, {foo: 'cookie'}); + var resultID = await model.load(this.params); + await model.notifyChanges(resultID, { foo: 'cookie' }); var record = model.get(resultID); assert.strictEqual(record.data.foo, 'cookie alligator', "onchange has been applied"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('onchange on a char with another many2one not set to a value', function (assert) { - assert.expect(2); - this.data.partner.records[0].product_id = false; - this.data.partner.fields.foo.onChange = true; - this.data.partner.onchanges.foo = function (obj) { - obj.foo = obj.foo + " alligator"; - }; + QUnit.test('onchange on a char with another many2one not set to a value', async function (assert) { + assert.expect(2); + this.data.partner.records[0].product_id = false; + this.data.partner.fields.foo.onChange = true; + this.data.partner.onchanges.foo = function (obj) { + obj.foo = obj.foo + " alligator"; + }; - this.params.fieldNames = ['foo', 'product_id']; - this.params.res_id = 1; + this.params.fieldNames = ['foo', 'product_id']; + this.params.res_id = 1; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.product_id, false, "product_id is not set"); - model.notifyChanges(resultID, {foo: 'cookie'}); + await model.notifyChanges(resultID, { foo: 'cookie' }); record = model.get(resultID); assert.strictEqual(record.data.foo, 'cookie alligator', "onchange has been applied"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('can get a many2many', function (assert) { - assert.expect(3); + QUnit.test('can get a many2many', async function (assert) { + assert.expect(3); - this.params.res_id = 1; - this.params.fieldsInfo = { - default: { - category: { - fieldsInfo: {default: {display_name: {}}}, - relatedFields: {display_name: {type: "char"}}, - viewType: 'default', + this.params.res_id = 1; + this.params.fieldsInfo = { + default: { + category: { + fieldsInfo: { default: { display_name: {} } }, + relatedFields: { display_name: { type: "char" } }, + viewType: 'default', + }, }, - }, - }; + }; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.category.data[0].res_id, 12, "should have loaded many2many res_ids"); assert.strictEqual(record.data.category.data[0].data.display_name, "gold", "should have loaded many2many display_name"); - record = model.get(resultID, {raw: true}); + record = model.get(resultID, { raw: true }); assert.deepEqual(record.data.category, [12], "with option raw, category should only return ids"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('can use command add and get many2many value with date field', function (assert) { - assert.expect(2); + QUnit.test('can use command add and get many2many value with date field', async function (assert) { + assert.expect(2); - this.params.fieldsInfo = { - default: { - category: { - fieldsInfo: {default: {date: {}}}, - relatedFields: {date: {type: "date"}}, - viewType: 'default', + this.params.fieldsInfo = { + default: { + category: { + fieldsInfo: { default: { date: {} } }, + relatedFields: { date: { type: "date" } }, + viewType: 'default', + }, }, - }, - }; + }; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var changes = { - category: {operation: 'ADD_M2M', ids: [{id: 12}]} + category: { operation: 'ADD_M2M', ids: [{ id: 12 }] } }; - model.notifyChanges(resultID, changes).then(function () { - var record = model.get(resultID); - assert.strictEqual(record.data.category.data.length, 1, "should have added one category"); - assert.strictEqual(record.data.category.data[0].data.date instanceof moment, - true, "should have a date parsed in a moment object"); - }); + await model.notifyChanges(resultID, changes); + var record = model.get(resultID); + assert.strictEqual(record.data.category.data.length, 1, "should have added one category"); + assert.strictEqual(record.data.category.data[0].data.date instanceof moment, + true, "should have a date parsed in a moment object"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('many2many with ADD_M2M command and context with parent key', function (assert) { - assert.expect(1); + QUnit.test('many2many with ADD_M2M command and context with parent key', async function (assert) { + assert.expect(1); - this.data.partner_type.fields.some_char = {type: "char"}; - this.params.fieldsInfo = { - default: { - category: { - fieldsInfo: {default: {some_char: { context: "{'a': parent.foo}"}}}, - relatedFields: {some_char: {type: "char"}}, - viewType: 'default', + this.data.partner_type.fields.some_char = { type: "char" }; + this.params.fieldsInfo = { + default: { + category: { + fieldsInfo: { default: { some_char: { context: "{'a': parent.foo}" } } }, + relatedFields: { some_char: { type: "char" } }, + viewType: 'default', + }, + foo: {}, }, - foo: {}, - }, - }; + }; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var changes = { - category: {operation: 'ADD_M2M', ids: [{id: 12}]} + category: { operation: 'ADD_M2M', ids: [{ id: 12 }] } }; - model.notifyChanges(resultID, changes).then(function () { - var record = model.get(resultID); - var categoryRecord = record.data.category.data[0]; - assert.deepEqual(categoryRecord.getContext({fieldName: 'some_char'}), {a:'gnap'}, - "should properly evaluate context"); + await model.notifyChanges(resultID, changes); + var record = model.get(resultID); + var categoryRecord = record.data.category.data[0]; + assert.deepEqual(categoryRecord.getContext({ fieldName: 'some_char' }), { a: 'gnap' }, + "should properly evaluate context"); + model.destroy(); + }); + + QUnit.test('can fetch a list', async function (assert) { + assert.expect(4); + + this.params.fieldNames = ['foo']; + this.params.domain = []; + this.params.groupedBy = []; + this.params.res_id = undefined; + this.params.context = { active_field: 2 }; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + assert.strictEqual(args.context.active_field, 2, + "should have sent the correct context"); + return this._super(route, args); + }, }); - }); - model.destroy(); - }); - - QUnit.test('can fetch a list', function (assert) { - assert.expect(4); - - this.params.fieldNames = ['foo']; - this.params.domain = []; - this.params.groupedBy = []; - this.params.res_id = undefined; - this.params.context = {active_field: 2}; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - assert.strictEqual(args.context.active_field, 2, - "should have sent the correct context"); - return this._super(route, args); - }, - }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.type, 'list', "record fetched should be a list"); assert.strictEqual(record.data.length, 2, "should have fetched 2 records"); assert.strictEqual(record.data[0].data.foo, 'blip', "first record should have 'blip' in foo field"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('fetch x2manys in list, with not too many rpcs', function (assert) { - assert.expect(3); + QUnit.test('fetch x2manys in list, with not too many rpcs', async function (assert) { + assert.expect(3); - this.data.partner.records[0].category = [12, 15]; - this.data.partner.records[1].category = [12, 14]; + this.data.partner.records[0].category = [12, 15]; + this.data.partner.records[1].category = [12, 14]; - this.params.fieldNames = ['category']; - this.params.domain = []; - this.params.groupedBy = []; - this.params.res_id = undefined; + this.params.fieldNames = ['category']; + this.params.domain = []; + this.params.groupedBy = []; + this.params.res_id = undefined; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - assert.step(route); - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + assert.step(route); + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data[0].data.category.data.length, 2, "first record should have 2 categories loaded"); assert.verifySteps(["/web/dataset/search_read"], "should have done 2 rpc (searchread and read category)"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('can make a default_record, no onchange', function (assert) { - assert.expect(5); - - this.params.context = {}; - this.params.fieldNames = ['product_id', 'category', 'product_ids']; - this.params.res_id = undefined; - this.params.type = 'record'; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - assert.step(args.method); - return this._super(route, args); - }, - }); + QUnit.test('can make a default_record, no onchange', async function (assert) { + assert.expect(5); - model.load(this.params).then(function (resultID) { + this.params.context = {}; + this.params.fieldNames = ['product_id', 'category', 'product_ids']; + this.params.res_id = undefined; + this.params.type = 'record'; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + assert.step(args.method); + return this._super(route, args); + }, + }); + + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.product_id, false, "m2o default value should be false"); assert.deepEqual(record.data.product_ids.data, [], "o2m default should be []"); assert.deepEqual(record.data.category.data, [], "m2m default should be []"); + + assert.verifySteps(['default_get'], + "there should be default_get"); + + model.destroy(); }); - assert.verifySteps(['default_get'], - "there should be default_get"); + QUnit.test('default_get returning a non requested field', async function (assert) { + // 'default_get' returns a default value for the fields given in + // arguments. It should not return a value for fields that have not be + // requested. However, it happens (e.g. res.users), and the webclient + // should not crash when this situation occurs (the field should simply + // be ignored). + assert.expect(2); - model.destroy(); - }); + this.params.context = {}; + this.params.fieldNames = ['category']; + this.params.res_id = undefined; + this.params.type = 'record'; - QUnit.test('default_get returning a non requested field', function (assert) { - // 'default_get' returns a default value for the fields given in - // arguments. It should not return a value for fields that have not be - // requested. However, it happens (e.g. res.users), and the webclient - // should not crash when this situation occurs (the field should simply - // be ignored). - assert.expect(2); - - this.params.context = {}; - this.params.fieldNames = ['category']; - this.params.res_id = undefined; - this.params.type = 'record'; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - var result = this._super(route, args); - if (args.method === 'default_get') { - result.product_ids = [[6, 0, [37, 41]]]; - } - return result; - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + var result = this._super(route, args); + if (args.method === 'default_get') { + result.product_ids = [[6, 0, [37, 41]]]; + } + return result; + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.ok('category' in record.data, "should have processed 'category'"); assert.notOk('product_ids' in record.data, "should have ignored 'product_ids'"); + + model.destroy(); }); - model.destroy(); - }); + QUnit.test('can make a default_record with default relational values', async function (assert) { + assert.expect(7); - QUnit.test('can make a default_record with default relational values', function (assert) { - assert.expect(7); - - this.data.partner.fields.product_id.default = 37; - this.data.partner.fields.product_ids.default = [ - [0, false, {name: 'xmac'}], - [0, false, {name: 'xcloud'}] - ]; - this.data.partner.fields.category.default = [ - [6, false, [12, 14]] - ]; - - 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: {} }, + this.data.partner.fields.product_id.default = 37; + this.data.partner.fields.product_ids.default = [ + [0, false, { name: 'xmac' }], + [0, false, { name: 'xcloud' }] + ]; + this.data.partner.fields.category.default = [ + [6, false, [12, 14]] + ]; + + 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', }, - relatedFields: this.data.product.fields, - viewType: 'default', }, - }, - }; - this.params.viewType = 'form'; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - assert.step(args.method); - return this._super(route, args); - }, - }); + }; + this.params.viewType = 'form'; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + assert.step(args.method); + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.deepEqual(record.data.product_id.data.display_name, 'xphone', "m2o default should be xphone"); @@ -754,217 +735,211 @@ QUnit.module('Views', { 'xmac', "first o2m default value should be xmac"); assert.deepEqual(record.data.category.res_ids, [12, 14], "m2m default should be [12, 14]"); - }); - assert.verifySteps(['default_get', 'name_get'], - "there should be default_get and name_get"); + assert.verifySteps(['default_get', 'name_get'], + "there should be default_get and name_get"); - model.destroy(); - }); + model.destroy(); + }); - QUnit.test('default_record, with onchange on many2one', function (assert) { - assert.expect(1); + QUnit.test('default_record, with onchange on many2one', async function (assert) { + assert.expect(1); - // the onchange is done by the mockRPC because we want to return a value - // of 'false', which does not work with the mockserver mockOnChange method. - this.data.partner.onchanges.product_id = true; + // the onchange is done by the mockRPC because we want to return a value + // of 'false', which does not work with the mockserver mockOnChange method. + this.data.partner.onchanges.product_id = true; - this.params.context = {}; - this.params.fieldNames = ['product_id']; - this.params.res_id = undefined; - this.params.type = 'record'; + this.params.context = {}; + this.params.fieldNames = ['product_id']; + this.params.res_id = undefined; + this.params.type = 'record'; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'onchange') { - return $.when({value: { product_id: false }}); - } - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'onchange') { + return Promise.resolve({ value: { product_id: false } }); + } + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.product_id, false, "m2o default value should be false"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('default record: batch namegets on same model and res_id', function (assert) { - assert.expect(3); - - var rpcCount = 0; - var fields = this.data.partner.fields; - fields.other_product_id = _.extend({}, fields.product_id); - fields.product_id.default = 37; - fields.other_product_id.default = 41; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - rpcCount++; - return this._super(route, args); - }, - }); + QUnit.test('default record: batch namegets on same model and res_id', async function (assert) { + assert.expect(3); + + var rpcCount = 0; + var fields = this.data.partner.fields; + fields.other_product_id = _.extend({}, fields.product_id); + fields.product_id.default = 37; + fields.other_product_id.default = 41; - var params = { - context: {}, - fieldNames: ['other_product_id', 'product_id'], - fields: fields, - modelName: 'partner', - type: 'record', - }; + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + rpcCount++; + return this._super(route, args); + }, + }); + + var params = { + context: {}, + fieldNames: ['other_product_id', 'product_id'], + fields: fields, + modelName: 'partner', + type: 'record', + }; - model.load(params).then(function (resultID) { + var resultID = await model.load(params); var record = model.get(resultID); assert.strictEqual(record.data.product_id.data.display_name, "xphone", "should have fetched correct name"); assert.strictEqual(record.data.other_product_id.data.display_name, "xpad", "should have fetched correct name"); assert.strictEqual(rpcCount, 2, "should have done 2 rpcs: default_get and 1 name_get"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('undoing a change makes the record not dirty', function (assert) { - assert.expect(4); + QUnit.test('undoing a change makes the record not dirty', async function (assert) { + assert.expect(4); - this.params.fieldNames = ['foo']; + this.params.fieldNames = ['foo']; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.foo, "gnap", "foo field should properly be set"); assert.ok(!model.isDirty(resultID), "record should not be dirty"); - model.notifyChanges(resultID, {foo: "hello"}); + await model.notifyChanges(resultID, { foo: "hello" }); assert.ok(model.isDirty(resultID), "record should be dirty"); - model.notifyChanges(resultID, {foo: "gnap"}); + await model.notifyChanges(resultID, { foo: "gnap" }); assert.ok(!model.isDirty(resultID), "record should not be dirty"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('isDirty works correctly on list made empty', function (assert) { - assert.expect(3); + QUnit.test('isDirty works correctly on list made empty', async function (assert) { + assert.expect(3); - this.params.fieldNames = ['category']; - this.params.res_id = 1; + this.params.fieldNames = ['category']; + this.params.res_id = 1; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); var category_value = record.data.category; assert.ok(_.isObject(category_value), "category field should have been fetched"); assert.strictEqual(category_value.data.length, 1, "category field should contain one record"); - model.notifyChanges(resultID, {category: { - operation: 'DELETE', - ids: [category_value.data[0].id], - }}); + await model.notifyChanges(resultID, { + category: { + operation: 'DELETE', + ids: [category_value.data[0].id], + } + }); assert.ok(model.isDirty(resultID), "record should be considered dirty"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('can duplicate a record', function (assert) { - assert.expect(4); + QUnit.test('can duplicate a record', async function (assert) { + assert.expect(4); - this.params.fieldNames = ['foo']; + this.params.fieldNames = ['foo']; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.display_name, "second partner", "record should have correct display name"); assert.strictEqual(record.data.foo, "gnap", "foo should be set to correct value"); - model.duplicateRecord(resultID).then(function (duplicateID) { - var duplicate = model.get(duplicateID); - assert.strictEqual(duplicate.data.display_name, "second partner (copy)", - "record should have been duplicated"); - assert.strictEqual(duplicate.data.foo, "gnap", "foo should be set to correct value"); - }); + var duplicateID = await model.duplicateRecord(resultID); + var duplicate = model.get(duplicateID); + assert.strictEqual(duplicate.data.display_name, "second partner (copy)", + "record should have been duplicated"); + assert.strictEqual(duplicate.data.foo, "gnap", "foo should be set to correct value"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('record with many2one set to some value, then set it to none', function (assert) { - assert.expect(3); + QUnit.test('record with many2one set to some value, then set it to none', async function (assert) { + assert.expect(3); - this.params.fieldNames = ['product_id']; + this.params.fieldNames = ['product_id']; - var self = this; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var self = this; + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.product_id.data.display_name, 'xpad', "product_id should be set"); - model.notifyChanges(resultID, {product_id: false}); + await model.notifyChanges(resultID, { product_id: false }); record = model.get(resultID); assert.strictEqual(record.data.product_id, false, "product_id should not be set"); - model.save(resultID); + await model.save(resultID); assert.strictEqual(self.data.partner.records[1].product_id, false, "should have saved the new product_id value"); - }); - model.destroy(); - }); - - QUnit.test('internal state of groups remains when reloading', function (assert) { - assert.expect(10); - - this.params.fieldNames = ['foo']; - this.params.domain = []; - this.params.limit = 80; - this.params.groupedBy = ['product_id']; - this.params.res_id = undefined; - - var filterEnabled = false; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'read_group' && filterEnabled) { - // as this is not yet supported by the MockServer, simulates - // a read_group that returns empty groups - // this is the case for several models (e.g. project.task - // grouped by stage_id) - return this._super.apply(this, arguments).then(function (result) { - // artificially filter out records of first group - result[0].product_id_count = 0; - return result; - }); - } - return this._super.apply(this, arguments); - }, - }); + model.destroy(); + }); + + QUnit.test('internal state of groups remains when reloading', async function (assert) { + assert.expect(10); + + this.params.fieldNames = ['foo']; + this.params.domain = []; + this.params.limit = 80; + this.params.groupedBy = ['product_id']; + this.params.res_id = undefined; + + var filterEnabled = false; + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'read_group' && filterEnabled) { + // as this is not yet supported by the MockServer, simulates + // a read_group that returns empty groups + // this is the case for several models (e.g. project.task + // grouped by stage_id) + return this._super.apply(this, arguments).then(function (result) { + // artificially filter out records of first group + result[0].product_id_count = 0; + return result; + }); + } + return this._super.apply(this, arguments); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.length, 2, "should have 2 groups"); var groupID = record.data[0].id; assert.strictEqual(model.localData[groupID].parentID, resultID, "parentID should be correctly set on groups"); - model.toggleGroup(groupID); + await model.toggleGroup(groupID); record = model.get(resultID); assert.ok(record.data[0].isOpen, "first group should be open"); @@ -976,7 +951,7 @@ QUnit.module('Views', { // change the limit and offset of the first group model.localData[record.data[0].id].limit = 10; - model.reload(resultID); + await model.reload(resultID); record = model.get(resultID); assert.ok(record.data[0].isOpen, "first group should still be open"); assert.strictEqual(record.data[0].data.length, 1, @@ -987,193 +962,188 @@ QUnit.module('Views', { // filter some records out: the open group should stay open but now // be empty filterEnabled = true; - model.reload(resultID); + await model.reload(resultID); record = model.get(resultID); assert.strictEqual(record.data[0].count, 0, "first group's count should be 0"); assert.strictEqual(record.data[0].data.length, 0, "first group's data should be empty'"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('group on date field with magic grouping method', function (assert) { - assert.expect(1); + QUnit.test('group on date field with magic grouping method', async function (assert) { + assert.expect(1); - this.params.fieldNames = ['foo']; - this.params.groupedBy = ['date:month']; - this.params.res_id = undefined; + this.params.fieldNames = ['foo']; + this.params.groupedBy = ['date:month']; + this.params.res_id = undefined; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'read_group') { - assert.deepEqual(args.kwargs.fields, ['foo', 'date'], - "should have correctly trimmed the magic grouping info from the field name"); - } - return this._super.apply(this, arguments); - }, + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'read_group') { + assert.deepEqual(args.kwargs.fields, ['foo', 'date'], + "should have correctly trimmed the magic grouping info from the field name"); + } + return this._super.apply(this, arguments); + }, + }); + + await model.load(this.params); + model.destroy(); }); - model.load(this.params); - model.destroy(); - }); + QUnit.test('read group when grouped by a selection field', async function (assert) { + assert.expect(5); - QUnit.test('read group when grouped by a selection field', function (assert) { - assert.expect(5); + this.data.partner.fields.selection = { + type: 'selection', + selection: [['a', 'A'], ['b', 'B']], + }; + this.data.partner.records[0].selection = 'a'; - this.data.partner.fields.selection = { - type: 'selection', - selection: [['a', 'A'], ['b', 'B']], - }; - this.data.partner.records[0].selection = 'a'; + var model = createModel({ + Model: BasicModel, + data: this.data, + }); + var params = { + modelName: 'partner', + fields: this.data.partner.fields, + fieldNames: ['foo'], + groupedBy: ['selection'], + }; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); - var params = { - modelName: 'partner', - fields: this.data.partner.fields, - fieldNames: ['foo'], - groupedBy: ['selection'], - }; - - model.load(params).then(function (resultID) { + var resultID = await model.load(params); var dataPoint = model.get(resultID); assert.strictEqual(dataPoint.data.length, 2, "should have two groups"); - var groupFalse = _.findWhere(dataPoint.data, {value: false}); + var groupFalse = _.findWhere(dataPoint.data, { value: false }); assert.ok(groupFalse, "should have a group for value false"); assert.deepEqual(groupFalse.domain, [['selection', '=', false]], "group's domain should be correct"); - var groupA = _.findWhere(dataPoint.data, {value: 'A'}); + var groupA = _.findWhere(dataPoint.data, { value: 'A' }); assert.ok(groupA, "should have a group for value 'a'"); assert.deepEqual(groupA.domain, [['selection', '=', 'a']], "group's domain should be correct"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('create record, then save', function (assert) { - assert.expect(5); - - this.params.fieldNames = ['product_ids']; - this.params.res_id = undefined; - this.params.type = 'record'; - this.params.context = {active_field: 2}; - - var id; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'create') { - // has to be done before the call to _super - assert.notOk('product_ids' in args.args[0], "should not have any value"); - assert.notOk('category' in args.args[0], "should not have other fields"); - - assert.strictEqual(args.kwargs.context.active_field, 2, - "record's context should be correctly passed"); - } - var result = this._super(route, args); - if (args.method === 'create') { - result.then(function (res) { - id = res; - }); - } - return result; - }, - }); + QUnit.test('create record, then save', async function (assert) { + assert.expect(5); + + this.params.fieldNames = ['product_ids']; + this.params.res_id = undefined; + this.params.type = 'record'; + this.params.context = { active_field: 2 }; - model.load(this.params).then(function (resultID) { + var id; + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'create') { + // has to be done before the call to _super + assert.notOk('product_ids' in args.args[0], "should not have any value"); + assert.notOk('category' in args.args[0], "should not have other fields"); + + assert.strictEqual(args.kwargs.context.active_field, 2, + "record's context should be correctly passed"); + } + var result = this._super(route, args); + if (args.method === 'create') { + result.then(function (res) { + id = res; + }); + } + return result; + }, + }); + + var resultID = await model.load(this.params); var record = model.get(resultID); - model.save(record.id, {reload: false}); + await model.save(record.id, { reload: false }); record = model.get(resultID); assert.strictEqual(record.res_id, id, "should have correct id from server"); assert.strictEqual(record.data.id, id, "should have correct id from server"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('write commands on a one2many', function (assert) { - assert.expect(4); + QUnit.test('write commands on a one2many', async function (assert) { + assert.expect(4); - this.data.partner.records[1].product_ids = [37]; + this.data.partner.records[1].product_ids = [37]; - this.params.fieldNames = ['product_ids']; + this.params.fieldNames = ['product_ids']; - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'write') { - assert.deepEqual(args.args[0], [2], "should write on res_id = 2"); - var commands = args.args[1].product_ids; - assert.deepEqual(commands[0], [4, 37, false], "first command should be a 4"); - // TO DO: uncomment next line - // assert.strictEqual(commands[1], [0, false, {name: "toy"}], "second command should be a 0"); - assert.strictEqual(commands[1][0], 0, "second command should be a 0"); - } - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'write') { + assert.deepEqual(args.args[0], [2], "should write on res_id = 2"); + var commands = args.args[1].product_ids; + assert.deepEqual(commands[0], [4, 37, false], "first command should be a 4"); + // TO DO: uncomment next line + // assert.strictEqual(commands[1], [0, false, {name: "toy"}], "second command should be a 0"); + assert.strictEqual(commands[1][0], 0, "second command should be a 0"); + } + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { - var record = model.get(resultID, {raw: true}); + var resultID = await model.load(this.params); + var record = model.get(resultID, { raw: true }); assert.deepEqual(record.data.product_ids, [37], "should have correct initial value"); - model.makeRecord('product', [{ - name: 'name', - string: "Product Name", - type: "char", - value: "xpod" - } - ]).then(function (relatedRecordID) { - model.notifyChanges(record.id, { - product_ids: {operation: "ADD", id: relatedRecordID} - }); - model.save(record.id); + var relatedRecordID = await model.makeRecord('product', [{ + name: 'name', + string: "Product Name", + type: "char", + value: "xpod" + } + ]); + await model.notifyChanges(record.id, { + product_ids: { operation: "ADD", id: relatedRecordID } }); + await model.save(record.id); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('create commands on a one2many', function (assert) { - assert.expect(3); + QUnit.test('create commands on a one2many', async function (assert) { + assert.expect(3); - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - return this._super(route, args); - }, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + return this._super(route, args); + }, + }); - this.params.fieldsInfo = { - default: { - product_ids: { - fieldsInfo: { - default: { - display_name: {type: 'string'}, - } - }, - viewType: 'default', + this.params.fieldsInfo = { + default: { + product_ids: { + fieldsInfo: { + default: { + display_name: { type: 'string' }, + } + }, + viewType: 'default', + } } - } - }; - this.params.res_id = undefined; - this.params.type = 'record'; + }; + this.params.res_id = undefined; + this.params.type = 'record'; - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.product_ids.data.length, 0, "one2many should start with a list of length 0"); - model.notifyChanges(record.id, { + await model.notifyChanges(record.id, { product_ids: { operation: "CREATE", data: { @@ -1186,279 +1156,275 @@ QUnit.module('Views', { "one2many should be a list of length 1"); assert.strictEqual(record.data.product_ids.data[0].data.display_name, "coucou", "one2many should have correct data"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('onchange with a one2many on a new record', function (assert) { - assert.expect(4); + QUnit.test('onchange with a one2many on a new record', async function (assert) { + assert.expect(4); - this.data.partner.fields.total.default = 50; - this.data.partner.fields.product_ids.onChange = true; - this.data.partner.onchanges.product_ids = function (obj) { - obj.total += 100; - }; + this.data.partner.fields.total.default = 50; + this.data.partner.fields.product_ids.onChange = true; + this.data.partner.onchanges.product_ids = function (obj) { + obj.total += 100; + }; - this.params.fieldNames = ['total', 'product_ids']; - this.params.res_id = undefined; - this.params.type = 'record'; - this.params.fieldsInfo = { - form: { - product_ids: { - fieldsInfo: { - default: { name: {} }, + 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', }, - relatedFields: this.data.product.fields, - viewType: 'default', + total: {}, }, - total: {}, - }, - }; - this.params.viewType = 'form'; - - var o2mRecordParams = { - fields: this.data.product.fields, - fieldNames: ['name'], - modelName: 'product', - type: 'record', - }; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'onchange' && args.args[1].total === 150) { - assert.deepEqual(args.args[1].product_ids, [[0, args.args[1].product_ids[0][1], {name: "xpod"}]], - "Should have sent the create command in the onchange"); - } - return this._super(route, args); - }, - }); + }; + this.params.viewType = 'form'; + + var o2mRecordParams = { + fields: this.data.product.fields, + fieldNames: ['name'], + modelName: 'product', + type: 'record', + }; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'onchange' && args.args[1].total === 150) { + assert.deepEqual(args.args[1].product_ids, [[0, args.args[1].product_ids[0][1], { name: "xpod" }]], + "Should have sent the create command in the onchange"); + } + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.product_ids.data.length, 0, "one2many should start with a list of length 0"); // make a default record for the related model - model.load(o2mRecordParams).then(function (relatedRecordID) { - // update the subrecord - model.notifyChanges(relatedRecordID, {name: 'xpod'}); - // add the subrecord to the o2m of the main record - model.notifyChanges(resultID, { - product_ids: {operation: "ADD", id: relatedRecordID} - }); - - record = model.get(resultID); - assert.strictEqual(record.data.product_ids.data.length, 1, - "one2many should be a list of length 1"); - assert.strictEqual(record.data.product_ids.data[0].data.name, "xpod", - "one2many should have correct data"); + var relatedRecordID = await model.load(o2mRecordParams); + // update the subrecord + await model.notifyChanges(relatedRecordID, { name: 'xpod' }); + // add the subrecord to the o2m of the main record + await model.notifyChanges(resultID, { + product_ids: { operation: "ADD", id: relatedRecordID } }); + + record = model.get(resultID); + assert.strictEqual(record.data.product_ids.data.length, 1, + "one2many should be a list of length 1"); + assert.strictEqual(record.data.product_ids.data[0].data.name, "xpod", + "one2many should have correct data"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('dates are properly loaded and parsed (record)', function (assert) { - assert.expect(2); + QUnit.test('dates are properly loaded and parsed (record)', async function (assert) { + assert.expect(2); - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - var params = { - fieldNames: ['date'], - fields: this.data.partner.fields, - modelName: 'partner', - res_id: 1, - }; + var params = { + fieldNames: ['date'], + fields: this.data.partner.fields, + modelName: 'partner', + res_id: 1, + }; - model.load(params).then(function (resultID) { - var record = model.get(resultID); - assert.ok(record.data.date instanceof moment, - "fetched date field should have been formatted"); - }); + model.load(params).then(function (resultID) { + var record = model.get(resultID); + assert.ok(record.data.date instanceof moment, + "fetched date field should have been formatted"); + }); - params.res_id = 2; + params.res_id = 2; - model.load(params).then(function (resultID) { - var record = model.get(resultID); - assert.strictEqual(record.data.date, false, - "unset date field should be false"); + model.load(params).then(function (resultID) { + var record = model.get(resultID); + assert.strictEqual(record.data.date, false, + "unset date field should be false"); + }); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('dates are properly loaded and parsed (list)', function (assert) { - assert.expect(2); + QUnit.test('dates are properly loaded and parsed (list)', async function (assert) { + assert.expect(2); - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - var params = { - fieldNames: ['date'], - fields: this.data.partner.fields, - modelName: 'partner', - type: 'list', - }; + var params = { + fieldNames: ['date'], + fields: this.data.partner.fields, + modelName: 'partner', + type: 'list', + }; - model.load(params).then(function (resultID) { - var record = model.get(resultID); - var firstRecord = record.data[0]; - var secondRecord = record.data[1]; - assert.ok(firstRecord.data.date instanceof moment, - "fetched date field should have been formatted"); - assert.strictEqual(secondRecord.data.date, false, - "if date is not set, it should be false"); + model.load(params).then(function (resultID) { + var record = model.get(resultID); + var firstRecord = record.data[0]; + var secondRecord = record.data[1]; + assert.ok(firstRecord.data.date instanceof moment, + "fetched date field should have been formatted"); + assert.strictEqual(secondRecord.data.date, false, + "if date is not set, it should be false"); + }); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('dates are properly loaded and parsed (default_get)', function (assert) { - assert.expect(1); + QUnit.test('dates are properly loaded and parsed (default_get)', async function (assert) { + assert.expect(1); - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - var params = { - fieldNames: ['date'], - fields: this.data.partner.fields, - modelName: 'partner', - type: 'record', - }; + var params = { + fieldNames: ['date'], + fields: this.data.partner.fields, + modelName: 'partner', + type: 'record', + }; - model.load(params).then(function (resultID) { - var record = model.get(resultID); - assert.strictEqual(record.data.date, false, "date default value should be false"); + model.load(params).then(function (resultID) { + var record = model.get(resultID); + assert.strictEqual(record.data.date, false, "date default value should be false"); + }); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('default_get on x2many may return a list of ids', function (assert) { - assert.expect(1); + QUnit.test('default_get on x2many may return a list of ids', async function (assert) { + assert.expect(1); - this.data.partner.fields.category.default = [12, 14]; + this.data.partner.fields.category.default = [12, 14]; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - var params = { - fieldNames: ['category'], - fields: this.data.partner.fields, - modelName: 'partner', - type: 'record', - }; + var params = { + fieldNames: ['category'], + fields: this.data.partner.fields, + modelName: 'partner', + type: 'record', + }; - model.load(params).then(function (resultID) { - var record = model.get(resultID); - assert.ok(_.isEqual(record.data.category.res_ids, [12, 14]), - "category field should have correct default value"); - }); + model.load(params).then(function (resultID) { + var record = model.get(resultID); + assert.ok(_.isEqual(record.data.category.res_ids, [12, 14]), + "category field should have correct default value"); + }); - model.destroy(); - }); + model.destroy(); + }); - QUnit.test('default_get: fetch many2one with default (empty & not) inside x2manys', function (assert) { - assert.expect(4); + QUnit.test('default_get: fetch many2one with default (empty & not) inside x2manys', async function (assert) { + assert.expect(4); - this.data.partner.fields.o2m = { - string: "O2M", type: 'one2many', relation: 'partner', default: [ - [6, 0, []], - [0, 0, {category: false}], - [0, 0, {category: 12}], - ], - }; - this.data.partner.fields.category.type = 'many2one'; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'name_get' && args.model === 'partner_type') { - assert.deepEqual(args.args, [[12]], "should name_get on category 12"); - } - return this._super(route, args); - }, - }); + this.data.partner.fields.o2m = { + string: "O2M", type: 'one2many', relation: 'partner', default: [ + [6, 0, []], + [0, 0, { category: false }], + [0, 0, { category: 12 }], + ], + }; + this.data.partner.fields.category.type = 'many2one'; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'name_get' && args.model === 'partner_type') { + assert.deepEqual(args.args, [[12]], "should name_get on category 12"); + } + return this._super(route, args); + }, + }); - var params = { - fieldNames: ['o2m'], - fields: this.data.partner.fields, - fieldsInfo: { - form: { - o2m: { - relatedFields: this.data.partner.fields, - fieldsInfo: { - list: { - category: { - relatedFields: { display_name: {} }, + var params = { + fieldNames: ['o2m'], + fields: this.data.partner.fields, + fieldsInfo: { + form: { + o2m: { + relatedFields: this.data.partner.fields, + fieldsInfo: { + list: { + category: { + relatedFields: { display_name: {} }, + }, }, }, + viewType: 'list', }, - viewType: 'list', }, }, - }, - modelName: 'partner', - type: 'record', - viewType: 'form', - }; + modelName: 'partner', + type: 'record', + viewType: 'form', + }; - model.load(params).then(function (resultID) { + var resultID = await model.load(params); var record = model.get(resultID); assert.strictEqual(record.data.o2m.count, 2, "o2m field should contain 2 records"); assert.strictEqual(record.data.o2m.data[0].data.category, false, "first category field should be empty"); assert.strictEqual(record.data.o2m.data[1].data.category.data.display_name, "gold", "second category field should have been correctly fetched"); - }); - model.destroy(); - }); + model.destroy(); + }); - QUnit.test('default_get: fetch x2manys inside x2manys', function (assert) { - assert.expect(3); + QUnit.test('default_get: fetch x2manys inside x2manys', async function (assert) { + assert.expect(3); - this.data.partner.fields.o2m = { - string: "O2M", type: 'one2many', relation: 'partner', default: [[6, 0, [1]]], - }; + this.data.partner.fields.o2m = { + string: "O2M", type: 'one2many', relation: 'partner', default: [[6, 0, [1]]], + }; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - var params = { - fieldNames: ['o2m'], - fields: this.data.partner.fields, - fieldsInfo: { - form: { - o2m: { - relatedFields: this.data.partner.fields, - fieldsInfo: { - list: { - category: { - relatedFields: { display_name: {} }, + var params = { + fieldNames: ['o2m'], + fields: this.data.partner.fields, + fieldsInfo: { + form: { + o2m: { + relatedFields: this.data.partner.fields, + fieldsInfo: { + list: { + category: { + relatedFields: { display_name: {} }, + }, }, }, + viewType: 'list', }, - viewType: 'list', }, }, - }, - modelName: 'partner', - type: 'record', - viewType: 'form', - }; + modelName: 'partner', + type: 'record', + viewType: 'form', + }; - model.load(params).then(function (resultID) { + var resultID = await model.load(params); var record = model.get(resultID); assert.strictEqual(record.data.o2m.count, 1, "o2m field should contain 1 record"); var categoryList = record.data.o2m.data[0].data.category; @@ -1466,25 +1432,24 @@ QUnit.module('Views', { "category field should contain 1 record"); assert.strictEqual(categoryList.data[0].data.display_name, 'gold', "category records should have been fetched"); - }); - model.destroy(); - }); + model.destroy(); + }); - QUnit.test('contexts and domains can be properly fetched', function (assert) { - assert.expect(8); + QUnit.test('contexts and domains can be properly fetched', async function (assert) { + assert.expect(8); - this.data.partner.fields.product_id.context = "{'hello': 'world', 'test': foo}"; - this.data.partner.fields.product_id.domain = "[['hello', 'like', 'world'], ['test', 'like', foo]]"; + this.data.partner.fields.product_id.context = "{'hello': 'world', 'test': foo}"; + this.data.partner.fields.product_id.domain = "[['hello', 'like', 'world'], ['test', 'like', foo]]"; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - this.params.fieldNames = ['product_id', 'foo']; + this.params.fieldNames = ['product_id', 'foo']; - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var recordPartner = model.get(resultID); assert.strictEqual(typeof recordPartner.getContext, "function", "partner record should have a getContext function"); @@ -1495,280 +1460,273 @@ QUnit.module('Views', { assert.deepEqual(recordPartner.getDomain(), [], "asking for a domain without a field name should fetch the session/user/view domain"); assert.deepEqual( - recordPartner.getContext({fieldName: "product_id"}), - {hello: "world", test: "gnap"}, + recordPartner.getContext({ fieldName: "product_id" }), + { hello: "world", test: "gnap" }, "asking for a context with a field name should fetch the field context (evaluated)"); assert.deepEqual( - recordPartner.getDomain({fieldName: "product_id"}), + recordPartner.getDomain({ fieldName: "product_id" }), [["hello", "like", "world"], ["test", "like", "gnap"]], "asking for a domain with a field name should fetch the field domain (evaluated)"); - }); - model.destroy(); + model.destroy(); - // Try again with xml override of field domain and context - model = createModel({ - Model: BasicModel, - data: this.data, - }); + // Try again with xml override of field domain and context + model = createModel({ + Model: BasicModel, + data: this.data, + }); - this.params.fieldsInfo = { - default: { - foo: {}, - product_id: { - context: "{'hello2': 'world', 'test2': foo}", - domain: "[['hello2', 'like', 'world'], ['test2', 'like', foo]]", - }, - } - }; + this.params.fieldsInfo = { + default: { + foo: {}, + product_id: { + context: "{'hello2': 'world', 'test2': foo}", + domain: "[['hello2', 'like', 'world'], ['test2', 'like', foo]]", + }, + } + }; - model.load(this.params).then(function (resultID) { - var recordPartner = model.get(resultID); + resultID = await model.load(this.params); + recordPartner = model.get(resultID); assert.deepEqual( - recordPartner.getContext({fieldName: "product_id"}), - {hello2: "world", test2: "gnap"}, + recordPartner.getContext({ fieldName: "product_id" }), + { hello2: "world", test2: "gnap" }, "field context should have been overriden by xml attribute"); assert.deepEqual( - recordPartner.getDomain({fieldName: "product_id"}), + recordPartner.getDomain({ fieldName: "product_id" }), [["hello2", "like", "world"], ["test2", "like", "gnap"]], "field domain should have been overriden by xml attribute"); + model.destroy(); }); - 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.fields.foo.onChange = true; - this.data.partner.onchanges.foo = function (obj) { - obj.bar = obj.foo.length; - }; - this.params.fieldsInfo = { - default: { - foo: {}, - bar: { - modifiers: { - readonly: true, + QUnit.test('dont write on readonly fields (write and create)', async function (assert) { + assert.expect(6); + + this.params.fieldNames = ['foo', 'bar']; + this.data.partner.fields.foo.onChange = true; + this.data.partner.onchanges.foo = function (obj) { + obj.bar = obj.foo.length; + }; + this.params.fieldsInfo = { + default: { + foo: {}, + bar: { + modifiers: { + 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 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); + }, + }); + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.bar, 2, "should be initialized with correct value"); - model.notifyChanges(resultID, {foo: "verylongstring"}); + await model.notifyChanges(resultID, { foo: "verylongstring" }); record = model.get(resultID); assert.strictEqual(record.data.bar, 14, "should be changed with correct value"); - model.save(resultID); - }); + await 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); + // start again, but with a new record + delete this.params.res_id; + resultID = await model.load(this.params); + record = model.get(resultID); assert.strictEqual(record.data.bar, 0, "should be initialized with correct value (0 as integer)"); - model.notifyChanges(resultID, {foo: "anotherverylongstring"}); + await model.notifyChanges(resultID, { foo: "anotherverylongstring" }); record = model.get(resultID); assert.strictEqual(record.data.bar, 21, "should be changed with correct value"); - model.save(resultID); + await model.save(resultID); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('dont write on readonly fields unless save attribute is set', function (assert) { - assert.expect(6); - - this.params.fieldNames = ['foo', 'bar']; - this.data.partner.fields.foo.onChange = true; - this.data.partner.onchanges.foo = function (obj) { - obj.bar = obj.foo.length; - }; - this.params.fieldsInfo = { - default: { - foo: {}, - bar: { - modifiers: { - readonly: true, + QUnit.test('dont write on readonly fields unless save attribute is set', async function (assert) { + assert.expect(6); + + this.params.fieldNames = ['foo', 'bar']; + this.data.partner.fields.foo.onChange = true; + this.data.partner.onchanges.foo = function (obj) { + obj.bar = obj.foo.length; + }; + this.params.fieldsInfo = { + default: { + foo: {}, + bar: { + modifiers: { + readonly: true, + }, + force_save: true, }, - force_save: true, - }, - } - }; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'write') { - assert.deepEqual(args.args[1], {bar: 14, foo: "verylongstring"}, - "should only save foo field"); - } - if (args.method === 'create') { - assert.deepEqual(args.args[0], {bar: 21, foo: "anotherverylongstring"}, - "should only save foo field"); } - return this._super(route, args); - }, - }); + }; - model.load(this.params).then(function (resultID) { + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'write') { + assert.deepEqual(args.args[1], { bar: 14, foo: "verylongstring" }, + "should only save foo field"); + } + if (args.method === 'create') { + assert.deepEqual(args.args[0], { bar: 21, foo: "anotherverylongstring" }, + "should only save foo field"); + } + return this._super(route, args); + }, + }); + + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data.bar, 2, "should be initialized with correct value"); - model.notifyChanges(resultID, {foo: "verylongstring"}); + await model.notifyChanges(resultID, { foo: "verylongstring" }); record = model.get(resultID); assert.strictEqual(record.data.bar, 14, "should be changed with correct value"); - model.save(resultID); - }); + await 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); + // start again, but with a new record + delete this.params.res_id; + resultID = await model.load(this.params); + record = model.get(resultID); assert.strictEqual(record.data.bar, 0, "should be initialized with correct value (0 as integer)"); - model.notifyChanges(resultID, {foo: "anotherverylongstring"}); + await model.notifyChanges(resultID, { foo: "anotherverylongstring" }); record = model.get(resultID); assert.strictEqual(record.data.bar, 21, "should be changed with correct value"); - model.save(resultID); + await model.save(resultID); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('default_get with one2many values', function (assert) { - assert.expect(1); - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'default_get') { - return $.when({ - product_ids: [[0, 0, {"name": "xdroid"}]] - }); - } - return this._super(route, args); - }, - }); - var params = { - fieldNames: ['product_ids'], - fields: this.data.partner.fields, - modelName: 'partner', - type: 'record', - fieldsInfo: { - form: { - product_ids: { - fieldsInfo: { - default: { name: {} }, + QUnit.test('default_get with one2many values', async function (assert) { + assert.expect(1); + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'default_get') { + return Promise.resolve({ + product_ids: [[0, 0, { "name": "xdroid" }]] + }); + } + return this._super(route, args); + }, + }); + var params = { + fieldNames: ['product_ids'], + fields: this.data.partner.fields, + modelName: 'partner', + type: 'record', + fieldsInfo: { + form: { + product_ids: { + fieldsInfo: { + default: { name: {} }, + }, + relatedFields: this.data.product.fields, + viewType: 'default', }, - relatedFields: this.data.product.fields, - viewType: 'default', }, }, - }, - viewType: 'form', - }; - model.load(params).then(function (resultID) { + viewType: 'form', + }; + var resultID = await model.load(params); assert.strictEqual(typeof resultID, 'string', "result should be a valid id"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('call makeRecord with a pre-fetched many2one field', function (assert) { - assert.expect(3); - var rpcCount = 0; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - rpcCount++; - return this._super(route, args); - }, - }); + QUnit.test('call makeRecord with a pre-fetched many2one field', async function (assert) { + assert.expect(3); + var rpcCount = 0; - model.makeRecord('coucou', [{ - name: 'partner_id', - relation: 'partner', - type: 'many2one', - value: [1, 'first partner'], - }], { - partner_id: { - options: { - no_open: true, + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + rpcCount++; + return this._super(route, args); }, - }, - }).then(function (recordID) { - var record = model.get(recordID); - assert.deepEqual(record.fieldsInfo.default.partner_id, {options: {no_open: true}}, - "makeRecord should have generated the fieldsInfo"); - assert.deepEqual(record.data.partner_id.data, {id: 1, display_name: 'first partner'}, - "many2one should contain the partner with id 1"); - assert.strictEqual(rpcCount, 0, "makeRecord should not have done any rpc"); - }); - model.destroy(); - }); + }); - QUnit.test('call makeRecord with a many2many field', function (assert) { - assert.expect(5); - var rpcCount = 0; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - rpcCount++; - return this._super(route, args); - }, + model.makeRecord('coucou', [{ + name: 'partner_id', + relation: 'partner', + type: 'many2one', + value: [1, 'first partner'], + }], { + partner_id: { + options: { + no_open: true, + }, + }, + }).then(function (recordID) { + var record = model.get(recordID); + assert.deepEqual(record.fieldsInfo.default.partner_id, { options: { no_open: true } }, + "makeRecord should have generated the fieldsInfo"); + assert.deepEqual(record.data.partner_id.data, { id: 1, display_name: 'first partner' }, + "many2one should contain the partner with id 1"); + assert.strictEqual(rpcCount, 0, "makeRecord should not have done any rpc"); + }); + model.destroy(); }); - model.makeRecord('coucou', [{ - name: 'partner_ids', - fields: [{ - name: 'id', - type: 'integer', - }, { - name: 'display_name', - type: 'char', - }], - relation: 'partner', - type: 'many2many', - value: [1, 2], - }]).then(function (recordID) { + QUnit.test('call makeRecord with a many2many field', async function (assert) { + assert.expect(5); + var rpcCount = 0; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + rpcCount++; + return this._super(route, args); + }, + }); + + var recordID = await model.makeRecord('coucou', [{ + name: 'partner_ids', + fields: [{ + name: 'id', + type: 'integer', + }, { + name: 'display_name', + type: 'char', + }], + relation: 'partner', + type: 'many2many', + value: [1, 2], + }]); var record = model.get(recordID); assert.deepEqual(record.fieldsInfo.default.partner_ids, {}, "makeRecord should have generated the fieldsInfo"); @@ -1776,45 +1734,44 @@ QUnit.module('Views', { "there should be 2 elements in the many2many"); assert.strictEqual(record.data.partner_ids.data.length, 2, "many2many should be a list of length 2"); - assert.deepEqual(record.data.partner_ids.data[0].data, {id: 1, display_name: 'first partner'}, + assert.deepEqual(record.data.partner_ids.data[0].data, { id: 1, display_name: 'first partner' }, "many2many should contain the partner with id 1"); assert.strictEqual(rpcCount, 1, "makeRecord should have done 1 rpc"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('call makeRecord with a pre-fetched many2many field', function (assert) { - assert.expect(5); - var rpcCount = 0; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - rpcCount++; - return this._super(route, args); - }, - }); + QUnit.test('call makeRecord with a pre-fetched many2many field', async function (assert) { + assert.expect(5); + var rpcCount = 0; - model.makeRecord('coucou', [{ - name: 'partner_ids', - fields: [{ - name: 'id', - type: 'integer', - }, { - name: 'display_name', - type: 'char', - }], - relation: 'partner', - type: 'many2many', - value: [{ - id: 1, - display_name: "first partner", - }, { - id: 2, - display_name: "second partner", - }], - }]).then(function (recordID) { + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + rpcCount++; + return this._super(route, args); + }, + }); + + var recordID = await model.makeRecord('coucou', [{ + name: 'partner_ids', + fields: [{ + name: 'id', + type: 'integer', + }, { + name: 'display_name', + type: 'char', + }], + relation: 'partner', + type: 'many2many', + value: [{ + id: 1, + display_name: "first partner", + }, { + id: 2, + display_name: "second partner", + }], + }]); var record = model.get(recordID); assert.deepEqual(record.fieldsInfo.default.partner_ids, {}, "makeRecord should have generated the fieldsInfo"); @@ -1822,154 +1779,149 @@ QUnit.module('Views', { "there should be 2 elements in the many2many"); assert.strictEqual(record.data.partner_ids.data.length, 2, "many2many should be a list of length 2"); - assert.deepEqual(record.data.partner_ids.data[0].data, {id: 1, display_name: 'first partner'}, + assert.deepEqual(record.data.partner_ids.data[0].data, { id: 1, display_name: 'first partner' }, "many2many should contain the partner with id 1"); assert.strictEqual(rpcCount, 0, "makeRecord should not have done any rpc"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('check id, active_id, active_ids, active_model values in record\'s context', function (assert) { - assert.expect(2); + QUnit.test('check id, active_id, active_ids, active_model values in record\'s context', async function (assert) { + assert.expect(2); - this.data.partner.fields.product_id.context = "{'id': id, 'active_id': active_id, 'active_ids': active_ids, 'active_model': active_model}"; + this.data.partner.fields.product_id.context = "{'id': id, 'active_id': active_id, 'active_ids': active_ids, 'active_model': active_model}"; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - this.params.fieldNames = ['product_id']; + this.params.fieldNames = ['product_id']; - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var recordPartner = model.get(resultID); assert.deepEqual( - recordPartner.getContext({fieldName: "product_id"}), - {id: 2, active_id: 2, active_ids: [2], active_model: "partner"}, + recordPartner.getContext({ fieldName: "product_id" }), + { id: 2, active_id: 2, active_ids: [2], active_model: "partner" }, "wrong values for id, active_id, active_ids or active_model"); - }); - // Try again without record - this.params.res_id = undefined; + // Try again without record + this.params.res_id = undefined; - model.load(this.params).then(function (resultID) { - var recordPartner = model.get(resultID); + resultID = await model.load(this.params); + recordPartner = model.get(resultID); assert.deepEqual( - recordPartner.getContext({fieldName: "product_id"}), - {id: false, active_id: false, active_ids: [], active_model: "partner"}, + recordPartner.getContext({ fieldName: "product_id" }), + { id: false, active_id: false, active_ids: [], active_model: "partner" }, "wrong values for id, active_id, active_ids or active_model. Have to be defined even if there is no record."); + + model.destroy(); }); - model.destroy(); - }); + QUnit.test('load model with many2many field properly fetched', async function (assert) { + assert.expect(2); - QUnit.test('load model with many2many field properly fetched', function (assert) { - assert.expect(2); + this.params.fieldNames = ['category']; + this.params.res_id = 1; - this.params.fieldNames = ['category']; - this.params.res_id = 1; + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + assert.step(args.method); + return this._super(route, args); + }, + }); - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - assert.step(args.method); - return this._super(route, args); - }, + model.load(this.params); + assert.verifySteps(['read'], + "there should be only one read"); + model.destroy(); }); - model.load(this.params); - assert.verifySteps(['read'], - "there should be only one read"); - model.destroy(); - }); + QUnit.test('data should contain all fields in view, default being false', async function (assert) { + assert.expect(1); - QUnit.test('data should contain all fields in view, default being false', function (assert) { - assert.expect(1); + this.data.partner.fields.product_ids.default = [ + [6, 0, []], + [0, 0, { name: 'new' }], + ]; + this.data.product.fields.date = { string: "Date", type: "date" }; + + var params = { + fieldNames: ['product_ids'], + modelName: 'partner', + fields: this.data.partner.fields, + fieldsInfo: { + form: { + product_ids: { + relatedFields: this.data.product.fields, + fieldsInfo: { list: { name: {}, date: {} } }, + viewType: 'list', + } + }, + }, + res_id: undefined, + type: 'record', + viewType: 'form', + }; - this.data.partner.fields.product_ids.default = [ - [6, 0, []], - [0, 0, {name: 'new'}], - ]; - this.data.product.fields.date = { string: "Date", type: "date" }; + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - var params = { - fieldNames: ['product_ids'], - modelName: 'partner', - fields: this.data.partner.fields, - fieldsInfo: { - form: { - product_ids: { - relatedFields: this.data.product.fields, - fieldsInfo: { list: { name: {}, date: {} } }, - viewType: 'list', - } - }, - }, - res_id: undefined, - type: 'record', - viewType: 'form', - }; - - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + model.load(params).then(function (resultID) { + var record = model.get(resultID); + assert.strictEqual(record.data.product_ids.data[0].data.date, false, + "date value should be in data, and should be false"); + }); - model.load(params).then(function (resultID) { - var record = model.get(resultID); - assert.strictEqual(record.data.product_ids.data[0].data.date, false, - "date value should be in data, and should be false"); + model.destroy(); }); - model.destroy(); - }); - - QUnit.test('changes are discarded when reloading from a new record', function (assert) { - // practical use case: click on 'Create' to open a form view in edit - // mode (new record), click on 'Discard', then open an existing record - assert.expect(2); + QUnit.test('changes are discarded when reloading from a new record', async function (assert) { + // practical use case: click on 'Create' to open a form view in edit + // mode (new record), click on 'Discard', then open an existing record + assert.expect(2); - this.data.partner.fields.foo.default = 'default'; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + this.data.partner.fields.foo.default = 'default'; + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - // load a new record (default_get) - var params = _.extend(this.params, { - res_id: undefined, - type: 'record', - fieldNames: ['foo'], - }); - model.load(params).then(function (resultID) { + // load a new record (default_get) + var params = _.extend(this.params, { + res_id: undefined, + type: 'record', + fieldNames: ['foo'], + }); + var resultID = await model.load(params); var record = model.get(resultID); assert.strictEqual(record.data.foo, 'default', "should be the default value"); // reload with id 2 - model.reload(record.id, {currentId: 2}).then(function (resultID) { - var record = model.get(resultID); - assert.strictEqual(record.data.foo, 'gnap', - "should be the value of record 2"); - }); - }); + resultID = await model.reload(record.id, { currentId: 2 }); + record = model.get(resultID); + assert.strictEqual(record.data.foo, 'gnap', + "should be the value of record 2"); - model.destroy(); - }); + model.destroy(); + }); - QUnit.test('has a proper evaluation context', function (assert) { - assert.expect(1); + QUnit.test('has a proper evaluation context', async function (assert) { + assert.expect(1); - this.params.fieldNames = Object.keys(this.data.partner.fields); - this.params.res_id = 1; + this.params.fieldNames = Object.keys(this.data.partner.fields); + this.params.res_id = 1; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.deepEqual(record.evalContext, { active_id: 1, @@ -1988,42 +1940,41 @@ QUnit.module('Views', { reference: false, total: 0 }, "should use the proper eval context"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('x2manys in contexts and domains are correctly evaluated', function (assert) { - assert.expect(4); - - this.data.partner.records[0].product_ids = [37, 41]; - this.params.fieldNames = Object.keys(this.data.partner.fields); - this.params.fieldsInfo = { - form: { - qux: { - context: "{'category': category, 'product_ids': product_ids}", - domain: "[['id', 'in', category], ['id', 'in', product_ids]]", - relatedFields: this.data.partner.fields, - }, - category: { - relatedFields: this.data.partner_type.fields, - }, - product_ids: { - relatedFields: this.data.product.fields, + QUnit.test('x2manys in contexts and domains are correctly evaluated', async function (assert) { + assert.expect(4); + + this.data.partner.records[0].product_ids = [37, 41]; + this.params.fieldNames = Object.keys(this.data.partner.fields); + this.params.fieldsInfo = { + form: { + qux: { + context: "{'category': category, 'product_ids': product_ids}", + domain: "[['id', 'in', category], ['id', 'in', product_ids]]", + relatedFields: this.data.partner.fields, + }, + category: { + relatedFields: this.data.partner_type.fields, + }, + product_ids: { + relatedFields: this.data.product.fields, + }, }, - }, - }; - this.params.viewType = 'form'; - this.params.res_id = 1; - - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + }; + this.params.viewType = 'form'; + this.params.res_id = 1; + + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); - var context = record.getContext({fieldName: 'qux'}); - var domain = record.getDomain({fieldName: 'qux'}); + var context = record.getContext({ fieldName: 'qux' }); + var domain = record.getDomain({ fieldName: 'qux' }); assert.deepEqual(context, { category: [12], @@ -2038,177 +1989,171 @@ QUnit.module('Views', { ], "x2many values in domains should be lists of ids"); assert.strictEqual(JSON.stringify(domain), "[[\"id\",\"in\",[12]],[\"id\",\"in\",[37,41]]]", - "x2many values in domains should be lists of ids"); + "x2many values in domains should be lists of ids"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('fetch references in list, with not too many rpcs', function (assert) { - assert.expect(5); - - this.data.partner.records[0].reference = 'product,37'; - this.data.partner.records[1].reference = 'product,41'; - - this.params.fieldNames = ['reference']; - this.params.domain = []; - this.params.groupedBy = []; - this.params.res_id = undefined; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - assert.step(route); - if (route === "/web/dataset/call_kw/product/name_get") { - assert.deepEqual(args.args, [[37, 41]], - "the name_get should contain the product ids"); - } - return this._super(route, args); - }, - }); + QUnit.test('fetch references in list, with not too many rpcs', async function (assert) { + assert.expect(5); + + this.data.partner.records[0].reference = 'product,37'; + this.data.partner.records[1].reference = 'product,41'; + + this.params.fieldNames = ['reference']; + this.params.domain = []; + this.params.groupedBy = []; + this.params.res_id = undefined; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + assert.step(route); + if (route === "/web/dataset/call_kw/product/name_get") { + assert.deepEqual(args.args, [[37, 41]], + "the name_get should contain the product ids"); + } + return this._super(route, args); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.strictEqual(record.data[0].data.reference.data.display_name, "xphone", "name_get should have been correctly fetched"); - assert.verifySteps(["/web/dataset/search_read", "/web/dataset/call_kw/product/name_get"], + assert.verifySteps(["/web/dataset/search_read", "/web/dataset/call_kw/product/name_get"], "should have done 2 rpc (searchread and name_get for product)"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('reload a new record', function (assert) { - assert.expect(6); - - this.params.context = {}; - this.params.fieldNames = ['product_id', 'category', 'product_ids']; - this.params.res_id = undefined; - this.params.type = 'record'; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route, args) { - assert.step(args.method); - return this._super(route, args); - }, - }); + QUnit.test('reload a new record', async function (assert) { + assert.expect(6); - model.load(this.params).then(function (recordID) { - model.reload(recordID).then(function (recordID) { - assert.verifySteps(['default_get', 'default_get'], - "two default_get RPCs should have been done"); - var record = model.get(recordID); - assert.strictEqual(record.data.product_id, false, - "m2o default value should be false"); - assert.deepEqual(record.data.product_ids.data, [], - "o2m default should be []"); - assert.deepEqual(record.data.category.data, [], - "m2m default should be []"); + this.params.context = {}; + this.params.fieldNames = ['product_id', 'category', 'product_ids']; + this.params.res_id = undefined; + this.params.type = 'record'; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route, args) { + assert.step(args.method); + return this._super(route, args); + }, }); - }); - model.destroy(); - }); + var recordID = await model.load(this.params); + recordID = await model.reload(recordID); + assert.verifySteps(['default_get', 'default_get'], + "two default_get RPCs should have been done"); + var record = model.get(recordID); + assert.strictEqual(record.data.product_id, false, + "m2o default value should be false"); + assert.deepEqual(record.data.product_ids.data, [], + "o2m default should be []"); + assert.deepEqual(record.data.category.data, [], + "m2m default should be []"); + + model.destroy(); + }); - QUnit.test('default_get with value false for a one2many', function (assert) { - assert.expect(1); + QUnit.test('default_get with value false for a one2many', async function (assert) { + assert.expect(1); - this.data.partner.fields.product_ids.default = false; - this.params.fieldNames = ['product_ids']; - this.params.res_id = undefined; - this.params.type = 'record'; + this.data.partner.fields.product_ids.default = false; + this.params.fieldNames = ['product_ids']; + this.params.res_id = undefined; + this.params.type = 'record'; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); assert.deepEqual(record.data.product_ids.data, [], "o2m default should be []"); - }); - - model.destroy(); - }); - QUnit.test('only x2many lists (static) should be sorted client-side', function (assert) { - assert.expect(1); - - this.params.modelName = 'partner_type'; - this.params.res_id = undefined; - this.params.orderedBy = [{name: 'display_name', asc: true}]; - - var model = createModel({ - Model: BasicModel, - data: this.data, - mockRPC: function (route) { - if (route === '/web/dataset/search_read') { - // simulate randomn sort form the server - return $.when({ - length: 3, - records: [ - {id: 12, display_name: "gold", date: "2017-01-25"}, - {id: 15, display_name: "bronze"}, - {id: 14, display_name: "silver"}, - ], - }); - } - return this._super.apply(this, arguments); - }, - }); + model.destroy(); + }); + + QUnit.test('only x2many lists (static) should be sorted client-side', async function (assert) { + assert.expect(1); + + this.params.modelName = 'partner_type'; + this.params.res_id = undefined; + this.params.orderedBy = [{ name: 'display_name', asc: true }]; + + var model = createModel({ + Model: BasicModel, + data: this.data, + mockRPC: function (route) { + if (route === '/web/dataset/search_read') { + // simulate randomn sort form the server + return Promise.resolve({ + length: 3, + records: [ + { id: 12, display_name: "gold", date: "2017-01-25" }, + { id: 15, display_name: "bronze" }, + { id: 14, display_name: "silver" }, + ], + }); + } + return this._super.apply(this, arguments); + }, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var list = model.get(resultID); assert.deepEqual(_.map(list.data, 'res_id'), [12, 15, 14], "should have kept the order from the server"); + model.destroy(); }); - model.destroy(); - }); - QUnit.test('onchange on a boolean field', function (assert) { - assert.expect(2); - - var newFields = { - foobool: { - type: 'boolean', - string: 'foobool', - }, - foobool2: { - type: 'boolean', - string: 'foobool2', - }, - }; - _.extend(this.data.partner.fields, newFields); - - this.data.partner.fields.foobool.onChange = true; - this.data.partner.onchanges.foobool = function (obj) { - if (obj.foobool) { - obj.foobool2 = true; - } - }; + QUnit.test('onchange on a boolean field', async function (assert) { + assert.expect(2); + + var newFields = { + foobool: { + type: 'boolean', + string: 'foobool', + }, + foobool2: { + type: 'boolean', + string: 'foobool2', + }, + }; + _.extend(this.data.partner.fields, newFields); + + this.data.partner.fields.foobool.onChange = true; + this.data.partner.onchanges.foobool = function (obj) { + if (obj.foobool) { + obj.foobool2 = true; + } + }; - this.data.partner.records[0].foobool = false; - this.data.partner.records[0].foobool2 = true; + this.data.partner.records[0].foobool = false; + this.data.partner.records[0].foobool2 = true; - this.params.res_id = 1; - this.params.fieldNames = ['foobool', 'foobool2']; - this.params.fields = this.data.partner.fields; - var model = createModel({ - Model: BasicModel, - data: this.data, - }); + this.params.res_id = 1; + this.params.fieldNames = ['foobool', 'foobool2']; + this.params.fields = this.data.partner.fields; + var model = createModel({ + Model: BasicModel, + data: this.data, + }); - model.load(this.params).then(function (resultID) { + var resultID = await model.load(this.params); var record = model.get(resultID); - model.notifyChanges(resultID, {foobool2: false}); + await model.notifyChanges(resultID, { foobool2: false }); record = model.get(resultID); assert.strictEqual(record.data.foobool2, false, "foobool2 field should be false"); - model.notifyChanges(resultID, {foobool: true}); + await model.notifyChanges(resultID, { foobool: true }); record = model.get(resultID); assert.strictEqual(record.data.foobool2, true, "foobool2 field should be true"); + model.destroy(); }); - model.destroy(); }); -});}); +}); diff --git a/addons/web/static/tests/views/calendar_mobile_tests.js b/addons/web/static/tests/views/calendar_mobile_tests.js index 969476b0081..60b5ed5f85f 100644 --- a/addons/web/static/tests/views/calendar_mobile_tests.js +++ b/addons/web/static/tests/views/calendar_mobile_tests.js @@ -4,7 +4,7 @@ odoo.define('web.calendar_mobile_tests', function (require) { var CalendarView = require('web.CalendarView'); var testUtils = require('web.test_utils'); -var createAsyncView = testUtils.createAsyncView; +var createView = testUtils.createView; var initialDate = new Date(2016, 11, 12, 8, 0, 0); initialDate = new Date(initialDate.getTime() - initialDate.getTimezoneOffset()*60*1000); @@ -24,7 +24,7 @@ QUnit.module('Views', { {id: 1, name: "event 1", start: "2016-12-11 00:00:00", stop: "2016-12-11 00:00:00"}, ], check_access_rights: function () { - return $.when(true); + return Promise.resolve(true); } }, }; @@ -37,7 +37,7 @@ QUnit.module('Views', { assert.expect(3); var done = assert.async(); - createAsyncView({ + createView({ View: CalendarView, model: 'event', data: this.data, diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index 9e0e7dc1e84..8061df08af4 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -7,7 +7,6 @@ var Dialog = require('web.Dialog'); var fieldUtils = require('web.field_utils'); var mixins = require('web.mixins'); var testUtils = require('web.test_utils'); -var time = require('web.time'); var session = require('web.session'); var createActionManager = testUtils.createActionManager; @@ -23,8 +22,7 @@ CalendarRenderer.include({ }); -var createView = testUtils.createView; -var createAsyncView = testUtils.createAsyncView; +var createCalendarView = testUtils.createCalendarView; var initialDate = new Date(2016, 11, 12, 8, 0, 0); initialDate = new Date(initialDate.getTime() - initialDate.getTimezoneOffset()*60*1000); @@ -57,7 +55,7 @@ QUnit.module('Views', { {id: 6, user_id: session.uid, partner_id: 1, name: "event 6", start: "2016-12-18 08:00:00", stop: "2016-12-18 09:00:00", allday: false, partner_ids: [3], type: 3} ], check_access_rights: function () { - return $.when(true); + return Promise.resolve(true); } }, user: { @@ -98,7 +96,7 @@ QUnit.module('Views', { ] }, }; - } + }, }, function () { QUnit.module('CalendarView'); @@ -125,9 +123,8 @@ QUnit.module('Views', { '</form>', }; - QUnit.test('simple calendar rendering', function (assert) { - assert.expect(25); - var done = assert.async(); + QUnit.test('simple calendar rendering', async function (assert) { + assert.expect(24); this.data.event.records.push({ id: 7, @@ -141,7 +138,7 @@ QUnit.module('Views', { type: 1 }); - createAsyncView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -159,73 +156,72 @@ QUnit.module('Views', { '<field name="partner_ids" write_model="filter_partner" write_field="partner_id"/>'+ '</calendar>', archs: archs, - viewOptions: { initialDate: initialDate, }, - }).then(function (calendar) { - - assert.ok(calendar.$('.o_calendar_view').find('.fc-view-container').length, "should instance of fullcalendar"); - - var $sidebar = calendar.$('.o_calendar_sidebar'); - - assert.strictEqual($sidebar.find('.ui-state-active').text(), "12", "should highlight the target day"); - - // test view scales - - assert.containsN(calendar, '.fc-event', 9, "should display 9 events on the week (4 event + 5 days event)"); - assert.strictEqual($sidebar.find('.o_selected_range').length, 7, "week scale should highlight 7 days in mini calendar"); - - calendar.$buttons.find('.o_calendar_button_day').trigger('click'); // display only one day - assert.containsN(calendar, '.fc-event', 2, "should display 2 events on the day"); - assert.strictEqual($sidebar.find('.o_selected_range').length, 1, "should highlight the target day in mini calendar"); - - calendar.$buttons.find('.o_calendar_button_month').trigger('click'); // display all the month - assert.containsN(calendar, '.fc-event', 7, "should display 7 events on the month (5 events + 2 week event - 1 'event 6' is filtered + 1 'Undefined event')"); - assert.strictEqual($sidebar.find('.o_selected_range').length, 31, "month scale should highlight all days in mini calendar"); - - // test filters - - assert.strictEqual($sidebar.find('.o_calendar_filter').length, 2, "should display 3 filters"); - - var $typeFilter = $sidebar.find('.o_calendar_filter:has(h3:contains(user))'); - assert.ok($typeFilter.length, "should display 'user' filter"); - assert.strictEqual($typeFilter.find('.o_calendar_filter_item').length, 3, "should display 3 filter items for 'user'"); + }); - // filters which has no value should show with string "Undefined", should not have any user image and should show at the last - assert.strictEqual($typeFilter.find('.o_calendar_filter_item:last').data('value'), false, "filters having false value should be displayed at last in filter items"); - assert.strictEqual($typeFilter.find('.o_calendar_filter_item:last span').text(), "Undefined", "filters having false value should display 'Undefined' string"); - assert.strictEqual($typeFilter.find('.o_calendar_filter_item:last label img').length, 0, "filters having false value should not have any user image"); + assert.ok(calendar.$('.o_calendar_view').find('.fc-view-container').length, + "should instance of fullcalendar"); - var $attendeesFilter = $sidebar.find('.o_calendar_filter:has(h3:contains(attendees))'); - assert.ok($attendeesFilter.length, "should display 'attendees' filter"); - assert.strictEqual($attendeesFilter.find('.o_calendar_filter_item').length, 3, "should display 3 filter items for 'attendees' who use write_model (2 saved + Everything)"); - assert.ok($attendeesFilter.find('.o_field_many2one').length, "should display one2many search bar for 'attendees' filter"); + var $sidebar = calendar.$('.o_calendar_sidebar'); - assert.containsN(calendar, '.fc-event', 7, - "should display 7 events ('event 5' counts for 2 because it spans two weeks and thus generate two fc-event elements)"); - testUtils.dom.click(calendar.$('.o_calendar_filter .custom-checkbox input').first()); - assert.containsN(calendar, '.fc-event', 4, "should now only display 4 event"); - testUtils.dom.click(calendar.$('.o_calendar_filter .custom-checkbox input').eq(1)); - assert.containsNone(calendar, '.fc-event', "should not display any event anymore"); + // test view scales + assert.containsN(calendar, '.fc-event', 9, + "should display 9 events on the week (4 event + 5 days event)"); + assert.containsN($sidebar, '.o_selected_range', 7, + "week scale should highlight 7 days in mini calendar"); - // test search bar in filter - $sidebar.find('input[type="text"]').trigger('click'); - assert.strictEqual($('ul.ui-autocomplete li:not(.o_m2o_dropdown_option)').length, 2, "should display 2 choices in one2many autocomplete"); // TODO: remove :not(.o_m2o_dropdown_option) because can't have "create & edit" choice - $('ul.ui-autocomplete li:first').trigger('click'); - assert.strictEqual($sidebar.find('.o_calendar_filter:has(h3:contains(attendees)) .o_calendar_filter_item').length, 4, "should display 4 filter items for 'attendees'"); - $sidebar.find('input[type="text"]').trigger('click'); - assert.strictEqual($('ul.ui-autocomplete li:not(.o_m2o_dropdown_option)').text(), "partner 4", "should display the last choice in one2many autocomplete"); // TODO: remove :not(.o_m2o_dropdown_option) because can't have "create & edit" choice - $sidebar.find('.o_calendar_filter_item .o_remove').first().trigger('click'); - assert.ok($('.modal-footer button.btn:contains(Ok)').length, "should display the confirm message"); - $('.modal-footer button.btn:contains(Ok)').trigger('click'); - assert.strictEqual($sidebar.find('.o_calendar_filter:has(h3:contains(attendees)) .o_calendar_filter_item').length, 3, "click on remove then should display 3 filter items for 'attendees'"); - calendar.destroy(); - done(); - }); + await testUtils.dom.click(calendar.$buttons.find('.o_calendar_button_day')); // display only one day + assert.containsN(calendar, '.fc-event', 2, "should display 2 events on the day"); + assert.containsOnce($sidebar, '.o_selected_range', + "should highlight the target day in mini calendar"); + + await testUtils.dom.click(calendar.$buttons.find('.o_calendar_button_month')); // display all the month + assert.containsN(calendar, '.fc-event', 7, + "should display 7 events on the month (5 events + 2 week event - 1 'event 6' is filtered + 1 'Undefined event')"); + assert.containsN($sidebar, '.o_selected_range', 31, + "month scale should highlight all days in mini calendar"); + + // test filters + assert.containsN($sidebar, '.o_calendar_filter', 2, "should display 2 filters"); + + var $typeFilter = $sidebar.find('.o_calendar_filter:has(h3:contains(user))'); + assert.ok($typeFilter.length, "should display 'user' filter"); + assert.containsN($typeFilter, '.o_calendar_filter_item', 3, "should display 3 filter items for 'user'"); + + // filters which has no value should show with string "Undefined", should not have any user image and should show at the last + assert.strictEqual($typeFilter.find('.o_calendar_filter_item:last').data('value'), false, "filters having false value should be displayed at last in filter items"); + assert.strictEqual($typeFilter.find('.o_calendar_filter_item:last span').text(), "Undefined", "filters having false value should display 'Undefined' string"); + assert.strictEqual($typeFilter.find('.o_calendar_filter_item:last label img').length, 0, "filters having false value should not have any user image"); + + var $attendeesFilter = $sidebar.find('.o_calendar_filter:has(h3:contains(attendees))'); + assert.ok($attendeesFilter.length, "should display 'attendees' filter"); + assert.containsN($attendeesFilter, '.o_calendar_filter_item', 3, "should display 3 filter items for 'attendees' who use write_model (2 saved + Everything)"); + assert.ok($attendeesFilter.find('.o_field_many2one').length, "should display one2many search bar for 'attendees' filter"); + + assert.containsN(calendar, '.fc-event', 7, + "should display 7 events ('event 5' counts for 2 because it spans two weeks and thus generate two fc-event elements)"); + await testUtils.dom.click(calendar.$('.o_calendar_filter .custom-checkbox input').first()); + assert.containsN(calendar, '.fc-event', 4, "should now only display 4 event"); + await testUtils.dom.click(calendar.$('.o_calendar_filter .custom-checkbox input').eq(1)); + assert.containsNone(calendar, '.fc-event', "should not display any event anymore"); + + // test search bar in filter + await testUtils.dom.click($sidebar.find('input[type="text"]')); + assert.strictEqual($('ul.ui-autocomplete li:not(.o_m2o_dropdown_option)').length, 2,"should display 2 choices in one2many autocomplete"); // TODO: remove :not(.o_m2o_dropdown_option) because can't have "create & edit" choice + await testUtils.dom.click($('ul.ui-autocomplete li:first')); + assert.containsN($sidebar, '.o_calendar_filter:has(h3:contains(attendees)) .o_calendar_filter_item', 4, "should display 4 filter items for 'attendees'"); + await testUtils.dom.click($sidebar.find('input[type="text"]')); + assert.strictEqual($('ul.ui-autocomplete li:not(.o_m2o_dropdown_option)').text(), "partner 4", "should display the last choice in one2many autocomplete"); // TODO: remove :not(.o_m2o_dropdown_option) because can't have "create & edit" choice + await testUtils.dom.click($sidebar.find('.o_calendar_filter_item .o_remove').first(), {allowInvisible: true}); + assert.ok($('.modal-footer button.btn:contains(Ok)').length, "should display the confirm message"); + await testUtils.dom.click($('.modal-footer button.btn:contains(Ok)')); + assert.containsN($sidebar, '.o_calendar_filter:has(h3:contains(attendees)) .o_calendar_filter_item', 3, "click on remove then should display 3 filter items for 'attendees'"); + calendar.destroy(); }); - QUnit.test('breadcrumbs are updated with the displayed period', function (assert) { + QUnit.test('breadcrumbs are updated with the displayed period', async function (assert) { assert.expect(3); var archs = { @@ -246,35 +242,34 @@ QUnit.module('Views', { views: [[1, 'calendar']], }]; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: actions, archs: archs, data: this.data, }); - actionManager.doAction(1); - + await actionManager.doAction(1); // displays month mode by default assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Meetings Test (Dec 11 – 17, 2016)', "should display the current week"); // switch to day mode - testUtils.dom.click($('.o_control_panel .o_calendar_button_day')); + await testUtils.dom.click($('.o_control_panel .o_calendar_button_day')); assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Meetings Test (December 12, 2016)', "should display the current day"); // switch to month mode - testUtils.dom.click($('.o_control_panel .o_calendar_button_month')); + await testUtils.dom.click($('.o_control_panel .o_calendar_button_month')); assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Meetings Test (December 2016)', "should display the current month"); actionManager.destroy(); }); - QUnit.test('create and change events', function (assert) { + QUnit.test('create and change events', async function (assert) { assert.expect(26); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -299,19 +294,19 @@ QUnit.module('Views', { // click on an existing event to open the formViewDialog - calendar.$('.fc-event:contains(event 4) .fc-content').trigger('click'); + await testUtils.dom.click(calendar.$('.fc-event:contains(event 4) .fc-content')); assert.ok($('.modal-body').length, "should open the form view in dialog when click on event"); assert.ok($('.modal-footer button.btn:contains(Edit)').length, "formViewDialog should be in readonly mode"); assert.ok($('.modal-footer button.btn:contains(Delete)').length, "formViewDialog should have a delete button"); - $('.modal-footer button.btn:contains(Edit)').trigger('click'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Edit)')); assert.ok($('.modal-body').length, "should switch the modal in edit mode"); assert.notOk($('.modal-footer button.btn:contains(Delete)').length, "formViewDialog should not have a delete button in edit mode"); - testUtils.fields.editInput($('.modal-body input:first'), 'event 4 modified'); - $('.modal-footer button.btn:contains(Save)').trigger('click'); + await testUtils.fields.editInput($('.modal-body input:first'), 'event 4 modified'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Save)')); assert.notOk($('.modal-body').length, "save button should close the modal"); assert.ok(calendar.$('.fc-event:contains(event 4 modified)').length, "should display the updated records"); @@ -322,11 +317,12 @@ QUnit.module('Views', { testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); + await testUtils.nextTick(); assert.ok($('.modal-sm').length, "should open the quick create dialog"); - testUtils.fields.editInput($('.modal-body input:first'), 'new event in quick create'); - $('.modal-footer button.btn:contains(Create)').trigger('click').trigger('click'); + await testUtils.fields.editInput($('.modal-body input:first'), 'new event in quick create'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Create)')); assert.strictEqual(calendar.$('.fc-event:contains(new event in quick create)').length, 1, "should display the new record after quick create"); assert.containsN(calendar, 'td.fc-event-container[colspan]', 2, "should the new record have only one day"); @@ -335,15 +331,19 @@ QUnit.module('Views', { testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); + await testUtils.nextTick(); assert.ok($('.modal-sm').length, "should open the quick create dialog"); + await testUtils.fields.editInput($('.modal-body input:first'), + 'new event in quick create validated by pressing enter key.'); $('.modal-body input:first') .val('new event in quick create validated by pressing enter key.') .trigger($.Event('keyup', {keyCode: $.ui.keyCode.ENTER})) .trigger($.Event('keyup', {keyCode: $.ui.keyCode.ENTER})); - - assert.strictEqual(calendar.$('.fc-event:contains(new event in quick create validated by pressing enter key.)').length, 1, "should display the new record by pressing enter key"); + await testUtils.nextTick(); + assert.containsOnce(calendar, '.fc-event:contains(new event in quick create validated by pressing enter key.)', + "should display the new record by pressing enter key"); // create a new event and edit it @@ -352,11 +352,12 @@ QUnit.module('Views', { testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); + await testUtils.nextTick(); assert.strictEqual($('.modal-sm').length, 1, "should open the quick create dialog"); testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); - $('.modal-footer button.btn:contains(Edit)').trigger('click'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Edit)')); assert.strictEqual($('.modal-lg .o_form_view').length, 1, "should open the slow create dialog"); assert.strictEqual($('.modal-lg .modal-title').text(), "Create: Events", @@ -364,9 +365,10 @@ QUnit.module('Views', { assert.strictEqual($('.modal-lg .o_form_view input[name="name"]').val(), "coucou", "should have set the name from the quick create dialog"); - $('.modal-lg button.btn:contains(Save)').trigger('click'); + await testUtils.dom.click($('.modal-lg button.btn:contains(Save)')); - assert.strictEqual(calendar.$('.fc-event:contains(coucou)').length, 1, "should display the new record with string attribute"); + assert.strictEqual(calendar.$('.fc-event:contains(coucou)').length, 1, + "should display the new record with string attribute"); // create a new event with 2 days @@ -375,41 +377,44 @@ QUnit.module('Views', { testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell.next(), "mousemove"); testUtils.dom.triggerMouseEvent($cell.next(), "mouseup"); + await testUtils.nextTick(); testUtils.fields.editInput($('.modal-dialog input:first'), 'new event in quick create 2'); - $('.modal-footer button.btn:contains(Edit)').trigger('click'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Edit)')); - assert.strictEqual($('.modal-lg input:first').val(), 'new event in quick create 2', "should open the formViewDialog with default values"); + assert.strictEqual($('.modal-lg input:first').val(),'new event in quick create 2', + "should open the formViewDialog with default values"); - $('.modal-lg button.btn:contains(Save)').trigger('click'); + await testUtils.dom.click($('.modal-lg button.btn:contains(Save)')); assert.notOk($('.modal').length, "should close dialogs"); var $newevent2 = calendar.$('.fc-event:contains(new event in quick create 2)'); assert.ok($newevent2.length, "should display the 2 days new record"); - assert.hasAttrValue($newevent2.closest('.fc-event-container'), 'colspan', "2", "the new record should have 2 days"); + assert.hasAttrValue($newevent2.closest('.fc-event-container'), + 'colspan', "2","the new record should have 2 days"); // delete the a record - calendar.$('.fc-event:contains(event 4) .fc-content').trigger('click'); - $('.modal-footer button.btn:contains(Delete)').trigger('click'); + await testUtils.dom.click(calendar.$('.fc-event:contains(event 4) .fc-content')); + await testUtils.dom.click($('.modal-footer button.btn:contains(Delete)')); assert.ok($('.modal-footer button.btn:contains(Ok)').length, "should display the confirm message"); - $('.modal-footer button.btn:contains(Ok)').trigger('click'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Ok)')); assert.notOk(calendar.$('.fc-event:contains(event 4) .fc-content').length, "the record should be deleted"); assert.containsN(calendar, '.fc-event-container .fc-event', 10, "should display 10 events"); // move to next month - testUtils.dom.click(calendar.$buttons.find('.o_calendar_button_next')); + await testUtils.dom.click(calendar.$buttons.find('.o_calendar_button_next')); assert.containsNone(calendar, '.fc-event-container .fc-event', "should display 0 events"); calendar.destroy(); }); - QUnit.test('quickcreate switching to actual create for required fields', function (assert) { + QUnit.test('quickcreate switching to actual create for required fields', async function (assert) { assert.expect(4); var event = $.Event(); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -430,11 +435,14 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (args.method === "create") { - return $.Deferred().reject({ - code: 200, - data: {}, - message: "Odoo server error", - }, event); + return Promise.reject({ + message: { + code: 200, + data: {}, + message: "Odoo server error", + }, + event: event + }); } return this._super(route, args); }, @@ -444,12 +452,14 @@ QUnit.module('Views', { var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)'); testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); + await testUtils.nextTick(); assert.strictEqual($('.modal-sm .modal-title').text(), 'Create: Events', "should open the quick create dialog"); - testUtils.fields.editInput($('.modal-body input:first'), 'new event in quick create'); - $('.modal-footer button.btn:contains(Create)').trigger('click').trigger('click'); + await testUtils.fields.editInput($('.modal-body input:first'), 'new event in quick create'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Create)')); + await testUtils.nextTick(); // If the event is not default-prevented, a traceback will be raised, which we do not want assert.ok(event.isDefaultPrevented(), "fail deferred event should have been default-prevented"); @@ -462,12 +472,12 @@ QUnit.module('Views', { calendar.destroy(); }); - QUnit.test('create event with timezone in week mode European locale', function (assert) { + QUnit.test('create event with timezone in week mode European locale', async function (assert) { assert.expect(5); this.data.event.records = []; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -507,11 +517,7 @@ QUnit.module('Views', { } return this._super(route, args); }, - }); - - - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position + }, {positionalClicks: true}); var top = calendar.$('.fc-axis:contains(8:00)').offset().top + 5; var left = calendar.$('.fc-day:eq(2)').offset().left + 5; @@ -520,7 +526,6 @@ QUnit.module('Views', { testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } @@ -529,9 +534,10 @@ QUnit.module('Views', { assert.strictEqual(calendar.$('.fc-content .fc-time').text(), "08:00 - 10:00", "should display the time in the calendar sticker"); - testUtils.dom.triggerPositionalMouseEvent(left, top + 60, "mouseup"); - testUtils.fields.editInput($('.modal input:first'), 'new event'); - $('.modal button.btn:contains(Create)').trigger('click'); + await testUtils.dom.triggerPositionalMouseEvent(left, top + 60, "mouseup"); + await testUtils.nextTick(); + await testUtils.fields.editInput($('.modal input:first'), 'new event'); + await testUtils.dom.click($('.modal button.btn:contains(Create)')); var $newevent = calendar.$('.fc-event:contains(new event)'); assert.strictEqual($newevent.text().replace(/[\s\n\r]+/g, ''), "08:00-10:00newevent12/13/201608:00:00False", @@ -550,13 +556,12 @@ QUnit.module('Views', { // delete record - $newevent.trigger('click'); - $('.modal button.btn-secondary:contains(Delete)').trigger('click'); - $('.modal button.btn-primary:contains(Ok)').trigger('click'); + await testUtils.dom.click($newevent); + await testUtils.dom.click($('.modal button.btn-secondary:contains(Delete)')); + await testUtils.dom.click($('.modal button.btn-primary:contains(Ok)')); assert.containsNone(calendar, '.fc-content', "should delete the record"); calendar.destroy(); - $view.remove(); }); QUnit.test('default week start (US)', function (assert) { @@ -564,7 +569,7 @@ QUnit.module('Views', { assert.expect(1); var done = assert.async(); - createAsyncView({ + createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -592,7 +597,7 @@ QUnit.module('Views', { assert.expect(1); var done = assert.async(); - createAsyncView({ + createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -625,7 +630,7 @@ QUnit.module('Views', { assert.expect(1); var done = assert.async(); - createAsyncView({ + createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -651,7 +656,7 @@ QUnit.module('Views', { }); }); - QUnit.test('create event with timezone in week mode with formViewDialog European locale', function (assert) { + QUnit.test('create event with timezone in week mode with formViewDialog European locale', async function (assert) { assert.expect(8); this.data.event.records = []; @@ -667,7 +672,7 @@ QUnit.module('Views', { } }; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -709,10 +714,7 @@ QUnit.module('Views', { } return this._super(route, args); }, - }); - - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position + }, {positionalClicks: true}); var top = calendar.$('.fc-axis:contains(8:00)').offset().top + 5; var left = calendar.$('.fc-day:eq(2)').offset().left + 5; @@ -721,42 +723,42 @@ QUnit.module('Views', { testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } testUtils.dom.triggerPositionalMouseEvent(left, top + 60, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top + 60, "mouseup"); - testUtils.fields.editInput($('.modal input:first'), 'new event'); - $('.modal button.btn:contains(Edit)').trigger('click'); - - assert.strictEqual($('.o_field_widget[name="start"] input').val(), "12/13/2016 08:00:00", - "should display the datetime"); + await testUtils.nextTick(); + await testUtils.fields.editInput($('.modal input:first'), 'new event'); + await testUtils.dom.click($('.modal button.btn:contains(Edit)')); - $('.modal-lg .o_field_boolean[name="allday"] input').trigger('click'); + assert.strictEqual($('.o_field_widget[name="start"] input').val(), + "12/13/2016 08:00:00", "should display the datetime"); - assert.strictEqual($('.o_field_widget[name="start_date"] input').val(), "12/13/2016", - "should display the date"); + await testUtils.dom.click($('.modal-lg .o_field_boolean[name="allday"] input')); + await testUtils.nextTick(); + assert.strictEqual($('input[name="start_date"]').val(), + "12/13/2016", "should display the date"); - $('.modal-lg .o_field_boolean[name="allday"] input').trigger('click'); + await testUtils.dom.click($('.modal-lg .o_field_boolean[name="allday"] input')); - assert.strictEqual($('.o_field_widget[name="start"] input').val(), "12/13/2016 02:00:00", - "should display the datetime from the date with the timezone"); + assert.strictEqual($('.o_field_widget[name="start"] input').val(), + "12/13/2016 02:00:00", "should display the datetime from the date with the timezone"); // use datepicker to enter a date: 12/13/2016 08:00:00 testUtils.dom.openDatepicker($('.o_field_widget[name="start"].o_datepicker')); - $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]').trigger('click'); - $('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour').trigger('click'); - $('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(08)').trigger('click'); - $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]').trigger('click'); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(08)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]')); // use datepicker to enter a date: 12/13/2016 10:00:00 testUtils.dom.openDatepicker($('.o_field_widget[name="stop"].o_datepicker')); - $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]').trigger('click'); - $('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour').trigger('click'); - $('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(10)').trigger('click'); - $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]').trigger('click'); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(10)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]')); - $('.modal-lg button.btn:contains(Save)').trigger('click'); + await testUtils.dom.click($('.modal-lg button.btn:contains(Save)')); var $newevent = calendar.$('.fc-event:contains(new event)'); assert.strictEqual($newevent.text().replace(/[\s\n\r]+/g, ''), "08:00-10:00newevent", @@ -787,6 +789,7 @@ QUnit.module('Views', { left = calendar.$('.fc-day:eq(1)').offset().left + 5; testUtils.dom.triggerPositionalMouseEvent(left, top, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top, "mouseup"); + await testUtils.nextTick(); // Move to "All day" expectedEvent = { @@ -798,17 +801,17 @@ QUnit.module('Views', { top = calendar.$('.fc-day:eq(1)').offset().top + 5; testUtils.dom.triggerPositionalMouseEvent(left, top, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top, "mouseup"); + await testUtils.nextTick(); calendar.destroy(); - $view.remove(); }); - QUnit.test('create event with timezone in week mode American locale', function (assert) { + QUnit.test('create event with timezone in week mode American locale', async function (assert) { assert.expect(5); this.data.event.records = []; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -848,11 +851,7 @@ QUnit.module('Views', { } return this._super(route, args); }, - }); - - - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position + }, {positionalClicks: true}); var top = calendar.$('.fc-axis:contains(8am)').offset().top + 5; var left = calendar.$('.fc-day:eq(2)').offset().left + 5; @@ -861,7 +860,6 @@ QUnit.module('Views', { testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } @@ -871,8 +869,9 @@ QUnit.module('Views', { "should display the time in the calendar sticker"); testUtils.dom.triggerPositionalMouseEvent(left, top + 60, "mouseup"); + await testUtils.nextTick(); testUtils.fields.editInput($('.modal input:first'), 'new event'); - $('.modal button.btn:contains(Create)').trigger('click'); + await testUtils.dom.click($('.modal button.btn:contains(Create)')); var $newevent = calendar.$('.fc-event:contains(new event)'); assert.strictEqual($newevent.text().replace(/[\s\n\r]+/g, ''), "8:00am-10:00amnewevent12/13/201608:00:00False", @@ -891,16 +890,15 @@ QUnit.module('Views', { // delete record - $newevent.trigger('click'); - $('.modal button.btn-secondary:contains(Delete)').trigger('click'); - $('.modal button.btn-primary:contains(Ok)').trigger('click'); + await testUtils.dom.click($newevent); + await testUtils.dom.click($('.modal button.btn-secondary:contains(Delete)')); + await testUtils.dom.click($('.modal button.btn-primary:contains(Ok)')); assert.containsNone(calendar, '.fc-content', "should delete the record"); calendar.destroy(); - $view.remove(); }); - QUnit.test('create event with timezone in week mode with formViewDialog American locale', function (assert) { + QUnit.test('create event with timezone in week mode with formViewDialog American locale', async function (assert) { assert.expect(8); this.data.event.records = []; @@ -916,7 +914,7 @@ QUnit.module('Views', { } }; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -958,10 +956,7 @@ QUnit.module('Views', { } return this._super(route, args); }, - }); - - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position + }, {positionalClicks: true}); var top = calendar.$('.fc-axis:contains(8am)').offset().top + 5; var left = calendar.$('.fc-day:eq(2)').offset().left + 5; @@ -970,42 +965,42 @@ QUnit.module('Views', { testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } testUtils.dom.triggerPositionalMouseEvent(left, top + 60, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top + 60, "mouseup"); + await testUtils.nextTick(); testUtils.fields.editInput($('.modal input:first'), 'new event'); - $('.modal button.btn:contains(Edit)').trigger('click'); + await testUtils.dom.click($('.modal button.btn:contains(Edit)')); assert.strictEqual($('.o_field_widget[name="start"] input').val(), "12/13/2016 08:00:00", "should display the datetime"); - $('.modal-lg .o_field_boolean[name="allday"] input').trigger('click'); + await testUtils.dom.click($('.modal-lg .o_field_boolean[name="allday"] input')); assert.strictEqual($('.o_field_widget[name="start_date"] input').val(), "12/13/2016", "should display the date"); - $('.modal-lg .o_field_boolean[name="allday"] input').trigger('click'); + await testUtils.dom.click($('.modal-lg .o_field_boolean[name="allday"] input')); assert.strictEqual($('.o_field_widget[name="start"] input').val(), "12/13/2016 02:00:00", "should display the datetime from the date with the timezone"); // use datepicker to enter a date: 12/13/2016 08:00:00 testUtils.dom.openDatepicker($('.o_field_widget[name="start"].o_datepicker')); - $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]').trigger('click'); - $('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour').trigger('click'); - $('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(08)').trigger('click'); - $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]').trigger('click'); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(08)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]')); // use datepicker to enter a date: 12/13/2016 10:00:00 testUtils.dom.openDatepicker($('.o_field_widget[name="stop"].o_datepicker')); - $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]').trigger('click'); - $('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour').trigger('click'); - $('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(10)').trigger('click'); - $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]').trigger('click'); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(10)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]')); - $('.modal-lg button.btn:contains(Save)').trigger('click'); + await testUtils.dom.click($('.modal-lg button.btn:contains(Save)')); var $newevent = calendar.$('.fc-event:contains(new event)'); assert.strictEqual($newevent.text().replace(/[\s\n\r]+/g, ''), "8:00am-10:00amnewevent", @@ -1036,6 +1031,7 @@ QUnit.module('Views', { left = calendar.$('.fc-day:eq(1)').offset().left + 5; testUtils.dom.triggerPositionalMouseEvent(left, top, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top, "mouseup"); + await testUtils.nextTick(); // Move to "All day" expectedEvent = { @@ -1047,15 +1043,15 @@ QUnit.module('Views', { top = calendar.$('.fc-day:eq(1)').offset().top + 5; testUtils.dom.triggerPositionalMouseEvent(left, top, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top, "mouseup"); + await testUtils.nextTick(); calendar.destroy(); - $view.remove(); }); - QUnit.test('check calendar week column timeformat and event content timeformat', function (assert) { + QUnit.test('check calendar week column timeformat and event content timeformat', async function (assert) { assert.expect(2); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1079,12 +1075,12 @@ QUnit.module('Views', { calendar.destroy(); }); - QUnit.test('create all day event in week mode', function (assert) { + QUnit.test('create all day event in week mode', async function (assert) { assert.expect(3); this.data.event.records = []; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1107,26 +1103,22 @@ QUnit.module('Views', { return 120; }, }, - }); - - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position - + }, {positionalClicks: true}); var pos = calendar.$('.fc-bg td:eq(4)').offset(); try { testUtils.dom.triggerPositionalMouseEvent(pos.left+15, pos.top+15, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } pos = calendar.$('.fc-bg td:eq(5)').offset(); testUtils.dom.triggerPositionalMouseEvent(pos.left+15, pos.top+15, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(pos.left+15, pos.top+15, "mouseup"); + await testUtils.nextTick(); testUtils.fields.editInput($('.modal input:first'), 'new event'); - $('.modal button.btn:contains(Create)').trigger('click'); + await testUtils.dom.click($('.modal button.btn:contains(Create)')); var $newevent = calendar.$('.fc-event:contains(new event)'); assert.strictEqual($newevent.text().replace(/[\s\n\r]+/g, ''), "newevent", @@ -1146,15 +1138,14 @@ QUnit.module('Views', { "the new record should have the utc datetime (quickCreate)"); calendar.destroy(); - $view.remove(); }); - QUnit.test('create all day event in week mode (no quickCreate)', function (assert) { + QUnit.test('create all day event in week mode (no quickCreate)', async function (assert) { assert.expect(1); this.data.event.records = []; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1186,18 +1177,13 @@ QUnit.module('Views', { "should send the correct data to create events"); }, }, - }); - - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position - + }, {positionalClicks: true}); var pos = calendar.$('.fc-bg td:eq(4)').offset(); try { testUtils.dom.triggerPositionalMouseEvent(pos.left+15, pos.top+15, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } pos = calendar.$('.fc-bg td:eq(5)').offset(); @@ -1205,15 +1191,14 @@ QUnit.module('Views', { testUtils.dom.triggerPositionalMouseEvent(pos.left+15, pos.top+15, "mouseup"); calendar.destroy(); - $view.remove(); }); - QUnit.test('create event in month mode', function (assert) { + QUnit.test('create event in month mode', async function (assert) { assert.expect(4); this.data.event.records = []; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1246,25 +1231,22 @@ QUnit.module('Views', { } return this._super(route, args); }, - }); - - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position + }, {positionalClicks: true}); var pos = calendar.$('.fc-bg td:eq(20)').offset(); try { testUtils.dom.triggerPositionalMouseEvent(pos.left+15, pos.top+15, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } pos = calendar.$('.fc-bg td:eq(21)').offset(); testUtils.dom.triggerPositionalMouseEvent(pos.left+15, pos.top+15, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(pos.left+15, pos.top+15, "mouseup"); + await testUtils.nextTick(); testUtils.fields.editInput($('.modal input:first'), 'new event'); - $('.modal button.btn:contains(Create)').trigger('click'); + await testUtils.dom.click($('.modal button.btn:contains(Create)')); var $newevent = calendar.$('.fc-event:contains(new event)'); assert.strictEqual($newevent.text().replace(/[\s\n\r]+/g, ''), "newevent", @@ -1281,13 +1263,12 @@ QUnit.module('Views', { }, "the new record should have the utc datetime (quickCreate)"); calendar.destroy(); - $view.remove(); }); - QUnit.test('use mini calendar', function (assert) { + QUnit.test('use mini calendar', async function (assert) { assert.expect(12); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1314,36 +1295,36 @@ QUnit.module('Views', { assert.containsOnce(calendar, '.fc-agendaWeek-view', "should be in week mode"); assert.containsN(calendar, '.fc-event', 9, "should display 9 events on the week (4 event + 5 days event)"); - testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(19)')); + await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(19)')); // Clicking on a day in another week should switch to the other week view assert.containsOnce(calendar, '.fc-agendaWeek-view', "should be in week mode"); assert.containsN(calendar, '.fc-event', 4, "should display 4 events on the week (1 event + 3 days event)"); // Clicking on a day in the same week should switch to that particular day view - testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); + await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); assert.containsOnce(calendar, '.fc-agendaDay-view', "should be in day mode"); assert.containsN(calendar, '.fc-event', 2, "should display 2 events on the day"); // Clicking on the same day should toggle between day, month and week views - testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); + await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); assert.containsOnce(calendar, '.fc-month-view', "should be in month mode"); assert.containsN(calendar, '.fc-event', 7, "should display 7 events on the month (event 5 is on multiple weeks and generates to .fc-event)"); - testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); + await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); assert.containsOnce(calendar, '.fc-agendaWeek-view', "should be in week mode"); assert.containsN(calendar, '.fc-event', 4, "should display 4 events on the week (1 event + 3 days event)"); - testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); + await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); assert.containsOnce(calendar, '.fc-agendaDay-view', "should be in day mode"); assert.containsN(calendar, '.fc-event', 2, "should display 2 events on the day"); calendar.destroy(); }); - QUnit.test('rendering, with many2many', function (assert) { + QUnit.test('rendering, with many2many', async function (assert) { assert.expect(5); this.data.event.fields.partner_ids.type = 'many2many'; this.data.event.records[0].partner_ids = [1,2,3,4,5]; this.data.partner.records.push({id: 5, display_name: "partner 5", image: 'EEE'}); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1365,21 +1346,21 @@ QUnit.module('Views', { "should have 3 avatars in the side bar"); var $event1Avatars = calendar.$('.fc-event .o_calendar_avatars').first(); - assert.strictEqual($event1Avatars.find('img').length, 1, "should have 1 avatar"); - assert.strictEqual($event1Avatars.find('span').length, 0, + assert.containsOnce($event1Avatars, 'img', "should have 1 avatar"); + assert.containsNone($event1Avatars, 'span', "should not have a span for more attendees since there is only one"); var $event2Avatars = calendar.$('.fc-event:contains(All day) .o_calendar_avatars'); - assert.strictEqual($event2Avatars.find('img').length, 3, "should have 3 avatars"); + assert.containsN($event2Avatars, 'img', 3, "should have 3 avatars"); assert.strictEqual($event2Avatars.find('span').text(), '+2', "should indicate there are 2 more attendees that we don't show"); calendar.destroy(); }); - QUnit.test('open form view', function (assert) { + QUnit.test('open form view', async function (assert) { assert.expect(3); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1399,7 +1380,7 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (args.method === "get_formview_id") { - return $.Deferred().resolve('A view'); + return Promise.resolve('A view'); } return this._super(route, args); }, @@ -1419,13 +1400,14 @@ QUnit.module('Views', { }, "should open the form view"); }); - calendar.$('.fc-event:contains(event 4) .fc-content').trigger('click'); + await testUtils.dom.click(calendar.$('.fc-event:contains(event 4) .fc-content')); // create a new event and edit it var $cell = calendar.$('.fc-day-grid .fc-row:eq(4) .fc-day:eq(2)'); testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); + await testUtils.nextTick(); testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); testUtils.mock.intercept(calendar, 'do_action', function (event) { @@ -1445,17 +1427,17 @@ QUnit.module('Views', { "should open the form view with the context default values"); }); - $('.modal button.btn:contains(Edit)').trigger('click'); + testUtils.dom.click($('.modal button.btn:contains(Edit)')); calendar.destroy(); assert.strictEqual($('#ui-datepicker-div:empty').length, 0, "should have a clean body"); }); - QUnit.test('create and edit event in month mode (all_day: false)', function (assert) { + QUnit.test('create and edit event in month mode (all_day: false)', async function (assert) { assert.expect(2); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1483,7 +1465,8 @@ QUnit.module('Views', { var $cell = calendar.$('.fc-day-grid .fc-row:eq(4) .fc-day:eq(2)'); testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); - testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); + await testUtils.nextTick(); + await testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); testUtils.mock.intercept(calendar, 'do_action', function (event) { assert.deepEqual(event.data.action, @@ -1501,18 +1484,18 @@ QUnit.module('Views', { "should open the form view with the context default values"); }); - $('.modal button.btn:contains(Edit)').trigger('click'); + await testUtils.dom.click($('.modal button.btn:contains(Edit)')); calendar.destroy(); assert.strictEqual($('#ui-datepicker-div:empty').length, 0, "should have a clean body"); }); - QUnit.test('readonly date_start field', function (assert) { + QUnit.test('readonly date_start field', async function (assert) { assert.expect(4); this.data.event.fields.start.readonly = true; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1532,7 +1515,7 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (args.method === "get_formview_id") { - return $.when(false); + return Promise.resolve(false); } return this._super(route, args); }, @@ -1554,14 +1537,15 @@ QUnit.module('Views', { }, "should open the form view"); }); - calendar.$('.fc-event:contains(event 4) .fc-content').trigger('click'); + await testUtils.dom.click(calendar.$('.fc-event:contains(event 4) .fc-content')); // create a new event and edit it var $cell = calendar.$('.fc-day-grid .fc-row:eq(4) .fc-day:eq(2)'); testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); - testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); + await testUtils.nextTick(); + await testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); testUtils.mock.intercept(calendar, 'do_action', function (event) { assert.deepEqual(event.data.action, @@ -1580,14 +1564,14 @@ QUnit.module('Views', { "should open the form view with the context default values"); }); - $('.modal button.btn:contains(Edit)').trigger('click'); + await testUtils.dom.click($('.modal button.btn:contains(Edit)')); calendar.destroy(); assert.strictEqual($('#ui-datepicker-div:empty').length, 0, "should have a clean body"); }); - QUnit.test('"all" filter', function (assert) { + QUnit.test('"all" filter', async function (assert) { assert.expect(6); var interval = [ @@ -1603,7 +1587,7 @@ QUnit.module('Views', { var i = 0; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1636,26 +1620,26 @@ QUnit.module('Views', { "should display 9 events on the week"); // Select the events only associated with partner 2 - testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-id=2] input')); + await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-id=2] input')); assert.containsN(calendar, '.fc-event', 4, "should display 4 events on the week"); // Click on the 'all' filter to reload all events - testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input')); + await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input')); assert.containsN(calendar, '.fc-event', 9, "should display 9 events on the week"); calendar.destroy(); }); - QUnit.test('create event with filters', function (assert) { + QUnit.test('create event with filters', async function (assert) { assert.expect(7); this.data.event.fields.user_id.default = 5; this.data.event.fields.partner_id.default = 3; this.data.user.records.push({id: 5, display_name: "user 5", partner_id: 3}); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1675,34 +1659,32 @@ QUnit.module('Views', { viewOptions: { initialDate: initialDate, }, - }); - - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position + }, {positionalClicks: true}); - testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); + await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); assert.containsN(calendar, '.o_calendar_filter_item', 5, "should display 5 filter items"); assert.containsN(calendar, '.fc-event', 3, "should display 3 events"); // quick create a record var left = calendar.$('.fc-bg td:eq(4)').offset().left+15; - var top = calendar.$('.fc-slats tr:eq(4) td:first').offset().top+15; + var top = calendar.$('.fc-slats tr:eq(12) td:first').offset().top+15; try { testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } testUtils.dom.triggerPositionalMouseEvent(left, top + 200, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top + 200, "mouseup"); + await testUtils.nextTick(); - testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); - $('.modal-footer button.btn:contains(Create)').trigger('click'); + await testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Create)')); assert.containsN(calendar, '.o_calendar_filter_item', 6, "should add the missing filter (active)"); assert.containsN(calendar, '.fc-event', 4, "should display the created item"); + await testUtils.nextTick(); // change default value for quick create an hide record this.data.event.fields.user_id.default = 4; @@ -1710,26 +1692,26 @@ QUnit.module('Views', { // quick create and other record left = calendar.$('.fc-bg td:eq(3)').offset().left+15; - top = calendar.$('.fc-slats tr:eq(4) td:first').offset().top+15; + top = calendar.$('.fc-slats tr:eq(12) td:first').offset().top+15; testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown"); testUtils.dom.triggerPositionalMouseEvent(left, top + 200, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top + 200, "mouseup"); + await testUtils.nextTick(); testUtils.fields.editInput($('.modal-body input:first'), 'coucou 2'); - $('.modal-footer button.btn:contains(Create)').trigger('click'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Create)')); assert.containsN(calendar, '.o_calendar_filter_item', 6, "should have the same filters"); assert.containsN(calendar, '.fc-event', 4, "should not display the created item"); - testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); + await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); assert.containsN(calendar, '.fc-event', 11, "should display all records"); calendar.destroy(); - $view.remove(); }); - QUnit.test('create event with filters (no quickCreate)', function (assert) { + QUnit.test('create event with filters (no quickCreate)', async function (assert) { assert.expect(4); this.data.event.fields.user_id.default = 5; @@ -1740,7 +1722,7 @@ QUnit.module('Views', { partner_id: 3 }); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1772,42 +1754,39 @@ QUnit.module('Views', { viewOptions: { initialDate: initialDate, }, - }); + }, {positionalClicks: true}); - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); // => select with click position - - testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); + await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); assert.containsN(calendar, '.o_calendar_filter_item', 5, "should display 5 filter items"); assert.containsN(calendar, '.fc-event', 3, "should display 3 events"); + await testUtils.nextTick(); // quick create a record var left = calendar.$('.fc-bg td:eq(4)').offset().left+15; - var top = calendar.$('.fc-slats tr:eq(4) td:first').offset().top+15; + var top = calendar.$('.fc-slats tr:eq(12) td:first').offset().top+15; try { testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown"); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.'); } testUtils.dom.triggerPositionalMouseEvent(left, top + 200, "mousemove"); testUtils.dom.triggerPositionalMouseEvent(left, top + 200, "mouseup"); + await testUtils.nextTick(); - testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); + await testUtils.fields.editInput($('.modal-body input:first'), 'coucou'); - $('.modal-footer button.btn:contains(Edit)').trigger('click'); - $('.modal-footer button.btn:contains(Save)').trigger('click'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Edit)')); + await testUtils.dom.click($('.modal-footer button.btn:contains(Save)')); assert.containsN(calendar, '.o_calendar_filter_item', 6, "should add the missing filter (active)"); assert.containsN(calendar, '.fc-event', 4, "should display the created item"); calendar.destroy(); - $view.remove(); }); - QUnit.test('Update event with filters', function (assert) { + QUnit.test('Update event with filters', async function (assert) { assert.expect(4); var records = this.data.user.records; @@ -1823,7 +1802,7 @@ QUnit.module('Views', { } }; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1857,16 +1836,16 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); + await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); assert.containsN(calendar, '.o_calendar_filter_item', 5, "should display 5 filter items"); assert.containsN(calendar, '.fc-event', 3, "should display 3 events"); - calendar.$('.fc-event:contains(event 2) .fc-content').trigger('click'); - $('.modal button.btn:contains(Edit)').trigger('click'); - $('.modal .o_field_widget[name="user_id"] input').trigger('click'); - $('.ui-menu-item a:contains(user 5)').trigger('mouseenter').trigger('click'); - $('.modal button.btn:contains(Save)').trigger('click'); + await testUtils.dom.click(calendar.$('.fc-event:contains(event 2) .fc-content')); + await testUtils.dom.click($('.modal button.btn:contains(Edit)')); + await testUtils.dom.click($('.modal .o_field_widget[name="user_id"] input')); + await testUtils.dom.click($('.ui-menu-item a:contains(user 5)').trigger('mouseenter')); + await testUtils.dom.click($('.modal button.btn:contains(Save)')); assert.containsN(calendar, '.o_calendar_filter_item', 6, "should add the missing filter (active)"); assert.containsN(calendar, '.fc-event', 3, "should display the updated item"); @@ -1874,7 +1853,7 @@ QUnit.module('Views', { calendar.destroy(); }); - QUnit.test('change pager with filters', function (assert) { + QUnit.test('change pager with filters', async function (assert) { assert.expect(3); this.data.user.records.push({ @@ -1914,7 +1893,7 @@ QUnit.module('Views', { type: 1 }); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1936,8 +1915,8 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); - testUtils.dom.click($('.o_calendar_button_prev')); + await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input')); + await testUtils.dom.click($('.o_calendar_button_prev')); assert.containsN(calendar, '.o_calendar_filter_item', 6, "should display 6 filter items"); assert.containsN(calendar, '.fc-event', 2, "should display 2 events"); @@ -1947,10 +1926,10 @@ QUnit.module('Views', { calendar.destroy(); }); - QUnit.test('ensure events are still shown if filters give an empty domain', function (assert) { + QUnit.test('ensure events are still shown if filters give an empty domain', async function (assert) { assert.expect(2); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1964,16 +1943,16 @@ QUnit.module('Views', { assert.containsN(calendar, '.fc-event', 5, "should display 5 events"); - testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input[type=checkbox]')); + await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input[type=checkbox]')); assert.containsN(calendar, '.fc-event', 5, "should display 5 events"); calendar.destroy(); }); - QUnit.test('events starting at midnight', function (assert) { - assert.expect(2); + QUnit.test('events starting at midnight', async function (assert) { + assert.expect(3); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -1984,19 +1963,22 @@ QUnit.module('Views', { translateParameters: { // Avoid issues due to localization formats time_format: "%H:%M:%S", }, - }); + }, {positionalClicks: true}); + + // Reset the scroll to 0 as we want to create an event from midnight + assert.ok(calendar.$('.fc-scroller')[0].scrollTop > 0, + "should scroll to 6:00 by default (this is true at least for resolutions up to 1900x1600)"); + calendar.$('.fc-scroller')[0].scrollTop = 0; // Click on Tuesday 12am - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); var top = calendar.$('.fc-axis:contains(0:00)').offset().top + 5; var left = calendar.$('.fc-day:eq(2)').offset().left + 5; try { testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown"); testUtils.dom.triggerPositionalMouseEvent(left, top, "mouseup"); + await testUtils.nextTick(); } catch (e) { calendar.destroy(); - $view.remove(); throw new Error('The test failed to simulate a click on the screen.' + 'Your screen is probably too small or your dev tools are open.'); } @@ -2005,20 +1987,19 @@ QUnit.module('Views', { // Creating the event testUtils.fields.editInput($('.modal-body input:first'), 'new event in quick create'); - $('.modal-footer button.btn:contains(Create)').trigger('click').trigger('click'); + await testUtils.dom.click($('.modal-footer button.btn:contains(Create)')); assert.strictEqual(calendar.$('.fc-event:contains(new event in quick create)').length, 1, "should display the new record after quick create dialog"); calendar.destroy(); - $view.remove(); }); - QUnit.test('set event as all day when field is date', function (assert) { + QUnit.test('set event as all day when field is date', async function (assert) { assert.expect(2); this.data.event.records[0].start_date = "2016-12-14"; - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -2051,11 +2032,11 @@ QUnit.module('Views', { calendar.destroy(); }); - QUnit.test('quickcreate avoid double event creation', function (assert) { + QUnit.test('quickcreate avoid double event creation', async function (assert) { assert.expect(1); var createCount = 0; - var def = $.Deferred(); - var calendar = createView({ + var prom = testUtils.makeTestPromise(); + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -2077,7 +2058,7 @@ QUnit.module('Views', { var result = this._super(route, args); if (args.method === "create") { createCount++; - return def.then(_.constant(result)); + return prom.then(_.constant(result)); } return result; }, @@ -2087,22 +2068,26 @@ QUnit.module('Views', { var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)'); testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); + await testUtils.nextTick(); + var $input = $('.modal input:first'); - testUtils.fields.editInput($input, 'new event in quick create'); + await testUtils.fields.editInput($input, 'new event in quick create'); // Simulate ENTER pressed on Create button (after a TAB) $input.trigger($.Event('keyup', { which: $.ui.keyCode.ENTER, keyCode: $.ui.keyCode.ENTER, })); - testUtils.dom.click($('.modal-footer button:first')); - def.resolve(); + await testUtils.nextTick(); + await testUtils.dom.click($('.modal-footer button:first')); + prom.resolve(); + await testUtils.nextTick(); assert.strictEqual(createCount, 1, "should create only one event"); calendar.destroy(); }); - QUnit.test('check if the view destroys all widgets and instances', function (assert) { + QUnit.test('check if the view destroys all widgets and instances', async function (assert) { assert.expect(1); var instanceNumber = 0; @@ -2141,13 +2126,13 @@ QUnit.module('Views', { }, }; - var calendar = createView(params); + var calendar = await createCalendarView(params); calendar.destroy(); var initialInstanceNumber = instanceNumber; instanceNumber = 0; - calendar = createView(params); + calendar = await createCalendarView(params); // call destroy function of controller to ensure that it correctly destroys everything calendar.__destroy(); @@ -2161,18 +2146,18 @@ QUnit.module('Views', { testUtils.mock.unpatch(mixins.ParentedMixin); }); - QUnit.test('create an event (async dialog) [REQUIRE FOCUS]', function (assert) { + QUnit.test('create an event (async dialog) [REQUIRE FOCUS]', async function (assert) { assert.expect(3); - var def = $.Deferred(); + var prom = testUtils.makeTestPromise(); testUtils.mock.patch(Dialog, { open: function () { var _super = this._super.bind(this); - def.then(_super); + prom.then(_super); return this; }, }); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -2197,11 +2182,13 @@ QUnit.module('Views', { var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)'); testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); + await testUtils.nextTick(); assert.strictEqual($('.modal').length, 0, "should not have opened the dialog yet"); - def.resolve(); + prom.resolve(); + await testUtils.nextTick(); assert.strictEqual($('.modal').length, 1, "should have opened the dialog"); @@ -2212,7 +2199,7 @@ QUnit.module('Views', { testUtils.mock.unpatch(Dialog); }); - QUnit.test('calendar is configured to have no groupBy menu', function (assert) { + QUnit.test('calendar is configured to have no groupBy menu', async function (assert) { assert.expect(1); var archs = { @@ -2233,22 +2220,22 @@ QUnit.module('Views', { views: [[1, 'calendar']] }]; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: actions, archs: archs, data: this.data, }); - actionManager.doAction(1); + await actionManager.doAction(1); assert.containsNone(actionManager.$('.o_control_panel .o_search_options span.fa.fa-bars'), "the control panel has no groupBy menu"); actionManager.destroy(); }); - QUnit.test('timezone does not affect current day', function (assert) { + QUnit.test('timezone does not affect current day', async function (assert) { assert.expect(2); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -2273,17 +2260,17 @@ QUnit.module('Views', { assert.strictEqual($sidebar.find('.ui-datepicker-current-day').text(), "12", "should highlight the target day"); // go to previous day - testUtils.dom.click($sidebar.find('.ui-datepicker-current-day').prev()); + await testUtils.dom.click($sidebar.find('.ui-datepicker-current-day').prev()); assert.strictEqual($sidebar.find('.ui-datepicker-current-day').text(), "11", "should highlight the selected day"); calendar.destroy(); }); - QUnit.test('form_view_id attribute works (for creating events)', function (assert) { + QUnit.test('form_view_id attribute works (for creating events)', async function (assert) { assert.expect(1); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -2305,7 +2292,7 @@ QUnit.module('Views', { // reject the create rpc (quick create), then the web client // fall back to a form view. This happens typically when a // model has required fields - return $.Deferred().reject('None shall pass!'); + return Promise.reject('None shall pass!'); } return this._super(route, args); }, @@ -2320,17 +2307,19 @@ QUnit.module('Views', { var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)'); testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); - var $input = $('.modal-body input:first'); - testUtils.fields.editInput($input, "It's just a fleshwound"); - $('.modal button.btn:contains(Create)').trigger('click'); + await testUtils.nextTick(); + var $input = $('.modal-body input:first'); + await testUtils.fields.editInput($input, "It's just a fleshwound"); + await testUtils.dom.click($('.modal button.btn:contains(Create)')); + await testUtils.nextTick(); // wait a little before to finish the test calendar.destroy(); }); - QUnit.test('calendar fallback to form view id in action if necessary', function (assert) { + QUnit.test('calendar fallback to form view id in action if necessary', async function (assert) { assert.expect(1); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, @@ -2352,7 +2341,7 @@ QUnit.module('Views', { // reject the create rpc (quick create), then the web client // fall back to a form view. This happens typically when a // model has required fields - return $.Deferred().reject('None shall pass!'); + return Promise.reject('None shall pass!'); } return this._super(route, args); }, @@ -2367,14 +2356,15 @@ QUnit.module('Views', { var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)'); testUtils.dom.triggerMouseEvent($cell, "mousedown"); testUtils.dom.triggerMouseEvent($cell, "mouseup"); - var $input = $('.modal-body input:first'); - testUtils.fields.editInput($input, "It's just a fleshwound"); - $('.modal button.btn:contains(Create)').trigger('click'); + await testUtils.nextTick(); + var $input = $('.modal-body input:first'); + await testUtils.fields.editInput($input, "It's just a fleshwound"); + await testUtils.dom.click($('.modal button.btn:contains(Create)')); calendar.destroy(); }); - QUnit.test('fullcalendar initializes with right locale', function (assert) { + QUnit.test('fullcalendar initializes with right locale', async function (assert) { assert.expect(1); var initialLocale = moment.locale(); @@ -2386,7 +2376,7 @@ QUnit.module('Views', { weekdaysShort: ["zz1.", "zz2.", "zz3.", "zz4.", "zz5.", "zz6.", "zz7."], }); - var calendar = createView({ + var calendar = await createCalendarView({ View: CalendarView, model: 'event', data: this.data, diff --git a/addons/web/static/tests/views/control_panel_tests.js b/addons/web/static/tests/views/control_panel_tests.js index f2654e55e1b..2e0a369ab4a 100644 --- a/addons/web/static/tests/views/control_panel_tests.js +++ b/addons/web/static/tests/views/control_panel_tests.js @@ -353,7 +353,7 @@ QUnit.module('Views', { search_disable_custom_filters: true, }, }); - testUtils.dom.click(controlPanel.$('.o_filters_menu_button')); + await testUtils.dom.click(controlPanel.$('.o_filters_menu_button')); assert.containsOnce(controlPanel, '.o_menu_item a:contains("A")'); assert.containsNone(controlPanel, '.o_menu_item a:contains("B")'); @@ -371,8 +371,8 @@ QUnit.module('Views', { data: this.data, searchMenuTypes: ['filter'], }); - testUtils.dom.click(controlPanel.$('.o_filters_menu_button')); - testUtils.dom.click($('.o_menu_item a')); + await testUtils.dom.click(controlPanel.$('.o_filters_menu_button')); + await testUtils.dom.click($('.o_menu_item a')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').text().trim(), 'A', 'should have a facet with A'); @@ -381,14 +381,16 @@ QUnit.module('Views', { which: $.ui.keyCode.BACKSPACE, keyCode: $.ui.keyCode.BACKSPACE, })); + await testUtils.nextTick(); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').length, 0, - 'there should be no facet'); + 'there should be no facet'); // delete nothing (should not crash) controlPanel.$('input.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.BACKSPACE, keyCode: $.ui.keyCode.BACKSPACE, })); + await testUtils.nextTick(); controlPanel.destroy(); }); @@ -409,7 +411,7 @@ QUnit.module('Views', { search_disable_custom_filters: true, }, }); - testUtils.dom.click(controlPanel.$('.o_filters_menu_button')); + await testUtils.dom.click(controlPanel.$('.o_filters_menu_button')); assert.containsOnce(controlPanel, '.o_menu_item a:contains("A")'); assert.containsNone(controlPanel, '.o_menu_item a:contains("B")'); diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index b1df6260359..96d36d8eec9 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -16,7 +16,6 @@ var Widget = require('web.Widget'); var _t = core._t; var createView = testUtils.createView; -var createAsyncView = testUtils.createAsyncView; var createActionManager = testUtils.createActionManager; QUnit.module('Views', { @@ -105,7 +104,6 @@ QUnit.module('Views', { ] }, }; - this.actions = [{ id: 1, name: 'Partners Action 1', @@ -113,15 +111,15 @@ QUnit.module('Views', { type: 'ir.actions.act_window', views: [[false, 'kanban'], [false, 'form']], }]; - } + }, }, function () { QUnit.module('FormView'); - QUnit.test('simple form rendering', function (assert) { + QUnit.test('simple form rendering', async function (assert) { assert.expect(12); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -174,10 +172,11 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('attributes are transferred on async widgets', function (assert) { + QUnit.test('attributes are transferred on async widgets', async function (assert) { assert.expect(1); + var done = assert.async(); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); var FieldChar = fieldRegistry.get('char'); fieldRegistry.add('asyncwidget', FieldChar.extend({ @@ -186,7 +185,7 @@ QUnit.module('Views', { }, })); - createAsyncView({ + createView({ View: FormView, model: 'partner', data: this.data, @@ -201,14 +200,16 @@ QUnit.module('Views', { "should apply style attribute on fields"); form.destroy(); delete fieldRegistry.map.asyncwidget; + done(); }); def.resolve(); + await testUtils.nextTick(); }); - QUnit.test('decoration works on widgets', function (assert) { + QUnit.test('decoration works on widgets', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -224,10 +225,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('decoration on widgets are reevaluated if necessary', function (assert) { + QUnit.test('decoration on widgets are reevaluated if necessary', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -239,15 +240,15 @@ QUnit.module('Views', { viewOptions: {mode: 'edit'}, }); assert.doesNotHaveClass(form.$('input[name="display_name"]'), 'text-danger'); - testUtils.fields.editInput(form.$('input[name=int_field]'), 3); + await testUtils.fields.editInput(form.$('input[name=int_field]'), 3); assert.hasClass(form.$('input[name="display_name"]'), 'text-danger'); form.destroy(); }); - QUnit.test('decoration on widgets works on same widget', function (assert) { + QUnit.test('decoration on widgets works on same widget', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -258,15 +259,15 @@ QUnit.module('Views', { viewOptions: {mode: 'edit'}, }); assert.doesNotHaveClass(form.$('input[name="int_field"]'), 'text-danger'); - testUtils.fields.editInput(form.$('input[name=int_field]'), 3); + await testUtils.fields.editInput(form.$('input[name=int_field]'), 3); assert.hasClass(form.$('input[name="int_field"]'), 'text-danger'); form.destroy(); }); - QUnit.test('only necessary fields are fetched with correct context', function (assert) { + QUnit.test('only necessary fields are fetched with correct context', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -287,10 +288,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('group rendering', function (assert) { + QUnit.test('group rendering', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -308,14 +309,14 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('Form and subview with _view_ref contexts', function (assert) { + QUnit.test('Form and subview with _view_ref contexts', async function (assert) { assert.expect(2); this.data.product.fields.partner_type_ids = {string: "one2many field", type: "one2many", relation: "partner_type"}, this.data.product.records = [{id: 1, name: 'Tromblon', partner_type_ids: [12,14]}]; this.data.partner.records[0].product_id = 1; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ data: this.data, archs: { 'product,false,form': '<form>'+ @@ -344,7 +345,7 @@ QUnit.module('Views', { }, }); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -356,7 +357,7 @@ QUnit.module('Views', { mockRPC: function(route, args) { if (args.method === 'get_formview_action') { - return $.when({ + return Promise.resolve({ res_id: 1, type: 'ir.actions.act_window', target: 'current', @@ -376,14 +377,14 @@ QUnit.module('Views', { }, }, }); - form.$('.o_field_widget[name="product_id"]').click(); + await testUtils.dom.click(form.$('.o_field_widget[name="product_id"]')); form.destroy(); actionManager.destroy(); }); - QUnit.test('invisible fields are properly hidden', function (assert) { + QUnit.test('invisible fields are properly hidden', async function (assert) { assert.expect(4); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -410,10 +411,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('invisible elements are properly hidden', function (assert) { + QUnit.test('invisible elements are properly hidden', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -446,7 +447,8 @@ QUnit.module('Views', { // we set the value bar to simulate a falsy boolean value. this.data.partner.records[0].bar = false; - var form = createView({ + + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -459,9 +461,11 @@ QUnit.module('Views', { '</group></sheet>' + '</form>', res_id: 1, + viewOptions: { + mode:'edit' + }, }); - testUtils.form.clickEdit(form); assert.hasClass(form.$('.foo_field'), 'o_invisible_modifier'); assert.hasClass(form.$('.bar_field'), 'o_invisible_modifier'); @@ -472,10 +476,11 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('asynchronous fields can be set invisible', function (assert) { + QUnit.test('asynchronous fields can be set invisible', async function (assert) { assert.expect(1); + var done = assert.async(); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); // we choose this widget because it is a quite simple widget with a non // empty qweb template @@ -486,7 +491,7 @@ QUnit.module('Views', { }, })); - createAsyncView({ + createView({ View: FormView, model: 'partner', data: this.data, @@ -501,15 +506,15 @@ QUnit.module('Views', { assert.hasClass(form.$('.o_field_widget[name="int_field"]'), 'o_invisible_modifier'); form.destroy(); delete fieldRegistry.map.asyncwidget; + done(); }); def.resolve(); }); - - QUnit.test('properly handle modifiers and attributes on notebook tags', function (assert) { + QUnit.test('properly handle modifiers and attributes on notebook tags', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -531,10 +536,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('invisible attrs on first notebook page', function (assert) { + QUnit.test('invisible attrs on first notebook page', async function (assert) { assert.expect(6); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -554,12 +559,12 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.hasClass(form.$('.o_notebook .nav .nav-link:first()'), 'active'); assert.doesNotHaveClass(form.$('.o_notebook .nav .nav-item:first()'), 'o_invisible_modifier'); // set a value on the m2o - testUtils.fields.many2one.searchAndClickItem('product_id'); + await testUtils.fields.many2one.searchAndClickItem('product_id'); assert.doesNotHaveClass(form.$('.o_notebook .nav .nav-link:first()'), 'active'); assert.hasClass(form.$('.o_notebook .nav .nav-item:first()'), 'o_invisible_modifier'); assert.hasClass(form.$('.o_notebook .nav .nav-link:nth(1)'), 'active'); @@ -567,10 +572,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('first notebook page invisible', function (assert) { + QUnit.test('first notebook page invisible', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -597,10 +602,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('autofocus on second notebook page', function (assert) { + QUnit.test('autofocus on second notebook page', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -626,10 +631,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('invisible attrs on group are re-evaluated on field change', function (assert) { + QUnit.test('invisible attrs on group are re-evaluated on field change', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -643,22 +648,24 @@ QUnit.module('Views', { '</group>' + '</sheet>' + '</form>', - res_id: 1 + res_id: 1, + viewOptions: { + mode: 'edit' + }, }); assert.containsOnce(form, 'div.o_group:visible'); - testUtils.form.clickEdit(form); - testUtils.dom.click('.o_field_boolean input', form); + await testUtils.dom.click('.o_field_boolean input', form); assert.containsOnce(form, 'div.o_group:hidden'); form.destroy(); }); - QUnit.test('invisible attrs with zero value in domain and unset value in data', function (assert) { + QUnit.test('invisible attrs with zero value in domain and unset value in data', async function (assert) { assert.expect(1); this.data.partner.fields.int_field.type = 'monetary'; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -677,10 +684,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('rendering stat buttons', function (assert) { + QUnit.test('rendering stat buttons', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -706,18 +713,18 @@ QUnit.module('Views', { assert.containsOnce(form, 'button.oe_stat_button.o_invisible_modifier'); var count = 0; - testUtils.mock.intercept(form, "execute_action", function () { + await testUtils.mock.intercept(form, "execute_action", function () { count++; }); - testUtils.dom.click('.oe_stat_button'); + await testUtils.dom.click('.oe_stat_button'); assert.strictEqual(count, 1, "should have triggered a execute action"); form.destroy(); }); - QUnit.test('label uses the string attribute', function (assert) { + QUnit.test('label uses the string attribute', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -736,10 +743,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('readonly attrs on fields are re-evaluated on field change', function (assert) { + QUnit.test('readonly attrs on fields are re-evaluated on field change', async function (assert) { assert.expect(4); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -753,24 +760,24 @@ QUnit.module('Views', { '</form>', res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsOnce(form, 'span[name="foo"]', "the foo field widget should be readonly"); - testUtils.dom.click(form.$('.o_field_boolean input')); + await testUtils.dom.click(form.$('.o_field_boolean input')); assert.containsOnce(form, 'input[name="foo"]', "the foo field widget should have been rerendered to now be editable"); - testUtils.dom.click(form.$('.o_field_boolean input')); + await testUtils.dom.click(form.$('.o_field_boolean input')); assert.containsOnce(form, 'span[name="foo"]', "the foo field widget should have been rerendered to now be readonly again"); - testUtils.dom.click(form.$('.o_field_boolean input')); + await testUtils.dom.click(form.$('.o_field_boolean input')); assert.containsOnce(form, 'input[name="foo"]', "the foo field widget should have been rerendered to now be editable again"); form.destroy(); }); - QUnit.test('empty fields have o_form_empty class in readonly mode', function (assert) { + QUnit.test('empty fields have o_form_empty class in readonly mode', async function (assert) { assert.expect(8); this.data.partner.fields.foo.default = false; // no default value for this test @@ -783,7 +790,7 @@ QUnit.module('Views', { } }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -804,21 +811,21 @@ QUnit.module('Views', { assert.containsN(form, '.o_form_label_empty', 2, "should have 2 muted labels (for the empty fieds) in readonly"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsOnce(form, '.o_field_empty', "in edit mode, only empty readonly fields should have the o_field_empty class"); assert.containsOnce(form, '.o_form_label_empty', "in edit mode, only labels associated to empty readonly fields should have the o_form_label_empty class"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'test'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'test'); assert.containsNone(form, '.o_field_empty', "after readonly modifier change, the o_field_empty class should have been removed"); assert.containsNone(form, '.o_form_label_empty', "after readonly modifier change, the o_form_label_empty class should have been removed"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'hello'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'hello'); assert.containsOnce(form, '.o_field_empty', "after value changed to false for a readonly field, the o_field_empty class should have been added"); @@ -828,14 +835,14 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('empty fields\' labels still get the empty class after widget rerender', function (assert) { + QUnit.test('empty fields\' labels still get the empty class after widget rerender', async function (assert) { assert.expect(6); this.data.partner.fields.foo.default = false; // no default value for this test this.data.partner.records[1].foo = false; // 1 is record with id=2 this.data.partner.records[1].display_name = false; // 1 is record with id=2 - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -848,21 +855,21 @@ QUnit.module('Views', { res_id: 2, }); - assert.containsN(form, '.o_field_widget.o_field_empty', 2) + assert.containsN(form, '.o_field_widget.o_field_empty', 2); assert.containsN(form, '.o_form_label_empty', 2, "should have 1 muted label (for the empty fied) in readonly"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsNone(form, '.o_field_empty', "in edit mode, only empty readonly fields should have the o_field_empty class"); assert.containsNone(form, '.o_form_label_empty', "in edit mode, only labels associated to empty readonly fields should have the o_form_label_empty class"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'readonly'); - testUtils.fields.editInput(form.$('input[name=foo]'), 'edit'); - testUtils.fields.editInput(form.$('input[name=display_name]'), 'some name'); - testUtils.fields.editInput(form.$('input[name=foo]'), 'readonly'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'readonly'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'edit'); + await testUtils.fields.editInput(form.$('input[name=display_name]'), 'some name'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'readonly'); assert.containsNone(form, '.o_field_empty', "there still should not be any empty class on fields as the readonly one is now set"); @@ -872,11 +879,11 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('empty inner readonly fields don\'t have o_form_empty class in "create" mode', function (assert) { + QUnit.test('empty inner readonly fields don\'t have o_form_empty class in "create" mode', async function (assert) { assert.expect(2); this.data.partner.fields.product_id.readonly = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -897,10 +904,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('form view can switch to edit mode', function (assert) { + QUnit.test('form view can switch to edit mode', async function (assert) { assert.expect(9); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -915,7 +922,7 @@ QUnit.module('Views', { assert.isVisible(form.$buttons.find('.o_form_buttons_view')); assert.isNotVisible(form.$buttons.find('.o_form_buttons_edit')); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.mode, 'edit', 'form view should be in edit mode'); assert.hasClass(form.$('.o_form_view'), 'o_form_editable'); @@ -925,10 +932,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('required attrs on fields are re-evaluated on field change', function (assert) { + QUnit.test('required attrs on fields are re-evaluated on field change', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -942,25 +949,25 @@ QUnit.module('Views', { '</form>', res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsOnce(form, 'input[name="foo"].o_required_modifier', "the foo field widget should be required"); - testUtils.dom.click('.o_field_boolean input'); + await testUtils.dom.click('.o_field_boolean input'); assert.containsOnce(form, 'input[name="foo"]:not(.o_required_modifier)', "the foo field widget should now have been marked as non-required"); - testUtils.dom.click('.o_field_boolean input'); + await testUtils.dom.click('.o_field_boolean input'); assert.containsOnce(form, 'input[name="foo"].o_required_modifier', "the foo field widget should now have been marked as required again"); form.destroy(); }); - QUnit.test('required fields should have o_required_modifier in readonly mode', function (assert) { + QUnit.test('required fields should have o_required_modifier in readonly mode', async function (assert) { assert.expect(2); this.data.partner.fields.foo.required = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -976,17 +983,17 @@ QUnit.module('Views', { assert.containsOnce(form, 'span.o_required_modifier', form); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsOnce(form, 'input.o_required_modifier', "in edit mode, should have 1 input with o_required_modifier"); form.destroy(); }); - QUnit.test('required float fields works as expected', function (assert) { + QUnit.test('required float fields works as expected', async function (assert) { assert.expect(10); this.data.partner.fields.qux.required = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1007,29 +1014,29 @@ QUnit.module('Views', { assert.strictEqual(form.$('input[name="qux"]').val(), "0.0", "qux input is 0 by default (float field)"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.containsNone(form.$('input[name="qux"]'), "should have switched to readonly"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=qux]'), '1'); + await testUtils.fields.editInput(form.$('input[name=qux]'), '1'); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); - assert.strictEqual(form.$('input[name="qux"]').val(), "1.0", + assert.strictEqual(form.$('input[name="qux"]').val(), "1.0", "qux input is properly formatted"); assert.verifySteps(['default_get', 'create', 'read', 'write', 'read']); form.destroy(); }); - QUnit.test('separators', function (assert) { + QUnit.test('separators', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1048,10 +1055,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('invisible attrs on separators', function (assert) { + QUnit.test('invisible attrs on separators', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1071,12 +1078,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('buttons in form view', function (assert) { + QUnit.test('buttons in form view', async function (assert) { assert.expect(8); var rpcCount = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1104,7 +1111,7 @@ QUnit.module('Views', { assert.containsOnce(form, 'button.p[name="post"]:contains(Confirm)'); assert.containsN(form, '.o_form_statusbar button:visible', 2); - testUtils.mock.intercept(form, 'execute_action', function (ev) { + await testUtils.mock.intercept(form, 'execute_action', function (ev) { assert.strictEqual(ev.data.action_data.name, 'post', "should trigger execute_action with correct method name"); assert.deepEqual(ev.data.env.currentID, 2, "should have correct id in ev data"); @@ -1112,14 +1119,14 @@ QUnit.module('Views', { ev.data.on_closed(); }); rpcCount = 0; - testUtils.dom.click('.o_form_statusbar button.p', form); + await testUtils.dom.click('.o_form_statusbar button.p', form); assert.strictEqual(rpcCount, 1, "should have done 1 rpcs to reload"); - testUtils.mock.intercept(form, 'execute_action', function (ev) { + await testUtils.mock.intercept(form, 'execute_action', function (ev) { ev.data.on_fail(); }); - testUtils.dom.click('.o_form_statusbar button.s', form); + await testUtils.dom.click('.o_form_statusbar button.s', form); assert.strictEqual(rpcCount, 1, "should have done 1 rpc, because we do not reload anymore if the server action fails"); @@ -1127,10 +1134,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('buttons classes in form view', function (assert) { + QUnit.test('buttons classes in form view', async function (assert) { assert.expect(16); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1180,13 +1187,13 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('buttons in form view, new record', function (assert) { + QUnit.test('buttons in form view, new record', async function (assert) { // this simulates a situation similar to the settings forms. assert.expect(7); var resID; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1213,20 +1220,20 @@ QUnit.module('Views', { }, }); - testUtils.mock.intercept(form, 'execute_action', function (event) { + await testUtils.mock.intercept(form, 'execute_action', function (event) { assert.step('execute_action'); assert.deepEqual(event.data.env.currentID, resID, "execute action should be done on correct record id"); event.data.on_success(); event.data.on_closed(); }); - testUtils.dom.click('.o_form_statusbar button.p', form); + await testUtils.dom.click('.o_form_statusbar button.p', form); assert.verifySteps(['default_get', 'create', 'read', 'execute_action', 'read']); form.destroy(); }); - QUnit.test('buttons in form view, new record, with field id in view', function (assert) { + QUnit.test('buttons in form view, new record, with field id in view', async function (assert) { assert.expect(7); // buttons in form view are one of the rare example of situation when we // save a record without reloading it immediately, because we only care @@ -1236,7 +1243,7 @@ QUnit.module('Views', { var resID; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1263,22 +1270,22 @@ QUnit.module('Views', { }, }); - testUtils.mock.intercept(form, 'execute_action', function (event) { + await testUtils.mock.intercept(form, 'execute_action', function (event) { assert.step('execute_action'); assert.deepEqual(event.data.env.currentID, resID, "execute action should be done on correct record id"); event.data.on_success(); event.data.on_closed(); }); - testUtils.dom.click('.o_form_statusbar button.p', form); + await testUtils.dom.click('.o_form_statusbar button.p', form); assert.verifySteps(['default_get', 'create', 'read', 'execute_action', 'read']); form.destroy(); }); - QUnit.test('change and save char', function (assert) { + QUnit.test('change and save char', async function (assert) { assert.expect(6); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1298,11 +1305,11 @@ QUnit.module('Views', { assert.containsOnce(form, 'span:contains(blip)', "should contain span with field value"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.mode, 'edit', 'form view should be in edit mode'); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); - testUtils.form.clickSave(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.form.clickSave(form); assert.strictEqual(form.mode, 'readonly', 'form view should be in readonly mode'); assert.containsOnce(form, 'span:contains(tralala)', @@ -1310,10 +1317,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('properly reload data from server', function (assert) { + QUnit.test('properly reload data from server', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1329,18 +1336,18 @@ QUnit.module('Views', { res_id: 2, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.form.clickSave(form); assert.containsOnce(form, 'span:contains(apple)', "should contain span with field value"); form.destroy(); }); - QUnit.test('disable buttons until reload data from server', function (assert) { + QUnit.test('disable buttons until reload data from server', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1353,22 +1360,23 @@ QUnit.module('Views', { } else if (args.method === 'read') { // Block the 'read' call var result = this._super.apply(this, arguments); - return $.when(def).then(_.constant(result)); + return Promise.resolve(def).then(result); } return this._super(route, args); }, res_id: 2, }); - var def = $.Deferred(); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); - testUtils.form.clickSave(form); + var def = testUtils.makeTestPromise(); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.form.clickSave(form); // Save button should be disabled assert.hasAttrValue(form.$buttons.find('.o_form_button_save'), 'disabled', 'disabled'); // Release the 'read' call - def.resolve(); + await def.resolve(); + await testUtils.nextTick(); // Edit button should be enabled after the reload assert.hasAttrValue(form.$buttons.find('.o_form_button_edit'), 'disabled', undefined); @@ -1376,7 +1384,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('properly apply onchange in simple case', function (assert) { + QUnit.test('properly apply onchange in simple case', async function (assert) { assert.expect(2); this.data.partner.onchanges = { @@ -1384,7 +1392,7 @@ QUnit.module('Views', { obj.int_field = obj.foo.length + 1000; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1394,19 +1402,19 @@ QUnit.module('Views', { res_id: 2, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('input[name=int_field]').val(), "9", "should contain input with initial value"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); assert.strictEqual(form.$('input[name=int_field]').val(), "1007", "should contain input with onchange applied"); form.destroy(); }); - QUnit.test('properly apply onchange when changed field is active field', function (assert) { + QUnit.test('properly apply onchange when changed field is active field', async function (assert) { assert.expect(3); this.data.partner.onchanges = { @@ -1414,7 +1422,7 @@ QUnit.module('Views', { obj.int_field = 14; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1429,27 +1437,27 @@ QUnit.module('Views', { assert.strictEqual(form.$('input[name=int_field]').val(), "9", "should contain input with initial value"); - testUtils.fields.editInput(form.$('input[name=int_field]'), '666'); + await testUtils.fields.editInput(form.$('input[name=int_field]'), '666'); assert.strictEqual(form.$('input[name=int_field]').val(), "14", "value should have been set to 14 by onchange"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); - assert.strictEqual(form.$('.o_field_widget[name=int_field]').text(), "14", + assert.strictEqual(form.$('.o_field_widget[name=int_field]').text(), "14", "value should still be 14"); form.destroy(); }); - QUnit.test('onchange send only the present fields to the server', function (assert) { + QUnit.test('onchange send only the present fields to the server', async function (assert) { assert.expect(1); this.data.partner.records[0].product_id = false; this.data.partner.onchanges.foo = function (obj) { obj.foo = obj.foo + " alligator"; }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1477,17 +1485,17 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); form.destroy(); }); - QUnit.test('onchange only send present fields value', function (assert) { + QUnit.test('onchange only send present fields value', async function (assert) { assert.expect(1); this.data.partner.onchanges.foo = function (obj) {}; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1515,22 +1523,24 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); // add a o2m row - testUtils.dom.click('.o_field_x2many_list_row_add a'); + await testUtils.dom.click('.o_field_x2many_list_row_add a'); form.$('.o_field_one2many input:first').focus(); - testUtils.fields.editInput(form.$('.o_field_one2many input[name=display_name]'), 'valid line'); + await testUtils.nextTick(); + await testUtils.fields.editInput(form.$('.o_field_one2many input[name=display_name]'), 'valid line'); form.$('.o_field_one2many input:last').focus(); - testUtils.fields.editInput(form.$('.o_field_one2many input[name=qux]'), '12.4'); + await testUtils.nextTick(); + await testUtils.fields.editInput(form.$('.o_field_one2many input[name=qux]'), '12.4'); // trigger an onchange by modifying foo - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); form.destroy(); }); - QUnit.test('evaluate in python field options', function (assert) { + QUnit.test('evaluate in python field options', async function (assert) { assert.expect(1); var isOk = false; @@ -1541,7 +1551,7 @@ QUnit.module('Views', { } return tmp.apply(tmp, arguments); }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1557,10 +1567,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('can create a record with default values', function (assert) { + QUnit.test('can create a record with default values', async function (assert) { assert.expect(5); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1586,23 +1596,23 @@ QUnit.module('Views', { }); var n = this.data.partner.records.length; - testUtils.form.clickCreate(form); + await testUtils.form.clickCreate(form); assert.strictEqual(form.mode, 'edit', 'form view should be in edit mode'); - assert.strictEqual(form.$('input[name=foo]').val(), "My little Foo Value", + assert.strictEqual(form.$('input:first').val(), "My little Foo Value", "should have correct default_get value"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.strictEqual(form.mode, 'readonly', 'form view should be in readonly mode'); assert.strictEqual(this.data.partner.records.length, n + 1, "should have created a record"); form.destroy(); }); - QUnit.test('default record with a one2many and an onchange on sub field', function (assert) { + QUnit.test('default record with a one2many and an onchange on sub field', async function (assert) { assert.expect(4); this.data.partner.onchanges.foo = function () {}; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1628,10 +1638,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('sidebar is hidden when switching to edit mode', function (assert) { + QUnit.test('sidebar is hidden when switching to edit mode', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1645,21 +1655,21 @@ QUnit.module('Views', { }); assert.isVisible(form.sidebar); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.isNotVisible(form.sidebar); - testUtils.form.clickDiscard(form); + await testUtils.form.clickDiscard(form); assert.isVisible(form.sidebar); form.destroy(); }); - QUnit.test('basic default record', function (assert) { + QUnit.test('basic default record', async function (assert) { assert.expect(2); this.data.partner.fields.foo.default = "default foo value"; var count = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1677,7 +1687,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('make default record with non empty one2many', function (assert) { + QUnit.test('make default record with non empty one2many', async function (assert) { assert.expect(4); this.data.partner.fields.p.default = [ @@ -1688,7 +1698,7 @@ QUnit.module('Views', { var nameGetCount = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1717,7 +1727,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('make default record with non empty many2one', function (assert) { + QUnit.test('make default record with non empty many2one', async function (assert) { var done = assert.async(); assert.expect(2); @@ -1725,7 +1735,7 @@ QUnit.module('Views', { var nameGetCount = 0; - createAsyncView({ + createView({ View: FormView, model: 'partner', data: this.data, @@ -1749,10 +1759,10 @@ QUnit.module('Views', { }); }); - QUnit.test('form view properly change its title', function (assert) { + QUnit.test('form view properly change its title', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1765,17 +1775,17 @@ QUnit.module('Views', { assert.strictEqual(form.$('.o_control_panel .breadcrumb').text(), 'first record', "should have the display name of the record as title"); - testUtils.form.clickCreate(form); + await testUtils.form.clickCreate(form); assert.strictEqual(form.$('.o_control_panel .breadcrumb').text(), _t("New"), "should have the display name of the record as title"); form.destroy(); }); - QUnit.test('can duplicate a record', function (assert) { + QUnit.test('can duplicate a record', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1789,8 +1799,8 @@ QUnit.module('Views', { assert.strictEqual(form.$('.o_control_panel .breadcrumb').text(), 'first record', "should have the display name of the record as title"); - testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(form.sidebar.$('a:contains(Duplicate)')); + await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(form.sidebar.$('a:contains(Duplicate)')); assert.strictEqual(form.$('.o_control_panel .breadcrumb').text(), 'first record (copy)', "should have duplicated the record"); @@ -1799,10 +1809,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('duplicating a record preserve the context', function (assert) { + QUnit.test('duplicating a record preserve the context', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1822,16 +1832,16 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(form.sidebar.$('a:contains(Duplicate)')); + await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(form.sidebar.$('a:contains(Duplicate)')); form.destroy(); }); - QUnit.test('cannot duplicate a record', function (assert) { + QUnit.test('cannot duplicate a record', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1849,10 +1859,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('clicking on stat buttons in edit mode', function (assert) { + QUnit.test('clicking on stat buttons in edit mode', async function (assert) { assert.expect(9); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1878,30 +1888,30 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); var count = 0; - testUtils.mock.intercept(form, "execute_action", function (event) { + await testUtils.mock.intercept(form, "execute_action", function (event) { event.stopPropagation(); count++; }); - testUtils.dom.click('.oe_stat_button'); + await testUtils.dom.click('.oe_stat_button'); assert.strictEqual(count, 1, "should have triggered a execute action"); assert.strictEqual(form.mode, "edit", "form view should be in edit mode"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); - testUtils.dom.click('.oe_stat_button:first'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.dom.click('.oe_stat_button:first'); - assert.strictEqual(form.mode, "edit", "form view should be in edit mode"); + assert.strictEqual(form.mode, "edit", "form view should be in edit mode"); assert.strictEqual(count, 2, "should have triggered a execute action"); assert.verifySteps(['read', 'write', 'read']); form.destroy(); }); - QUnit.test('clicking on stat buttons save and reload in edit mode', function (assert) { + QUnit.test('clicking on stat buttons save and reload in edit mode', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1930,23 +1940,23 @@ QUnit.module('Views', { assert.strictEqual(form.$('.o_control_panel .breadcrumb').text(), 'second record', "should have correct display_name"); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=name]'), 'some other name'); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=name]'), 'some other name'); - testUtils.dom.click('.oe_stat_button'); + await testUtils.dom.click('.oe_stat_button'); assert.strictEqual(form.$('.o_control_panel .breadcrumb').text(), 'GOLDORAK', "should have correct display_name"); form.destroy(); }); - QUnit.test('buttons with attr "special" do not trigger a save', function (assert) { + QUnit.test('buttons with attr "special" do not trigger a save', async function (assert) { assert.expect(4); var executeActionCount = 0; var writeCount = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1961,34 +1971,35 @@ QUnit.module('Views', { writeCount++; } return this._super(route, args); - } + }, }); - testUtils.mock.intercept(form, "execute_action", function () { + await testUtils.mock.intercept(form, "execute_action", function () { executeActionCount++; }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); // make the record dirty - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); - testUtils.dom.click(form.$('button:contains(Do something)')); + await testUtils.dom.click(form.$('button:contains(Do something)')); + //TODO: VSC: add a next tick ? assert.strictEqual(writeCount, 1, "should have triggered a write"); assert.strictEqual(executeActionCount, 1, "should have triggered a execute action"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'abcdef'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'abcdef'); - testUtils.dom.click(form.$('button:contains(Or discard)')); + await testUtils.dom.click(form.$('button:contains(Or discard)')); assert.strictEqual(writeCount, 1, "should not have triggered a write"); assert.strictEqual(executeActionCount, 2, "should have triggered a execute action"); form.destroy(); }); - QUnit.test('buttons with attr "special=save" save', function (assert) { + QUnit.test('buttons with attr "special=save" save', async function (assert) { assert.expect(5); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2011,18 +2022,18 @@ QUnit.module('Views', { }, }); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); - testUtils.dom.click(form.$('.o_form_view button')); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.dom.click(form.$('button[special="save"]')); assert.verifySteps(['read', 'write', 'read', 'execute_action']); form.destroy(); }); - QUnit.test('missing widgets do not crash', function (assert) { + QUnit.test('missing widgets do not crash', async function (assert) { assert.expect(1); this.data.partner.fields.foo.type = 'new field type without widget'; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2035,10 +2046,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('nolabel', function (assert) { + QUnit.test('nolabel', async function (assert) { assert.expect(6); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2073,13 +2084,13 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('many2one in a one2many', function (assert) { + QUnit.test('many2one in a one2many', async function (assert) { assert.expect(1); this.data.partner.records[0].p = [2]; this.data.partner.records[1].product_id = 37; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2096,13 +2107,13 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('circular many2many\'s', function (assert) { + QUnit.test('circular many2many\'s', async function (assert) { assert.expect(4); - this.data.partner_type.fields.partner_ids = {string: "partners", type: "many2many", relation: 'partner'} + this.data.partner_type.fields.partner_ids = {string: "partners", type: "many2many", relation: 'partner'}; this.data.partner.records[0].timmy = [12]; this.data.partner_type.records[0].partner_ids = [1]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2128,24 +2139,24 @@ QUnit.module('Views', { assert.containsOnce(form, 'td:contains(gold)', "should display the name of the many2many on the original form"); - testUtils.dom.click(form.$('td:contains(gold)')); + await testUtils.dom.click(form.$('td:contains(gold)')); assert.containsOnce(document.body, '.modal'); assert.containsOnce($('.modal'), 'td:contains(first record)', "should display the name of the many2many on the modal form"); - testUtils.dom.click('.modal td:contains(first record)'); + await testUtils.dom.click('.modal td:contains(first record)'); assert.containsN(document.body, '.modal', 2, "there should be 2 modals (partner on top of partner_type) opened"); form.destroy(); }); - QUnit.test('discard changes on a non dirty form view', function (assert) { + QUnit.test('discard changes on a non dirty form view', async function (assert) { assert.expect(4); var nbWrite = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2160,25 +2171,24 @@ QUnit.module('Views', { }); // switch to edit mode - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('input[name=foo]').val(), 'yop', "input should contain yop"); // click on discard - testUtils.form.clickDiscard(form); - assert.containsNone(document.body, '.modal', "no confirm modal should be displayed"); - assert.strictEqual(form.$('.o_field_widget').text(), 'yop', - "field in readonly should display yop"); + await testUtils.form.clickDiscard(form); + assert.containsNone(document.body, '.modal', 'no confirm modal should be displayed'); + assert.strictEqual(form.$('.o_field_widget').text(), 'yop', 'field in readonly should display yop'); assert.strictEqual(nbWrite, 0, "no write RPC should have been done"); form.destroy(); }); - QUnit.test('discard changes on a dirty form view', function (assert) { + QUnit.test('discard changes on a dirty form view', async function (assert) { assert.expect(7); var nbWrite = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2193,31 +2203,29 @@ QUnit.module('Views', { }); // switch to edit mode and edit the foo field - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('input[name=foo]').val(), 'yop', "input should contain yop"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'new value'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'new value'); assert.strictEqual(form.$('input[name=foo]').val(), 'new value', "input should contain new value"); // click on discard and cancel the confirm request - testUtils.form.clickDiscard(form); + await testUtils.form.clickDiscard(form); assert.containsOnce(document.body, '.modal', "a confirm modal should be displayed"); - testUtils.dom.click('.modal-footer .btn-secondary'); - assert.strictEqual(form.$('input[name=foo]').val(), 'new value', - "input should still contain new value"); + await testUtils.dom.click('.modal-footer .btn-secondary'); + assert.strictEqual(form.$('input').val(), 'new value', 'input should still contain new value'); // click on discard and confirm - testUtils.form.clickDiscard(form); + await testUtils.form.clickDiscard(form); assert.containsOnce(document.body, '.modal', "a confirm modal should be displayed"); - testUtils.dom.click('.modal-footer .btn-primary'); - assert.strictEqual(form.$('.o_field_widget').text(), 'yop', - "field in readonly should display yop"); + await testUtils.dom.click('.modal-footer .btn-primary'); + assert.strictEqual(form.$('.o_field_widget').text(), 'yop', 'field in readonly should display yop'); assert.strictEqual(nbWrite, 0, "no write RPC should have been done"); form.destroy(); }); - QUnit.test('discard changes on a dirty form view (for date field)', function (assert) { + QUnit.test('discard changes on a dirty form view (for date field)', async function (assert) { assert.expect(1); // this test checks that the basic model properly handles date object @@ -2226,7 +2234,7 @@ QUnit.module('Views', { // then parsed into string, which is wrong. this.data.partner.fields.date.default = "2017-01-25"; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2243,18 +2251,18 @@ QUnit.module('Views', { // event to properly focusout the input, otherwise it crashes later on // when the 'blur' event is triggered by the re-rendering) form.$buttons.find('.o_form_button_cancel').focus(); - testUtils.dom.click('.o_form_button_cancel'); + await testUtils.dom.click('.o_form_button_cancel'); form.$buttons.find('.o_form_button_save').focus(); - testUtils.dom.click('.o_form_button_save'); + await testUtils.dom.click('.o_form_button_save'); assert.containsOnce(form, 'span:contains(2017)'); form.destroy(); }); - QUnit.test('discard changes on relational data on new record', function (assert) { + QUnit.test('discard changes on relational data on new record', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2278,28 +2286,28 @@ QUnit.module('Views', { }); // edit the p field - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); - testUtils.fields.many2one.clickOpenDropdown('product_id'); - testUtils.fields.many2one.clickHighlightedItem('product_id'); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.fields.many2one.clickOpenDropdown('product_id'); + await testUtils.fields.many2one.clickHighlightedItem('product_id'); assert.strictEqual(form.$('.o_field_widget[name=product_id] input').val(), 'xphone', "input should contain xphone"); // click on discard and confirm - testUtils.form.clickDiscard(form); - testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm + await testUtils.form.clickDiscard(form); + await testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm assert.notOk(form.$el.prop('outerHTML').match('xphone'), "the string xphone should not be present after discarding"); form.destroy(); }); - QUnit.test('discard changes on a new (non dirty, except for defaults) form view', function (assert) { + QUnit.test('discard changes on a new (non dirty, except for defaults) form view', async function (assert) { assert.expect(3); this.data.partner.fields.foo.default = "ABC"; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2315,7 +2323,7 @@ QUnit.module('Views', { assert.strictEqual(form.$('input[name=foo]').val(), 'ABC', "input should contain ABC"); - testUtils.form.clickDiscard(form); + await testUtils.form.clickDiscard(form); assert.containsNone(document.body, '.modal', "there should not be a confirm modal"); @@ -2323,12 +2331,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('discard changes on a new (dirty) form view', function (assert) { + QUnit.test('discard changes on a new (dirty) form view', async function (assert) { assert.expect(8); this.data.partner.fields.foo.default = "ABC"; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2346,35 +2354,30 @@ QUnit.module('Views', { }); // edit the foo field - assert.strictEqual(form.$('input[name=foo]').val(), 'ABC', - "input should contain ABC"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'DEF'); + assert.strictEqual(form.$('input').val(), 'ABC', 'input should contain ABC'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'DEF'); // discard the changes and check it has properly been discarded - testUtils.form.clickDiscard(form); - assert.containsOnce(document.body, '.modal', "there should be a confirm modal"); - assert.strictEqual(form.$('input[name=foo]').val(), 'DEF', - "input should be DEF"); - testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm - assert.strictEqual(form.$('input[name=foo]').val(), 'ABC', - "input should now be ABC"); + await testUtils.form.clickDiscard(form); + assert.containsOnce(document.body, '.modal', 'there should be a confirm modal'); + assert.strictEqual(form.$('input').val(), 'DEF', 'input should be DEF'); + await testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm + assert.strictEqual(form.$('input').val(), 'ABC', 'input should now be ABC'); // redirty and discard the field foo (to make sure initial changes haven't been lost) - testUtils.fields.editInput(form.$('input[name=foo]'), 'GHI'); - testUtils.form.clickDiscard(form); - assert.strictEqual(form.$('input[name=foo]').val(), 'GHI', - "input should be GHI"); - testUtils.dom.click('.modal-footer .btn-primary') // click on confirm - assert.strictEqual(form.$('input[name=foo]').val(), 'ABC', - "input should now be ABC"); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'GHI'); + await testUtils.form.clickDiscard(form); + assert.strictEqual(form.$('input').val(), 'GHI', 'input should be GHI'); + await testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm + assert.strictEqual(form.$('input').val(), 'ABC', 'input should now be ABC'); form.destroy(); }); - QUnit.test('discard changes on a duplicated record', function (assert) { + QUnit.test('discard changes on a duplicated record', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2382,28 +2385,27 @@ QUnit.module('Views', { res_id: 1, viewOptions: {hasSidebar: true}, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.form.clickSave(form); - testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(form.sidebar.$('a:contains(Duplicate)')); + await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(form.sidebar.$('a:contains(Duplicate)')); - assert.strictEqual(form.$('input[name=foo]').val(), 'tralala', - "input should contain ABC"); + assert.strictEqual(form.$('input[name=foo]').val(), 'tralala', 'input should contain ABC'); - testUtils.form.clickDiscard(form); + await testUtils.form.clickDiscard(form); assert.containsNone(document.body, '.modal', "there should not be a confirm modal"); form.destroy(); }); - QUnit.test('switching to another record from a dirty one', function (assert) { + QUnit.test("switching to another record from a dirty one", async function (assert) { assert.expect(11); var nbWrite = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2425,26 +2427,25 @@ QUnit.module('Views', { assert.strictEqual(form.$('.o_pager_limit').text(), '2', "pager limit should be 2"); // switch to edit mode - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('input[name=foo]').val(), 'yop', "input should contain yop"); // edit the foo field - testUtils.fields.editInput(form.$('input[name=foo]'), 'new value'); - assert.strictEqual(form.$('input[name=foo]').val(), 'new value', - "input should contain new value"); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'new value'); + assert.strictEqual(form.$('input').val(), 'new value', 'input should contain new value'); // click on the pager to switch to the next record and cancel the confirm request - testUtils.dom.click('.o_pager_next'); // click on next + await testUtils.dom.click('.o_pager_next'); // click on next assert.containsOnce(document.body, '.modal', "a confirm modal should be displayed"); - testUtils.dom.click('.modal-footer .btn-secondary'); // click on cancel + await testUtils.dom.click('.modal-footer .btn-secondary'); // click on cancel assert.strictEqual(form.$('input[name=foo]').val(), 'new value', "input should still contain new value"); assert.strictEqual(form.$('.o_pager_value').text(), '1', "pager value should still be 1"); // click on the pager to switch to the next record and confirm - testUtils.dom.click('.o_pager_next'); // click on next + await testUtils.dom.click('.o_pager_next'); // click on next assert.containsOnce(document.body, '.modal', "a confirm modal should be displayed"); - testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm + await testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm assert.strictEqual(form.$('input[name=foo]').val(), 'blip', "input should contain blip"); assert.strictEqual(form.$('.o_pager_value').text(), '2', "pager value should be 2"); @@ -2452,10 +2453,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('handling dirty state: switching to another record', function (assert) { + QUnit.test('handling dirty state: switching to another record', async function (assert) { assert.expect(12); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2472,18 +2473,18 @@ QUnit.module('Views', { assert.strictEqual(form.$('.o_pager_value').text(), '1', "pager value should be 1"); // switch to edit mode - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('input[name=foo]').val(), 'yop', "input should contain yop"); // edit the foo field - testUtils.fields.editInput(form.$('input[name=foo]'), 'new value'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'new value'); assert.strictEqual(form.$('input[name=foo]').val(), 'new value', "input should contain new value"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); // click on the pager to switch to the next record and cancel the confirm request - testUtils.dom.click('.o_pager_next'); // click on next + await testUtils.dom.click('.o_pager_next'); // click on next assert.containsNone(document.body, '.modal:visible', "no confirm modal should be displayed"); assert.strictEqual(form.$('.o_pager_value').text(), '2', "pager value should be 2"); @@ -2492,35 +2493,35 @@ QUnit.module('Views', { 'priority widget should have been rendered with correct value'); // edit the value in readonly - testUtils.dom.click(form.$('.o_priority .fa-star-o:first')); // click on the first star + await testUtils.dom.click(form.$('.o_priority .fa-star-o:first')); // click on the first star assert.containsOnce(form, '.o_priority .fa-star', 'priority widget should have been updated'); - testUtils.dom.click('.o_pager_next'); // click on next + await testUtils.dom.click('.o_pager_next'); // click on next assert.containsNone(document.body, '.modal:visible', "no confirm modal should be displayed"); assert.strictEqual(form.$('.o_pager_value').text(), '1', "pager value should be 1"); // switch to edit mode - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('input[name=foo]').val(), 'new value', "input should contain yop"); // edit the foo field - testUtils.fields.editInput(form.$('input[name=foo]'), 'wrong value'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'wrong value'); - testUtils.form.clickDiscard(form); + await testUtils.form.clickDiscard(form); assert.containsOnce(document.body, '.modal', "a confirm modal should be displayed"); - testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm - testUtils.dom.click('.o_pager_next'); // click on next + await testUtils.dom.click('.modal-footer .btn-primary'); // click on confirm + await testUtils.dom.click('.o_pager_next'); // click on next assert.strictEqual(form.$('.o_pager_value').text(), '2', "pager value should be 2"); form.destroy(); }); - QUnit.test('restore local state when switching to another record', function (assert) { + QUnit.test('restore local state when switching to another record', async function (assert) { assert.expect(4); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2542,23 +2543,23 @@ QUnit.module('Views', { }); // click on second page tab - testUtils.dom.click(form.$('.o_notebook .nav-link:eq(1)')); + await testUtils.dom.click(form.$('.o_notebook .nav-link:eq(1)')); assert.doesNotHaveClass(form.$('.o_notebook .nav-link:eq(0)'), 'active'); assert.hasClass(form.$('.o_notebook .nav-link:eq(1)'), 'active'); // click on the pager to switch to the next record - testUtils.dom.click('.o_pager_next'); + await testUtils.dom.click('.o_pager_next'); assert.doesNotHaveClass(form.$('.o_notebook .nav-link:eq(0)'), 'active'); assert.hasClass(form.$('.o_notebook .nav-link:eq(1)'), 'active'); form.destroy(); }); - QUnit.test('pager is hidden in create mode', function (assert) { + QUnit.test('pager is hidden in create mode', async function (assert) { assert.expect(7); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2578,10 +2579,10 @@ QUnit.module('Views', { assert.strictEqual(form.pager.$('.o_pager_limit').text(), "2", "current pager limit should be 1"); - testUtils.form.clickCreate(form); + await testUtils.form.clickCreate(form); assert.isNotVisible(form.pager); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.isVisible(form.pager); assert.strictEqual(form.pager.$('.o_pager_value').text(), "3", "current pager value should be 3"); @@ -2591,12 +2592,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('switching to another record, in readonly mode', function (assert) { + QUnit.test('switching to another record, in readonly mode', async function (assert) { assert.expect(5); var pushStateCount = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2615,7 +2616,7 @@ QUnit.module('Views', { assert.strictEqual(form.mode, 'readonly', 'form view should be in readonly mode'); assert.strictEqual(form.pager.$('.o_pager_value').text(), "1", 'pager value should be 1'); - testUtils.dom.click(form.pager.$('.o_pager_next')); + await testUtils.dom.click(form.pager.$('.o_pager_next')); assert.strictEqual(form.pager.$('.o_pager_value').text(), "2", 'pager value should be 2'); assert.strictEqual(form.mode, 'readonly', 'form view should be in readonly mode'); @@ -2624,10 +2625,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('modifiers are reevaluated when creating new record', function (assert) { + QUnit.test('modifiers are reevaluated when creating new record', async function (assert) { assert.expect(4); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2643,7 +2644,7 @@ QUnit.module('Views', { assert.containsOnce(form, 'span.foo_field'); assert.isNotVisible(form.$('span.foo_field')); - testUtils.form.clickCreate(form); + await testUtils.form.clickCreate(form); assert.containsOnce(form, 'input.foo_field'); assert.isVisible(form.$('input.foo_field')); @@ -2651,14 +2652,14 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('empty readonly fields are visible on new records', function (assert) { + QUnit.test('empty readonly fields are visible on new records', async function (assert) { assert.expect(2); this.data.partner.fields.foo.readonly = true; this.data.partner.fields.foo.default = undefined; this.data.partner.records[0].foo = undefined; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2672,16 +2673,16 @@ QUnit.module('Views', { assert.containsOnce(form, '.o_field_empty'); - testUtils.form.clickCreate(form); + await testUtils.form.clickCreate(form); assert.containsNone(form, '.o_field_empty'); form.destroy(); }); - QUnit.test('all group children have correct layout classname', function (assert) { + QUnit.test('all group children have correct layout classname', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2703,10 +2704,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('deleting a record', function (assert) { + QUnit.test('deleting a record', async function (assert) { assert.expect(8); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2726,13 +2727,13 @@ QUnit.module('Views', { assert.ok(!$('.modal:visible').length, 'no confirm modal should be displayed'); // open sidebar and delete - testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(form.sidebar.$('a:contains(Delete)')); + await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(form.sidebar.$('a:contains(Delete)')); assert.ok($('.modal').length, 'a confirm modal should be displayed'); // confirm the delete - testUtils.dom.click($('.modal-footer button.btn-primary')); + await testUtils.dom.click($('.modal-footer button.btn-primary')); assert.strictEqual(form.pager.$('.o_pager_value').text(), "1", 'pager value should be 1'); assert.strictEqual(form.pager.$('.o_pager_limit').text(), "2", 'pager limit should be 2'); @@ -2741,10 +2742,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('deleting the last record', function (assert) { + QUnit.test('deleting the last record', async function (assert) { assert.expect(6); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2762,27 +2763,27 @@ QUnit.module('Views', { }); // open sidebar, click on delete and confirm - testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(form.sidebar.$('a:contains(Delete)')); + await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(form.sidebar.$('a:contains(Delete)')); - testUtils.mock.intercept(form, 'history_back', function () { + await testUtils.mock.intercept(form, 'history_back', function () { assert.step('history_back'); }); assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed'); - testUtils.dom.click($('.modal-footer button.btn-primary')); + await testUtils.dom.click($('.modal-footer button.btn-primary')); assert.strictEqual($('.modal').length, 0, 'no confirm modal should be displayed'); assert.verifySteps(['read', 'unlink', 'history_back']); form.destroy(); }); - QUnit.test('empty required fields cannot be saved', function (assert) { + QUnit.test('empty required fields cannot be saved', async function (assert) { assert.expect(5); this.data.partner.fields.foo.required = true; delete this.data.partner.fields.foo.default; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2804,22 +2805,22 @@ QUnit.module('Views', { }, }); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.hasClass(form.$('label.o_form_label'),'o_field_invalid'); assert.hasClass(form.$('input[name=foo]'),'o_field_invalid'); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); assert.containsNone(form, '.o_field_invalid'); form.destroy(); }); - QUnit.test('changes in a readonly form view are saved directly', function (assert) { + QUnit.test('changes in a readonly form view are saved directly', async function (assert) { assert.expect(10); var nbWrite = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2844,36 +2845,36 @@ QUnit.module('Views', { 'priority widget should have been rendered with correct value'); // edit the value in readonly - testUtils.dom.click(form.$('.o_priority .fa-star-o:first')); + await testUtils.dom.click(form.$('.o_priority .fa-star-o:first')); assert.strictEqual(nbWrite, 1, 'should have saved directly'); assert.containsOnce(form, '.o_priority .fa-star', 'priority widget should have been updated'); // switch to edit mode and edit the value again - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsN(form, '.o_priority .o_priority_star', 2, 'priority widget should have been correctly rendered'); assert.containsOnce(form, '.o_priority .fa-star', 'priority widget should have correct value'); - testUtils.dom.click(form.$('.o_priority .fa-star-o:first')); + await testUtils.dom.click(form.$('.o_priority .fa-star-o:first')); assert.strictEqual(nbWrite, 1, 'should not have saved directly'); assert.containsN(form, '.o_priority .fa-star', 2, 'priority widget should have been updated'); // save - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.strictEqual(nbWrite, 2, 'should not have saved directly'); assert.containsN(form, '.o_priority .fa-star', 2, 'priority widget should have correct value'); form.destroy(); }); - QUnit.test('display a dialog if onchange result is a warning', function (assert) { + QUnit.test('display a dialog if onchange result is a warning', async function (assert) { assert.expect(5); this.data.partner.onchanges = { foo: true }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2883,7 +2884,7 @@ QUnit.module('Views', { res_id: 2, mockRPC: function (route, args) { if (args.method === 'onchange') { - return $.when({ + return Promise.resolve({ value: { int_field: 10 }, warning: { title: "Warning", @@ -2905,23 +2906,23 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('input[name=int_field]').val(), '9'); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); assert.strictEqual(form.$('input[name=int_field]').val(), '10'); form.destroy(); }); - QUnit.test('can create record even if onchange returns a warning', function (assert) { + QUnit.test('can create record even if onchange returns a warning', async function (assert) { assert.expect(2); this.data.partner.onchanges = { foo: true }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2930,7 +2931,7 @@ QUnit.module('Views', { '</form>', mockRPC: function (route, args) { if (args.method === 'onchange') { - return $.when({ + return Promise.resolve({ value: { int_field: 10 }, warning: { title: "Warning", @@ -2942,7 +2943,7 @@ QUnit.module('Views', { }, intercepts: { warning: function (event) { - assert.step('warning'); + assert.ok(true, 'should trigger a warning'); }, }, }); @@ -2952,12 +2953,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('do nothing if add a line in one2many result in a onchange with a warning', function (assert) { - assert.expect(2); + QUnit.test('do nothing if add a line in one2many result in a onchange with a warning', async function (assert) { + assert.expect(3); this.data.partner.onchanges = { foo: true }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2971,7 +2972,7 @@ QUnit.module('Views', { res_id: 2, mockRPC: function (route, args) { if (args.method === 'onchange') { - return $.when({ + return Promise.resolve({ value: {}, warning: { title: "Warning", @@ -2989,19 +2990,20 @@ QUnit.module('Views', { }); // go to edit mode, click to add a record in the o2m - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); assert.containsNone(form, 'tr.o_data_row', "should not have added a line"); + assert.verifySteps(["should have triggered a warning"]); form.destroy(); }); - QUnit.test('default_get, onchange which fails, should still work', function (assert) { + QUnit.test('default_get, onchange which fails, should still work', async function (assert) { assert.expect(1); this.data.partner.onchanges.foo = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3013,8 +3015,8 @@ QUnit.module('Views', { // we simulate a validation error. In the 'real' web client, // the server error will be used by the session to display // an error dialog. From the point of view of the basic - // model, the deferred is just rejected. - return $.Deferred().reject(); + // model, the promise is just rejected. + return Promise.reject(); } return this._super.apply(this, arguments); }, @@ -3034,7 +3036,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('attrs are properly transmitted to new records', function (assert) { + QUnit.test('attrs are properly transmitted to new records', async function (assert) { assert.expect(2); // this test checks that the fieldsInfo have been transmitted to the @@ -3046,7 +3048,7 @@ QUnit.module('Views', { string_false: "Test Environment", hover_false: "Switch to production environment" }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3062,7 +3064,7 @@ QUnit.module('Views', { assert.strictEqual(form.$('.o_stat_text.o_not_hover:contains(Production Environment)').length, 1, "button should contain correct string"); - testUtils.form.clickCreate(form); + await testUtils.form.clickCreate(form); assert.strictEqual(form.$('.o_stat_text.o_not_hover:contains(Test Environment)').length, 1, "button should contain correct string"); @@ -3070,10 +3072,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('button box is rendered in create mode', function (assert) { + QUnit.test('button box is rendered in create mode', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3092,21 +3094,21 @@ QUnit.module('Views', { "button box should be displayed in readonly"); // edit mode - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsOnce(form, '.oe_stat_button', "button box should be displayed in edit on an existing record"); // create mode (leave edition first!) - testUtils.form.clickDiscard(form); - testUtils.form.clickCreate(form); + await testUtils.form.clickDiscard(form); + await testUtils.form.clickCreate(form); assert.containsOnce(form, '.oe_stat_button', "button box should be displayed when creating a new record as well"); form.destroy(); }); - QUnit.test('properly apply onchange on one2many fields', function (assert) { + QUnit.test('properly apply onchange on one2many fields', async function (assert) { assert.expect(5); this.data.partner.records[0].p = [4]; @@ -3119,7 +3121,7 @@ QUnit.module('Views', { ]; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3140,8 +3142,8 @@ QUnit.module('Views', { "the 'display_name' of the one2many record should be correct"); // switch to edit mode - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'let us trigger an onchange'); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'let us trigger an onchange'); var $o2m = form.$('.o_field_one2many'); assert.strictEqual($o2m.find('.o_data_row').length, 2, "there should be two linked record"); @@ -3153,7 +3155,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('update many2many value in one2many after onchange', function (assert) { + QUnit.test('update many2many value in one2many after onchange', async function (assert) { assert.expect(2); this.data.partner.records[1].p = [4]; @@ -3168,7 +3170,7 @@ QUnit.module('Views', { ]; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3185,21 +3187,21 @@ QUnit.module('Views', { }); assert.strictEqual($('div[name="p"] .o_data_row td').text().trim(), "aaaNo records", "should have proper initial content"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'tralala'); assert.strictEqual($('div[name="p"] .o_data_row td').text().trim(), "goldNo records", "should have proper initial content"); form.destroy(); }); - QUnit.test('delete a line in a one2many while editing another line triggers a warning', function (assert) { + QUnit.test('delete a line in a one2many while editing another line triggers a warning', async function (assert) { assert.expect(3); this.data.partner.records[0].p = [1, 2]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3213,15 +3215,15 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('.o_data_cell').first()); - testUtils.fields.editInput(form.$('input[name=display_name]'), ''); - testUtils.dom.click(form.$('.fa-trash-o').eq(1)); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_data_cell').first()); + await testUtils.fields.editInput(form.$('input[name=display_name]'), ''); + await testUtils.dom.click(form.$('.fa-trash-o').eq(1)); assert.strictEqual($('.modal').find('.modal-title').first().text(), "Warning", "Clicking out of a dirty line while editing should trigger a warning modal."); - testUtils.dom.click($('.modal').find('.btn-primary')); + await testUtils.dom.click($('.modal').find('.btn-primary')); assert.strictEqual(form.$('.o_data_cell').first().text(), "first record", "Value should have been reset to what it was before editing began."); @@ -3230,7 +3232,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('properly apply onchange on many2many fields', function (assert) { + QUnit.test('properly apply onchange on many2many fields', async function (assert) { assert.expect(14); this.data.partner.onchanges = { @@ -3242,7 +3244,7 @@ QUnit.module('Views', { ]; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3274,8 +3276,8 @@ QUnit.module('Views', { "there should be no many2many record linked at first"); // switch to edit mode - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'let us trigger an onchange'); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'let us trigger an onchange'); var $m2m = form.$('.o_field_many2many'); assert.strictEqual($m2m.find('.o_data_row').length, 2, "there should be two linked records"); @@ -3284,14 +3286,14 @@ QUnit.module('Views', { assert.strictEqual($m2m.find('.o_data_row:nth(1) td:first').text(), 'silver', "the 'display_name' of the second m2m record should be correctly displayed"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.verifySteps(['read', 'onchange', 'read', 'write', 'read', 'read']); form.destroy(); }); - QUnit.test('display_name not sent for onchanges if not in view', function (assert) { + QUnit.test('display_name not sent for onchanges if not in view', async function (assert) { assert.expect(7); this.data.partner.records[0].timmy = [12]; @@ -3302,7 +3304,7 @@ QUnit.module('Views', { name: function () {}, }; var readInModal = false; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3367,17 +3369,17 @@ QUnit.module('Views', { }); // trigger the onchange - testUtils.fields.editInput(form.$('.o_field_widget[name=foo]'), "coucou"); + await testUtils.fields.editInput(form.$('.o_field_widget[name=foo]'), "coucou"); // open a subrecord and trigger an onchange readInModal = true; - testUtils.dom.click(form.$('.o_data_row .o_data_cell:first')); - testUtils.fields.editInput($('.modal .o_field_widget[name=name]'), "new name"); + await testUtils.dom.click(form.$('.o_data_row .o_data_cell:first')); + await testUtils.fields.editInput($('.modal .o_field_widget[name=name]'), "new name"); form.destroy(); }); - QUnit.test('onchanges on date(time) fields', function (assert) { + QUnit.test('onchanges on date(time) fields', async function (assert) { assert.expect(6); this.data.partner.onchanges = { @@ -3386,7 +3388,7 @@ QUnit.module('Views', { obj.datetime = '2021-12-12 10:55:05'; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3410,7 +3412,7 @@ QUnit.module('Views', { assert.strictEqual(form.$('.o_field_widget[name=datetime]').text(), '12/12/2016 12:55:05', "the initial datetime should be correct"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('.o_field_widget[name=date] input').val(), '01/25/2017', "the initial date should be correct in edit"); @@ -3418,7 +3420,7 @@ QUnit.module('Views', { '12/12/2016 12:55:05', "the initial datetime should be correct in edit"); // trigger the onchange - testUtils.fields.editInput(form.$('.o_field_widget[name="foo"]'), "coucou"); + await testUtils.fields.editInput(form.$('.o_field_widget[name="foo"]'), "coucou"); assert.strictEqual(form.$('.o_field_widget[name=date] input').val(), '12/12/2021', "the initial date should be correct in edit"); @@ -3428,7 +3430,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('onchanges are not sent for each keystrokes', function (assert) { + QUnit.test('onchanges are not sent for each keystrokes', async function (assert) { var done = assert.async(); assert.expect(5); @@ -3439,8 +3441,8 @@ QUnit.module('Views', { obj.int_field = obj.foo.length + 1000; }, }; - var def = $.Deferred(); - var form = createView({ + var def = testUtils.makeTestPromise(); + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3462,19 +3464,18 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); testUtils.fields.editInput(form.$('input[name=foo]'), '1'); assert.strictEqual(onchangeNbr, 0, "no onchange has been called yet"); testUtils.fields.editInput(form.$('input[name=foo]'), '12'); assert.strictEqual(onchangeNbr, 0, "no onchange has been called yet"); - return waitForFinishedOnChange().then(function () { + return waitForFinishedOnChange().then(async function () { assert.strictEqual(onchangeNbr, 1, "one onchange has been called"); // add something in the input, then focus another input - testUtils.fields.editInput(form.$('input[name=foo]'), '123'); - form.$('input[name=foo]').change(); + await testUtils.fields.editAndTrigger(form.$('input[name=foo]'), '123', ['change']); assert.strictEqual(onchangeNbr, 2, "one onchange has been called immediately"); return waitForFinishedOnChange(); @@ -3487,13 +3488,13 @@ QUnit.module('Views', { function waitForFinishedOnChange() { return def.then(function () { - def = $.Deferred(); + def = testUtils.makeTestPromise(); return concurrency.delay(0); }); } }); - QUnit.test('onchanges are not sent for invalid values', function (assert) { + QUnit.test('onchanges are not sent for invalid values', async function (assert) { assert.expect(6); this.data.partner.onchanges = { @@ -3501,7 +3502,7 @@ QUnit.module('Views', { obj.foo = String(obj.int_field); }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3515,21 +3516,21 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); // edit int_field, and check that an onchange has been applied - testUtils.fields.editInput(form.$('input[name="int_field"]'), "123"); + await testUtils.fields.editInput(form.$('input[name="int_field"]'), "123"); assert.strictEqual(form.$('input[name="foo"]').val(), "123", "the onchange has been applied"); // enter an invalid value in a float, and check that no onchange has // been applied - testUtils.fields.editInput(form.$('input[name="int_field"]'), "123a"); + await testUtils.fields.editInput(form.$('input[name="int_field"]'), "123a"); assert.strictEqual(form.$('input[name="foo"]').val(), "123", "the onchange has not been applied"); // save, and check that the int_field input is marked as invalid - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.hasClass(form.$('input[name="int_field"]'),'o_field_invalid', "input int_field is marked as invalid"); @@ -3537,10 +3538,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('rpc complete after destroying parent', function (assert) { + QUnit.test('rpc complete after destroying parent', async function (assert) { // We just test that there is no crash in this situation assert.expect(0); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3555,20 +3556,20 @@ QUnit.module('Views', { } } }); - testUtils.dom.click(form.$('.o_form_button_update')); + await testUtils.dom.click(form.$('.o_form_button_update')); }); - QUnit.test('onchanges that complete after discarding', function (assert) { - assert.expect(4); + QUnit.test('onchanges that complete after discarding', async function (assert) { + assert.expect(5); - var def1 = $.Deferred(); + var def1 = testUtils.makeTestPromise(); this.data.partner.onchanges = { foo: function (obj) { obj.int_field = obj.foo.length + 1000; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3591,31 +3592,33 @@ QUnit.module('Views', { // go into edit mode assert.strictEqual(form.$('span[name="foo"]').text(), "blip", "field foo should be displayed to initial value"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); // edit a value - testUtils.fields.editInput(form.$('input[name=foo]'), '1234'); + await testUtils.fields.editInput(form.$('input[name=foo]'), '1234'); // discard changes - testUtils.form.clickDiscard(form); - testUtils.dom.click($('.modal-footer .btn-primary')); + await testUtils.form.clickDiscard(form); + await testUtils.dom.click($('.modal-footer .btn-primary')); assert.strictEqual(form.$('span[name="foo"]').text(), "blip", "field foo should still be displayed to initial value"); // complete the onchange def1.resolve(); + await testUtils.nextTick(); assert.strictEqual(form.$('span[name="foo"]').text(), "blip", "field foo should still be displayed to initial value"); + assert.verifySteps(['onchange is done']); form.destroy(); }); - QUnit.test('discarding before save returns', function (assert) { + QUnit.test('discarding before save returns', async function (assert) { assert.expect(4); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3635,10 +3638,10 @@ QUnit.module('Views', { }, }); - testUtils.fields.editInput(form.$('input[name=foo]'), '1234'); + await testUtils.fields.editInput(form.$('input[name=foo]'), '1234'); // save the value and discard directly - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); form.discardChanges(); // Simulate click on breadcrumb assert.strictEqual(form.$('.o_field_widget[name="foo"]').val(), "1234", @@ -3648,7 +3651,7 @@ QUnit.module('Views', { // complete the write def.resolve(); - + await testUtils.nextTick(); assert.strictEqual($('.modal').length, 0, "Confirm dialog should not be displayed"); assert.strictEqual(form.$('.o_field_widget[name="foo"]').text(), "1234", @@ -3657,7 +3660,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('unchanged relational data is sent for onchanges', function (assert) { + QUnit.test('unchanged relational data is sent for onchanges', async function (assert) { assert.expect(1); this.data.partner.records[1].p = [4]; @@ -3666,7 +3669,7 @@ QUnit.module('Views', { obj.int_field = obj.foo.length + 1000; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3692,13 +3695,13 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'trigger an onchange'); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'trigger an onchange'); form.destroy(); }); - QUnit.test('onchanges on unknown fields of o2m are ignored', function (assert) { + QUnit.test('onchanges on unknown fields of o2m are ignored', async function (assert) { // many2one fields need to be postprocessed (the onchange returns [id, // display_name]), but if we don't know the field, we can't know it's a // many2one, so it isn't ignored, its value is an array instead of a @@ -3709,7 +3712,7 @@ QUnit.module('Views', { this.data.partner.onchanges = { foo: function () {}, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3732,7 +3735,7 @@ QUnit.module('Views', { res_id: 2, mockRPC: function (route, args) { if (args.method === 'onchange') { - return $.when({ + return Promise.resolve({ value: { p: [ [5], @@ -3753,25 +3756,25 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'trigger an onchange'); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'trigger an onchange'); assert.strictEqual(form.$('.o_data_row td:first').text(), 'foo changed', "onchange should have been correctly applied on field in o2m list"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); form.destroy(); }); - QUnit.test('onchange value are not discarded on o2m edition', function (assert) { + QUnit.test('onchange value are not discarded on o2m edition', async function (assert) { assert.expect(4); this.data.partner.records[1].p = [4]; this.data.partner.onchanges = { foo: function () {}, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3794,7 +3797,7 @@ QUnit.module('Views', { res_id: 2, mockRPC: function (route, args) { if (args.method === 'onchange') { - return $.when({ + return Promise.resolve({ value: { p: [[5], [1, 4, {foo: 'foo changed'}]], }, @@ -3809,17 +3812,17 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('.o_data_row td:first').text(), 'My little Foo Value', "the initial value should be the default one"); - testUtils.fields.editInput(form.$('input[name=foo]'), 'trigger an onchange'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'trigger an onchange'); assert.strictEqual(form.$('.o_data_row td:first').text(), 'foo changed', "onchange should have been correctly applied on field in o2m list"); - testUtils.dom.click(form.$('.o_data_row')); + await testUtils.dom.click(form.$('.o_data_row')); assert.strictEqual($('.modal .modal-title').text().trim(), 'Open: one2many field', "the field string is displayed in the modal title"); assert.strictEqual($('.modal .o_field_widget').val(), 'foo changed', @@ -3828,7 +3831,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('args of onchanges in o2m fields are correct (inline edition)', function (assert) { + QUnit.test('args of onchanges in o2m fields are correct (inline edition)', async function (assert) { assert.expect(3); this.data.partner.records[1].p = [4]; @@ -3839,7 +3842,7 @@ QUnit.module('Views', { obj.foo = '[' + obj.rel_field.foo + '] ' + obj.int_field; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3857,26 +3860,26 @@ QUnit.module('Views', { res_id: 2, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('.o_data_row td:first').text(), 'My little Foo Value', "the initial value should be the default one"); - testUtils.dom.click(form.$('.o_data_row td:nth(1)')); - testUtils.fields.editInput(form.$('.o_data_row input:nth(1)'), 77); + await testUtils.dom.click(form.$('.o_data_row td:nth(1)')); + await testUtils.fields.editInput(form.$('.o_data_row input:nth(1)'), 77); assert.strictEqual(form.$('.o_data_row input:first').val(), '[blip] 77', "onchange should have been correctly applied"); // create a new o2m record - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); assert.strictEqual(form.$('.o_data_row input:first').val(), '[blip] 14', "onchange should have been correctly applied after default get"); form.destroy(); }); - QUnit.test('args of onchanges in o2m fields are correct (dialog edition)', function (assert) { + QUnit.test('args of onchanges in o2m fields are correct (dialog edition)', async function (assert) { assert.expect(6); this.data.partner.records[1].p = [4]; @@ -3887,7 +3890,7 @@ QUnit.module('Views', { obj.foo = '[' + obj.rel_field.foo + '] ' + obj.int_field; }, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3908,39 +3911,41 @@ QUnit.module('Views', { res_id: 2, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('.o_data_row td:first').text(), 'My little Foo Value', "the initial value should be the default one"); - testUtils.dom.click(form.$('.o_data_row td:first')); - testUtils.fields.editInput($('.modal input:nth(1)'), 77); + await testUtils.dom.click(form.$('.o_data_row td:first')); + await testUtils.nextTick(); + await testUtils.fields.editInput($('.modal input:nth(1)'), 77); assert.strictEqual($('.modal input:first').val(), '[blip] 77', "onchange should have been correctly applied"); - testUtils.dom.click($('.modal-footer .btn-primary')); + await testUtils.dom.click($('.modal-footer .btn-primary')); assert.strictEqual(form.$('.o_data_row td:first').text(), '[blip] 77', "onchange should have been correctly applied"); // create a new o2m record - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); assert.strictEqual($('.modal .modal-title').text().trim(), 'Create custom label', "the custom field label is applied in the modal title"); assert.strictEqual($('.modal input:first').val(), '[blip] 14', "onchange should have been correctly applied after default get"); - testUtils.dom.clickFirst($('.modal-footer .btn-primary')); + await testUtils.dom.clickFirst($('.modal-footer .btn-primary')); + await testUtils.nextTick(); assert.strictEqual(form.$('.o_data_row:nth(1) td:first').text(), '[blip] 14', "onchange should have been correctly applied after default get"); form.destroy(); }); - QUnit.test('context of onchanges contains the context of changed fields', function (assert) { + QUnit.test('context of onchanges contains the context of changed fields', async function (assert) { assert.expect(2); this.data.partner.onchanges = { foo: function () {}, }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3962,16 +3967,16 @@ QUnit.module('Views', { res_id: 2, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name=foo]'), 'coucou'); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'coucou'); form.destroy(); }); - QUnit.test('navigation with tab key in form view', function (assert) { + QUnit.test('navigation with tab key in form view', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -3988,7 +3993,7 @@ QUnit.module('Views', { }); // go to edit mode - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); // focus first input, trigger tab form.$('input[name="foo"]').focus(); @@ -4010,12 +4015,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('navigation with tab key in readonly form view', function (assert) { + QUnit.test('navigation with tab key in readonly form view', async function (assert) { assert.expect(3); this.data.partner.records[1].product_id = 37; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4055,12 +4060,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('skip invisible fields when navigating with TAB', function (assert) { + QUnit.test('skip invisible fields when navigating with TAB', async function (assert) { assert.expect(1); this.data.partner.records[0].bar = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4075,7 +4080,7 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); form.$('input[name="foo"]').focus(); form.$('input[name="foo"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB})); assert.strictEqual(form.$('input[name="int_field"]')[0], document.activeElement, @@ -4084,10 +4089,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('clicking on a stat button with a context', function (assert) { + QUnit.test('clicking on a stat button with a context', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4114,15 +4119,15 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('.oe_stat_button')); + await testUtils.dom.click(form.$('.oe_stat_button')); form.destroy(); }); - QUnit.test('clicking on a stat button with no context', function (assert) { + QUnit.test('clicking on a stat button with no context', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4148,15 +4153,15 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('.oe_stat_button')); + await testUtils.dom.click(form.$('.oe_stat_button')); form.destroy(); }); - QUnit.test('diplay a stat button outside a buttonbox', function (assert) { + QUnit.test('diplay a stat button outside a buttonbox', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4180,10 +4185,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('diplay something else than a button in a buttonbox', function (assert) { + QUnit.test('diplay something else than a button in a buttonbox', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4208,10 +4213,41 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('display correctly buttonbox, in large size class', function (assert) { + QUnit.test('invisible fields are not considered as visible in a buttonbox', async function (assert) { + assert.expect(2); + + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form>' + + '<div name="button_box" class="oe_button_box">' + + '<field name="foo" invisible="1"/>' + + '<field name="bar" invisible="1"/>' + + '<field name="int_field" invisible="1"/>' + + '<field name="qux" invisible="1"/>' + + '<field name="display_name" invisible="1"/>' + + '<field name="state" invisible="1"/>' + + '<field name="date" invisible="1"/>' + + '<field name="datetime" invisible="1"/>' + + '<button type="object" class="oe_stat_button" icon="fa-check-square"/>' + + '</div>' + + '</form>', + res_id: 2, + }); + + assert.strictEqual(form.$('.oe_button_box').children().length, 9, + "button box should contain nine children"); + assert.hasClass(form.$('.oe_button_box'), 'o_not_full', + "the buttonbox should not be full (only 1 visible child)"); + + form.destroy(); + }); + + QUnit.test('display correctly buttonbox, in large size class', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4237,12 +4273,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('one2many default value creation', function (assert) { + QUnit.test('one2many default value creation', async function (assert) { assert.expect(1); this.data.partner.records[0].product_ids = [37]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4259,7 +4295,7 @@ QUnit.module('Views', { '</form>', mockRPC: function (route, args) { if (args.method === 'default_get') { - return $.when({ + return Promise.resolve({ product_ids: [[0, 0, { name: 'xdroid', partner_type_id: 12, @@ -4274,14 +4310,14 @@ QUnit.module('Views', { return this._super.apply(this, arguments); }, }); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); form.destroy(); }); - QUnit.test('many2manys inside one2manys are saved correctly', function (assert) { + QUnit.test('many2manys inside one2manys are saved correctly', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4306,19 +4342,19 @@ QUnit.module('Views', { }); // add a o2m subrecord with a m2m tag - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); - testUtils.fields.many2one.clickOpenDropdown('timmy'); - testUtils.fields.many2one.clickHighlightedItem('timmy'); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.fields.many2one.clickOpenDropdown('timmy'); + await testUtils.fields.many2one.clickHighlightedItem('timmy'); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); form.destroy(); }); - QUnit.test('one2manys (list editable) inside one2manys are saved correctly', function (assert) { + QUnit.test('one2manys (list editable) inside one2manys are saved correctly', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4353,10 +4389,10 @@ QUnit.module('Views', { }); // add a o2m subrecord - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); - testUtils.dom.click($('.modal-body .o_field_one2many .o_field_x2many_list_row_add a')); - testUtils.fields.editInput($('.modal-body input'), 'xtv'); - testUtils.dom.click($('.modal-footer button:first')); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.dom.click($('.modal-body .o_field_one2many .o_field_x2many_list_row_add a')); + await testUtils.fields.editInput($('.modal-body input'), 'xtv'); + await testUtils.dom.click($('.modal-footer button:first')); assert.strictEqual($('.modal').length, 0, "dialog should be closed"); @@ -4364,16 +4400,16 @@ QUnit.module('Views', { assert.strictEqual(row.children()[0].textContent, '1 record', "the cell should contains the number of record: 1"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); form.destroy(); }); - QUnit.test('*_view_ref in context are passed correctly', function (assert) { + QUnit.test('*_view_ref in context are passed correctly', async function (assert) { var done = assert.async(); assert.expect(3); - createAsyncView({ + createView({ View: FormView, model: 'partner', data: this.data, @@ -4401,15 +4437,15 @@ QUnit.module('Views', { } return this._super.apply(this, arguments); }, - }).then(function (form) { + }).then(async function (form) { // reload to check that the record's context hasn't been modified - form.reload(); + await form.reload(); form.destroy(); done(); }); }); - QUnit.test('readonly fields with modifiers may be saved', function (assert) { + QUnit.test('readonly fields with modifiers may be saved', async function (assert) { // the readonly property on the field description only applies on view, // this is not a DB constraint. It should be seen as a default value, // that may be overriden in views, for example with modifiers. So @@ -4417,7 +4453,7 @@ QUnit.module('Views', { assert.expect(3); this.data.partner.fields.foo.readonly = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4439,13 +4475,13 @@ QUnit.module('Views', { // bar being set to true, foo shouldn't be readonly and thus its value // could be saved, even if in its field description it is readonly - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsOnce(form, 'input[name="foo"]', "foo field should be editable"); - testUtils.fields.editInput(form.$('input[name="foo"]'), 'New foo value'); + await testUtils.fields.editInput(form.$('input[name="foo"]'), 'New foo value'); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.strictEqual(form.$('.o_field_widget[name=foo]').text(), 'New foo value', "new value for foo field should have been saved"); @@ -4453,10 +4489,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('check if id and active_id are defined', function (assert) { + QUnit.test('check if id and active_id are defined', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4474,8 +4510,8 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (args.method === 'default_get' && args.args[0][0] === 'trululu') { - assert.strictEqual(args.kwargs.context.current_id, false, - "current_id should be false"); + assert.strictEqual(args.kwargs.context.current_id, false, + "current_id should be false"); assert.strictEqual(args.kwargs.context.default_trululu, false, "default_trululu should be false"); } @@ -4483,14 +4519,14 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); form.destroy(); }); - QUnit.test('modifiers are considered on multiple <footer/> tags', function (assert) { + QUnit.test('modifiers are considered on multiple <footer/> tags', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4515,7 +4551,7 @@ QUnit.module('Views', { assert.deepEqual(getVisibleButtonTexts(), ["Hello", "World"], "only the first button section should be visible"); - testUtils.dom.click(form.$(".o_field_boolean input")); + await testUtils.dom.click(form.$(".o_field_boolean input")); assert.deepEqual(getVisibleButtonTexts(), ["Foo"], "only the second button section should be visible"); @@ -4530,10 +4566,10 @@ QUnit.module('Views', { } }); - QUnit.test('buttons in footer are moved to $buttons if necessary', function (assert) { + QUnit.test('buttons in footer are moved to $buttons if necessary', async function (assert) { assert.expect(4); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4551,7 +4587,7 @@ QUnit.module('Views', { assert.containsNone(form.$('.o_form_view'), 'button.infooter'); // check that this still works after a reload - testUtils.form.reload(form); + await testUtils.form.reload(form); assert.containsOnce(form.$('.o_control_panel'), 'button.infooter'); assert.containsNone(form.$('.o_form_view'), 'button.infooter'); @@ -4559,10 +4595,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('render stat button with string inline', function (assert) { + QUnit.test('render stat button with string inline', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', res_id: 1, @@ -4581,11 +4617,11 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('renderer waits for asynchronous fields rendering', function (assert) { + QUnit.test('renderer waits for asynchronous fields rendering', async function (assert) { assert.expect(1); var done = assert.async(); - testUtils.createAsyncView({ + testUtils.createView({ View: FormView, model: 'partner', data: this.data, @@ -4603,7 +4639,7 @@ QUnit.module('Views', { }); }); - QUnit.test('open one2many form containing one2many', function (assert) { + QUnit.test('open one2many form containing one2many', async function (assert) { assert.expect(9); this.data.partner.records[0].product_ids = [37]; @@ -4612,7 +4648,7 @@ QUnit.module('Views', { }; this.data.product.records[0].partner_type_ids = [12]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', res_id: 1, @@ -4652,7 +4688,7 @@ QUnit.module('Views', { var row = form.$('.o_field_one2many .o_list_view .o_data_row'); assert.strictEqual(row.children()[1].textContent, '1 record', "the cell should contains the number of record: 1"); - testUtils.dom.click(row); + await testUtils.dom.click(row); var modal_row = $('.modal-body .o_form_sheet .o_field_one2many .o_list_view .o_data_row'); assert.strictEqual(modal_row.children().length, 2, "the row should contains the 2 fields defined in the form view"); @@ -4663,10 +4699,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('in edit mode, first field is focused', function (assert) { + QUnit.test('in edit mode, first field is focused', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4676,7 +4712,7 @@ QUnit.module('Views', { '</form>', res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(document.activeElement, form.$('input[name="foo"]')[0], "foo field should have focus"); @@ -4686,10 +4722,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('autofocus fields are focused', function (assert) { + QUnit.test('autofocus fields are focused', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4699,17 +4735,17 @@ QUnit.module('Views', { '</form>', res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(document.activeElement, form.$('input[name="foo"]')[0], "foo field should have focus"); form.destroy(); }); - QUnit.test('in create mode, autofocus fields are focused', function (assert) { + QUnit.test('in create mode, autofocus fields are focused', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4724,9 +4760,9 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('create with false values', function (assert) { + QUnit.test('create with false values', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4742,15 +4778,15 @@ QUnit.module('Views', { }, }); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); form.destroy(); }); - QUnit.test('autofocus first visible field', function (assert) { + QUnit.test('autofocus first visible field', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4765,10 +4801,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('no autofocus with disable_autofocus option [REQUIRE FOCUS]', function (assert) { + QUnit.test('no autofocus with disable_autofocus option [REQUIRE FOCUS]', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4783,7 +4819,7 @@ QUnit.module('Views', { assert.notStrictEqual(document.activeElement, form.$('input[name="int_field"]')[0], "int_field field should not have focus"); - form.update({}); + await form.update({}); assert.notStrictEqual(document.activeElement, form.$('input[name="int_field"]')[0], "int_field field should not have focus"); @@ -4791,7 +4827,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('open one2many form containing many2many_tags', function (assert) { + QUnit.test('open one2many form containing many2many_tags', async function (assert) { assert.expect(4); this.data.partner.records[0].product_ids = [37]; @@ -4800,7 +4836,7 @@ QUnit.module('Views', { }; this.data.product.records[0].partner_type_ids = [12, 14]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', res_id: 1, @@ -4833,21 +4869,21 @@ QUnit.module('Views', { }, }); var row = form.$('.o_field_one2many .o_list_view .o_data_row'); - testUtils.dom.click(row); + await testUtils.dom.click(row); assert.verifySteps(['read', 'read', 'read'], "there should be 3 read rpcs"); form.destroy(); }); - QUnit.test('onchanges are applied before checking if it can be saved', function (assert) { + QUnit.test('onchanges are applied before checking if it can be saved', async function (assert) { assert.expect(4); this.data.partner.onchanges.foo = function (obj) {}; this.data.partner.fields.foo.required = true; - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4876,20 +4912,21 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name="foo"]'), ''); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name="foo"]'), ''); + await testUtils.form.clickSave(form); def.resolve(); + await testUtils.nextTick(); assert.verifySteps(['read', 'onchange', 'warning']); form.destroy(); }); - QUnit.test('display toolbar', function (assert) { + QUnit.test('display toolbar', async function (assert) { assert.expect(8); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -4915,7 +4952,7 @@ QUnit.module('Views', { "the active_id shoud be 1."); assert.deepEqual(args.context.active_ids, [1], "the active_ids should be an array with 1 inside."); - return $.when({}); + return Promise.resolve({}); } return this._super.apply(this, arguments); }, @@ -4932,19 +4969,19 @@ QUnit.module('Views', { var $customAction = $actionMenu.find('.dropdown-item:last'); assert.strictEqual($customAction.text().trim(), 'Action partner', "the custom action should have 'Action partner' as name"); - testUtils.mock.intercept(form, 'do_action', function (event) { + await testUtils.mock.intercept(form, 'do_action', function (event) { var context = event.data.action.context.__contexts[1]; assert.strictEqual(context.active_id, 1, "the active_id shoud be 1."); assert.deepEqual(context.active_ids, [1], "the active_ids should be an array with 1 inside."); }); - testUtils.dom.click($customAction); + await testUtils.dom.click($customAction); form.destroy(); }); - QUnit.test('check interactions between multiple FormViewDialogs', function (assert) { + QUnit.test('check interactions between multiple FormViewDialogs', async function (assert) { assert.expect(8); this.data.product.fields.product_ids = { @@ -4953,7 +4990,7 @@ QUnit.module('Views', { this.data.partner.records[0].product_id = 37; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', res_id: 1, @@ -4979,7 +5016,7 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (route === '/web/dataset/call_kw/product/get_formview_id') { - return $.when(false); + return Promise.resolve(false); } else if (args.method === 'write') { assert.strictEqual(args.model, 'product', "should write on product model"); @@ -4990,9 +5027,9 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); // Open first dialog - testUtils.dom.click(form.$('.o_external_button')); + await testUtils.dom.click(form.$('.o_external_button')); assert.strictEqual($('.modal').length, 1, "One FormViewDialog should be opened"); var $firstModal = $('.modal'); @@ -5002,27 +5039,27 @@ QUnit.module('Views', { "display_name should be correctly displayed"); // Open second dialog - testUtils.dom.click($firstModal.find('.o_field_x2many_list_row_add a')); + await testUtils.dom.click($firstModal.find('.o_field_x2many_list_row_add a')); assert.strictEqual($('.modal').length, 2, "two FormViewDialogs should be opened"); var $secondModal = $('.modal:nth(1)'); // Add new value - testUtils.fields.editInput($secondModal.find('input'), 'xtv'); - testUtils.dom.click($secondModal.find('.modal-footer button:first')); + await testUtils.fields.editInput($secondModal.find('input'), 'xtv'); + await testUtils.dom.click($secondModal.find('.modal-footer button:first')); assert.strictEqual($('.modal').length, 1, "last opened dialog should be closed"); // Check that data in first dialog is correctly updated assert.strictEqual($firstModal.find('tr.o_data_row td').text(), 'xtv', "should have added a line with xtv as new record"); - testUtils.dom.click($firstModal.find('.modal-footer button:first')); + await testUtils.dom.click($firstModal.find('.modal-footer button:first')); form.destroy(); }); - QUnit.test('fields and record contexts are not mixed', function (assert) { + QUnit.test('fields and record contexts are not mixed', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5047,15 +5084,15 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('.o_field_widget[name=trululu] input')); + await testUtils.dom.click(form.$('.o_field_widget[name=trululu] input')); form.destroy(); }); - QUnit.test('do not activate an hidden tab when switching between records', function (assert) { + QUnit.test('do not activate an hidden tab when switching between records', async function (assert) { assert.expect(6); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5084,14 +5121,14 @@ QUnit.module('Views', { "first tab should be active"); // click on the pager to switch to the next record - testUtils.dom.click(form.pager.$('.o_pager_next')); + await testUtils.dom.click(form.pager.$('.o_pager_next')); assert.strictEqual(form.$('.o_notebook .nav-item:not(.o_invisible_modifier)').length, 1, "only the second tab should be visible"); assert.hasClass(form.$('.o_notebook .nav-item:not(.o_invisible_modifier) .nav-link'),'active', "the visible tab should be active"); // click on the pager to switch back to the previous record - testUtils.dom.click(form.pager.$('.o_pager_previous')); + await testUtils.dom.click(form.pager.$('.o_pager_previous')); assert.strictEqual(form.$('.o_notebook .nav-item:not(.o_invisible_modifier)').length, 2, "both tabs should be visible again"); assert.hasClass(form.$('.o_notebook .nav-link:nth(1)'),'active', @@ -5100,16 +5137,16 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('support anchor tags with action type', function (assert) { + QUnit.test('support anchor tags with action type', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, arch: '<form string="Partners">' + '<a type="action" name="42"><i class="fa fa-arrow-right"/> Click me !</a>' + - '</form>', + '</form>', res_id: 1, intercepts: { do_action: function (event) { @@ -5118,17 +5155,17 @@ QUnit.module('Views', { } } }); - testUtils.dom.click(form.$('a[type="action"]')); + await testUtils.dom.click(form.$('a[type="action"]')); form.destroy(); }); - QUnit.test('do not perform extra RPC to read invisible many2one fields', function (assert) { + QUnit.test('do not perform extra RPC to read invisible many2one fields', async function (assert) { assert.expect(2); this.data.partner.fields.trululu.default = 2; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5148,14 +5185,14 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('do not perform extra RPC to read invisible x2many fields', function (assert) { + QUnit.test('do not perform extra RPC to read invisible x2many fields', async function (assert) { assert.expect(2); this.data.partner.records[0].p = [2]; // one2many this.data.partner.records[0].product_ids = [37]; // one2many this.data.partner.records[0].timmy = [12]; // many2many - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5180,13 +5217,13 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('default_order on x2many embedded view', function (assert) { + QUnit.test('default_order on x2many embedded view', async function (assert) { assert.expect(11); this.data.partner.fields.display_name.sortable = true; this.data.partner.records[0].p = [1, 4]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5215,14 +5252,14 @@ QUnit.module('Views', { assert.ok(form.$('.o_field_one2many tbody tr:first td:contains(yop)').length, "record 1 should be first"); - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); assert.strictEqual($('.modal').length, 1, "FormViewDialog should be opened"); - testUtils.fields.editInput($('.modal input[name="foo"]'), 'xop') - testUtils.dom.click($('.modal-footer button:eq(1)')); - testUtils.fields.editInput($('.modal input[name="foo"]'), 'zop') - testUtils.dom.click($('.modal-footer button:first')); + await testUtils.fields.editInput($('.modal input[name="foo"]'), 'xop'); + await testUtils.dom.click($('.modal-footer button:eq(1)')); + await testUtils.fields.editInput($('.modal input[name="foo"]'), 'zop'); + await testUtils.dom.click($('.modal-footer button:first')); // client-side sort assert.ok(form.$('.o_field_one2many tbody tr:eq(0) td:contains(zop)').length, @@ -5233,7 +5270,7 @@ QUnit.module('Views', { "record xop should be third"); // server-side sort - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.ok(form.$('.o_field_one2many tbody tr:eq(0) td:contains(zop)').length, "record zop should be first"); assert.ok(form.$('.o_field_one2many tbody tr:eq(1) td:contains(yop)').length, @@ -5242,10 +5279,10 @@ QUnit.module('Views', { "record xop should be third"); // client-side sort on edit - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('.o_field_one2many tbody tr:eq(1) td:contains(yop)')); - testUtils.fields.editInput($('.modal input[name="foo"]'), 'zzz'); - testUtils.dom.click($('.modal-footer button:first')); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_field_one2many tbody tr:eq(1) td:contains(yop)')); + await testUtils.fields.editInput($('.modal input[name="foo"]'), 'zzz'); + await testUtils.dom.click($('.modal-footer button:first')); assert.ok(form.$('.o_field_one2many tbody tr:eq(0) td:contains(zzz)').length, "record zzz should be first"); assert.ok(form.$('.o_field_one2many tbody tr:eq(1) td:contains(zop)').length, @@ -5256,10 +5293,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('action context is used when evaluating domains', function (assert) { + QUnit.test('action context is used when evaluating domains', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5280,16 +5317,16 @@ QUnit.module('Views', { return this._super.apply(this, arguments); }, }); - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('div[name="trululu"] input')); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('div[name="trululu"] input')); form.destroy(); }); - QUnit.test('form rendering with groups with col/colspan', function (assert) { + QUnit.test('form rendering with groups with col/colspan', async function (assert) { assert.expect(46); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5411,10 +5448,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('outer and inner groups string attribute', function (assert) { + QUnit.test('outer and inner groups string attribute', async function (assert) { assert.expect(5); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5451,10 +5488,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('form group with newline tag inside', function (assert) { + QUnit.test('form group with newline tag inside', async function (assert) { assert.expect(6); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5508,11 +5545,11 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('custom open record dialog title', function (assert) { + QUnit.test('custom open record dialog title', async function (assert) { assert.expect(1); this.data.partner.records[0].p = [2]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5530,14 +5567,14 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.dom.click(form.$('.o_data_row:first')); + await testUtils.dom.click(form.$('.o_data_row:first')); assert.strictEqual($('.modal .modal-title').first().text().trim(), 'Open: custom label', "modal should use the python field string as title"); form.destroy(); }); - QUnit.test('display translation alert', function (assert) { + QUnit.test('display translation alert', async function (assert) { assert.expect(2); this.data.partner.fields.foo.translate = true; @@ -5546,7 +5583,7 @@ QUnit.module('Views', { var multi_lang = _t.database.multi_lang; _t.database.multi_lang = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5561,15 +5598,15 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name="foo"]'), "test"); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name="foo"]'), "test"); + await testUtils.form.clickSave(form); assert.containsOnce(form, '.o_form_view > .alert > div .oe_field_translate', "should have single translation alert"); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name="display_name"]'), "test2"); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name="display_name"]'), "test2"); + await testUtils.form.clickSave(form); assert.containsN(form, '.o_form_view > .alert > div .oe_field_translate', 2, "should have two translate fields in translation alert"); @@ -5578,7 +5615,7 @@ QUnit.module('Views', { _t.database.multi_lang = multi_lang; }); - QUnit.test('translation alerts are preserved on pager change', function (assert) { + QUnit.test('translation alerts are preserved on pager change', async function (assert) { assert.expect(5); this.data.partner.fields.foo.translate = true; @@ -5586,7 +5623,7 @@ QUnit.module('Views', { var multi_lang = _t.database.multi_lang; _t.database.multi_lang = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5602,27 +5639,27 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name="foo"]'), "test"); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name="foo"]'), "test"); + await testUtils.form.clickSave(form); assert.containsOnce(form, '.o_form_view > .alert > div',"should have a translation alert"); // click on the pager to switch to the next record - form.pager.$('.o_pager_next').click(); + await testUtils.dom.click(form.pager.$('.o_pager_next')); assert.strictEqual(form.$('.o_form_view > .alert > div').length, 0, "should not have a translation alert"); // click on the pager to switch back to the previous record - form.pager.$('.o_pager_previous').click(); + await testUtils.dom.click(form.pager.$('.o_pager_previous')); assert.containsOnce(form, '.o_form_view > .alert > div',"should have a translation alert"); // remove translation alert by click X and check alert even after form reload - form.$('.o_form_view > .alert > .close').click(); + await testUtils.dom.click(form.$('.o_form_view > .alert > .close')); assert.strictEqual(form.$('.o_form_view > .alert > div').length, 0, "should not have a translation alert"); - form.reload(); + await form.reload(); assert.strictEqual(form.$('.o_form_view > .alert > div').length, 0, "should not have a translation alert after reload"); @@ -5630,7 +5667,7 @@ QUnit.module('Views', { _t.database.multi_lang = multi_lang; }); - QUnit.test('translation alerts preseved on reverse breadcrumb', function (assert) { + QUnit.test('translation alerts preseved on reverse breadcrumb', async function (assert) { assert.expect(2); this.data['ir.translation'] = { @@ -5678,23 +5715,21 @@ QUnit.module('Views', { flags: {'search_view': true, 'action_buttons': true}, }]; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: actions, archs: archs, data: this.data, }); - actionManager.doAction(1); - - actionManager.$('.o_form_button_edit').click(); + await actionManager.doAction(1); actionManager.$('input[name="foo"]').val("test").trigger("input"); - actionManager.$('.o_form_button_save').click(); + await testUtils.dom.click(actionManager.$('.o_form_button_save')); assert.strictEqual(actionManager.$('.o_form_view > .alert > div').length, 1, "should have a translation alert"); var currentController = actionManager.getCurrentController().widget; - actionManager.doAction(2, { + await actionManager.doAction(2, { on_reverse_breadcrumb: function () { if (!_.isEmpty(currentController.renderer.alertFields)) { currentController.renderer.displayTranslationAlert(); @@ -5703,7 +5738,7 @@ QUnit.module('Views', { }, }); - $('.o_control_panel .breadcrumb a:first').click(); + await testUtils.dom.click($('.o_control_panel .breadcrumb a:first')); assert.strictEqual(actionManager.$('.o_form_view > .alert > div').length, 1, "should have a translation alert"); @@ -5711,7 +5746,7 @@ QUnit.module('Views', { _t.database.multi_lang = multi_lang; }); - QUnit.test('translate event correctly handled with multiple controllers', function (assert) { + QUnit.test('translate event correctly handled with multiple controllers', async function (assert) { assert.expect(3); this.data.product.fields.name.translate = true; @@ -5721,7 +5756,7 @@ QUnit.module('Views', { var multi_lang = _t.database.multi_lang; _t.database.multi_lang = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5745,23 +5780,23 @@ QUnit.module('Views', { res_id: 1, mockRPC: function (route, args) { if (route === '/web/dataset/call_kw/product/get_formview_id') { - return $.when(false); + return Promise.resolve(false); } else if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { assert.deepEqual(args.args, ["product",37,"name",{}], 'should call "call_button" route'); nbTranslateCalls++; - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, }); - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('[name="product_id"] .o_external_button')); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('[name="product_id"] .o_external_button')); assert.strictEqual($('.modal-body .o_field_translate').length, 1, "there should be a translate button in the modal"); - testUtils.dom.click($('.modal-body .o_field_translate')); + await testUtils.dom.click($('.modal-body .o_field_translate')); assert.strictEqual(nbTranslateCalls, 1, "should call_button translate once"); @@ -5769,12 +5804,12 @@ QUnit.module('Views', { _t.database.multi_lang = multi_lang; }); - QUnit.test('buttons are disabled until status bar action is resolved', function (assert) { + QUnit.test('buttons are disabled until status bar action is resolved', async function (assert) { assert.expect(9); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5811,9 +5846,9 @@ QUnit.module('Views', { assert.strictEqual(form.$('.oe_button_box button:not(:disabled)').length, 1, "stat buttons should be enabled"); - testUtils.dom.clickFirst(form.$('.o_form_statusbar button')); + await testUtils.dom.clickFirst(form.$('.o_form_statusbar button')); - // The unresolved deferred lets us check the state of the buttons + // The unresolved promise lets us check the state of the buttons assert.strictEqual(form.$buttons.find('button:disabled').length, 4, "control panel buttons should be disabled"); assert.containsN(form, '.o_form_statusbar button:disabled', 2, @@ -5822,7 +5857,7 @@ QUnit.module('Views', { "stat buttons should be disabled"); def.resolve(); - + await testUtils.nextTick(); assert.strictEqual(form.$buttons.find('button:not(:disabled)').length, 4, "control panel buttons should be enabled"); assert.strictEqual(form.$('.o_form_statusbar button:not(:disabled)').length, 2, @@ -5833,12 +5868,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('buttons are disabled until button box action is resolved', function (assert) { + QUnit.test('buttons are disabled until button box action is resolved', async function (assert) { assert.expect(9); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5875,9 +5910,9 @@ QUnit.module('Views', { assert.strictEqual(form.$('.oe_button_box button:not(:disabled)').length, 1, "stat buttons should be enabled"); - testUtils.dom.click(form.$('.oe_button_box button')); + await testUtils.dom.click(form.$('.oe_button_box button')); - // The unresolved deferred lets us check the state of the buttons + // The unresolved promise lets us check the state of the buttons assert.strictEqual(form.$buttons.find('button:disabled').length, 4, "control panel buttons should be disabled"); assert.containsN(form, '.o_form_statusbar button:disabled', 2, @@ -5886,7 +5921,7 @@ QUnit.module('Views', { "stat buttons should be disabled"); def.resolve(); - + await testUtils.nextTick(); assert.strictEqual(form.$buttons.find('button:not(:disabled)').length, 4, "control panel buttons should be enabled"); assert.strictEqual(form.$('.o_form_statusbar button:not(:disabled)').length, 2, @@ -5897,10 +5932,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('buttons with "confirm" attribute save before calling the method', function (assert) { + QUnit.test('buttons with "confirm" attribute save before calling the method', async function (assert) { assert.expect(9); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5920,36 +5955,36 @@ QUnit.module('Views', { intercepts: { execute_action: function (event) { assert.step('execute_action'); + event.data.on_success(); }, }, }); // click on button, and cancel in confirm dialog - testUtils.dom.click(form.$('.o_statusbar_buttons button')); + await testUtils.dom.click(form.$('.o_statusbar_buttons button')); assert.ok(form.$('.o_statusbar_buttons button').prop('disabled'), 'button should be disabled'); - testUtils.dom.click($('.modal-footer button.btn-secondary')); + await testUtils.dom.click($('.modal-footer button.btn-secondary')); assert.ok(!form.$('.o_statusbar_buttons button').prop('disabled'), 'button should no longer be disabled'); assert.verifySteps(['default_get']); // click on button, and click on ok in confirm dialog - testUtils.dom.click(form.$('.o_statusbar_buttons button')); - assert.verifySteps(['default_get']); - testUtils.dom.click($('.modal-footer button.btn-primary')); - - assert.verifySteps(['default_get', 'create', 'read', 'execute_action']); + await testUtils.dom.click(form.$('.o_statusbar_buttons button')); + assert.verifySteps([]); + await testUtils.dom.click($('.modal-footer button.btn-primary')); + assert.verifySteps(['create', 'read', 'execute_action']); form.destroy(); }); - QUnit.test('buttons are disabled until action is resolved (in dialogs)', function (assert) { + QUnit.test('buttons are disabled until action is resolved (in dialogs)', async function (assert) { assert.expect(3); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -5982,7 +6017,7 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (args.method === 'get_formview_id') { - return $.when(false); + return Promise.resolve(false); } return this._super.apply(this, arguments); }, @@ -5991,30 +6026,30 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('.o_external_button')); + await testUtils.dom.click(form.$('.o_external_button')); assert.notOk($('.modal .oe_button_box button').attr('disabled'), "stat buttons should be enabled"); - testUtils.dom.click($('.modal .oe_button_box button')); + await testUtils.dom.click($('.modal .oe_button_box button')); assert.ok($('.modal .oe_button_box button').attr('disabled'), "stat buttons should be disabled"); def.resolve(); - + await testUtils.nextTick(); assert.notOk($('.modal .oe_button_box button').attr('disabled'), "stat buttons should be enabled"); form.destroy(); }); - QUnit.test('multiple clicks on save should reload only once', function (assert) { + QUnit.test('multiple clicks on save should reload only once', async function (assert) { assert.expect(4); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6039,13 +6074,13 @@ QUnit.module('Views', { }, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name="foo"]'), "test"); - testUtils.form.clickSave(form); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name="foo"]'), "test"); + await testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); def.resolve(); - + await testUtils.nextTick(); assert.verifySteps([ 'read', // initial read to render the view 'write', // write on save @@ -6055,10 +6090,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('form view is not broken if save operation fails', function (assert) { + QUnit.test('form view is not broken if save operation fails', async function (assert) { assert.expect(5); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6073,19 +6108,19 @@ QUnit.module('Views', { mockRPC: function (route, args) { assert.step(args.method); if (args.method === 'write' && args.args[1].foo === 'incorrect value') { - return $.Deferred().reject(); + return Promise.reject(); } return this._super.apply(this, arguments); }, }); - testUtils.form.clickEdit(form); - testUtils.fields.editInput(form.$('input[name="foo"]'), "incorrect value"); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.fields.editInput(form.$('input[name="foo"]'), "incorrect value"); + await testUtils.form.clickSave(form); - testUtils.fields.editInput(form.$('input[name="foo"]'), "correct value"); + await testUtils.fields.editInput(form.$('input[name="foo"]'), "correct value"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.verifySteps([ 'read', // initial read to render the view @@ -6097,10 +6132,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('support password attribute', function (assert) { + QUnit.test('support password attribute', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6112,7 +6147,7 @@ QUnit.module('Views', { assert.strictEqual(form.$('span[name="foo"]').text(), '***', "password should be displayed with stars"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(form.$('input[name="foo"]').val(), 'yop', "input value should be the password"); assert.strictEqual(form.$('input[name="foo"]').prop('type'), 'password', @@ -6120,10 +6155,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('support autocomplete attribute', function (assert) { + QUnit.test('support autocomplete attribute', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6133,16 +6168,16 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.hasAttrValue(form.$('input[name="display_name"]'), 'autocomplete', 'coucou', "attribute autocomplete should be set"); form.destroy(); }); - QUnit.test('context is correctly passed after save & new in FormViewDialog', function (assert) { + QUnit.test('context is correctly passed after save & new in FormViewDialog', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', res_id: 4, @@ -6174,27 +6209,30 @@ QUnit.module('Views', { return this._super.apply(this, arguments); }, }); - - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.nextTick(); assert.strictEqual($('.modal').length, 1, "One FormViewDialog should be opened"); // set a value on the m2o - testUtils.fields.many2one.clickOpenDropdown('partner_type_id'); - testUtils.fields.many2one.clickHighlightedItem('partner_type_id'); + await testUtils.fields.many2one.clickOpenDropdown('partner_type_id'); + await testUtils.fields.many2one.clickHighlightedItem('partner_type_id'); - testUtils.dom.click($('.modal-footer button:eq(1)')); - testUtils.dom.click($('.modal .o_field_many2one input')); - testUtils.dom.click($('.modal-footer button:first')); + await testUtils.dom.click($('.modal-footer button:eq(1)')); + await testUtils.nextTick(); + await testUtils.dom.click($('.modal .o_field_many2one input')); + await testUtils.fields.many2one.clickHighlightedItem('partner_type_id'); + await testUtils.dom.click($('.modal-footer button:first')); + await testUtils.nextTick(); form.destroy(); }); - QUnit.test('render domain field widget without model', function (assert) { + QUnit.test('render domain field widget without model', async function (assert) { assert.expect(3); this.data.partner.fields.model_name = { string: "Model name", type: "char" }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6209,11 +6247,11 @@ QUnit.module('Views', { assert.strictEqual(args.model, 'test', "should search_count on test"); if (!args.kwargs.domain) { - return $.Deferred().reject({ + return Promise.reject({message:{ code: 200, data: {}, message: "MockServer._getRecords: given domain has to be an array.", - }, $.Event()); + }, event: $.Event()}); } } return this._super.apply(this, arguments); @@ -6222,13 +6260,13 @@ QUnit.module('Views', { assert.strictEqual(form.$('.o_field_widget[name="display_name"]').text(), "Select a model to add a filter.", "should contain an error message saying the model is missing"); - testUtils.fields.editInput(form.$('input[name="model_name"]'), "test"); + await testUtils.fields.editInput(form.$('input[name="model_name"]'), "test"); assert.notStrictEqual(form.$('.o_field_widget[name="display_name"]').text(), "Select a model to add a filter.", "should not contain an error message anymore"); form.destroy(); }); - QUnit.test('readonly fields are not sent when saving', function (assert) { + QUnit.test('readonly fields are not sent when saving', async function (assert) { assert.expect(6); // define an onchange on display_name to check that the value of readonly @@ -6239,7 +6277,7 @@ QUnit.module('Views', { }; var checkOnchange = false; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6274,31 +6312,33 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.nextTick(); assert.strictEqual($('.modal input.o_field_widget[name=foo]').length, 1, - 'foo should be editable'); + 'foo should be editable'); checkOnchange = true; - testUtils.fields.editInput($('.modal .o_field_widget[name=foo]'), 'foo value'); - testUtils.fields.editInput($('.modal .o_field_widget[name=display_name]'), 'readonly'); + await testUtils.fields.editInput($('.modal .o_field_widget[name=foo]'), 'foo value'); + await testUtils.fields.editInput($('.modal .o_field_widget[name=display_name]'), 'readonly'); assert.strictEqual($('.modal span.o_field_widget[name=foo]').length, 1, - 'foo should be readonly'); - testUtils.dom.clickFirst($('.modal-footer .btn-primary')); + 'foo should be readonly'); + await testUtils.dom.clickFirst($('.modal-footer .btn-primary')); + await testUtils.nextTick(); checkOnchange = false; - testUtils.dom.click(form.$('.o_data_row')); + await testUtils.dom.click(form.$('.o_data_row')); assert.strictEqual($('.modal .o_field_widget[name=foo]').text(), 'foo value', - "the edited value should have been kept"); - testUtils.dom.clickFirst($('.modal-footer .btn-primary')); - - testUtils.form.clickSave(form); // save the record + "the edited value should have been kept"); + await testUtils.dom.clickFirst($('.modal-footer .btn-primary')); + await testUtils.nextTick(); + await testUtils.form.clickSave(form); // save the record form.destroy(); }); - QUnit.test('id is False in evalContext for new records', function (assert) { + QUnit.test('id is False in evalContext for new records', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6311,8 +6351,8 @@ QUnit.module('Views', { assert.hasClass(form.$('.o_field_widget[name=foo]'),'o_readonly_modifier', "foo should be readonly in 'Create' mode"); - testUtils.form.clickSave(form); - testUtils.form.clickEdit(form); + await testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); assert.doesNotHaveClass(form.$('.o_field_widget[name=foo]'), 'o_readonly_modifier', "foo should not be readonly anymore"); @@ -6320,11 +6360,11 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('delete a duplicated record', function (assert) { + QUnit.test('delete a duplicated record', async function (assert) { assert.expect(5); var newRecordID; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6350,20 +6390,20 @@ QUnit.module('Views', { }); // duplicate record 1 - testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(form.sidebar.$('a:contains(Duplicate)')); + await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(form.sidebar.$('a:contains(Duplicate)')); assert.containsOnce(form, '.o_form_editable', "form should be in edit mode"); assert.strictEqual(form.$('.o_field_widget').val(), 'first record (copy)', "duplicated record should have correct name"); - testUtils.form.clickSave(form); // save duplicated record + await testUtils.form.clickSave(form); // save duplicated record // delete duplicated record - testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(form.sidebar.$('a:contains(Delete)')); + await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(form.sidebar.$('a:contains(Delete)')); assert.strictEqual($('.modal').length, 1, "should have opened a confirm dialog"); - testUtils.dom.click($('.modal-footer .btn-primary')); + await testUtils.dom.click($('.modal-footer .btn-primary')); assert.strictEqual(form.$('.o_field_widget').text(), 'first record', "should have come back to previous record"); @@ -6371,13 +6411,13 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('display tooltips for buttons', function (assert) { + QUnit.test('display tooltips for buttons', async function (assert) { assert.expect(2); var initialDebugMode = config.debug; config.debug = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6409,7 +6449,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('reload event is handled only once', function (assert) { + QUnit.test('reload event is handled only once', async function (assert) { // In this test, several form controllers are nested (two of them are // opened in dialogs). When the users clicks on save in the last // opened dialog, a 'reload' event is triggered up to reload the (direct) @@ -6422,7 +6462,7 @@ QUnit.module('Views', { '<field name="display_name"/>' + '<field name="trululu"/>' + '</form>'; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6434,7 +6474,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { assert.step(args.method); if (args.method === 'get_formview_id') { - return $.when(false); + return Promise.resolve(false); } return this._super.apply(this, arguments); }, @@ -6443,11 +6483,11 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('.o_external_button')); - testUtils.dom.click($('.modal .o_external_button')); + await testUtils.dom.click(form.$('.o_external_button')); + await testUtils.dom.click($('.modal .o_external_button')); - testUtils.fields.editInput($('.modal:nth(1) .o_field_widget[name=display_name]'), 'new name'); - testUtils.dom.click($('.modal:nth(1) footer .btn-primary').first()); + await testUtils.fields.editInput($('.modal:nth(1) .o_field_widget[name=display_name]'), 'new name'); + await testUtils.dom.click($('.modal:nth(1) footer .btn-primary').first()); assert.strictEqual($('.modal .o_field_widget[name=trululu] input').val(), 'new name', "record should have been reloaded"); @@ -6466,12 +6506,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('process the context for inline subview', function (assert) { + QUnit.test('process the context for inline subview', async function (assert) { assert.expect(1); this.data.partner.records[0].p = [2]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6493,12 +6533,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('process the context for subview not inline', function (assert) { + QUnit.test('process the context for subview not inline', async function (assert) { assert.expect(1); this.data.partner.records[0].p = [2]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6521,12 +6561,12 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('can toggle column in x2many in sub form view', function (assert) { + QUnit.test('can toggle column in x2many in sub form view', async function (assert) { assert.expect(2); this.data.partner.records[2].p = [1,2]; this.data.partner.fields.foo.sortable = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6536,7 +6576,7 @@ QUnit.module('Views', { res_id: 1, mockRPC: function (route, args) { if (route === '/web/dataset/call_kw/partner/get_formview_id') { - return $.when(false); + return Promise.resolve(false); } return this._super.apply(this, arguments); }, @@ -6551,20 +6591,20 @@ QUnit.module('Views', { }, viewOptions: {mode: 'edit'}, }); - testUtils.dom.click(form.$('.o_external_button')); + await testUtils.dom.click(form.$('.o_external_button')); assert.strictEqual($('.modal-body .o_form_view .o_list_view .o_data_cell').text(), "yopblip", "table has some initial order"); - testUtils.dom.click($('.modal-body .o_form_view .o_list_view th')); + await testUtils.dom.click($('.modal-body .o_form_view .o_list_view th')); assert.strictEqual($('.modal-body .o_form_view .o_list_view .o_data_cell').text(), "blipyop", "table is now sorted"); form.destroy(); }); - QUnit.test('rainbowman attributes correctly passed on button click', function (assert) { + QUnit.test('rainbowman attributes correctly passed on button click', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6578,14 +6618,14 @@ QUnit.module('Views', { var effectDescription = pyUtils.py_eval(event.data.action_data.effect); assert.deepEqual(effectDescription, {message: 'Congrats!'}, "should have correct effect description"); } - } + }, }); - testUtils.dom.click(form.$('.o_form_statusbar .btn-secondary')); + await testUtils.dom.click(form.$('.o_form_statusbar .btn-secondary')); form.destroy(); }); - QUnit.test('basic support for widgets', function (assert) { + QUnit.test('basic support for widgets', async function (assert) { assert.expect(1); var MyWidget = Widget.extend({ @@ -6598,7 +6638,7 @@ QUnit.module('Views', { }); widgetRegistry.add('test', MyWidget); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6616,10 +6656,10 @@ QUnit.module('Views', { delete widgetRegistry.map.test; }); - QUnit.test('support header button as widgets on form statusbar', function (assert) { + QUnit.test('support header button as widgets on form statusbar', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6638,7 +6678,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('basic support for widgets', function (assert) { + QUnit.test('basic support for widgets', async function (assert) { assert.expect(1); var MyWidget = Widget.extend({ @@ -6654,7 +6694,7 @@ QUnit.module('Views', { }); widgetRegistry.add('test', MyWidget); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6664,7 +6704,7 @@ QUnit.module('Views', { '</form>', }); - testUtils.fields.editInput(form.$('input[name="foo"]'), "I am alive"); + await testUtils.fields.editInput(form.$('input[name="foo"]'), "I am alive"); assert.strictEqual(form.$('.o_widget').text(), 'I am alive!', "widget should have been updated"); @@ -6673,10 +6713,10 @@ QUnit.module('Views', { }); - QUnit.test('bounce edit button in readonly mode', function (assert) { + QUnit.test('bounce edit button in readonly mode', async function (assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6694,24 +6734,24 @@ QUnit.module('Views', { }); // in readonly - testUtils.dom.click(form.$('[name="display_name"]')); + await testUtils.dom.click(form.$('[name="display_name"]')); assert.verifySteps(['bounce']); // in edit - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('[name="display_name"]')); - assert.verifySteps(['bounce']); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('[name="display_name"]')); + assert.verifySteps([]); form.destroy(); }); - QUnit.test('proper stringification in debug mode tooltip', function (assert) { + QUnit.test('proper stringification in debug mode tooltip', async function (assert) { assert.expect(4); var initialDebugMode = config.debug; config.debug = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6740,14 +6780,14 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('autoresize of text fields is done when switching to edit mode', function (assert) { + QUnit.test('autoresize of text fields is done when switching to edit mode', async function (assert) { assert.expect(4); this.data.partner.fields.text_field = { string: 'Text field', type: 'text' }; this.data.partner.fields.text_field.default = "some\n\nmulti\n\nline\n\ntext\n"; this.data.partner.records[0].text_field = "a\nb\nc\nd\ne\nf"; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6761,7 +6801,7 @@ QUnit.module('Views', { }); // switch to edit mode to ensure that autoresize is correctly done - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); var height = form.$('.o_field_widget[name=text_field]').height(); // focus the field to manually trigger autoresize form.$('.o_field_widget[name=text_field]').trigger('focus'); @@ -6772,8 +6812,8 @@ QUnit.module('Views', { assert.ok(height > 80, "textarea should have an height of at least 80px"); // save and create a new record to ensure that autoresize is correctly done - testUtils.form.clickSave(form); - testUtils.form.clickCreate(form); + await testUtils.form.clickSave(form); + await testUtils.form.clickCreate(form); height = form.$('.o_field_widget[name=text_field]').height(); // focus the field to manually trigger autoresize form.$('.o_field_widget[name=text_field]').trigger('focus'); @@ -6784,11 +6824,11 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('check if the view destroys all widgets and instances', function (assert) { + QUnit.test('check if the view destroys all widgets and instances', async function (assert) { assert.expect(1); var instanceNumber = 0; - testUtils.mock.patch(mixins.ParentedMixin, { + await testUtils.mock.patch(mixins.ParentedMixin, { init: function () { instanceNumber++; return this._super.apply(this, arguments); @@ -6844,13 +6884,13 @@ QUnit.module('Views', { res_id: 1, }; - var form = createView(params); + var form = await createView(params); form.destroy(); var initialInstanceNumber = instanceNumber; instanceNumber = 0; - form = createView(params); + form = await createView(params); // call destroy function of controller to ensure that it correctly destroys everything form.__destroy(); @@ -6861,13 +6901,13 @@ QUnit.module('Views', { form.destroy(); - testUtils.mock.unpatch(mixins.ParentedMixin); + await testUtils.mock.unpatch(mixins.ParentedMixin); }); - QUnit.test('do not change pager when discarding current record', function (assert) { + QUnit.test('do not change pager when discarding current record', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6884,8 +6924,8 @@ QUnit.module('Views', { assert.strictEqual(form.pager.$('.o_pager_counter').text().trim(), '2 / 2', 'pager should indicate that we are on second record'); - testUtils.form.clickEdit(form); - testUtils.form.clickDiscard(form); + await testUtils.form.clickEdit(form); + await testUtils.form.clickDiscard(form); assert.strictEqual(form.pager.$('.o_pager_counter').text().trim(), '2 / 2', 'pager should not have changed'); @@ -6893,14 +6933,14 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('edition in form view on a "noCache" model', function (assert) { - assert.expect(4); + QUnit.test('edition in form view on a "noCache" model', async function (assert) { + assert.expect(5); - testUtils.mock.patch(BasicModel, { + await testUtils.mock.patch(BasicModel, { noCacheModels: BasicModel.prototype.noCacheModels.concat(['partner']), }); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6922,23 +6962,25 @@ QUnit.module('Views', { }); core.bus.on('clear_cache', form, assert.step.bind(assert, 'clear_cache')); - testUtils.fields.editInput(form.$('.o_field_widget[name=display_name]'), 'new value'); - testUtils.form.clickSave(form); + await testUtils.fields.editInput(form.$('.o_field_widget[name=display_name]'), 'new value'); + await testUtils.form.clickSave(form); assert.verifySteps(['write', 'clear_cache']); form.destroy(); - testUtils.mock.unpatch(BasicModel); + await testUtils.mock.unpatch(BasicModel); + + assert.verifySteps(['clear_cache']); // triggered by the test environment on destroy }); - QUnit.test('creation in form view on a "noCache" model', function (assert) { - assert.expect(4); + QUnit.test('creation in form view on a "noCache" model', async function (assert) { + assert.expect(5); - testUtils.mock.patch(BasicModel, { + await testUtils.mock.patch(BasicModel, { noCacheModels: BasicModel.prototype.noCacheModels.concat(['partner']), }); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6956,23 +6998,25 @@ QUnit.module('Views', { }); core.bus.on('clear_cache', form, assert.step.bind(assert, 'clear_cache')); - testUtils.fields.editInput(form.$('.o_field_widget[name=display_name]'), 'value'); - testUtils.form.clickSave(form); + await testUtils.fields.editInput(form.$('.o_field_widget[name=display_name]'), 'value'); + await testUtils.form.clickSave(form); assert.verifySteps(['create', 'clear_cache']); form.destroy(); - testUtils.mock.unpatch(BasicModel); + await testUtils.mock.unpatch(BasicModel); + + assert.verifySteps(['clear_cache']); // triggered by the test environment on destroy }); - QUnit.test('deletion in form view on a "noCache" model', function (assert) { - assert.expect(4); + QUnit.test('deletion in form view on a "noCache" model', async function (assert) { + assert.expect(5); - testUtils.mock.patch(BasicModel, { + await testUtils.mock.patch(BasicModel, { noCacheModels: BasicModel.prototype.noCacheModels.concat(['partner']), }); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -6994,17 +7038,19 @@ QUnit.module('Views', { }); core.bus.on('clear_cache', form, assert.step.bind(assert, 'clear_cache')); - testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(form.sidebar.$('a:contains(Delete)')); - testUtils.dom.click($('.modal-footer .btn-primary')); + await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(form.sidebar.$('a:contains(Delete)')); + await testUtils.dom.click($('.modal-footer .btn-primary')); assert.verifySteps(['unlink', 'clear_cache']); form.destroy(); - testUtils.mock.unpatch(BasicModel); + await testUtils.mock.unpatch(BasicModel); + + assert.verifySteps(['clear_cache']); // triggered by the test environment on destroy }); - QUnit.test('reload currencies when writing on records of model res.currency', function (assert) { + QUnit.test('reload currencies when writing on records of model res.currency', async function (assert) { assert.expect(5); this.data['res.currency'] = { @@ -7012,7 +7058,7 @@ QUnit.module('Views', { records: [{id: 1, display_name: "some currency"}], }; - var form = createView({ + var form = await createView({ View: FormView, model: 'res.currency', data: this.data, @@ -7032,8 +7078,8 @@ QUnit.module('Views', { }, }); - testUtils.fields.editInput(form.$('.o_field_widget[name=display_name]'), 'new value'); - testUtils.form.clickSave(form); + await testUtils.fields.editInput(form.$('.o_field_widget[name=display_name]'), 'new value'); + await testUtils.form.clickSave(form); assert.verifySteps([ 'read', @@ -7045,8 +7091,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('a popup window should automatically close after a do_action event', function (assert) { - + QUnit.test('a popup window should automatically close after a do_action event', async function (assert) { // Having clicked on a one2many in a form view and clicked on a many2one // field in the resulting popup window that popup window should automatically close. @@ -7055,7 +7100,7 @@ QUnit.module('Views', { this.data.partner.records[0].product_ids = [37]; this.data.product.records[0].partner_type_id = 12; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7068,7 +7113,7 @@ QUnit.module('Views', { res_id: 1, mockRPC: function (route, args) { if (args.method === 'get_formview_action' && args.model === 'partner_type') { - return $.when(); + return Promise.resolve(); } return this._super(route, args); }, @@ -7079,16 +7124,16 @@ QUnit.module('Views', { }, }); // Open one2many - testUtils.dom.click(form.$('.o_data_row')); + await testUtils.dom.click(form.$('.o_data_row')); assert.strictEqual($('.modal-content').length, 1, "a popup window should have opened"); // Click on many2one and trigger do_action - testUtils.dom.click($('.modal-content a[name="partner_type_id"]')); + await testUtils.dom.click($('.modal-content a[name="partner_type_id"]')); assert.strictEqual($('.modal-content').length, 0, "the popup window should have closed"); form.destroy(); }); - QUnit.test('all popup windows should automatically close after a do_action event', function (assert) { + QUnit.test('all popup windows should automatically close after a do_action event', async function (assert) { // Having clicked successively on two different one2many in form views // and clicked on a many2one in the last popup window all popup @@ -7100,7 +7145,7 @@ QUnit.module('Views', { this.data.partner.records[1].product_ids = [37]; this.data.product.records[0].partner_type_id = 12; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7118,7 +7163,7 @@ QUnit.module('Views', { res_id: 1, mockRPC: function (route, args) { if (args.method === 'get_formview_action' && args.model === 'partner_type') { - return $.when(); + return Promise.resolve(); } return this._super(route, args); }, @@ -7129,21 +7174,21 @@ QUnit.module('Views', { }, }); // Open two one2manys - testUtils.dom.click(form.$('.o_data_row')); - testUtils.dom.click($('.modal-content .o_data_row')); + await testUtils.dom.click(form.$('.o_data_row')); + await testUtils.dom.click($('.modal-content .o_data_row')); assert.strictEqual($('.modal-content').length, 2, "Two popup windows should have opened."); // Click on many2one and trigger do_action - testUtils.dom.click($('.modal-content a[name="partner_type_id"]')); + await testUtils.dom.click($('.modal-content a[name="partner_type_id"]')); assert.strictEqual($('.modal-content').length, 0, "All popup windows should have closed."); form.destroy(); }); - QUnit.test('keep editing after call_button fail', function (assert) { + QUnit.test('keep editing after call_button fail', async function (assert) { assert.expect(4); var values; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7175,21 +7220,20 @@ QUnit.module('Views', { }); // add a row and partially fill it - form.$('.o_field_x2many_list_row_add a').click(); - form.$('input[name=display_name]').val('abc').trigger('input'); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.fields.editInput(form.$('input[name=display_name]'), 'abc'); // click button which will trigger_up 'execute_action' (this will save) values = { display_name: 'abc', product_id: false, }; - form.$('button.p').click(); - + await testUtils.dom.click(form.$('button.p')); // edit the new row again and set a many2one value - form.$('.o_form_view .o_field_one2many .o_data_row .o_data_cell').click(); - var $dropdown = form.$('.o_field_many2one input').autocomplete('widget'); - form.$('.o_field_many2one input').click(); - $dropdown.find('li:first()').click(); + await testUtils.dom.clickLast(form.$('.o_form_view .o_field_one2many .o_data_row .o_data_cell')); + await testUtils.nextTick(); + await testUtils.fields.many2one.clickOpenDropdown('product_id'); + await testUtils.fields.many2one.clickHighlightedItem('product_id'); assert.strictEqual(form.$('.o_field_many2one input').val(), 'xphone', "value of the m2o should have been correctly updated"); @@ -7197,15 +7241,15 @@ QUnit.module('Views', { values = { product_id: 37, }; - form.$buttons.find('.o_form_button_save').click(); + await testUtils.form.clickSave(form); form.destroy(); }); - QUnit.test('asynchronous rendering of a widget tag', function (assert) { + QUnit.test('asynchronous rendering of a widget tag', async function (assert) { assert.expect(1); - var def1 = $.Deferred(); + var def1 = testUtils.makeTestPromise(); var MyWidget = Widget.extend({ willStart: function() { @@ -7215,7 +7259,7 @@ QUnit.module('Views', { widgetRegistry.add('test', MyWidget); - createAsyncView({ + createView({ View: FormView, model: 'partner', data: this.data, @@ -7230,9 +7274,10 @@ QUnit.module('Views', { }); def1.resolve(); + await testUtils.nextTick(); }); - QUnit.test('no deadlock when saving with uncommitted changes', function (assert) { + QUnit.test('no deadlock when saving with uncommitted changes', async function (assert) { // Before saving a record, all field widgets are asked to commit their changes (new values // that they wouldn't have sent to the model yet). This test is added alongside a bug fix // ensuring that we don't end up in a deadlock when a widget actually has some changes to @@ -7245,7 +7290,7 @@ QUnit.module('Views', { // on 'Save'. assert.expect(6); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7254,16 +7299,18 @@ QUnit.module('Views', { assert.step(args.method); return this._super.apply(this, arguments); }, - // we set a fieldDebounce to precisely mock the behavior of the webclient: changes are - // not sent to the model at keystrokes, but when the input is left + // we set a fieldDebounce to precisely mock the behavior of the webclient: changes are + // not sent to the model at keystrokes, but when the input is left fieldDebounce: 5000, }); - testUtils.fields.editInput(form.$('input[name=foo]'), 'some foo value'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'some foo value'); // manually save the record, to prevent the field widget to notify the model of its new // value before being requested to form.saveRecord(); + await testUtils.nextTick(); + assert.containsOnce(form, '.o_form_readonly', "form view should be in readonly"); assert.strictEqual(form.$('.o_form_view').text().trim(), 'some foo value', "foo field should have correct value"); @@ -7272,7 +7319,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('save record with onchange on one2many with required field', function (assert) { + QUnit.test('save record with onchange on one2many with required field', async function (assert) { // in this test, we have a one2many with a required field, whose value is // set by an onchange on another field ; we manually set the value of that // first field, and directly click on Save (before the onchange RPC returns @@ -7287,7 +7334,7 @@ QUnit.module('Views', { }; var onchangeDef; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7302,7 +7349,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { var result = this._super.apply(this, arguments); if (args.method === 'onchange') { - return $.when(onchangeDef).then(_.constant(result)); + return Promise.resolve(onchangeDef).then(_.constant(result)); } if (args.method === 'create') { assert.step('create'); @@ -7313,28 +7360,29 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), '', "display_name should be the empty string by default"); assert.strictEqual(form.$('.o_field_widget[name=foo]').val(), '', "foo should be the empty string by default"); - onchangeDef = $.Deferred(); // delay the onchange + onchangeDef = testUtils.makeTestPromise(); // delay the onchange - testUtils.fields.editInput(form.$('.o_field_widget[name=display_name]'), 'some value'); + await testUtils.fields.editInput(form.$('.o_field_widget[name=display_name]'), 'some value'); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.step('resolve'); onchangeDef.resolve(); + await testUtils.nextTick(); assert.verifySteps(['resolve', 'create']); form.destroy(); }); - QUnit.test('call canBeRemoved while saving', function (assert) { + QUnit.test('call canBeRemoved while saving', async function (assert) { assert.expect(10); this.data.partner.onchanges = { @@ -7344,8 +7392,8 @@ QUnit.module('Views', { }; var onchangeDef; - var createDef = $.Deferred(); - var form = createView({ + var createDef = testUtils.makeTestPromise(); + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7353,41 +7401,43 @@ QUnit.module('Views', { mockRPC: function (route, args) { var result = this._super.apply(this, arguments); if (args.method === 'onchange') { - return $.when(onchangeDef).then(_.constant(result)); + return Promise.resolve(onchangeDef).then(_.constant(result)); } if (args.method === 'create') { - return $.when(createDef).then(_.constant(result)); + return Promise.resolve(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'); + onchangeDef = testUtils.makeTestPromise(); + await 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')); + await 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(); + await testUtils.nextTick(); 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(); - + await testUtils.nextTick(); 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(); + await testUtils.nextTick(); assert.hasClass(form.$('.o_form_view'), 'o_form_readonly'); assert.strictEqual(form.$('.o_field_widget[name=display_name]').text(), 'changed'); @@ -7397,7 +7447,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('domain returned by onchange is cleared on discard', function (assert) { + QUnit.test('domain returned by onchange is cleared on discard', async function (assert) { assert.expect(4); this.data.partner.onchanges = { @@ -7406,7 +7456,7 @@ QUnit.module('Views', { var domain = ['id', '=', 1]; var expectedDomain = domain; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7414,7 +7464,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { if (args.method === 'onchange' && args.args[0][0] === 1) { // onchange returns a domain only on record 1 - return $.when({ + return Promise.resolve({ domain: { trululu: domain, }, @@ -7435,30 +7485,30 @@ QUnit.module('Views', { assert.strictEqual(form.$('input[name=foo]').val(), 'yop', "should be on record 1"); // change foo to trigger the onchange - testUtils.fields.editInput(form.$('input[name=foo]'), 'new value'); + await testUtils.fields.editInput(form.$('input[name=foo]'), 'new value'); // open many2one dropdown to check if the domain is applied - testUtils.fields.many2one.clickOpenDropdown('trululu'); + await testUtils.fields.many2one.clickOpenDropdown('trululu'); // switch to another record (should ask to discard changes, and reset the domain) - testUtils.dom.click(form.pager.$('.o_pager_next')); + await testUtils.dom.click(form.pager.$('.o_pager_next')); // discard changes by clicking on confirm in the dialog - testUtils.dom.click($('.modal .modal-footer .btn-primary:first')); + await testUtils.dom.click($('.modal .modal-footer .btn-primary:first')); assert.strictEqual(form.$('input[name=foo]').val(), 'blip', "should be on record 2"); // open many2one dropdown to check if the domain is applied expectedDomain = []; - testUtils.fields.many2one.clickOpenDropdown('trululu'); + await testUtils.fields.many2one.clickOpenDropdown('trululu'); form.destroy(); }); - QUnit.test('discard after a failed save', function (assert) { + QUnit.test('discard after a failed save', async function (assert) { assert.expect(2); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ data: this.data, archs: { 'partner,false,form': '<form>' + @@ -7472,13 +7522,13 @@ QUnit.module('Views', { actions: this.actions, }); - actionManager.doAction(1); + await actionManager.doAction(1); - testUtils.dom.click('.o_control_panel .o-kanban-button-new'); + await testUtils.dom.click('.o_control_panel .o-kanban-button-new'); //cannot save because there is a required field - testUtils.dom.click('.o_control_panel .o_form_button_save'); - testUtils.dom.click('.o_control_panel .o_form_button_cancel'); + await testUtils.dom.click('.o_control_panel .o_form_button_save'); + await testUtils.dom.click('.o_control_panel .o_form_button_cancel'); assert.containsNone(actionManager, '.o_form_view'); assert.containsOnce(actionManager, '.o_kanban_view'); @@ -7486,10 +7536,10 @@ QUnit.module('Views', { actionManager.destroy(); }); - QUnit.test("one2many create record dialog shouldn't have a 'remove' button", function (assert) { + QUnit.test("one2many create record dialog shouldn't have a 'remove' button", async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7510,8 +7560,8 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickCreate(form); - form.$('.o-kanban-button-new').click(); + await testUtils.form.clickCreate(form); + await testUtils.dom.click(form.$('.o-kanban-button-new')); assert.containsOnce(document.body, '.modal'); assert.strictEqual($('.modal .modal-footer .o_btn_remove').length, 0, @@ -7522,10 +7572,10 @@ QUnit.module('Views', { QUnit.module('FormViewTABMainButtons'); - QUnit.test('using tab in an empty required string field should not move to the next field',function(assert) { + QUnit.test('using tab in an empty required string field should not move to the next field', async function(assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7539,7 +7589,7 @@ QUnit.module('Views', { '</form>', }); - testUtils.dom.click(form.$('input[name=display_name]')); + await testUtils.dom.click(form.$('input[name=display_name]')); assert.strictEqual(form.$('input[name="display_name"]')[0], document.activeElement, "display_name should be focused"); form.$('input[name="display_name"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB})); @@ -7550,10 +7600,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('using tab in an empty required date field should not move to the next field',function(assert) { + QUnit.test('using tab in an empty required date field should not move to the next field', async function(assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7567,7 +7617,7 @@ QUnit.module('Views', { '</form>', }); - testUtils.dom.click(form.$('input[name=date]')); + await testUtils.dom.click(form.$('input[name=date]')); assert.strictEqual(form.$('input[name="date"]')[0], document.activeElement, "display_name should be focused"); form.$('input[name="date"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB})); @@ -7577,10 +7627,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('Edit button get the focus when pressing TAB from form', function (assert) { + QUnit.test('Edit button get the focus when pressing TAB from form', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7593,7 +7643,7 @@ QUnit.module('Views', { }); // in edit - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); form.$('input[name="display_name"]').focus().trigger($.Event('keydown', {which: $.ui.keyCode.TAB})); assert.strictEqual(form.$buttons.find('.btn-primary:visible')[0], document.activeElement, "the first primary button (save) should be focused"); @@ -7602,9 +7652,9 @@ QUnit.module('Views', { QUnit.module('FormViewTABFormButtons'); - QUnit.test('In Edition mode, after navigating to the last field, the default button when pressing TAB is SAVE', function (assert) { + QUnit.test('In Edition mode, after navigating to the last field, the default button when pressing TAB is SAVE', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7634,10 +7684,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('In READ mode, the default button with focus is the first primary button of the form', function (assert) { + QUnit.test('In READ mode, the default button with focus is the first primary button of the form', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7662,10 +7712,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('In READ mode, the default button when pressing TAB is EDIT when there is no primary button on the form', function (assert) { + QUnit.test('In READ mode, the default button when pressing TAB is EDIT when there is no primary button on the form', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7690,10 +7740,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('In Edition mode, when an attribute is dynamically required (and not required), TAB should navigate to the next field', function (assert) { + QUnit.test('In Edition mode, when an attribute is dynamically required (and not required), TAB should navigate to the next field', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7718,10 +7768,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('In Edition mode, when an attribute is dynamically required, TAB should stop on the field if it is required', function (assert) { + QUnit.test('In Edition mode, when an attribute is dynamically required, TAB should stop on the field if it is required', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7739,7 +7789,7 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(form.$('div[name="bar"]>input')); + await testUtils.dom.click(form.$('div[name="bar"]>input')); form.$('input[name="foo"]').focus(); $(document.activeElement).trigger($.Event('keydown', {which: $.ui.keyCode.TAB})); @@ -7747,10 +7797,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('display tooltips for save and discard buttons', function (assert) { + QUnit.test('display tooltips for save and discard buttons', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7762,12 +7812,13 @@ QUnit.module('Views', { form.$buttons.find('.o_form_buttons_edit').tooltip('show',false); assert.strictEqual($('.tooltip .oe_tooltip_string').length, 1, "should have rendered a tooltip"); + await testUtils.nextTick(); form.destroy(); }); - QUnit.test('if the focus is on the save button, hitting ENTER should save', function (assert) { + QUnit.test('if the focus is on the save button, hitting ENTER should save', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7786,14 +7837,15 @@ QUnit.module('Views', { }); form.$buttons.find('.o_form_button_save') - .focus() - .trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); + .focus() + .trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); + await testUtils.nextTick(); form.destroy(); }); - QUnit.test('if the focus is on the discard button, hitting ENTER should save', function (assert) { + QUnit.test('if the focus is on the discard button, hitting ENTER should save', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7812,14 +7864,15 @@ QUnit.module('Views', { }); form.$buttons.find('.o_form_button_cancel') - .focus() - .trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); + .focus() + .trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); + await testUtils.nextTick(); form.destroy(); }); - QUnit.test('if the focus is on the save button, hitting ESCAPE should discard', function (assert) { + QUnit.test('if the focus is on the save button, hitting ESCAPE should discard', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7838,14 +7891,15 @@ QUnit.module('Views', { }); form.$buttons.find('.o_form_button_save') - .focus() - .trigger($.Event('keydown', {which: $.ui.keyCode.ESCAPE})); + .focus() + .trigger($.Event('keydown', {which: $.ui.keyCode.ESCAPE})); + await testUtils.nextTick(); form.destroy(); }); - QUnit.test('if the focus is on the discard button, hitting ESCAPE should discard', function (assert) { + QUnit.test('if the focus is on the discard button, hitting ESCAPE should discard', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -7864,18 +7918,19 @@ QUnit.module('Views', { }); form.$buttons.find('.o_form_button_cancel') - .focus() - .trigger($.Event('keydown', {which: $.ui.keyCode.ESCAPE})); + .focus() + .trigger($.Event('keydown', {which: $.ui.keyCode.ESCAPE})); + await testUtils.nextTick(); form.destroy(); }); - QUnit.test('if the focus is on the save button, hitting TAB should not move to the next button', function (assert) { + QUnit.test('if the focus is on the save button, hitting TAB should not move to the next button', async function (assert) { assert.expect(1); /* this test has only one purpose: to say that it is normal that the focus stays within a button primary even after the TAB key has been pressed. It is not possible here to execute the default action of the TAB on a button : https://stackoverflow.com/questions/32428993/why-doesnt-simulating-a-tab-keypress-move-focus-to-the-next-input-field so writing a test that will always succeed is not useful. - */ + */ assert.ok("Behavior can't be tested"); }); }); diff --git a/addons/web/static/tests/views/graph_tests.js b/addons/web/static/tests/views/graph_tests.js index d9cec90bc4e..cae561e4889 100644 --- a/addons/web/static/tests/views/graph_tests.js +++ b/addons/web/static/tests/views/graph_tests.js @@ -59,10 +59,10 @@ QUnit.module('Views', { QUnit.module('GraphView'); - QUnit.test('simple graph rendering', function (assert) { + QUnit.test('simple graph rendering', async function (assert) { assert.expect(4); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -92,10 +92,10 @@ QUnit.module('Views', { }); }); - QUnit.test('default type attribute', function (assert) { + QUnit.test('default type attribute', async function (assert) { assert.expect(1); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -107,10 +107,10 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('title attribute', function (assert) { + QUnit.test('title attribute', async function (assert) { assert.expect(1); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -123,10 +123,10 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('switching mode', function (assert) { + QUnit.test('switching mode', async function (assert) { assert.expect(6); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -139,7 +139,7 @@ QUnit.module('Views', { 'bar type button should not be active'); assert.hasClass(graph.$buttons.find('button[data-mode="line"]'),'active', 'line type button should be active'); - testUtils.dom.click(graph.$buttons.find('button[data-mode="bar"]')); + await testUtils.dom.click(graph.$buttons.find('button[data-mode="bar"]')); assert.strictEqual(graph.renderer.state.mode, "bar", "should be in bar chart mode by default"); assert.doesNotHaveClass(graph.$buttons.find('button[data-mode="line"]'), 'active', 'line type button should not be active'); @@ -148,13 +148,13 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('displaying line chart with only 1 data point', function (assert) { + QUnit.test('displaying line chart with only 1 data point', async function (assert) { assert.expect(1); // this test makes sure the line chart does not crash when only one data // point is displayed. var done = assert.async(); this.data.foo.records = this.data.foo.records.slice(0,1); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -169,12 +169,12 @@ QUnit.module('Views', { }); }); - QUnit.test('displaying line chart data with multiple groupbys', function (assert) { + QUnit.test('displaying line chart data with multiple groupbys', async function (assert) { // this test makes sure the line chart shows all data labels (X axis) when // it is grouped by several fields assert.expect(3); - var graph = createView({ + var graph = await createView({ View: GraphView, model: 'foo', data: this.data, @@ -192,10 +192,10 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('displaying bar chart data with multiple groupbys', function (assert) { + QUnit.test('displaying bar chart data with multiple groupbys', async function (assert) { assert.expect(3); - var graph = createView({ + var graph = await createView({ View: GraphView, model: 'foo', data: this.data, @@ -213,13 +213,13 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('switching measures', function (assert) { + QUnit.test('switching measures', async function (assert) { var done = assert.async(); assert.expect(4); var rpcCount = 0; - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -249,11 +249,11 @@ QUnit.module('Views', { }); }); - QUnit.test('no content helper (bar chart)', function (assert) { + QUnit.test('no content helper (bar chart)', async function (assert) { assert.expect(2); this.data.foo.records = []; - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -268,11 +268,11 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('no content helper (pie chart)', function (assert) { + QUnit.test('no content helper (pie chart)', async function (assert) { assert.expect(2); this.data.foo.records = [] - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -287,10 +287,10 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('render pie chart in comparison mode', function (assert) { + QUnit.test('render pie chart in comparison mode', async function (assert) { assert.expect(2); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -316,11 +316,10 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('no content helper after update', function (assert) { - var done = assert.async(); + QUnit.test('no content helper after update', async function (assert) { assert.expect(4); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -328,27 +327,26 @@ QUnit.module('Views', { '<field name="product_id"/>' + '</graph>', }); - return concurrency.delay(0).then(function () { - assert.ok(graph.$('div.o_graph_svg_container svg.nvd3-svg').length, - "should contain a div with a svg element"); - assert.notOk(graph.$('div.o_view_nocontent').length, - "should not display the no content helper"); - testUtils.graph.reload(graph, {domain: [['product_id', '=', 4]]}); - - assert.notOk(graph.$('div.o_graph_svg_container svg.nvd3-svg').length, - "should not contain a div with a svg element"); - assert.ok(graph.$('div.o_view_nocontent').length, - "should display the no content helper"); - graph.destroy(); - done(); - }); + + await testUtils.nextTick(); + assert.ok(graph.$('div.o_graph_svg_container svg.nvd3-svg').length, + "should contain a div with a svg element"); + assert.notOk(graph.$('div.o_view_nocontent').length, + "should not display the no content helper"); + testUtils.graph.reload(graph, {domain: [['product_id', '=', 4]]}); + await testUtils.nextTick(); + assert.notOk(graph.$('div.o_graph_svg_container svg.nvd3-svg').length, + "should not contain a div with a svg element"); + assert.ok(graph.$('div.o_view_nocontent').length, + "should display the no content helper"); + graph.destroy(); }); - QUnit.test('can reload with other group by', function (assert) { + QUnit.test('can reload with other group by', async function (assert) { var done = assert.async(); assert.expect(4); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -375,11 +373,11 @@ QUnit.module('Views', { }); }); - QUnit.test('getOwnedQueryParams correctly returns mode, measure, groupbys and interval mapping', function (assert) { + QUnit.test('getOwnedQueryParams correctly returns mode, measure, groupbys and interval mapping', async function (assert) { var done = assert.async(); assert.expect(4); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -442,14 +440,14 @@ QUnit.module('Views', { }); }); - QUnit.test('correctly uses graph_ keys from the context', function (assert) { + QUnit.test('correctly uses graph_ keys from the context', async function (assert) { var done = assert.async(); assert.expect(6); var lastOne = _.last(this.data.foo.records); lastOne.color_id = 14; - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -485,14 +483,14 @@ QUnit.module('Views', { }); }); - QUnit.test('correctly use group_by key from the context', function (assert) { + QUnit.test('correctly use group_by key from the context', async function (assert) { var done = assert.async(); assert.expect(2); var lastOne = _.last(this.data.foo.records); lastOne.color_id = 14; - var graph = createView({ + var graph = await createView({ View: GraphView, model: 'foo', data: this.data, @@ -515,14 +513,14 @@ QUnit.module('Views', { }); }); - QUnit.test('correctly uses graph_ keys from the context (at reload)', function (assert) { + QUnit.test('correctly uses graph_ keys from the context (at reload)', async function (assert) { var done = assert.async(); assert.expect(8); var lastOne = _.last(this.data.foo.records); lastOne.color_id = 14; - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -564,10 +562,10 @@ QUnit.module('Views', { }); }); - QUnit.test('reload graph with correct fields', function (assert) { + QUnit.test('reload graph with correct fields', async function (assert) { assert.expect(2); - var graph = createView({ + var graph = await createView({ View: GraphView, model: 'foo', data: this.data, @@ -584,16 +582,16 @@ QUnit.module('Views', { }, }); - testUtils.graph.reload(graph, {groupBy: []}); + await testUtils.graph.reload(graph, {groupBy: []}); graph.destroy(); }); - QUnit.test('initial groupby is kept when reloading', function (assert) { + QUnit.test('initial groupby is kept when reloading', async function (assert) { var done = assert.async(); assert.expect(4); - var graph = createView({ + var graph = await createView({ View: GraphView, model: 'foo', data: this.data, @@ -626,10 +624,10 @@ QUnit.module('Views', { }); }); - QUnit.test('use a many2one as a measure should work (without groupBy)', function (assert) { + QUnit.test('use a many2one as a measure should work (without groupBy)', async function (assert) { assert.expect(3); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -651,10 +649,10 @@ QUnit.module('Views', { }); }); - QUnit.test('use a many2one as a measure should work (with groupBy)', function (assert) { + QUnit.test('use a many2one as a measure should work (with groupBy)', async function (assert) { assert.expect(4); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -679,10 +677,10 @@ QUnit.module('Views', { }); }); - QUnit.test('use a many2one as a measure and as a groupby should work', function (assert) { + QUnit.test('use a many2one as a measure and as a groupby should work', async function (assert) { assert.expect(2); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -693,27 +691,24 @@ QUnit.module('Views', { additionalMeasures: ['product_id'], }, }); - var done = assert.async(); - return concurrency.delay(0).then(function () { - // need to set the measure this way because it cannot be set in the - // arch. - testUtils.dom.click(graph.$buttons.find('.dropdown-toggle:contains(Measures)')); - testUtils.dom.click(graph.$buttons.find('.dropdown-item[data-field="product_id"]')); + await testUtils.nextTick(); + // need to set the measure this way because it cannot be set in the + // arch. + await testUtils.dom.click(graph.$buttons.find('.dropdown-toggle:contains(Measures)')); + await testUtils.dom.click(graph.$buttons.find('.dropdown-item[data-field="product_id"]')); - assert.strictEqual(graph.model.chart.data[0].value, 1, - "should have first datapoint with value 1"); - assert.strictEqual(graph.model.chart.data[1].value, 1, - "should have second datapoint with value 1"); + assert.strictEqual(graph.model.chart.data[0].value, 1, + "should have first datapoint with value 1"); + assert.strictEqual(graph.model.chart.data[1].value, 1, + "should have second datapoint with value 1"); - graph.destroy(); - done(); - }); + graph.destroy(); }); - QUnit.test('not use a many2one as a measure by default', function (assert) { + QUnit.test('not use a many2one as a measure by default', async function (assert) { assert.expect(1); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -730,10 +725,10 @@ QUnit.module('Views', { }); }); - QUnit.test('use a many2one as a measure if set as additional fields', function (assert) { + QUnit.test('use a many2one as a measure if set as additional fields', async function (assert) { assert.expect(1); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -753,10 +748,10 @@ QUnit.module('Views', { }); }); - QUnit.test('graph view crash when moving from search view using Down key', function (assert) { + QUnit.test('graph view crash when moving from search view using Down key', async function (assert) { assert.expect(1); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: this.data, @@ -769,13 +764,13 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('graph measures should be alphabetically sorted', function (assert) { + QUnit.test('graph measures should be alphabetically sorted', async function (assert) { assert.expect(2); var data = this.data; data.foo.fields.bouh = {string: "bouh", type: "integer"}; - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", data: data, @@ -793,10 +788,10 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.test('Undefined should appear in bar, pie graph but not in line graph', function (assert) { + QUnit.test('Undefined should appear in bar, pie graph but not in line graph', async function (assert) { assert.expect(4); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", groupBy:['date'], @@ -809,17 +804,17 @@ QUnit.module('Views', { assert.strictEqual(graph.$("svg.nvd3-svg .nv-x:contains('Undefined')").length, 0); assert.strictEqual(graph.$("svg.nvd3-svg:contains('January')").length, 1); - testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=bar]')); + await testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=bar]')); assert.strictEqual(graph.$("svg.nvd3-svg .nv-x:contains('Undefined')").length, 1); assert.strictEqual(graph.$("svg.nvd3-svg:contains('January')").length, 1); graph.destroy(); }); - QUnit.test('Undefined should appear in bar, pie graph but not in line graph with multiple groupbys', function (assert) { + QUnit.test('Undefined should appear in bar, pie graph but not in line graph with multiple groupbys', async function (assert) { assert.expect(6); - var graph = createView({ + var graph = await createView({ View: GraphView, model: "foo", groupBy:['date', 'color_id'], @@ -832,12 +827,12 @@ QUnit.module('Views', { assert.strictEqual(graph.$("svg.nvd3-svg .nv-x:contains('Undefined')").length, 0); assert.strictEqual(graph.$("svg.nvd3-svg:contains('January')").length, 1); - graph.$buttons.find('.o_graph_button[data-mode=bar]').click(); + await testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=bar]')); assert.strictEqual(graph.$("svg.nvd3-svg .nv-x:contains('Undefined')").length, 1); assert.strictEqual(graph.$("svg.nvd3-svg:contains('January')").length, 1); // Undefined should not appear after switching back to line chart - graph.$buttons.find('.o_graph_button[data-mode=line]').click(); + await testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=line]')); assert.strictEqual(graph.$("svg.nvd3-svg .nv-x:contains('Undefined')").length, 0); assert.strictEqual(graph.$("svg.nvd3-svg:contains('January')").length, 1); graph.destroy(); diff --git a/addons/web/static/tests/views/kanban_mobile_tests.js b/addons/web/static/tests/views/kanban_mobile_tests.js index cc8354432e3..52bfa60a180 100644 --- a/addons/web/static/tests/views/kanban_mobile_tests.js +++ b/addons/web/static/tests/views/kanban_mobile_tests.js @@ -55,10 +55,10 @@ QUnit.module('Views', { QUnit.module('KanbanView Mobile'); - QUnit.test('mobile grouped rendering', function (assert) { + QUnit.test('mobile grouped rendering', async function (assert) { assert.expect(13); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -83,13 +83,13 @@ QUnit.module('Views', { "there is no records in next tab. Records will be loaded when it will be opened"); // quick create in first column - testUtils.dom.click(kanban.$buttons.find('.o-kanban-button-new')); - assert.hasClass(kanban.$('.o_kanban_group:nth(0) > div:nth(1)'), 'o_kanban_quick_create', + await testUtils.dom.click(kanban.$buttons.find('.o-kanban-button-new')); + assert.hasClass(kanban.$('.o_kanban_group:nth(0) > div:nth(1)'),'o_kanban_quick_create', "clicking on create should open the quick_create in the first column"); // move to second column - kanban.$('.o_kanban_mobile_tab:nth(1)').trigger('click'); - assert.hasClass(kanban.$('.o_kanban_mobile_tab:nth(1)'), 'o_current', + await testUtils.dom.click(kanban.$('.o_kanban_mobile_tab:nth(1)')); + 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'"); @@ -97,8 +97,8 @@ QUnit.module('Views', { "the 2 records of the second group have now been loaded"); // quick create in second column - testUtils.dom.click(kanban.$buttons.find('.o-kanban-button-new')); - assert.hasClass(kanban.$('.o_kanban_group:nth(1) > div:nth(1)'), 'o_kanban_quick_create', + await testUtils.dom.click(kanban.$buttons.find('.o-kanban-button-new')); + assert.hasClass(kanban.$('.o_kanban_group:nth(1) > div:nth(1)'),'o_kanban_quick_create', "clicking on create should open the quick_create in the second column"); // kanban column should match kanban mobile tabs @@ -118,10 +118,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('mobile grouped with undefined column', function (assert) { + + QUnit.test('mobile grouped with undefined column', async function (assert) { assert.expect(5); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -155,10 +156,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('mobile grouped on many2one rendering', function (assert) { + + QUnit.test('mobile grouped on many2one rendering', async function (assert) { assert.expect(5); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -193,10 +195,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('kanban with searchpanel: there should be no searchpanel in mobile', function (assert) { + QUnit.test('kanban with searchpanel: there should be no searchpanel in mobile', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, diff --git a/addons/web/static/tests/views/kanban_model_tests.js b/addons/web/static/tests/views/kanban_model_tests.js index 20eb2918d04..29dd60c3d15 100644 --- a/addons/web/static/tests/views/kanban_model_tests.js +++ b/addons/web/static/tests/views/kanban_model_tests.js @@ -57,12 +57,13 @@ QUnit.module('Views', { openGroupByDefault: true, viewType: 'kanban', }; - } + }, }, function () { QUnit.module('KanbanModel'); - QUnit.test('load grouped + add a new group', function (assert) { + QUnit.test('load grouped + add a new group', async function (assert) { + var done = assert.async(); assert.expect(22); var calledRoutes = {}; @@ -84,7 +85,7 @@ QUnit.module('Views', { fieldNames: ['foo'], }); - model.load(params).then(function (resultID) { + model.load(params).then(async function (resultID) { // various checks on the load result var state = model.get(resultID); assert.ok(_.isEqual(state.groupedBy, ['product_id']), 'should be grouped by "product_id"'); @@ -102,7 +103,7 @@ QUnit.module('Views', { assert.strictEqual(xphoneGroup.limit, 40, 'limit in a group should be 40'); // add a new group - model.createGroup('xpod', resultID); + await model.createGroup('xpod', resultID); state = model.get(resultID); assert.strictEqual(state.data.length, 3, 'should now have 3 groups'); assert.strictEqual(state.count, 2, 'there are still 2 records'); @@ -123,10 +124,12 @@ QUnit.module('Views', { assert.strictEqual(nbSearchRead, 2, 'should have done 2 search_read'); assert.strictEqual(nbNameCreate, 1, 'should have done 1 name_create'); model.destroy(); + done(); }); }); - QUnit.test('archive/restore a column', function (assert) { + QUnit.test('archive/restore a column', async function (assert) { + var done = assert.async(); assert.expect(4); var model = createModel({ @@ -139,7 +142,7 @@ QUnit.module('Views', { fieldNames: ['foo'], }); - model.load(params).then(function (resultID) { + model.load(params).then(async function (resultID) { var state = model.get(resultID); var xphoneGroup = _.findWhere(state.data, {res_id: 37}); var xpadGroup = _.findWhere(state.data, {res_id: 41}); @@ -148,17 +151,19 @@ QUnit.module('Views', { // archive the column 'xphone' var recordIDs = _.pluck(xphoneGroup.data, 'id'); - model.toggleActive(recordIDs, false, xphoneGroup.id); + await model.toggleActive(recordIDs, false, xphoneGroup.id); state = model.get(resultID); xphoneGroup = _.findWhere(state.data, {res_id: 37}); assert.strictEqual(xphoneGroup.count, 0, 'xphone group has no record anymore'); xpadGroup = _.findWhere(state.data, {res_id: 41}); assert.strictEqual(xpadGroup.count, 1, 'xpad group still has one record'); model.destroy(); + done(); }); }); - QUnit.test('kanban model does not allow nested groups', function (assert) { + QUnit.test('kanban model does not allow nested groups', async function (assert) { + var done = assert.async(); assert.expect(2); var model = createModel({ @@ -185,10 +190,11 @@ QUnit.module('Views', { "the second level of groupBy should have been removed"); model.destroy(); + done(); }); }); - QUnit.test('resequence columns and records', function (assert) { + QUnit.test('resequence columns and records', async function (assert) { var done = assert.async(); assert.expect(8); @@ -252,7 +258,8 @@ QUnit.module('Views', { }); }); - QUnit.test('add record to group', function (assert) { + QUnit.test('add record to group', async function (assert) { + var done = assert.async(); assert.expect(8); var self = this; @@ -289,9 +296,11 @@ QUnit.module('Views', { assert.strictEqual(state.data[0].data[0].data.foo, 'new record', "new record should have been fetched"); }); - }); + }).then(function() { + model.destroy(); + done(); + }) - model.destroy(); }); }); diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index bb2231a025d..956578f2bf8 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -11,6 +11,8 @@ var testUtils = require('web.test_utils'); var Widget = require('web.Widget'); var widgetRegistry = require('web.widget_registry'); +var makeTestPromise = testUtils.makeTestPromise; +var nextTick = testUtils.nextTick; var createView = testUtils.createView; QUnit.module('Views', { @@ -69,10 +71,10 @@ QUnit.module('Views', { QUnit.module('KanbanView'); - QUnit.test('basic ungrouped rendering', function (assert) { + QUnit.test('basic ungrouped rendering', async function (assert) { assert.expect(5); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -92,10 +94,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('basic grouped rendering', function (assert) { + QUnit.test('basic grouped rendering', async function (assert) { assert.expect(13); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -131,20 +133,21 @@ QUnit.module('Views', { // the next line makes sure that reload works properly. It looks useless, // but it actually test that a grouped local record can be reloaded without // changing its result. - testUtils.kanban.reload(kanban); + await kanban.reload(kanban); assert.containsN(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record', 3); kanban.destroy(); }); - QUnit.test('basic grouped rendering with active field (archivable by default)', function (assert) { + QUnit.test('basic grouped rendering with active field (archivable by default)', async function (assert) { + // var done = assert.async(); assert.expect(9); // add active field on partner model and make all records active this.data.partner.fields.active = {string: 'Active', type: 'char', default: true}; var envIDs = [1, 2, 3, 4]; // the ids that should be in the environment during this test - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -166,28 +169,28 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_group:last .o_kanban_record', 3); testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_archive_records')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_archive_records')); assert.containsOnce(document.body, '.modal', "a confirm modal should be displayed"); - testUtils.modal.clickButton('Cancel'); + await testUtils.modal.clickButton('Cancel'); assert.containsN(kanban, '.o_kanban_group:last .o_kanban_record', 3, "still last column should contain 3 records"); testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_archive_records')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_archive_records')); assert.ok($('.modal').length, 'a confirm modal should be displayed'); - testUtils.modal.clickButton('Ok'); + await testUtils.modal.clickButton('Ok'); assert.containsNone(kanban, '.o_kanban_group:last .o_kanban_record', "last column should not contain any records"); envIDs = [4]; assert.deepEqual(kanban.exportState().resIds, envIDs); kanban.destroy(); }); - QUnit.test('basic grouped rendering with active field and archive enabled (archivable true)', function (assert) { + QUnit.test('basic grouped rendering with active field and archive enabled (archivable true)', async function (assert) { assert.expect(7); // add active field on partner model and make all records active this.data.partner.fields.active = {string: 'Active', type: 'char', default: true}; var envIDs = [1, 2, 3, 4]; // the ids that should be in the environment during this test - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -209,26 +212,26 @@ QUnit.module('Views', { "last column should contain 3 records"); envIDs = [4]; testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_archive_records')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_archive_records')); assert.ok($('.modal').length, 'a confirm modal should be displayed'); - testUtils.modal.clickButton('Cancel'); // Click on 'Cancel' + await testUtils.modal.clickButton('Cancel'); // Click on 'Cancel' assert.containsN(kanban, '.o_kanban_group:last .o_kanban_record', 3, "still last column should contain 3 records"); testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_archive_records')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_archive_records')); assert.ok($('.modal').length, 'a confirm modal should be displayed'); - testUtils.modal.clickButton('Ok'); // Click on 'Ok' + await testUtils.modal.clickButton('Ok'); // Click on 'Ok' assert.containsNone(kanban, '.o_kanban_group:last .o_kanban_record', "last column should not contain any records"); kanban.destroy(); }); - QUnit.test('basic grouped rendering with active field and hidden archive buttons (archivable false)', function (assert) { + QUnit.test('basic grouped rendering with active field and hidden archive buttons (archivable false)', async function (assert) { assert.expect(2); // add active field on partner model and make all records active this.data.partner.fields.active = {string: 'Active', type: 'char', default: true}; var envIDs = [1, 2, 3, 4]; // the ids that should be in the environment during this test - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -251,10 +254,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('context can be used in kanban template', function (assert) { + QUnit.test('context can be used in kanban template', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -281,10 +284,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('pager should be hidden in grouped mode', function (assert) { + QUnit.test('pager should be hidden in grouped mode', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -295,17 +298,17 @@ QUnit.module('Views', { '</t></templates></kanban>', groupBy: ['bar'], }); - kanban.renderPager(); + await kanban.renderPager(); assert.isNotVisible(kanban.pager.$el, "pager should be hidden in grouped kanban"); kanban.destroy(); }); - QUnit.test('pager, ungrouped, with default limit', function (assert) { + QUnit.test('pager, ungrouped, with default limit', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -325,10 +328,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('pager, ungrouped, with limit given in options', function (assert) { + QUnit.test('pager, ungrouped, with limit given in options', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -350,11 +353,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('pager, ungrouped, with limit set on arch and given in options', function (assert) { + QUnit.test('pager, ungrouped, with limit set on arch and given in options', async function (assert) { assert.expect(3); // the limit given in the arch should take the priority over the one given in options - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -376,10 +379,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('create in grouped on m2o', function (assert) { + QUnit.test('create in grouped on m2o', async function (assert) { assert.expect(5); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -399,7 +402,7 @@ QUnit.module('Views', { assert.hasClass(kanban.$('.o_kanban_view > div:last'),'o_column_quick_create', "column quick create should be enabled when grouped by a many2one field)"); - testUtils.kanban.clickCreate(kanban); // Click on 'Create' + await testUtils.kanban.clickCreate(kanban); // Click on 'Create' assert.hasClass(kanban.$('.o_kanban_group:first() > div:nth(1)'),'o_kanban_quick_create', "clicking on create should open the quick_create in the first column"); @@ -408,10 +411,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('create in grouped on char', function (assert) { + QUnit.test('create in grouped on char', async function (assert) { assert.expect(4); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -433,10 +436,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record without quick_create_view', function (assert) { + QUnit.test('quick create record without quick_create_view', async function (assert) { assert.expect(16); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -460,7 +463,7 @@ QUnit.module('Views', { "first column should contain one record"); // click on 'Create' -> should open the quick create in the first column - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); assert.strictEqual($quickCreate.length, 1, @@ -469,14 +472,14 @@ QUnit.module('Views', { "should have rendered an XXS form view"); assert.strictEqual($quickCreate.find('input').length, 1, "should have only one input"); - assert.hasClass($quickCreate.find('input'),'o_required_modifier', + assert.hasClass($quickCreate.find('input'), 'o_required_modifier', "the field should be required"); assert.strictEqual($quickCreate.find('input[placeholder=Title]').length, 1, "input placeholder should be 'Title'"); // fill the quick create and validate - testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); - testUtils.dom.click($quickCreate.find('button.o_kanban_add')); + await testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); + await testUtils.dom.click($quickCreate.find('button.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "first column should contain two records"); @@ -494,10 +497,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record with quick_create_view', function (assert) { + QUnit.test('quick create record with quick_create_view', async function (assert) { assert.expect(19); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -532,7 +535,7 @@ QUnit.module('Views', { "first column should contain one record"); // click on 'Create' -> should open the quick create in the first column - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); assert.strictEqual($quickCreate.length, 1, @@ -546,10 +549,10 @@ QUnit.module('Views', { "should have rendered three widgets"); // fill the quick create and validate - testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=foo]'), 'new partner'); - testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=int_field]'), '4'); - testUtils.dom.click($quickCreate.find('.o_field_widget[name=state] .o_priority_star:first')); - testUtils.dom.click($quickCreate.find('button.o_kanban_add')); + await testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=foo]'), 'new partner'); + await testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=int_field]'), '4'); + await testUtils.dom.click($quickCreate.find('.o_field_widget[name=state] .o_priority_star:first')); + await testUtils.dom.click($quickCreate.find('button.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "first column should contain two records"); @@ -569,10 +572,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped on m2o (no quick_create_view)', function (assert) { + QUnit.test('quick create record in grouped on m2o (no quick_create_view)', async function (assert) { assert.expect(12); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -604,10 +607,10 @@ QUnit.module('Views', { "first column should contain two records"); // click on 'Create', fill the quick create and validate - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); - testUtils.dom.click($quickCreate.find('button.o_kanban_add')); + await testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); + await testUtils.dom.click($quickCreate.find('button.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 3, "first column should contain three records"); @@ -625,10 +628,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped on m2o (with quick_create_view)', function (assert) { + QUnit.test('quick create record in grouped on m2o (with quick_create_view)', async function (assert) { assert.expect(14); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -669,12 +672,12 @@ QUnit.module('Views', { "first column should contain two records"); // click on 'Create', fill the quick create and validate - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=foo]'), 'new partner'); - testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=int_field]'), '4'); - testUtils.dom.click($quickCreate.find('.o_field_widget[name=state] .o_priority_star:first')); - testUtils.dom.click($quickCreate.find('button.o_kanban_add')); + await testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=foo]'), 'new partner'); + await testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=int_field]'), '4'); + await testUtils.dom.click($quickCreate.find('.o_field_widget[name=state] .o_priority_star:first')); + await testUtils.dom.click($quickCreate.find('button.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 3, "first column should contain three records"); @@ -694,7 +697,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record with default values and onchanges', function (assert) { + QUnit.test('quick create record with default values and onchanges', async function (assert) { assert.expect(11); this.data.partner.fields.int_field.default = 4; @@ -706,7 +709,7 @@ QUnit.module('Views', { }, }; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -729,16 +732,17 @@ QUnit.module('Views', { }); // click on 'Create' -> should open the quick create in the first column - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); + var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); assert.strictEqual($quickCreate.length, 1, "should have a quick create element in the first column"); assert.strictEqual($quickCreate.find('.o_field_widget[name=int_field]').val(), '4', "default value should be set"); // fill the 'foo' field -> should trigger the onchange - testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=foo]'), 'new partner'); + await testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=foo]'), 'new partner'); assert.strictEqual($quickCreate.find('.o_field_widget[name=int_field]').val(), '8', "onchange should have been triggered"); @@ -756,10 +760,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record with quick_create_view: modifiers', function (assert) { + QUnit.test('quick create record with quick_create_view: modifiers', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -778,7 +782,7 @@ QUnit.module('Views', { }); // create a new record - testUtils.dom.click(kanban.$('.o_kanban_group:first .o_kanban_quick_add')); + await testUtils.dom.click(kanban.$('.o_kanban_group:first .o_kanban_quick_add')); var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); assert.hasClass($quickCreate.find('.o_field_widget[name=foo]'),'o_required_modifier', @@ -787,7 +791,7 @@ QUnit.module('Views', { "int_field should be invisible"); // fill 'foo' field - testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=foo]'), 'new partner'); + await testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=foo]'), 'new partner'); assert.doesNotHaveClass($quickCreate.find('.o_field_widget[name=int_field]'), 'o_invisible_modifier', "int_field should now be visible"); @@ -795,7 +799,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record and change state in grouped mode', function (assert) { + QUnit.test('quick create record and change state in grouped mode', async function (assert) { assert.expect(1); this.data.partner.fields.kanban_state = { @@ -804,7 +808,7 @@ QUnit.module('Views', { selection: [["normal", "Grey"], ["done", "Green"], ["blocked", "Red"]], }; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -820,24 +824,24 @@ QUnit.module('Views', { }); // Quick create kanban record - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); var $quickAdd = kanban.$('.o_kanban_quick_create'); $quickAdd.find('.o_input').val('Test'); - testUtils.dom.click($quickAdd.find('.o_kanban_add')); + await testUtils.dom.click($quickAdd.find('.o_kanban_add')); // Select state in kanban - testUtils.dom.click(kanban.$('.o_status').first()); - testUtils.dom.click(kanban.$('.o_selection .dropdown-item:first')); + await testUtils.dom.click(kanban.$('.o_status').first()); + await testUtils.dom.click(kanban.$('.o_selection .dropdown-item:first')); assert.hasClass(kanban.$('.o_status').first(),'o_status_green', "Kanban state should be done (Green)"); kanban.destroy(); }); - QUnit.test('quick create record: cancel and validate without using the buttons', function (assert) { + QUnit.test('quick create record: cancel and validate without using the buttons', async function (assert) { assert.expect(8); var nbRecords = 4; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -852,7 +856,7 @@ QUnit.module('Views', { assert.strictEqual(kanban.exportState().resIds.length, nbRecords); // click to add an element and cancel the quick creation by pressing ESC - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); var $quickCreate = kanban.$('.o_kanban_quick_create'); assert.strictEqual($quickCreate.length, 1, "should have a quick create element"); @@ -865,18 +869,18 @@ QUnit.module('Views', { "should have destroyed the quick create element"); // click to add and element and click outside, should cancel the quick creation - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); - testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); assert.containsNone(kanban, '.o_kanban_quick_create', "the quick create should be destroyed when the user clicks outside"); // click to really add an element - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); $quickCreate = kanban.$('.o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); + await testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); // clicking outside should no longer destroy the quick create as it is dirty - testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); + await testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); assert.containsOnce(kanban, '.o_kanban_quick_create', "the quick create should not have been destroyed"); @@ -887,6 +891,7 @@ QUnit.module('Views', { which: $.ui.keyCode.ENTER, })); + await nextTick(); assert.strictEqual(this.data.partner.records.length, 5, "should have created a partner"); assert.strictEqual(_.last(this.data.partner.records).name, "new partner", @@ -896,7 +901,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record: validate with ENTER', function (assert) { + QUnit.test('quick create record: validate with ENTER', async function (assert) { // in this test, we accurately mock the behavior of the webclient by specifying a // fieldDebounce > 0, meaning that the changes in an InputField aren't notified to the model // on 'input' events, but they wait for the 'change' event (or a call to 'commitChanges', @@ -906,7 +911,7 @@ QUnit.module('Views', { // directly assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -929,8 +934,8 @@ QUnit.module('Views', { "should have 4 records at the beginning"); // add an element and confirm by pressing ENTER - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); - testUtils.kanban.quickCreate(kanban, 'new partner', 'foo'); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.kanban.quickCreate(kanban, 'new partner', 'foo'); // triggers a navigation event, leading to the 'commitChanges' and record creation assert.containsN(kanban, '.o_kanban_record', 5, @@ -941,11 +946,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record: prevent multiple adds with ENTER', function (assert) { + QUnit.test('quick create record: prevent multiple adds with ENTER', async function (assert) { assert.expect(9); - var def; - var kanban = createView({ + var prom = makeTestPromise(); + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -969,7 +974,9 @@ QUnit.module('Views', { var result = this._super.apply(this, arguments); if (args.method === 'create') { assert.step('create'); - return $.when(def).then(_.constant(result)); + return prom.then(function () { + return result; + }); } return result; }, @@ -979,8 +986,7 @@ QUnit.module('Views', { "should have 4 records at the beginning"); // add an element and press ENTER twice - def = $.Deferred(); - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); var enterEvent = { keyCode: $.ui.keyCode.ENTER, which: $.ui.keyCode.ENTER, @@ -995,10 +1001,11 @@ QUnit.module('Views', { "should not have created the record yet"); assert.strictEqual(kanban.$('.o_kanban_quick_create input[name=foo]').val(), 'new partner', "quick create should not be empty yet"); - assert.hasClass(kanban.$('.o_kanban_quick_create'),'o_disabled', + assert.hasClass(kanban.$('.o_kanban_quick_create'), 'o_disabled', "quick create should be disabled"); - def.resolve(); + prom.resolve(); + await nextTick(); assert.containsN(kanban, '.o_kanban_record', 5, "should have created a new record"); @@ -1012,11 +1019,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record: prevent multiple adds with Add clicked', function (assert) { + QUnit.test('quick create record: prevent multiple adds with Add clicked', async function (assert) { assert.expect(9); - var def; - var kanban = createView({ + var prom = makeTestPromise(); + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1036,7 +1043,9 @@ QUnit.module('Views', { var result = this._super.apply(this, arguments); if (args.method === 'create') { assert.step('create'); - return $.when(def).then(_.constant(result)); + return prom.then(function () { + return result; + }); } return result; }, @@ -1046,11 +1055,10 @@ QUnit.module('Views', { "should have 4 records at the beginning"); // add an element and click 'Add' twice - def = $.Deferred(); - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create').find('input[name=foo]'), 'new partner'); - testUtils.dom.click(kanban.$('.o_kanban_quick_create').find('.o_kanban_add')); - testUtils.dom.click(kanban.$('.o_kanban_quick_create').find('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create').find('input[name=foo]'), 'new partner'); + await testUtils.dom.click(kanban.$('.o_kanban_quick_create').find('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_quick_create').find('.o_kanban_add')); assert.containsN(kanban, '.o_kanban_record', 4, "should not have created the record yet"); @@ -1059,8 +1067,9 @@ QUnit.module('Views', { assert.hasClass(kanban.$('.o_kanban_quick_create'),'o_disabled', "quick create should be disabled"); - def.resolve(); + prom.resolve(); + await nextTick(); assert.containsN(kanban, '.o_kanban_record', 5, "should have created a new record"); assert.strictEqual(kanban.$('.o_kanban_quick_create input[name=foo]').val(), '', @@ -1073,7 +1082,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record: prevent multiple adds with ENTER, with onchange', function (assert) { + QUnit.test('quick create record: prevent multiple adds with ENTER, with onchange', async function (assert) { assert.expect(13); this.data.partner.onchanges = { @@ -1081,8 +1090,9 @@ QUnit.module('Views', { obj.int_field += (obj.foo ? 3 : 0); }, }; - var def; - var kanban = createView({ + var shouldDelayOnchange = false; + var prom = makeTestPromise(); + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1102,7 +1112,11 @@ QUnit.module('Views', { var result = this._super.apply(this, arguments); if (args.method === 'onchange') { assert.step('onchange'); - return $.when(def).then(_.constant(result)); + if (shouldDelayOnchange) { + return Promise.resolve(prom).then(function () { + return result + }); + } } if (args.method === 'create') { assert.step('create'); @@ -1123,8 +1137,8 @@ QUnit.module('Views', { "should have 4 records at the beginning"); // add an element and press ENTER twice - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); - def = $.Deferred(); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + shouldDelayOnchange = true; var enterEvent = { keyCode: $.ui.keyCode.ENTER, which: $.ui.keyCode.ENTER, @@ -1143,8 +1157,9 @@ QUnit.module('Views', { assert.hasClass(kanban.$('.o_kanban_quick_create'),'o_disabled', "quick create should be disabled"); - def.resolve(); + prom.resolve(); + await nextTick(); assert.containsN(kanban, '.o_kanban_record', 5, "should have created a new record"); assert.strictEqual(kanban.$('.o_kanban_quick_create input[name=foo]').val(), '', @@ -1162,7 +1177,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record: click Add to create, with delayed onchange', function (assert) { + QUnit.test('quick create record: click Add to create, with delayed onchange', async function (assert) { assert.expect(13); this.data.partner.onchanges = { @@ -1170,8 +1185,9 @@ QUnit.module('Views', { obj.int_field += (obj.foo ? 3 : 0); }, }; - var def; - var kanban = createView({ + var shouldDelayOnchange = false; + var prom = makeTestPromise(); + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1191,7 +1207,11 @@ QUnit.module('Views', { var result = this._super.apply(this, arguments); if (args.method === 'onchange') { assert.step('onchange'); - return $.when(def).then(_.constant(result)); + if (shouldDelayOnchange) { + return Promise.resolve(prom).then(function () { + return result + }); + } } if (args.method === 'create') { assert.step('create'); @@ -1208,10 +1228,10 @@ QUnit.module('Views', { "should have 4 records at the beginning"); // add an element and click 'add' - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); - def = $.Deferred(); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create').find('input[name=foo]'), 'new partner'); - testUtils.dom.click(kanban.$('.o_kanban_quick_create').find('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + shouldDelayOnchange = true; + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create').find('input[name=foo]'), 'new partner'); + await testUtils.dom.click(kanban.$('.o_kanban_quick_create').find('.o_kanban_add')); assert.containsN(kanban, '.o_kanban_record', 4, "should not have created the record yet"); @@ -1220,8 +1240,9 @@ QUnit.module('Views', { assert.hasClass(kanban.$('.o_kanban_quick_create'),'o_disabled', "quick create should be disabled"); - def.resolve(); // the onchange returns + prom.resolve(); // the onchange returns + await nextTick(); assert.containsN(kanban, '.o_kanban_record', 5, "should have created a new record"); assert.strictEqual(kanban.$('.o_kanban_quick_create input[name=foo]').val(), '', @@ -1239,10 +1260,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create when first column is folded', function (assert) { + QUnit.test('quick create when first column is folded', async function (assert) { assert.expect(6); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1259,13 +1280,13 @@ QUnit.module('Views', { // fold the first column testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:first')); - testUtils.dom.click(kanban.$('.o_kanban_group:first .o_kanban_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_kanban_group:first .o_kanban_toggle_fold')); assert.hasClass(kanban.$('.o_kanban_group:first'),'o_column_folded', "first column should be folded"); // click on 'Create' to open the quick create in the first column - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); assert.doesNotHaveClass(kanban.$('.o_kanban_group:first'), 'o_column_folded', "first column should no longer be folded"); @@ -1275,7 +1296,7 @@ QUnit.module('Views', { // fold again the first column testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:first')); - testUtils.dom.click(kanban.$('.o_kanban_group:first .o_kanban_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_kanban_group:first .o_kanban_toggle_fold')); assert.hasClass(kanban.$('.o_kanban_group:first'),'o_column_folded', "first column should be folded"); @@ -1285,10 +1306,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record: cancel when not dirty', function (assert) { + QUnit.test('quick create record: cancel when not dirty', async function (assert) { assert.expect(9); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1304,22 +1325,22 @@ QUnit.module('Views', { "first column should contain one record"); // click to add an element - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); assert.containsOnce(kanban, '.o_kanban_quick_create', "should have open the quick create widget"); // click again to add an element -> should have kept the quick create open - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); assert.containsOnce(kanban, '.o_kanban_quick_create', "should have kept the quick create open"); // click outside: should remove the quick create - testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); + await testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); assert.containsNone(kanban, '.o_kanban_quick_create', "the quick create should not have been destroyed"); // click to reopen the quick create - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); assert.containsOnce(kanban, '.o_kanban_quick_create', "should have open the quick create widget"); @@ -1332,13 +1353,13 @@ QUnit.module('Views', { "quick create widget should have been removed"); // click to reopen the quick create - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); assert.containsOnce(kanban, '.o_kanban_quick_create', "should have open the quick create widget"); // click on 'Discard': should remove the quick create - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); - testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); assert.containsNone(kanban, '.o_kanban_quick_create', "the quick create should be destroyed when the user clicks outside"); @@ -1348,10 +1369,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record: cancel when dirty', function (assert) { + QUnit.test('quick create record: cancel when dirty', async function (assert) { assert.expect(7); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1367,15 +1388,15 @@ QUnit.module('Views', { "first column should contain one record"); // click to add an element and edit it - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); assert.containsOnce(kanban, '.o_kanban_quick_create', "should have open the quick create widget"); var $quickCreate = kanban.$('.o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('input'), 'some value'); + await testUtils.fields.editInput($quickCreate.find('input'), 'some value'); // click outside: should not remove the quick create - testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); + await testUtils.dom.click(kanban.$('.o_kanban_group .o_kanban_record:first')); assert.containsOnce(kanban, '.o_kanban_quick_create', "the quick create should not have been destroyed"); @@ -1388,15 +1409,15 @@ QUnit.module('Views', { "quick create widget should have been removed"); // click to reopen quick create and edit it - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); assert.containsOnce(kanban, '.o_kanban_quick_create', "should have open the quick create widget"); $quickCreate = kanban.$('.o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('input'), 'some value'); + await testUtils.fields.editInput($quickCreate.find('input'), 'some value'); // click on 'Discard': should remove the quick create - testUtils.dom.click(kanban.$('.o_kanban_quick_create .o_kanban_cancel')); + await testUtils.dom.click(kanban.$('.o_kanban_quick_create .o_kanban_cancel')); assert.containsNone(kanban, '.o_kanban_quick_create', "the quick create should be destroyed when the user clicks outside"); @@ -1406,11 +1427,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record and edit in grouped mode', function (assert) { + QUnit.test('quick create record and edit in grouped mode', async function (assert) { assert.expect(6); var newRecordID; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1444,10 +1465,10 @@ QUnit.module('Views', { // click to add and edit an element var $quickCreate = kanban.$('.o_kanban_quick_create'); - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); $quickCreate = kanban.$('.o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); - testUtils.dom.click($quickCreate.find('button.o_kanban_edit')); + await testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); + await testUtils.dom.click($quickCreate.find('button.o_kanban_edit')); assert.strictEqual(this.data.partner.records.length, 5, "should have created a partner"); @@ -1459,10 +1480,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create several records in a row', function (assert) { + QUnit.test('quick create several records in a row', async function (assert) { assert.expect(6); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1478,12 +1499,12 @@ QUnit.module('Views', { "first column should contain one record"); // click to add an element, fill the input and press ENTER - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); assert.containsOnce(kanban, '.o_kanban_quick_create', "the quick create should be open"); - testUtils.kanban.quickCreate(kanban, 'new partner 1'); + await testUtils.kanban.quickCreate(kanban, 'new partner 1'); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "first column should now contain two records"); @@ -1491,7 +1512,7 @@ QUnit.module('Views', { "the quick create should still be open"); // create a second element in a row - testUtils.kanban.quickCreate(kanban, 'new partner 2'); + await testUtils.kanban.quickCreate(kanban, 'new partner 2'); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 3, "first column should now contain three records"); @@ -1501,11 +1522,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create is disabled until record is created and read', function (assert) { + QUnit.test('quick create is disabled until record is created and read', async function (assert) { assert.expect(6); - var def; - var kanban = createView({ + var prom = makeTestPromise(); + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1518,7 +1539,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { var result = this._super.apply(this, arguments); if (args.method === 'read') { - return $.when(def).then(_.constant(result)); + return prom.then(_.constant(result)); } return result; }, @@ -1528,22 +1549,21 @@ QUnit.module('Views', { "first column should contain one record"); // click to add a record, and add two in a row (first one will be delayed) - testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); + await testUtils.dom.click(kanban.$('.o_kanban_header .o_kanban_quick_add i').first()); assert.containsOnce(kanban, '.o_kanban_quick_create', "the quick create should be open"); - def = $.Deferred(); - - testUtils.kanban.quickCreate(kanban, 'new partner 1'); + await testUtils.kanban.quickCreate(kanban, 'new partner 1'); assert.containsOnce(kanban, '.o_kanban_group:first .o_kanban_record', "first column should still contain one record"); assert.containsOnce(kanban, '.o_kanban_quick_create.o_disabled', "quick create should be disabled"); - def.resolve(); + prom.resolve(); + await nextTick(); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "first column should now contain two records"); assert.strictEqual(kanban.$('.o_kanban_quick_create:not(.o_disabled)').length, 1, @@ -1552,10 +1572,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record fail in grouped by many2one', function (assert) { + QUnit.test('quick create record fail in grouped by many2one', async function (assert) { assert.expect(8); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1574,11 +1594,14 @@ QUnit.module('Views', { groupBy: ['product_id'], mockRPC: function (route, args) { if (args.method === 'name_create') { - return $.Deferred().reject({ - code: 200, - data: {}, - message: "Odoo server error", - }, $.Event()); + return Promise.reject({ + message: { + code: 200, + data: {}, + message: "Odoo server error", + }, + event: $.Event() + }); } return this._super.apply(this, arguments); }, @@ -1587,11 +1610,11 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "there should be 2 records in first column"); - testUtils.kanban.clickCreate(kanban); // Click on 'Create' + await testUtils.kanban.clickCreate(kanban); // Click on 'Create' assert.hasClass(kanban.$('.o_kanban_group:first() > div:nth(1)'),'o_kanban_quick_create', "clicking on create should open the quick_create in the first column"); - testUtils.kanban.quickCreate(kanban, 'test'); + await testUtils.kanban.quickCreate(kanban, 'test'); assert.strictEqual($('.modal .o_form_view.o_form_editable').length, 1, "a form view dialog should have been opened (in edit)"); @@ -1599,8 +1622,8 @@ QUnit.module('Views', { "the correct product_id should already be set"); // specify a name and save - testUtils.fields.editInput($('.modal input[name=foo]'), 'test'); - testUtils.modal.clickButton('Save'); + await testUtils.fields.editInput($('.modal input[name=foo]'), 'test'); + await testUtils.modal.clickButton('Save'); assert.strictEqual($('.modal').length, 0, "the modal should be closed"); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 3, @@ -1614,10 +1637,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record is re-enabled after discard on failure', function (assert) { + QUnit.test('quick create record is re-enabled after discard on failure', async function (assert) { assert.expect(4); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1636,26 +1659,29 @@ QUnit.module('Views', { groupBy: ['product_id'], mockRPC: function (route, args) { if (args.method === 'name_create') { - return $.Deferred().reject({ - code: 200, - data: {}, - message: "Odoo server error", - }, $.Event()); + return Promise.reject({ + message: { + code: 200, + data: {}, + message: "Odoo server error", + }, + event: $.Event() + }); } return this._super.apply(this, arguments); } }); - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); assert.containsOnce(kanban, '.o_kanban_quick_create', "should have a quick create widget"); - testUtils.kanban.quickCreate(kanban, 'test'); + await testUtils.kanban.quickCreate(kanban, 'test'); assert.strictEqual($('.modal .o_form_view.o_form_editable').length, 1, "a form view dialog should have been opened (in edit)"); - testUtils.modal.clickButton('Discard'); + await testUtils.modal.clickButton('Discard'); assert.strictEqual($('.modal').length, 0, "the modal should be closed"); assert.strictEqual(kanban.$('.o_kanban_quick_create:not(.o_disabled)').length, 1, @@ -1664,10 +1690,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record fails in grouped by char', function (assert) { + QUnit.test('quick create record fails in grouped by char', async function (assert) { assert.expect(7); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1683,11 +1709,14 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (args.method === 'name_create') { - return $.Deferred().reject({ - code: 200, - data: {}, - message: "Odoo server error", - }, $.Event()); + return Promise.reject({ + message: { + code: 200, + data: {}, + message: "Odoo server error", + }, + event: $.Event() + }); } if (args.method === 'create') { assert.deepEqual(args.args[0], {foo: 'yop'}, @@ -1703,15 +1732,15 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_kanban_group:first .o_kanban_record', "there should be 1 record in first column"); - testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'test'); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'test'); + await testUtils.dom.click(kanban.$('.o_kanban_add')); assert.strictEqual($('.modal .o_form_view.o_form_editable').length, 1, "a form view dialog should have been opened (in edit)"); assert.strictEqual($('.modal .o_field_widget[name=foo]').val(), 'yop', "the correct default value for foo should already be set"); - testUtils.modal.clickButton('Save'); + await testUtils.modal.clickButton('Save'); assert.strictEqual($('.modal').length, 0, "the modal should be closed"); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, @@ -1720,10 +1749,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record fails in grouped by selection', function (assert) { + QUnit.test('quick create record fails in grouped by selection', async function (assert) { assert.expect(7); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1739,11 +1768,14 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (args.method === 'name_create') { - return $.Deferred().reject({ - code: 200, - data: {}, - message: "Odoo server error", - }, $.Event()); + return Promise.reject({ + message: { + code: 200, + data: {}, + message: "Odoo server error", + }, + event: $.Event() + }); } if (args.method === 'create') { assert.deepEqual(args.args[0], {state: 'abc'}, @@ -1759,16 +1791,16 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_kanban_group:first .o_kanban_record', "there should be 1 record in first column"); - testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'test'); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'test'); + await testUtils.dom.click(kanban.$('.o_kanban_add')); assert.strictEqual($('.modal .o_form_view.o_form_editable').length, 1, "a form view dialog should have been opened (in edit)"); assert.strictEqual($('.modal .o_field_widget[name=state]').val(), '"abc"', "the correct default value for state should already be set"); - testUtils.modal.clickButton('Save'); + await testUtils.modal.clickButton('Save'); assert.strictEqual($('.modal').length, 0, "the modal should be closed"); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, @@ -1777,10 +1809,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in empty grouped kanban', function (assert) { + QUnit.test('quick create record in empty grouped kanban', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1800,7 +1832,7 @@ QUnit.module('Views', { {__domain: [['product_id', '=', 3]], product_id_count: 0}, {__domain: [['product_id', '=', 5]], product_id_count: 0}, ]; - return $.when(result); + return Promise.resolve(result); } return this._super.apply(this, arguments); }, @@ -1811,7 +1843,7 @@ QUnit.module('Views', { assert.containsNone(kanban, '.o_kanban_record', "both columns should be empty"); - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); assert.containsOnce(kanban, '.o_kanban_group:first .o_kanban_quick_create', "should have opened the quick create in the first column"); @@ -1819,10 +1851,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped on date(time) field', function (assert) { + QUnit.test('quick create record in grouped on date(time) field', async function (assert) { assert.expect(6); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1846,27 +1878,27 @@ QUnit.module('Views', { "quick create should be disabled when grouped on a date field"); // clicking on CREATE in control panel should not open a quick create - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); assert.containsNone(kanban, '.o_kanban_quick_create', "should not have opened the quick create widget"); - kanban.reload({groupBy: ['datetime']}); + await kanban.reload({groupBy: ['datetime']}); assert.containsNone(kanban, '.o_kanban_header .o_kanban_quick_add i', "quick create should be disabled when grouped on a datetime field"); // clicking on CREATE in control panel should not open a quick create - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); assert.containsNone(kanban, '.o_kanban_quick_create', "should not have opened the quick create widget"); kanban.destroy(); }); - QUnit.test('quick create record feature is properly enabled/disabled at reload', function (assert) { + QUnit.test('quick create record feature is properly enabled/disabled at reload', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1881,12 +1913,12 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_header .o_kanban_quick_add i', 3, "quick create should be enabled when grouped on a char field"); - kanban.reload({groupBy: ['date']}); + await kanban.reload({groupBy: ['date']}); assert.containsNone(kanban, '.o_kanban_header .o_kanban_quick_add i', "quick create should now be disabled (grouped on date field)"); - kanban.reload({groupBy: ['bar']}); + await kanban.reload({groupBy: ['bar']}); assert.containsN(kanban, '.o_kanban_header .o_kanban_quick_add i', 2, "quick create should be enabled again (grouped on boolean field)"); @@ -1894,10 +1926,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped by char field', function (assert) { + QUnit.test('quick create record in grouped by char field', async function (assert) { assert.expect(4); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1921,9 +1953,9 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_kanban_group:first .o_kanban_record', "first column should contain 1 record"); - testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record'); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record'); + await testUtils.dom.click(kanban.$('.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "first column should now contain 2 records"); @@ -1931,10 +1963,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped by boolean field', function (assert) { + QUnit.test('quick create record in grouped by boolean field', async function (assert) { assert.expect(4); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1958,9 +1990,9 @@ QUnit.module('Views', { assert.strictEqual(kanban.$('.o_kanban_group:nth(1) .o_kanban_record').length, 3, "second column (true) should contain 3 records"); - testUtils.dom.click(kanban.$('.o_kanban_header:nth(1) .o_kanban_quick_add i')); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record'); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_header:nth(1) .o_kanban_quick_add i')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record'); + await testUtils.dom.click(kanban.$('.o_kanban_add')); assert.strictEqual(kanban.$('.o_kanban_group:nth(1) .o_kanban_record').length, 4, "second column (true) should now contain 4 records"); @@ -1968,10 +2000,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped on selection field', function (assert) { + QUnit.test('quick create record in grouped on selection field', async function (assert) { assert.expect(4); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1995,9 +2027,9 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_kanban_group:first .o_kanban_record', "first column (abc) should contain 1 record"); - testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record'); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record'); + await testUtils.dom.click(kanban.$('.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "first column (abc) should contain 2 records"); @@ -2005,10 +2037,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped by char field (within quick_create_view)', function (assert) { + QUnit.test('quick create record in grouped by char field (within quick_create_view)', async function (assert) { assert.expect(6); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2039,10 +2071,10 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_kanban_group:first .o_kanban_record', "first column should contain 1 record"); - testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); + await testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); assert.strictEqual(kanban.$('.o_kanban_quick_create input').val(), 'yop', "should have set the correct foo value by default"); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "first column should now contain 2 records"); @@ -2050,10 +2082,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped by boolean field (within quick_create_view)', function (assert) { + QUnit.test('quick create record in grouped by boolean field (within quick_create_view)', async function (assert) { assert.expect(6); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2084,10 +2116,10 @@ QUnit.module('Views', { assert.strictEqual(kanban.$('.o_kanban_group:nth(1) .o_kanban_record').length, 3, "second column (true) should contain 3 records"); - testUtils.dom.click(kanban.$('.o_kanban_header:nth(1) .o_kanban_quick_add i')); + await testUtils.dom.click(kanban.$('.o_kanban_header:nth(1) .o_kanban_quick_add i')); assert.ok(kanban.$('.o_kanban_quick_create .o_field_boolean input').is(':checked'), "should have set the correct bar value by default"); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_add')); assert.strictEqual(kanban.$('.o_kanban_group:nth(1) .o_kanban_record').length, 4, "second column (true) should now contain 4 records"); @@ -2095,10 +2127,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record in grouped by selection field (within quick_create_view)', function (assert) { + QUnit.test('quick create record in grouped by selection field (within quick_create_view)', async function (assert) { assert.expect(6); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2129,10 +2161,10 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_kanban_group:first .o_kanban_record', "first column (abc) should contain 1 record"); - testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); + await testUtils.dom.click(kanban.$('.o_kanban_header:first .o_kanban_quick_add i')); assert.strictEqual(kanban.$('.o_kanban_quick_create select').val(), '"abc"', "should have set the correct state value by default"); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2, "first column (abc) should now contain 2 records"); @@ -2140,11 +2172,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create record while adding a new column', function (assert) { + QUnit.test('quick create record while adding a new column', async function (assert) { assert.expect(10); - var def = $.Deferred(); - var kanban = createView({ + var def = testUtils.makeTestPromise(); + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2170,17 +2202,17 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_column_quick_create'); assert.isNotVisible(kanban.$('.o_column_quick_create input')); - testUtils.dom.click(kanban.$('.o_quick_create_folded')); + await 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')); + await testUtils.fields.editInput(kanban.$('.o_column_quick_create input'), 'new column'); + await 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')); + await 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 @@ -2188,20 +2220,20 @@ QUnit.module('Views', { // unlock column creation def.resolve(); - + await testUtils.nextTick(); 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')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record'); + await 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) { + QUnit.test('many2many_tags in kanban views', async function (assert) { assert.expect(12); this.data.partner.records[0].category_ids = [6, 7]; @@ -2212,7 +2244,7 @@ QUnit.module('Views', { color: 0, }); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2254,10 +2286,8 @@ QUnit.module('Views', { 'there should be only one tag in second record'); // Write on the record using the priority widget to trigger a re-render in readonly - testUtils.dom.click(kanban.$('.o_field_widget.o_priority a.o_priority_star.fa-star-o').first()); + await testUtils.dom.click(kanban.$('.o_field_widget.o_priority a.o_priority_star.fa-star-o').first()); assert.verifySteps([ - '/web/dataset/search_read', - '/web/dataset/call_kw/category/read', '/web/dataset/call_kw/partner/write', '/web/dataset/call_kw/partner/read', '/web/dataset/call_kw/category/read' @@ -2266,19 +2296,19 @@ QUnit.module('Views', { 'first record should still contain only 2 tags'); // click on a tag (should trigger switch_view) - testUtils.dom.click(kanban.$('.o_tag:contains(gold):first')); + await testUtils.dom.click(kanban.$('.o_tag:contains(gold):first')); kanban.destroy(); }); - QUnit.test('Do not open record when clicking on `a` with `href`', function (assert) { + QUnit.test('Do not open record when clicking on `a` with `href`', async function (assert) { assert.expect(5); this.data.partner.records = [ { id: 1, foo: 'yop' }, ]; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2331,13 +2361,13 @@ QUnit.module('Views', { ev.preventDefault(); }); - testUtils.dom.click($testLink); + await testUtils.dom.click($testLink); $(document.body).off('click.o_test'); kanban.destroy(); }); - QUnit.test('o2m loaded in only one batch', function (assert) { + QUnit.test('o2m loaded in only one batch', async function (assert) { assert.expect(9); this.data.subtask = { @@ -2357,7 +2387,7 @@ QUnit.module('Views', { this.data.partner.records[0].subtask_ids = [1]; this.data.partner.records[1].subtask_ids = [2]; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2376,7 +2406,7 @@ QUnit.module('Views', { }, }); - kanban.reload(); + await kanban.reload(); assert.verifySteps([ 'read_group', '/web/dataset/search_read', @@ -2390,10 +2420,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('m2m loaded in only one batch', function (assert) { + QUnit.test('m2m loaded in only one batch', async function (assert) { assert.expect(9); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2412,7 +2442,7 @@ QUnit.module('Views', { }, }); - testUtils.kanban.reload(kanban); + await kanban.reload(kanban); assert.verifySteps([ 'read_group', '/web/dataset/search_read', @@ -2426,7 +2456,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('fetch reference in only one batch', function (assert) { + QUnit.test('fetch reference in only one batch', async function (assert) { assert.expect(9); this.data.partner.records[0].ref_product = 'product,3'; @@ -2436,7 +2466,7 @@ QUnit.module('Views', { type: 'reference', }; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2455,7 +2485,7 @@ QUnit.module('Views', { }, }); - kanban.reload(); + await kanban.reload(); assert.verifySteps([ 'read_group', '/web/dataset/search_read', @@ -2469,12 +2499,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('wait x2manys batch fetches to re-render', function (assert) { - assert.expect(4); + QUnit.test('wait x2manys batch fetches to re-render', async function (assert) { + assert.expect(7); var done = assert.async(); - var def; - var kanban = createView({ + var def = Promise.resolve(); + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2490,7 +2520,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { var result = this._super(route, args); if (args.method === 'read') { - return $.when(def).then(function() { + return def.then(function() { return result; }); } @@ -2498,13 +2528,17 @@ QUnit.module('Views', { }, }); - def = $.Deferred(); + def = testUtils.makeTestPromise(); assert.containsN(kanban, '.o_tag', 2); + assert.containsN(kanban, '.o_kanban_group', 2); kanban.update({groupBy: ['state']}); + def.then(async function () { + assert.containsN(kanban, '.o_kanban_group', 2); + await testUtils.nextTick(); + assert.containsN(kanban, '.o_kanban_group', 3); - def.resolve().then(function () { assert.containsN(kanban, '.o_tag', 2, - 'Should display 2 tags after update'); + 'Should display 2 tags after update'); assert.strictEqual(kanban.$('.o_kanban_group:eq(1) .o_tag').text(), 'gold', 'First category should be \'gold\''); assert.strictEqual(kanban.$('.o_kanban_group:eq(2) .o_tag').text(), @@ -2512,20 +2546,22 @@ QUnit.module('Views', { kanban.destroy(); done(); }); + await testUtils.nextTick(); + def.resolve(); }); - QUnit.test('can drag and drop a record from one column to the next', function (assert) { + QUnit.test('can drag and drop a record from one column to the next', async function (assert) { assert.expect(9); // @todo: remove this resequenceDef whenever the jquery upgrade branch // is merged. This is currently necessary to simulate the reality: we // need the click handlers to be executed after the end of the drag and // drop operation, not before. - var resequenceDef = $.Deferred(); + var resequenceDef = testUtils.makeTestPromise(); var envIDs = [1, 3, 2, 4]; // the ids that should be in the environment during this test this.data.partner.fields.sequence = {type: 'number', string: "Sequence"}; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2554,9 +2590,10 @@ QUnit.module('Views', { var $record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first'); var $group = kanban.$('.o_kanban_group:nth-child(2)'); envIDs = [3, 2, 4, 1]; // first record of first column moved to the bottom of second column - testUtils.dom.dragAndDrop($record, $group, {withTrailingClick: true}); + await testUtils.dom.dragAndDrop($record, $group, {withTrailingClick: true}); resequenceDef.resolve(); + await testUtils.nextTick(); assert.containsOnce(kanban, '.o_kanban_group:nth-child(1) .o_kanban_record'); assert.containsN(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record', 3); assert.containsN(kanban, '.thisiseditable', 4); @@ -2566,10 +2603,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('drag and drop a record, grouped by selection', function (assert) { + QUnit.test('drag and drop a record, grouped by selection', async function (assert) { assert.expect(6); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2584,7 +2621,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { if (route === '/web/dataset/resequence') { assert.ok(true, "should call resequence"); - return $.when(true); + return Promise.resolve(true); } if (args.model === 'partner' && args.method === 'write') { assert.deepEqual(args.args[1], {state: 'def'}); @@ -2597,18 +2634,19 @@ QUnit.module('Views', { var $record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first'); var $group = kanban.$('.o_kanban_group:nth-child(2)'); - testUtils.dom.dragAndDrop($record, $group); + await testUtils.dom.dragAndDrop($record, $group); + await nextTick(); // wait for resequence after drag and drop assert.containsNone(kanban, '.o_kanban_group:nth-child(1) .o_kanban_record'); assert.containsN(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record', 2); kanban.destroy(); }); - QUnit.test('prevent drag and drop of record if grouped by readonly', function (assert) { + QUnit.test('prevent drag and drop of record if grouped by readonly', async function (assert) { assert.expect(12); this.data.partner.fields.foo.readonly = true; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2622,7 +2660,7 @@ QUnit.module('Views', { '</kanban>', mockRPC: function (route, args) { if (route === '/web/dataset/resequence') { - return $.when(); + return Promise.resolve(); } if (args.model === 'partner' && args.method === 'write') { throw new Error('should not be draggable'); @@ -2631,27 +2669,29 @@ QUnit.module('Views', { }, }); // simulate an update coming from the searchview, with another groupby given - kanban.update({groupBy: ['state']}); + await kanban.update({groupBy: ['state']}); assert.containsOnce(kanban, '.o_kanban_group:nth-child(1) .o_kanban_record'); assert.containsOnce(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record'); // 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.dom.dragAndDrop($record, $group); + await testUtils.dom.dragAndDrop($record, $group); + await nextTick(); // wait for resequence after drag and drop // should not be draggable assert.containsOnce(kanban, '.o_kanban_group:nth-child(1) .o_kanban_record'); assert.containsOnce(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record'); // simulate an update coming from the searchview, with another groupby given - kanban.update({groupBy: ['foo']}); + await kanban.update({groupBy: ['foo']}); assert.containsOnce(kanban, '.o_kanban_group:nth-child(1) .o_kanban_record'); assert.containsN(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record', 2); // drag&drop a record in another column $record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first'); $group = kanban.$('.o_kanban_group:nth-child(2)'); - testUtils.dom.dragAndDrop($record, $group); + await testUtils.dom.dragAndDrop($record, $group); + await nextTick(); // wait for resequence after drag and drop // should not be draggable assert.containsOnce(kanban, '.o_kanban_group:nth-child(1) .o_kanban_record'); assert.containsN(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record', 2); @@ -2661,7 +2701,7 @@ QUnit.module('Views', { var $record2 = kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(1)'); assert.strictEqual($record1.text(), "blipDEF", "first record should be DEF"); assert.strictEqual($record2.text(), "blipGHI", "second record should be GHI"); - testUtils.dom.dragAndDrop($record2, $record1, {position: 'top'}); + await testUtils.dom.dragAndDrop($record2, $record1, {position: 'top'}); // should still be able to resequence assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(0)').text(), "blipGHI", "records should have been resequenced"); @@ -2671,7 +2711,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('prevent drag and drop if grouped by date/datetime field', function (assert) { + QUnit.test('prevent drag and drop if grouped by date/datetime field', async function (assert) { assert.expect(5); this.data.partner.records[0].date = '2017-01-08'; @@ -2679,7 +2719,7 @@ QUnit.module('Views', { this.data.partner.records[2].date = '2017-02-08'; this.data.partner.records[3].date = '2017-02-10'; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2700,7 +2740,7 @@ QUnit.module('Views', { // 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); + await testUtils.dragAndDrop($record, $group); // should not drag&drop record assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length , 2, @@ -2710,13 +2750,13 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('kanban view with default_group_by', function (assert) { + QUnit.test('kanban view with default_group_by', async function (assert) { assert.expect(7); this.data.partner.records.product_id = 1; this.data.product.records.push({id: 1, display_name: "third product"}); var readGroupCount = 0; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2746,19 +2786,19 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_group', 2, "should have " + 2 + " columns"); // simulate an update coming from the searchview, with another groupby given - kanban.update({groupBy: ['product_id']}); + await kanban.update({groupBy: ['product_id']}); assert.containsN(kanban, '.o_kanban_group', 2, "should now have " + 3 + " columns"); // simulate an update coming from the searchview, removing the previously set groupby - kanban.update({groupBy: []}); + await kanban.update({groupBy: []}); assert.containsN(kanban, '.o_kanban_group', 2, "should have " + 2 + " columns again"); kanban.destroy(); }); - QUnit.test('kanban view with create=False', function (assert) { + QUnit.test('kanban view with create=False', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2773,10 +2813,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('clicking on a link triggers correct event', function (assert) { + QUnit.test('clicking on a link triggers correct event', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2793,15 +2833,15 @@ QUnit.module('Views', { model: 'partner', }); }); - testUtils.dom.click(kanban.$('a').first()); + await testUtils.dom.click(kanban.$('a').first()); kanban.destroy(); }); - QUnit.test('environment is updated when (un)folding groups', function (assert) { + QUnit.test('environment is updated when (un)folding groups', async function (assert) { assert.expect(3); var envIDs = [1, 3, 2, 4]; // the ids that should be in the environment during this test - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2819,24 +2859,24 @@ QUnit.module('Views', { // fold the second group and check that the res_ids it contains are no // longer in the environment envIDs = [1, 3]; - testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_kanban_toggle_fold')); + await testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_kanban_toggle_fold')); assert.deepEqual(kanban.exportState().resIds, envIDs); // re-open the second group and check that the res_ids it contains are // back in the environment envIDs = [1, 3, 2, 4]; - testUtils.dom.click(kanban.$('.o_kanban_group:last')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last')); assert.deepEqual(kanban.exportState().resIds, envIDs); kanban.destroy(); }); - QUnit.test('create a column in grouped on m2o', function (assert) { + QUnit.test('create a column in grouped on m2o', async function (assert) { assert.expect(14); var nbRPCs = 0; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2855,7 +2895,7 @@ QUnit.module('Views', { //Create column will call resequence to set column order if (route === '/web/dataset/resequence') { assert.ok(true, "should call resequence"); - return $.when(true); + return Promise.resolve(true); } return this._super(route, args); }, @@ -2864,25 +2904,25 @@ QUnit.module('Views', { assert.notOk(kanban.$('.o_column_quick_create input').is(':visible'), "the input should not be visible"); - testUtils.dom.click(kanban.$('.o_quick_create_folded')); + await testUtils.dom.click(kanban.$('.o_quick_create_folded')); assert.ok(kanban.$('.o_column_quick_create input').is(':visible'), "the input should be visible"); // discard the column creation and click it again - kanban.$('.o_column_quick_create input').trigger($.Event('keydown', { + await kanban.$('.o_column_quick_create input').trigger($.Event('keydown', { keyCode: $.ui.keyCode.ESCAPE, which: $.ui.keyCode.ESCAPE, })); assert.notOk(kanban.$('.o_column_quick_create input').is(':visible'), "the input should not be visible after discard"); - testUtils.dom.click(kanban.$('.o_quick_create_folded')); + await testUtils.dom.click(kanban.$('.o_quick_create_folded')); assert.ok(kanban.$('.o_column_quick_create input').is(':visible'), "the input should be visible"); - kanban.$('.o_column_quick_create input').val('new value'); - testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + await kanban.$('.o_column_quick_create input').val('new value').trigger('input'); + await testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); assert.strictEqual(kanban.$('.o_kanban_group:last span:contains(new value)').length, 1, "the last column should be the newly created one"); @@ -2893,22 +2933,22 @@ QUnit.module('Views', { // fold and unfold the created column, and check that no RPC is done (as there is no record) nbRPCs = 0; - testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_kanban_toggle_fold')); + await testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_kanban_toggle_fold')); assert.hasClass(kanban.$('.o_kanban_group:last'),'o_column_folded', 'the created column should now be folded'); - testUtils.dom.click(kanban.$('.o_kanban_group:last')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last')); assert.doesNotHaveClass(kanban.$('.o_kanban_group:last'), 'o_column_folded'); assert.strictEqual(nbRPCs, 0, 'no rpc should have been done when folding/unfolding'); // quick create a record - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); assert.hasClass(kanban.$('.o_kanban_group:first() > div:nth(1)'),'o_kanban_quick_create', "clicking on create should open the quick_create in the first column"); kanban.destroy(); }); - QUnit.test('auto fold group when reach the limit', function (assert) { + QUnit.test('auto fold group when reach the limit', async function (assert) { assert.expect(9); var data = this.data; @@ -2924,7 +2964,7 @@ QUnit.module('Views', { }); } - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: data, @@ -2965,10 +3005,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('hide and display help message (ESC) in kanban quick create', function (assert) { + QUnit.test('hide and display help message (ESC) in kanban quick create', async function (assert) { assert.expect(2); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -2981,20 +3021,20 @@ QUnit.module('Views', { groupBy: ['product_id'], }); - testUtils.dom.click(kanban.$('.o_quick_create_folded')); + await testUtils.dom.click(kanban.$('.o_quick_create_folded')); assert.ok(kanban.$('.o_discard_msg').is(':visible'), 'the ESC to discard message is visible'); // click outside the column (to lose focus) - testUtils.dom.clickFirst(kanban.$('.o_kanban_header')); + await testUtils.dom.clickFirst(kanban.$('.o_kanban_header')); assert.notOk(kanban.$('.o_discard_msg').is(':visible'), 'the ESC to discard message is no longer visible'); kanban.destroy(); }); - QUnit.test('delete a column in grouped on m2o', function (assert) { - assert.expect(36); + QUnit.test('delete a column in grouped on m2o', async function (assert) { + assert.expect(37); testUtils.mock.patch(KanbanRenderer, { _renderGrouped: function () { @@ -3008,7 +3048,7 @@ QUnit.module('Views', { var resequencedIDs; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3024,7 +3064,7 @@ QUnit.module('Views', { resequencedIDs = args.ids; assert.strictEqual(_.reject(args.ids, _.isNumber).length, 0, "column resequenced should be existing records with IDs"); - return $.when(true); + return Promise.resolve(true); } if (args.method) { assert.step(args.method); @@ -3056,15 +3096,15 @@ QUnit.module('Views', { // delete second column (first cancel the confirm request, then confirm) testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_delete')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_delete')); assert.ok($('.modal').length, 'a confirm modal should be displayed'); - testUtils.modal.clickButton('Cancel'); // click on cancel + await testUtils.modal.clickButton('Cancel'); // click on cancel assert.strictEqual(kanban.$('.o_kanban_group:last').data('id'), 5, 'column [5, "xmo"] should still be there'); testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_delete')); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_delete')); assert.ok($('.modal').length, 'a confirm modal should be displayed'); - testUtils.modal.clickButton('Ok'); // click on confirm + await testUtils.modal.clickButton('Ok'); // click on confirm assert.strictEqual(kanban.$('.o_kanban_group:last').data('id'), 3, 'last column should now be [3, "hello"]'); assert.containsN(kanban, '.o_kanban_group', 2, "should still have two columns"); @@ -3084,37 +3124,39 @@ QUnit.module('Views', { "the old widgets should have been correctly deleted"); // test column drag and drop having an 'Undefined' column - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( kanban.$('.o_kanban_header_title:first'), kanban.$('.o_kanban_header_title:last'), {position: 'right'} ); assert.strictEqual(resequencedIDs, undefined, "resequencing require at least 2 not Undefined columns"); - testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); kanban.$('.o_column_quick_create input').val('once third column'); - testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); var newColumnID = kanban.$('.o_kanban_group:last').data('id'); - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( kanban.$('.o_kanban_header_title:first'), kanban.$('.o_kanban_header_title:last'), {position: 'right'} ); assert.deepEqual([3, newColumnID], resequencedIDs, "moving the Undefined column should not affect order of other columns") - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( kanban.$('.o_kanban_header_title:first'), kanban.$('.o_kanban_header_title:nth(1)'), {position: 'right'} ); + await nextTick(); // wait for resequence after drag and drop assert.deepEqual([newColumnID, 3], resequencedIDs, "moved column should be resequenced accordingly") + assert.verifySteps(['name_create', 'read', 'read', 'read']); kanban.destroy(); testUtils.mock.unpatch(KanbanRenderer); }); - QUnit.test('create a column, delete it and create another one', function (assert) { + QUnit.test('create a column, delete it and create another one', async function (assert) { assert.expect(5); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3129,21 +3171,21 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_group', 2, "should have two columns"); - testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); kanban.$('.o_column_quick_create input').val('new column 1'); - testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group', 3, "should have two columns"); testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:last')); - testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_delete')); - testUtils.modal.clickButton('Ok'); + await testUtils.dom.click(kanban.$('.o_kanban_group:last .o_column_delete')); + await testUtils.modal.clickButton('Ok'); assert.containsN(kanban, '.o_kanban_group', 2, "should have twos columns"); - testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); kanban.$('.o_column_quick_create input').val('new column 2'); - testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); assert.containsN(kanban, '.o_kanban_group', 3, "should have three columns"); assert.strictEqual(kanban.$('.o_kanban_group:last span:contains(new column 2)').length, 1, @@ -3151,11 +3193,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('edit a column in grouped on m2o', function (assert) { + QUnit.test('edit a column in grouped on m2o', async function (assert) { assert.expect(12); var nbRPCs = 0; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3179,14 +3221,14 @@ QUnit.module('Views', { // edit the title of column [5, 'xmo'] and close without saving testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group[data-id=5]')); - testUtils.dom.click(kanban.$('.o_kanban_group[data-id=5] .o_column_edit')); + await testUtils.dom.click(kanban.$('.o_kanban_group[data-id=5] .o_column_edit')); assert.containsOnce(document.body, '.modal .o_form_editable', "a form view should be open in a modal"); assert.strictEqual($('.modal .o_form_editable input').val(), 'xmo', 'the name should be "xmo"'); - testUtils.fields.editInput($('.modal .o_form_editable input'), 'ged'); // change the value + await testUtils.fields.editInput($('.modal .o_form_editable input'), 'ged'); // change the value nbRPCs = 0; - testUtils.dom.click($('.modal-header .close')); + await testUtils.dom.click($('.modal-header .close')); assert.containsNone(document.body, '.modal'); assert.strictEqual(kanban.$('.o_kanban_group[data-id=5] .o_column_title').text(), 'xmo', 'title of the column should still be "xmo"'); @@ -3194,10 +3236,10 @@ QUnit.module('Views', { // edit the title of column [5, 'xmo'] and discard testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group[data-id=5]')); - testUtils.dom.click(kanban.$('.o_kanban_group[data-id=5] .o_column_edit')); - testUtils.fields.editInput($('.modal .o_form_editable input'), 'ged'); // change the value + await testUtils.dom.click(kanban.$('.o_kanban_group[data-id=5] .o_column_edit')); + await testUtils.fields.editInput($('.modal .o_form_editable input'), 'ged'); // change the value nbRPCs = 0; - testUtils.modal.clickButton('Discard'); + await testUtils.modal.clickButton('Discard'); assert.containsNone(document.body, '.modal'); assert.strictEqual(kanban.$('.o_kanban_group[data-id=5] .o_column_title').text(), 'xmo', 'title of the column should still be "xmo"'); @@ -3205,10 +3247,10 @@ QUnit.module('Views', { // edit the title of column [5, 'xmo'] and save testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group[data-id=5]')); - testUtils.dom.click(kanban.$('.o_kanban_group[data-id=5] .o_column_edit')); - testUtils.fields.editInput($('.modal .o_form_editable input'), 'ged'); // change the value + await testUtils.dom.click(kanban.$('.o_kanban_group[data-id=5] .o_column_edit')); + await testUtils.fields.editInput($('.modal .o_form_editable input'), 'ged'); // change the value nbRPCs = 0; - testUtils.modal.clickButton('Save'); // click on save + await testUtils.modal.clickButton('Save'); // click on save assert.ok(!$('.modal').length, 'the modal should be closed'); assert.strictEqual(kanban.$('.o_kanban_group[data-id=5] .o_column_title').text(), 'ged', 'title of the column should be "ged"'); @@ -3216,10 +3258,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create column should be opened if there is no column', function (assert) { + QUnit.test('quick create column should be opened if there is no column', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3241,10 +3283,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick create several columns in a row', function (assert) { + QUnit.test('quick create several columns in a row', async function (assert) { assert.expect(10); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3267,13 +3309,13 @@ QUnit.module('Views', { "the ColumnQuickCreate should be folded"); // add a new column - testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); assert.containsNone(kanban, '.o_column_quick_create .o_quick_create_folded:visible', "the ColumnQuickCreate should be unfolded"); assert.containsOnce(kanban, '.o_column_quick_create .o_quick_create_unfolded:visible', "the ColumnQuickCreate should be unfolded"); kanban.$('.o_column_quick_create input').val('New Column 1'); - testUtils.dom.click(kanban.$('.o_column_quick_create .btn-primary')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .btn-primary')); assert.containsN(kanban, '.o_kanban_group', 3, "should now have three columns"); @@ -3283,14 +3325,14 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_column_quick_create .o_quick_create_unfolded:visible', "the ColumnQuickCreate should still be unfolded"); kanban.$('.o_column_quick_create input').val('New Column 2'); - testUtils.dom.click(kanban.$('.o_column_quick_create .btn-primary')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .btn-primary')); assert.containsN(kanban, '.o_kanban_group', 4, "should now have four columns"); kanban.destroy(); }); - QUnit.test('quick create column and examples', function (assert) { + QUnit.test('quick create column and examples', async function (assert) { assert.expect(12); kanbanExamplesRegistry.add('test', [{ @@ -3302,7 +3344,7 @@ QUnit.module('Views', { columns: ["Col 1", "Col 2"], }]); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3319,13 +3361,13 @@ QUnit.module('Views', { "should have a ColumnQuickCreate widget"); // open the quick create - testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .o_quick_create_folded')); assert.containsOnce(kanban, '.o_column_quick_create .o_kanban_examples:visible', "should have a link to see examples"); // click to see the examples - testUtils.dom.click(kanban.$('.o_column_quick_create .o_kanban_examples')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .o_kanban_examples')); assert.strictEqual($('.modal .o_kanban_examples_dialog').length, 1, "should have open the examples dialog"); @@ -3355,10 +3397,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('if view was not grouped at start, it can be grouped and ungrouped', function (assert) { + QUnit.test('if view was not grouped at start, it can be grouped and ungrouped', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3371,22 +3413,22 @@ QUnit.module('Views', { }); assert.doesNotHaveClass(kanban.$('.o_kanban_view'), 'o_kanban_grouped'); - kanban.update({groupBy: ['product_id']}); + await kanban.update({groupBy: ['product_id']}); assert.hasClass(kanban.$('.o_kanban_view'),'o_kanban_grouped'); - kanban.update({groupBy: []}); + await kanban.update({groupBy: []}); assert.doesNotHaveClass(kanban.$('.o_kanban_view'), 'o_kanban_grouped'); kanban.destroy(); }); - QUnit.test('no content helper when no data', function (assert) { + QUnit.test('no content helper when no data', async function (assert) { assert.expect(3); var records = this.data.partner.records; this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3410,17 +3452,17 @@ QUnit.module('Views', { "should have rendered no content helper from action"); this.data.partner.records = records; - kanban.reload(); + await kanban.reload(); assert.containsNone(kanban, '.o_view_nocontent', "should not display the no content helper"); kanban.destroy(); }); - QUnit.test('no nocontent helper for grouped kanban with empty groups', function (assert) { + QUnit.test('no nocontent helper for grouped kanban with empty groups', async function (assert) { assert.expect(2); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3460,12 +3502,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('no nocontent helper for grouped kanban with no records', function (assert) { + QUnit.test('no nocontent helper for grouped kanban with no records', async function (assert) { assert.expect(4); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3493,12 +3535,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('no nocontent helper is shown when no longer creating column', function (assert) { + QUnit.test('no nocontent helper is shown when no longer creating column', async function (assert) { assert.expect(3); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3520,7 +3562,7 @@ QUnit.module('Views', { // creating a new column kanban.$('.o_column_quick_create .o_input').val('applejack'); - testUtils.dom.click(kanban.$('.o_column_quick_create .o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_column_quick_create .o_kanban_add')); assert.containsNone(kanban, '.o_view_nocontent', "there should be no nocontent helper (still in 'column creation mode')"); @@ -3537,12 +3579,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('no nocontent helper is hidden when quick creating a column', function (assert) { + QUnit.test('no nocontent helper is hidden when quick creating a column', async function (assert) { assert.expect(2); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3557,7 +3599,7 @@ QUnit.module('Views', { var result = [ {__domain: [['product_id', '=', 3]], product_id_count: 0, product_id: [3, 'hello']}, ]; - return $.when(result); + return Promise.resolve(result); } return this._super.apply(this, arguments); }, @@ -3571,7 +3613,7 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_view_nocontent', "there should be a nocontent helper"); - testUtils.dom.click(kanban.$('.o_kanban_add_column')); + await testUtils.dom.click(kanban.$('.o_kanban_add_column')); assert.containsNone(kanban, '.o_view_nocontent', "there should be no nocontent helper (we are in 'column creation mode')"); @@ -3579,12 +3621,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('remove nocontent helper after adding a record', function (assert) { + QUnit.test('remove nocontent helper after adding a record', async function (assert) { assert.expect(2); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3599,7 +3641,7 @@ QUnit.module('Views', { var result = [ {__domain: [['product_id', '=', 3]], product_id_count: 0, product_id: [3, 'hello']}, ]; - return $.when(result); + return Promise.resolve(result); } return this._super.apply(this, arguments); }, @@ -3614,9 +3656,9 @@ QUnit.module('Views', { "there should be a nocontent helper"); // add a record - testUtils.dom.click(kanban.$('.o_kanban_quick_add')); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create .o_input'), 'twilight sparkle'); - testUtils.dom.click(kanban.$('.o_kanban_quick_create button.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_quick_add')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create .o_input'), 'twilight sparkle'); + await testUtils.dom.click(kanban.$('.o_kanban_quick_create button.o_kanban_add')); assert.containsNone(kanban, '.o_view_nocontent', "there should be no nocontent helper (there is now one record)"); @@ -3624,12 +3666,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('remove nocontent helper when adding a record', function (assert) { + QUnit.test('remove nocontent helper when adding a record', async function (assert) { assert.expect(2); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3644,7 +3686,7 @@ QUnit.module('Views', { var result = [ {__domain: [['product_id', '=', 3]], product_id_count: 0, product_id: [3, 'hello']}, ]; - return $.when(result); + return Promise.resolve(result); } return this._super.apply(this, arguments); }, @@ -3659,8 +3701,8 @@ QUnit.module('Views', { "there should be a nocontent helper"); // add a record - testUtils.dom.click(kanban.$('.o_kanban_quick_add')); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create .o_input'), 'twilight sparkle'); + await testUtils.dom.click(kanban.$('.o_kanban_quick_add')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create .o_input'), 'twilight sparkle'); assert.containsNone(kanban, '.o_view_nocontent', "there should be no nocontent helper (there is now one record)"); @@ -3668,12 +3710,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('nocontent helper is displayed again after canceling quick create', function (assert) { + QUnit.test('nocontent helper is displayed again after canceling quick create', async function (assert) { assert.expect(1); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3688,7 +3730,7 @@ QUnit.module('Views', { var result = [ {__domain: [['product_id', '=', 3]], product_id_count: 0, product_id: [3, 'hello']}, ]; - return $.when(result); + return Promise.resolve(result); } return this._super.apply(this, arguments); }, @@ -3700,9 +3742,9 @@ QUnit.module('Views', { }); // add a record - testUtils.dom.click(kanban.$('.o_kanban_quick_add')); + await testUtils.dom.click(kanban.$('.o_kanban_quick_add')); - testUtils.dom.click(kanban.$('.o_kanban_view')); + await testUtils.dom.click(kanban.$('.o_kanban_view')); assert.containsOnce(kanban, '.o_view_nocontent', "there should be again a nocontent helper"); @@ -3710,12 +3752,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('nocontent helper for grouped kanban with no records with no group_create', function (assert) { + QUnit.test('nocontent helper for grouped kanban with no records with no group_create', async function (assert) { assert.expect(4); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3743,12 +3785,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('buttons with modifiers', function (assert) { + QUnit.test('buttons with modifiers', async function (assert) { assert.expect(2); this.data.partner.records[1].bar = false; // so that test is more complete - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: "partner", data: this.data, @@ -3774,10 +3816,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('button executes action and reloads', function (assert) { + QUnit.test('button executes action and reloads', async function (assert) { assert.expect(6); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: "partner", data: this.data, @@ -3802,10 +3844,10 @@ QUnit.module('Views', { count++; event.data.on_closed(); }); - testUtils.dom.click($('button[data-name="a1"]').first()); + await testUtils.dom.click($('button[data-name="a1"]').first()); assert.strictEqual(count, 1, "should have triggered a execute action"); - testUtils.dom.click($('button[data-name="a1"]').first()); + await testUtils.dom.click($('button[data-name="a1"]').first()); assert.strictEqual(count, 1, "double-click on kanban actions should be debounced"); assert.verifySteps([ @@ -3816,7 +3858,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('button executes action and check domain', function (assert) { + QUnit.test('button executes action and check domain', async function (assert) { assert.expect(2); var data = this.data; @@ -3825,7 +3867,7 @@ QUnit.module('Views', { data.partner.records[k].active = true; } - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: "partner", data: data, @@ -3846,16 +3888,16 @@ QUnit.module('Views', { }); assert.strictEqual(kanban.$('.o_kanban_record:contains(yop)').length, 1, "should display 'yop' record"); - testUtils.dom.click(kanban.$('.o_kanban_record:contains(yop) button[data-name="toggle_active"]')); + await testUtils.dom.click(kanban.$('.o_kanban_record:contains(yop) button[data-name="toggle_active"]')); assert.strictEqual(kanban.$('.o_kanban_record:contains(yop)').length, 0, "should remove 'yop' record from the view"); kanban.destroy(); }); - QUnit.test('button executes action with domain field not in view', function (assert) { + QUnit.test('button executes action with domain field not in view', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: "partner", data: this.data, @@ -3875,7 +3917,7 @@ QUnit.module('Views', { }); try { - testUtils.dom.click(kanban.$('.o_kanban_record:contains(yop) button[data-name="toggle_action"]')); + await testUtils.dom.click(kanban.$('.o_kanban_record:contains(yop) button[data-name="toggle_action"]')); assert.strictEqual(true, true, 'Everything went fine'); } catch (e) { assert.strictEqual(true, false, 'Error triggered at action execution'); @@ -3883,13 +3925,13 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('rendering date and datetime', function (assert) { + QUnit.test('rendering date and datetime', async function (assert) { assert.expect(2); this.data.partner.records[0].date = "2017-01-25"; this.data.partner.records[1].datetime= "2016-12-12 10:55:05"; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3913,12 +3955,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('evaluate conditions on relational fields', function (assert) { + QUnit.test('evaluate conditions on relational fields', async function (assert) { assert.expect(3); this.data.partner.records[0].product_id = false; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3944,12 +3986,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('resequence columns in grouped by m2o', function (assert) { + QUnit.test('resequence columns in grouped by m2o', async function (assert) { assert.expect(6); this.data.product.fields.sequence = {string: "Sequence", type: "integer"}; var envIDs = [1, 3, 2, 4]; // the ids that should be in the environment during this test - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -3975,7 +4017,8 @@ QUnit.module('Views', { // for this test, and directly call the event handler envIDs = [2, 4, 1, 3]; // the columns will be inverted kanban._onResequenceColumn({data: {ids: [5, 3]}}); - kanban.update({}, {reload: false}); // re-render without reloading + await nextTick(); // wait for resequencing before re-rendering + await kanban.update({}, {reload: false}); // re-render without reloading assert.strictEqual(kanban.$('.o_kanban_group:first').data('id'), 5, "first column should be id 5 after resequencing"); @@ -3984,10 +4027,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('properly evaluate more complex domains', function (assert) { + QUnit.test('properly evaluate more complex domains', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4011,14 +4054,14 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('edit the kanban color with the colorpicker', function (assert) { + QUnit.test('edit the kanban color with the colorpicker', async function (assert) { assert.expect(5); var writeOnColor; this.data.category.records[0].color = 12; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'category', data: this.data, @@ -4061,7 +4104,7 @@ QUnit.module('Views', { // Set a color testUtils.kanban.toggleRecordDropdown($firstRecord); - testUtils.dom.click($firstRecord.find('.oe_kanban_colorpicker a.oe_kanban_color_9')); + await testUtils.dom.click($firstRecord.find('.oe_kanban_colorpicker a.oe_kanban_color_9')); assert.ok(writeOnColor, "should write on the color field"); $firstRecord = kanban.$('.o_kanban_record:first()'); // First record is reloaded here assert.ok($firstRecord.is('.oe_kanban_color_9'), @@ -4070,11 +4113,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('load more records in column', function (assert) { - assert.expect(12); + QUnit.test('load more records in column', async function (assert) { + assert.expect(13); var envIDs = [1, 2, 4]; // the ids that should be in the environment during this test - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4089,7 +4132,7 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (route === '/web/dataset/search_read') { - assert.step([args.limit, args.offset]); + assert.step(args.limit + ' - ' + args.offset); } return this._super.apply(this, arguments); }, @@ -4101,24 +4144,25 @@ QUnit.module('Views', { // load more envIDs = [1, 2, 3, 4]; // id 3 will be loaded - testUtils.dom.click(kanban.$('.o_kanban_group:eq(1)').find('.o_kanban_load_more')); + await testUtils.dom.click(kanban.$('.o_kanban_group:eq(1)').find('.o_kanban_load_more')); assert.strictEqual(kanban.$('.o_kanban_group:eq(1) .o_kanban_record').length, 3, "there should now be 3 records in the column"); - assert.verifySteps([[2, undefined], [2, undefined], [2, 2]], + assert.verifySteps(['2 - undefined', '2 - undefined', '2 - 2'], "the records should be correctly fetched"); assert.deepEqual(kanban.exportState().resIds, envIDs); // reload - kanban.reload(); + await kanban.reload(); assert.strictEqual(kanban.$('.o_kanban_group:eq(1) .o_kanban_record').length, 3, "there should still be 3 records in the column after reload"); assert.deepEqual(kanban.exportState().resIds, envIDs); + assert.verifySteps(['4 - undefined', '2 - undefined']); kanban.destroy(); }); - QUnit.test('load more records in column with x2many', function (assert) { + QUnit.test('load more records in column with x2many', async function (assert) { assert.expect(10); this.data.partner.records[0].category_ids = [7]; @@ -4128,7 +4172,7 @@ QUnit.module('Views', { // record [2] will be loaded after - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4146,7 +4190,7 @@ QUnit.module('Views', { }, mockRPC: function (route, args) { if (args.model === 'category' && args.method === 'read') { - assert.step(args.args[0]); + assert.step(String(args.args[0])); } if (route === '/web/dataset/search_read') { if (args.limit) { @@ -4165,25 +4209,25 @@ QUnit.module('Views', { assert.strictEqual(kanban.$('.o_kanban_group:eq(1) .o_kanban_record').length, 2, "there should be 2 records in the column"); - assert.verifySteps([[7]], "only the appearing category should be fetched"); + assert.verifySteps(['7'], "only the appearing category should be fetched"); // load more - testUtils.dom.click(kanban.$('.o_kanban_group:eq(1)').find('.o_kanban_load_more')); + await testUtils.dom.click(kanban.$('.o_kanban_group:eq(1)').find('.o_kanban_load_more')); assert.strictEqual(kanban.$('.o_kanban_group:eq(1) .o_kanban_record').length, 3, "there should now be 3 records in the column"); - assert.verifySteps([[7], [6]], "the other categories should not be fetched"); + assert.verifySteps(['6'], "the other categories should not be fetched"); kanban.destroy(); }); - QUnit.test('update buttons after column creation', function (assert) { + QUnit.test('update buttons after column creation', async function (assert) { assert.expect(2); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4197,19 +4241,19 @@ QUnit.module('Views', { assert.isNotVisible(kanban.$buttons.find('.o-kanban-button-new'), "Create button should be hidden"); - testUtils.dom.click(kanban.$('.o_column_quick_create')); + await testUtils.dom.click(kanban.$('.o_column_quick_create')); kanban.$('.o_column_quick_create input').val('new column'); - testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); assert.isVisible(kanban.$buttons.find('.o-kanban-button-new'), "Create button should now be visible"); kanban.destroy(); }); - QUnit.test('group_by_tooltip option when grouping on a many2one', function (assert) { + QUnit.test('group_by_tooltip option when grouping on a many2one', async function (assert) { assert.expect(12); delete this.data.partner.records[3].product_id; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4236,7 +4280,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_group', 2, "should have " + 2 + " columns"); // simulate an update coming from the searchview, with another groupby given - kanban.update({groupBy: ['product_id']}); + await kanban.update({groupBy: ['product_id']}); assert.containsN(kanban, '.o_kanban_group', 3, "should have " + 3 + " columns"); assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1, "column should contain 1 record(s)"); @@ -4257,12 +4301,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('move a record then put it again in the same column', function (assert) { + QUnit.test('move a record then put it again in the same column', async function (assert) { assert.expect(6); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4274,18 +4318,18 @@ QUnit.module('Views', { groupBy: ['product_id'], }); - testUtils.dom.click(kanban.$('.o_column_quick_create')); + await testUtils.dom.click(kanban.$('.o_column_quick_create')); kanban.$('.o_column_quick_create input').val('column1'); - testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); - testUtils.dom.click(kanban.$('.o_column_quick_create')); + await testUtils.dom.click(kanban.$('.o_column_quick_create')); kanban.$('.o_column_quick_create input').val('column2'); - testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); - testUtils.dom.click(kanban.$('.o_kanban_group:eq(1) .o_kanban_quick_add i')); + await testUtils.dom.click(kanban.$('.o_kanban_group:eq(1) .o_kanban_quick_add i')); var $quickCreate = kanban.$('.o_kanban_group:eq(1) .o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); - testUtils.dom.click($quickCreate.find('button.o_kanban_add')); + await testUtils.fields.editInput($quickCreate.find('input'), 'new partner'); + await testUtils.dom.click($quickCreate.find('button.o_kanban_add')); assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_record').length, 0, "column should contain 0 record"); @@ -4294,7 +4338,8 @@ QUnit.module('Views', { var $record = kanban.$('.o_kanban_group:eq(1) .o_kanban_record:eq(0)'); var $group = kanban.$('.o_kanban_group:eq(0)'); - testUtils.dom.dragAndDrop($record, $group); + await testUtils.dom.dragAndDrop($record, $group); + await nextTick(); // wait for resequencing after drag and drop assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_record').length, 1, "column should contain 1 records"); @@ -4304,7 +4349,8 @@ QUnit.module('Views', { $record = kanban.$('.o_kanban_group:eq(0) .o_kanban_record:eq(0)'); $group = kanban.$('.o_kanban_group:eq(1)'); - testUtils.dom.dragAndDrop($record, $group); + await testUtils.dom.dragAndDrop($record, $group); + await nextTick(); // wait for resequencing after drag and drop assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_record').length, 0, "column should contain 0 records"); @@ -4313,13 +4359,13 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('resequence a record twice', function (assert) { + QUnit.test('resequence a record twice', async function (assert) { assert.expect(10); this.data.partner.records = []; var nbResequence = 0; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4332,25 +4378,25 @@ QUnit.module('Views', { mockRPC: function (route) { if (route === '/web/dataset/resequence') { nbResequence++; - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, }); - testUtils.dom.click(kanban.$('.o_column_quick_create')); + await testUtils.dom.click(kanban.$('.o_column_quick_create')); kanban.$('.o_column_quick_create input').val('column1'); - testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); - testUtils.dom.click(kanban.$('.o_kanban_group:eq(0) .o_kanban_quick_add i')); + await testUtils.dom.click(kanban.$('.o_kanban_group:eq(0) .o_kanban_quick_add i')); var $quickCreate = kanban.$('.o_kanban_group:eq(0) .o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('input'), 'record1'); - testUtils.dom.click($quickCreate.find('button.o_kanban_add')); + await testUtils.fields.editInput($quickCreate.find('input'), 'record1'); + await testUtils.dom.click($quickCreate.find('button.o_kanban_add')); - testUtils.dom.click(kanban.$('.o_kanban_group:eq(0) .o_kanban_quick_add i')); + await testUtils.dom.click(kanban.$('.o_kanban_group:eq(0) .o_kanban_quick_add i')); $quickCreate = kanban.$('.o_kanban_group:eq(0) .o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('input'), 'record2'); - testUtils.dom.click($quickCreate.find('button.o_kanban_add')); + await testUtils.fields.editInput($quickCreate.find('input'), 'record2'); + await testUtils.dom.click($quickCreate.find('button.o_kanban_add')); assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_record').length, 2, "column should contain 2 records"); @@ -4361,7 +4407,7 @@ QUnit.module('Views', { var $record1 = kanban.$('.o_kanban_group:eq(0) .o_kanban_record:eq(1)'); var $record2 = kanban.$('.o_kanban_group:eq(0) .o_kanban_record:eq(0)'); - testUtils.dom.dragAndDrop($record1, $record2, {position: 'top'}); + await testUtils.dom.dragAndDrop($record1, $record2, {position: 'top'}); assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_record').length, 2, "column should contain 2 records"); @@ -4370,7 +4416,7 @@ QUnit.module('Views', { assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_record:eq(1)').text(), "record2", "records should be correctly ordered"); - testUtils.dom.dragAndDrop($record2, $record1, {position: 'top'}); + await testUtils.dom.dragAndDrop($record2, $record1, {position: 'top'}); assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_record').length, 2, "column should contain 2 records"); @@ -4382,7 +4428,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('basic support for widgets', function (assert) { + QUnit.test('basic support for widgets', async function (assert) { assert.expect(1); var MyWidget = Widget.extend({ @@ -4395,7 +4441,7 @@ QUnit.module('Views', { }); widgetRegistry.add('test', MyWidget); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4415,10 +4461,10 @@ QUnit.module('Views', { delete widgetRegistry.map.test; }); - QUnit.test('column progressbars properly work', function (assert) { + QUnit.test('column progressbars properly work', async function (assert) { assert.expect(2); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4444,10 +4490,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('column progressbars should not crash in non grouped views', function (assert) { + QUnit.test('column progressbars should not crash in non grouped views', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4475,10 +4521,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('column progressbars: creating a new column should create a new progressbar', function (assert) { + QUnit.test('column progressbars: creating a new column should create a new progressbar', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4499,9 +4545,9 @@ QUnit.module('Views', { // Create a new column: this should create an empty progressbar var $columnQuickCreate = kanban.$('.o_column_quick_create'); - testUtils.dom.click($columnQuickCreate.find('.o_quick_create_folded')); + await testUtils.dom.click($columnQuickCreate.find('.o_quick_create_folded')); $columnQuickCreate.find('input').val('test'); - testUtils.dom.click($columnQuickCreate.find('.btn-primary')); + await testUtils.dom.click($columnQuickCreate.find('.btn-primary')); assert.containsN(kanban, '.o_kanban_counter', nbProgressBars + 1, "a new column with a new column progressbar should have been created"); @@ -4509,10 +4555,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('column progressbars on quick create properly update counter', function (assert) { + QUnit.test('column progressbars on quick create properly update counter', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4529,20 +4575,22 @@ QUnit.module('Views', { }); var initialCount = parseInt(kanban.$('.o_kanban_counter_side:first').text()); - testUtils.dom.click(kanban.$('.o_kanban_quick_add:first')); - testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'Test'); - testUtils.dom.click(kanban.$('.o_kanban_add')); + await testUtils.dom.click(kanban.$('.o_kanban_quick_add:first')); + await testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'Test'); + await testUtils.dom.click(kanban.$('.o_kanban_add')); var lastCount = parseInt(kanban.$('.o_kanban_counter_side:first').text()); + await nextTick(); // await update + await nextTick(); // await read assert.strictEqual(lastCount, initialCount + 1, "kanban counters should have updated on quick create"); kanban.destroy(); }); - QUnit.test('column progressbars are working with load more', function (assert) { + QUnit.test('column progressbars are working with load more', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4560,8 +4608,8 @@ QUnit.module('Views', { }); // we have 1 record shown, load 2 more and check it worked - testUtils.dom.click(kanban.$('.o_kanban_group').find('.o_kanban_load_more')); - testUtils.dom.click(kanban.$('.o_kanban_group').find('.o_kanban_load_more')); + await testUtils.dom.click(kanban.$('.o_kanban_group').find('.o_kanban_load_more')); + await testUtils.dom.click(kanban.$('.o_kanban_group').find('.o_kanban_load_more')); var shownIDs = _.map(kanban.$('.o_kanban_record'), function(record) { return parseInt(record.innerText); }); @@ -4570,13 +4618,13 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('column progressbars on archiving records update counter', function (assert) { + QUnit.test('column progressbars on archiving records update counter', async function (assert) { assert.expect(4); // add active field on partner model and make all records active this.data.partner.fields.active = {string: 'Active', type: 'char', default: true}; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4602,8 +4650,8 @@ QUnit.module('Views', { // archive all records of the second columns testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:eq(1)')); - testUtils.dom.click(kanban.$('.o_column_archive_records:visible')); - testUtils.dom.click($('.modal-footer button:first')); + await testUtils.dom.click(kanban.$('.o_column_archive_records:visible')); + await testUtils.dom.click($('.modal-footer button:first')); assert.strictEqual(kanban.$('.o_kanban_group:eq(1) .o_kanban_counter_side').text(), "0", "counter should contain the correct value"); @@ -4613,13 +4661,13 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('kanban with progressbars: correctly update env when archiving records', function (assert) { + QUnit.test('kanban with progressbars: correctly update env when archiving records', async function (assert) { assert.expect(2); // add active field on partner model and make all records active this.data.partner.fields.active = {string: 'Active', type: 'char', default: true}; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4642,18 +4690,18 @@ QUnit.module('Views', { // archive all records of the first column testUtils.kanban.toggleGroupSettings(kanban.$('.o_kanban_group:first')); - testUtils.dom.click(kanban.$('.o_column_archive_records:visible')); - testUtils.dom.click($('.modal-footer button:first')); + await testUtils.dom.click(kanban.$('.o_column_archive_records:visible')); + await testUtils.dom.click($('.modal-footer button:first')); assert.deepEqual(kanban.exportState().resIds, [1, 2, 3]); kanban.destroy(); }); - QUnit.test('RPCs when (re)loading kanban view progressbars', function (assert) { + QUnit.test('RPCs when (re)loading kanban view progressbars', async function (assert) { assert.expect(9); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4675,30 +4723,30 @@ QUnit.module('Views', { }, }); - kanban.reload(); + await kanban.reload(); assert.verifySteps([ // initial load 'read_group', + 'read_progress_bar', '/web/dataset/search_read', '/web/dataset/search_read', - 'read_progress_bar', // reload 'read_group', + 'read_progress_bar', '/web/dataset/search_read', '/web/dataset/search_read', - 'read_progress_bar', ]); kanban.destroy(); }); - QUnit.test('drag & drop records grouped by m2o with progressbar', function (assert) { + QUnit.test('drag & drop records grouped by m2o with progressbar', async function (assert) { assert.expect(4); this.data.partner.records[0].product_id = false; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4714,7 +4762,7 @@ QUnit.module('Views', { groupBy: ['product_id'], mockRPC: function (route, args) { if (route === '/web/dataset/resequence') { - return $.when(true); + return Promise.resolve(true); } return this._super(route, args); }, @@ -4723,25 +4771,28 @@ QUnit.module('Views', { assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_counter_side').text(), "1", "counter should contain the correct value"); - testUtils.dom.dragAndDrop(kanban.$('.o_kanban_group:eq(0) .o_kanban_record:eq(0)'), kanban.$('.o_kanban_group:eq(1)')); + await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_group:eq(0) .o_kanban_record:eq(0)'), kanban.$('.o_kanban_group:eq(1)')); + await nextTick(); // wait for update resulting from drag and drop assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_counter_side').text(), "0", "counter should contain the correct value"); - testUtils.dom.dragAndDrop(kanban.$('.o_kanban_group:eq(1) .o_kanban_record:eq(2)'), kanban.$('.o_kanban_group:eq(0)')); + await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_group:eq(1) .o_kanban_record:eq(2)'), kanban.$('.o_kanban_group:eq(0)')); + await nextTick(); // wait for update resulting from drag and drop assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_counter_side').text(), "1", "counter should contain the correct value"); - testUtils.dom.dragAndDrop(kanban.$('.o_kanban_group:eq(0) .o_kanban_record:eq(0)'), kanban.$('.o_kanban_group:eq(1)')); + await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_group:eq(0) .o_kanban_record:eq(0)'), kanban.$('.o_kanban_group:eq(1)')); + await nextTick(); // wait for update resulting from drag and drop assert.strictEqual(kanban.$('.o_kanban_group:eq(0) .o_kanban_counter_side').text(), "0", "counter should contain the correct value"); kanban.destroy(); }); - QUnit.test('progress bar subgroup count recompute', function (assert) { + QUnit.test('progress bar subgroup count recompute', async function (assert) { assert.expect(2); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4761,7 +4812,7 @@ QUnit.module('Views', { var initialCount = parseInt($secondGroup.find('.o_kanban_counter_side').text()); assert.strictEqual(initialCount, 3, "Initial count should be Three"); - testUtils.dom.click($secondGroup.find('.bg-success-full')); + await testUtils.dom.click($secondGroup.find('.bg-success-full')); var lastCount = parseInt($secondGroup.find('.o_kanban_counter_side').text()); assert.strictEqual(lastCount, 1, "kanban counters should vary according to what subgroup is selected"); @@ -4769,10 +4820,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('column progressbars on quick create with quick_create_view are updated', function (assert) { + QUnit.test('column progressbars on quick create with quick_create_view are updated', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4795,11 +4846,11 @@ QUnit.module('Views', { var initialCount = parseInt(kanban.$('.o_kanban_counter_side:first').text()); - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); // fill the quick create and validate var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); - testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=int_field]'), '44'); - testUtils.dom.click($quickCreate.find('button.o_kanban_add')); + await testUtils.fields.editInput($quickCreate.find('.o_field_widget[name=int_field]'), '44'); + await testUtils.dom.click($quickCreate.find('button.o_kanban_add')); var lastCount = parseInt(kanban.$('.o_kanban_counter_side:first').text()); assert.strictEqual(lastCount, initialCount + 44, @@ -4808,10 +4859,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('keep adding quickcreate in first column after a record from this column was moved', function (assert) { + QUnit.test('keep adding quickcreate in first column after a record from this column was moved', async function (assert) { assert.expect(2); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4825,7 +4876,7 @@ QUnit.module('Views', { groupBy: ['foo'], mockRPC: function (route, args) { if (route === '/web/dataset/resequence') { - return $.when(true); + return Promise.resolve(true); } return this._super(route, args); }, @@ -4833,13 +4884,13 @@ QUnit.module('Views', { var $quickCreateGroup; var $groups; - _quickCreateAndTest(); - testUtils.dom.dragAndDrop($groups.first().find('.o_kanban_record:first'), $groups.eq(1)); - _quickCreateAndTest(); + await _quickCreateAndTest(); + await testUtils.dom.dragAndDrop($groups.first().find('.o_kanban_record:first'), $groups.eq(1)); + await _quickCreateAndTest(); kanban.destroy(); - function _quickCreateAndTest() { - testUtils.kanban.clickCreate(kanban); + async function _quickCreateAndTest() { + await testUtils.kanban.clickCreate(kanban); $quickCreateGroup = kanban.$('.o_kanban_quick_create').closest('.o_kanban_group'); $groups = kanban.$('.o_kanban_group'); assert.strictEqual($quickCreateGroup[0], $groups[0], @@ -4847,10 +4898,10 @@ QUnit.module('Views', { } }); - QUnit.test('test displaying image (URL)', function (assert) { + QUnit.test('test displaying image (URL)', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4869,10 +4920,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('test displaying image (binary & placeholder)', function (assert) { + QUnit.test('test displaying image (binary & placeholder)', async function (assert) { assert.expect(2); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4886,7 +4937,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { if (route === 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACAA==') { assert.ok("The view's image should have been fetched."); - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, @@ -4899,7 +4950,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('check if the view destroys all widgets and instances', function (assert) { + QUnit.test('check if the view destroys all widgets and instances', async function (assert) { assert.expect(1); var instanceNumber = 0; @@ -4936,13 +4987,13 @@ QUnit.module('Views', { '</kanban>', }; - var kanban = createView(params); + var kanban = await createView(params); kanban.destroy(); var initialInstanceNumber = instanceNumber; instanceNumber = 0; - kanban = createView(params); + kanban = await createView(params); // call destroy function of controller to ensure that it correctly destroys everything kanban.__destroy(); @@ -4956,15 +5007,15 @@ QUnit.module('Views', { testUtils.mock.unpatch(mixins.ParentedMixin); }); - QUnit.test('grouped kanban becomes ungrouped when clearing domain then clearing groupby', function (assert) { + QUnit.test('grouped kanban becomes ungrouped when clearing domain then clearing groupby', async function (assert) { // in this test, we simulate that clearing the domain is slow, so that // clearing the groupby does not corrupt the data handled while // reloading the kanban view. assert.expect(4); - var def = $.Deferred(); + var prom = makeTestPromise(); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -4982,7 +5033,9 @@ QUnit.module('Views', { args.kwargs.groupby && args.kwargs.groupby[0] === 'bar'; if (isFirstUpdate) { - return def.then(_.constant(result)); + return prom.then(function () { + return result; + }); } } return result; @@ -4996,8 +5049,9 @@ QUnit.module('Views', { kanban.update({domain: []}); // 1st update on kanban view kanban.update({groupBy: false}); // 2n update on kanban view - def.resolve(); // simulate slow 1st update of kanban view + prom.resolve(); // simulate slow 1st update of kanban view + await nextTick(); assert.doesNotHaveClass(kanban.$('.o_kanban_view'), 'o_kanban_grouped', "the kanban view should not longer be grouped"); assert.hasClass(kanban.$('.o_kanban_view'),'o_kanban_ungrouped', @@ -5006,10 +5060,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('quick_create on grouped kanban without column', function (assert) { + QUnit.test('quick_create on grouped kanban without column', async function (assert) { assert.expect(1); this.data.partner.records = []; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -5027,14 +5081,14 @@ QUnit.module('Views', { }, }, }); - testUtils.kanban.clickCreate(kanban); + await testUtils.kanban.clickCreate(kanban); kanban.destroy(); }); - QUnit.test('keyboard navigation on kanban basic rendering', function (assert) { + QUnit.test('keyboard navigation on kanban basic rendering', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -5060,10 +5114,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('keyboard navigation on kanban grouped rendering', function (assert) { + QUnit.test('keyboard navigation on kanban grouped rendering', async function (assert) { assert.expect(3); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -5096,13 +5150,13 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('keyboard navigation on kanban grouped rendering with empty columns', function (assert) { + QUnit.test('keyboard navigation on kanban grouped rendering with empty columns', async function (assert) { assert.expect(2); var data = this.data; data.partner.records[1].state = "abc"; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: data, @@ -5165,9 +5219,9 @@ QUnit.module('Views', { }); QUnit.test('keyboard navigation on kanban when the focus is on a link that ' + - 'has an action and the kanban has no oe_kanban_global_... class', function (assert) { + 'has an action and the kanban has no oe_kanban_global_... class', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -5188,18 +5242,19 @@ QUnit.module('Views', { keyCode: $.ui.keyCode.ENTER, which: $.ui.keyCode.ENTER, })); + await testUtils.nextTick(); kanban.destroy(); }); - QUnit.test('asynchronous rendering of a field widget (ungrouped)', function (assert) { - assert.expect(2); + QUnit.test('asynchronous rendering of a field widget (ungrouped)', async function (assert) { + assert.expect(4); - var fooFieldDef = $.Deferred(); + var fooFieldProm = makeTestPromise(); var FieldChar = fieldRegistry.get('char'); fieldRegistry.add('asyncwidget', FieldChar.extend({ willStart: function () { - return fooFieldDef; + return fooFieldProm; }, start: function () { this.$el.html('LOADED'); @@ -5207,7 +5262,7 @@ QUnit.module('Views', { })); var kanbanController; - testUtils.createAsyncView({ + testUtils.createView({ View: KanbanView, model: 'partner', data: this.data, @@ -5220,21 +5275,33 @@ QUnit.module('Views', { assert.strictEqual($('.o_kanban_record').length, 0, "kanban view is not ready yet"); - fooFieldDef.resolve(); + fooFieldProm.resolve(); + await nextTick(); + assert.strictEqual($('.o_kanban_record').text(), "LOADEDLOADEDLOADEDLOADED"); + + // reload with a domain + fooFieldProm = makeTestPromise(); + kanbanController.reload({domain: [['id', '=', 1]]}); + await nextTick(); + assert.strictEqual($('.o_kanban_record').text(), "LOADEDLOADEDLOADEDLOADED"); + fooFieldProm.resolve(); + await nextTick(); + assert.strictEqual($('.o_kanban_record').text(), "LOADED"); + kanbanController.destroy(); delete fieldRegistry.map.asyncWidget; }); - QUnit.test('asynchronous rendering of a field widget (grouped)', function (assert) { - assert.expect(2); + QUnit.test('asynchronous rendering of a field widget (grouped)', async function (assert) { + assert.expect(4); - var fooFieldDef = $.Deferred(); + var fooFieldProm = makeTestPromise(); var FieldChar = fieldRegistry.get('char'); fieldRegistry.add('asyncwidget', FieldChar.extend({ willStart: function () { - return fooFieldDef; + return fooFieldProm; }, start: function () { this.$el.html('LOADED'); @@ -5242,7 +5309,7 @@ QUnit.module('Views', { })); var kanbanController; - testUtils.createAsyncView({ + testUtils.createView({ View: KanbanView, model: 'partner', data: this.data, @@ -5256,13 +5323,24 @@ QUnit.module('Views', { assert.strictEqual($('.o_kanban_record').length, 0, "kanban view is not ready yet"); - fooFieldDef.resolve(); + fooFieldProm.resolve(); + await nextTick(); + assert.strictEqual($('.o_kanban_record').text(), "LOADEDLOADEDLOADEDLOADED"); + + // reload with a domain + fooFieldProm = makeTestPromise(); + kanbanController.reload({domain: [['id', '=', 1]]}); + await nextTick(); + assert.strictEqual($('.o_kanban_record').text(), "LOADEDLOADEDLOADEDLOADED"); + fooFieldProm.resolve(); + await nextTick(); + assert.strictEqual($('.o_kanban_record').text(), "LOADED"); + kanbanController.destroy(); delete fieldRegistry.map.asyncWidget; }); - }); }); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index f5e0088173b..3b9e5836cb5 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -15,6 +15,7 @@ var Widget = require('web.Widget'); var createView = testUtils.createView; + QUnit.module('Views', { beforeEach: function () { this.data = { @@ -95,10 +96,10 @@ QUnit.module('Views', { QUnit.module('ListView'); - QUnit.test('simple readonly list', function (assert) { + QUnit.test('simple readonly list', async function (assert) { assert.expect(10); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -120,19 +121,16 @@ QUnit.module('Views', { assert.strictEqual(list.$('tbody tr:first td:nth(2)').css('text-align'), 'right', "integer cells should be right aligned"); - assert.ok(list.$buttons.find('.o_list_button_add').is(':visible'), - "should have a visible Create button"); - assert.ok(!list.$buttons.find('.o_list_button_save').is(':visible'), - "should not have a visible save button"); - assert.ok(!list.$buttons.find('.o_list_button_discard').is(':visible'), - "should not have a visible save button"); + assert.isVisible(list.$buttons.find('.o_list_button_add')); + assert.isNotVisible(list.$buttons.find('.o_list_button_save')); + assert.isNotVisible(list.$buttons.find('.o_list_button_discard')); list.destroy(); }); - QUnit.test('list with create="0"', function (assert) { + QUnit.test('list with create="0"', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -141,16 +139,16 @@ QUnit.module('Views', { assert.hasClass(list.$el,'o_cannot_create', "should have className 'o_cannot_create'"); - assert.strictEqual(list.$buttons.find('.o_list_button_add').length, 0, + assert.containsNone(list.$buttons, '.o_list_button_add', "should not have the 'Create' button"); list.destroy(); }); - QUnit.test('list with delete="0"', function (assert) { + QUnit.test('list with delete="0"', async function (assert) { assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -161,17 +159,17 @@ QUnit.module('Views', { assert.isNotVisible(list.sidebar.$el, 'sidebar should be invisible'); assert.ok(list.$('tbody td.o_list_record_selector').length, 'should have at least one record'); - testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input')); + await testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input')); assert.isVisible(list.sidebar.$el, 'sidebar should be visible'); assert.notOk(list.sidebar.$('a:contains(Delete)').length, 'sidebar should not have Delete button'); list.destroy(); }); - QUnit.test('simple editable rendering', function (assert) { + QUnit.test('simple editable rendering', async function (assert) { assert.expect(12); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -180,39 +178,39 @@ QUnit.module('Views', { assert.containsN(list, 'th', 3, "should have 2 th"); assert.containsN(list, 'th', 3, "should have 3 th"); - assert.strictEqual(list.$('td:contains(yop)').length, 1, "should contain yop"); + assert.containsOnce(list, 'td:contains(yop)', "should contain yop"); - assert.ok(list.$buttons.find('.o_list_button_add').is(':visible'), + assert.isVisible(list.$buttons.find('.o_list_button_add'), "should have a visible Create button"); - assert.ok(!list.$buttons.find('.o_list_button_save').is(':visible'), + assert.isNotVisible(list.$buttons.find('.o_list_button_save'), "should not have a visible save button"); - assert.ok(!list.$buttons.find('.o_list_button_discard').is(':visible'), + assert.isNotVisible(list.$buttons.find('.o_list_button_discard'), "should not have a visible discard button"); - testUtils.dom.click(list.$('td:not(.o_list_record_selector)').first()); + await testUtils.dom.click(list.$('td:not(.o_list_record_selector)').first()); - assert.ok(!list.$buttons.find('.o_list_button_add').is(':visible'), + assert.isNotVisible(list.$buttons.find('.o_list_button_add'), "should not have a visible Create button"); - assert.ok(list.$buttons.find('.o_list_button_save').is(':visible'), + assert.isVisible(list.$buttons.find('.o_list_button_save'), "should have a visible save button"); - assert.ok(list.$buttons.find('.o_list_button_discard').is(':visible'), + assert.isVisible(list.$buttons.find('.o_list_button_discard'), "should have a visible discard button"); - testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_save')); - assert.ok(list.$buttons.find('.o_list_button_add').is(':visible'), + assert.isVisible(list.$buttons.find('.o_list_button_add'), "should have a visible Create button"); - assert.ok(!list.$buttons.find('.o_list_button_save').is(':visible'), + assert.isNotVisible(list.$buttons.find('.o_list_button_save'), "should not have a visible save button"); - assert.ok(!list.$buttons.find('.o_list_button_discard').is(':visible'), + assert.isNotVisible(list.$buttons.find('.o_list_button_discard'), "should not have a visible discard button"); list.destroy(); }); - QUnit.test('invisible columns are not displayed', function (assert) { + QUnit.test('invisible columns are not displayed', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -227,10 +225,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('record-depending invisible lines are correctly aligned', function (assert) { + QUnit.test('record-depending invisible lines are correctly aligned', async function (assert) { assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -247,17 +245,17 @@ QUnit.module('Views', { "there should be 1 invisible bar cell"); assert.hasClass(list.$('tbody tr:first td:eq(2)'),'o_invisible_modifier', "the 3rd cell should be invisible"); - assert.strictEqual(list.$('tbody tr:eq(0) td:visible').length, list.$('tbody tr:eq(1) td:visible').length, + assert.containsN(list, 'tbody tr:eq(0) td:visible', list.$('tbody tr:eq(1) td:visible').length, "there should be the same number of visible cells in different rows"); list.destroy(); }); - QUnit.test('do not perform extra RPC to read invisible many2one fields', function (assert) { + QUnit.test('do not perform extra RPC to read invisible many2one fields', async function (assert) { assert.expect(3); this.data.foo.fields.m2o.default = 2; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -271,16 +269,16 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.verifySteps(['search_read', 'default_get'], "no nameget should be done"); list.destroy(); }); - QUnit.test('at least 4 rows are rendered, even if less data', function (assert) { + QUnit.test('at least 4 rows are rendered, even if less data', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -292,10 +290,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('basic grouped list rendering', function (assert) { + QUnit.test('basic grouped list rendering', async function (assert) { assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -310,10 +308,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('basic grouped list rendering 1 col without selector', function (assert) { + QUnit.test('basic grouped list rendering 1 col without selector', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -327,10 +325,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('basic grouped list rendering 1 col with selector', function (assert) { + QUnit.test('basic grouped list rendering 1 col with selector', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -344,10 +342,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('basic grouped list rendering 2 col without selector', function (assert) { + QUnit.test('basic grouped list rendering 2 col without selector', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -361,10 +359,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('basic grouped list rendering 2 col with selector', function (assert) { + QUnit.test('basic grouped list rendering 2 col with selector', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -378,14 +376,14 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('ordered list, sort attribute in context', function (assert) { + QUnit.test('ordered list, sort attribute in context', async function (assert) { assert.expect(1); // Equivalent to saving a custom filter this.data.foo.fields.foo.sortable = true; this.data.foo.fields.date.sortable = true; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -396,11 +394,11 @@ QUnit.module('Views', { }); // Descending order on Foo - list.$('th.o_column_sortable:contains("Foo")').click(); - list.$('th.o_column_sortable:contains("Foo")').click(); + await testUtils.dom.click(list.$('th.o_column_sortable:contains("Foo")')); + await testUtils.dom.click(list.$('th.o_column_sortable:contains("Foo")')); // Ascending order on Date - list.$('th.o_column_sortable:contains("Date")').click(); + await testUtils.dom.click(list.$('th.o_column_sortable:contains("Date")')); var listContext = list.getOwnedQueryParams(); assert.deepEqual(listContext, @@ -416,14 +414,14 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('loading a filter with a sort attribute', function (assert) { + QUnit.test('Loading a filter with a sort attribute', async function (assert) { assert.expect(2); this.data.foo.fields.foo.sortable = true; this.data.foo.fields.date.sortable = true; var searchReads = 0; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -446,7 +444,7 @@ QUnit.module('Views', { }, intercepts: { load_filters: function (event) { - return $.when([ + return Promise.resolve([ { context: "{}", domain: "[]", @@ -469,15 +467,15 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(list.$('.o_control_panel .o_search_options button.o_favorites_menu_button')); - testUtils.dom.click(list.$('.o_control_panel .o_search_options .o_favorites_menu .o_menu_item').eq(1)); + await testUtils.dom.click(list.$('.o_control_panel .o_search_options button.o_favorites_menu_button')); + await testUtils.dom.click(list.$('.o_control_panel .o_search_options .o_favorites_menu .o_menu_item').eq(1)); list.destroy(); }); - QUnit.test('many2one field rendering', function (assert) { + QUnit.test('many2one field rendering', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -489,10 +487,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('grouped list view, with 1 open group', function (assert) { + QUnit.test('grouped list view, with 1 open group', async function (assert) { assert.expect(6); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -500,20 +498,21 @@ QUnit.module('Views', { groupBy: ['foo'], }); - testUtils.dom.click(list.$('th.o_group_name:nth(1)')); - assert.strictEqual(list.$('tbody:eq(1) tr').length, 2, "open group should contain 2 records"); + await testUtils.dom.click(list.$('th.o_group_name:nth(1)')); + await testUtils.nextTick(); + assert.containsN(list, 'tbody:eq(1) tr', 2, "open group should contain 2 records"); assert.containsN(list, 'tbody', 3, "should contain 3 tbody"); - assert.strictEqual(list.$('td:contains(9)').length, 1, "should contain 9"); - assert.strictEqual(list.$('td:contains(-4)').length, 1, "should contain -4"); - assert.strictEqual(list.$('td:contains(10)').length, 1, "should contain 10"); - assert.strictEqual(list.$('tr.o_group_header td:contains(10)').length, 1, "but 10 should be in a header"); + assert.containsOnce(list, 'td:contains(9)', "should contain 9"); + assert.containsOnce(list, 'td:contains(-4)', "should contain -4"); + assert.containsOnce(list, 'td:contains(10)', "should contain 10"); + assert.containsOnce(list, 'tr.o_group_header td:contains(10)', "but 10 should be in a header"); list.destroy(); }); - QUnit.test('opening records when clicking on record', function (assert) { + QUnit.test('opening records when clicking on record', async function (assert) { assert.expect(3); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -526,19 +525,20 @@ QUnit.module('Views', { testUtils.dom.click(list.$('tr td:not(.o_list_record_selector)').first()); list.update({groupBy: ['foo']}); + await testUtils.nextTick(); assert.containsN(list, 'tr.o_group_header', 3, "list should be grouped"); - testUtils.dom.click(list.$('th.o_group_name').first()); + await testUtils.dom.click(list.$('th.o_group_name').first()); testUtils.dom.click(list.$('tr:not(.o_group_header) td:not(.o_list_record_selector)').first()); list.destroy(); }); - QUnit.test('editable list view: readonly fields cannot be edited', function (assert) { + QUnit.test('editable list view: readonly fields cannot be edited', async function (assert) { assert.expect(4); this.data.foo.fields.foo.readonly = true; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -551,7 +551,7 @@ QUnit.module('Views', { var $td = list.$('td:not(.o_list_record_selector)').first(); var $second_td = list.$('td:not(.o_list_record_selector)').eq(1); var $third_td = list.$('td:not(.o_list_record_selector)').eq(2); - testUtils.dom.click($td); + await testUtils.dom.click($td); assert.hasClass($td.parent(),'o_selected_row', "row should be in edit mode"); assert.hasClass($td,'o_readonly_modifier', @@ -563,7 +563,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list view: line with no active element', function (assert) { + QUnit.test('editable list view: line with no active element', async function (assert) { assert.expect(3); this.data.bar = { @@ -578,7 +578,7 @@ QUnit.module('Views', { }; this.data.foo.records[0].o2m = [1, 2]; - var form = createView({ + var form = await createView({ View: FormView, model: 'foo', data: this.data, @@ -606,18 +606,19 @@ QUnit.module('Views', { var $td2 = form.$('.o_data_cell').eq(1); assert.hasClass($td, 'o_readonly_modifier'); assert.hasClass($td2, 'o_boolean_toggle_cell'); - testUtils.dom.click($td); - testUtils.dom.click($td2.find('.o_boolean_toggle input')); - - testUtils.form.clickSave(form); + await testUtils.dom.click($td); + await testUtils.dom.click($td2.find('.o_boolean_toggle input')); + await testUtils.nextTick(); + await testUtils.form.clickSave(form); + await testUtils.nextTick(); form.destroy(); }); - QUnit.test('basic operations for editable list renderer', function (assert) { + QUnit.test('basic operations for editable list renderer', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -626,12 +627,12 @@ QUnit.module('Views', { var $td = list.$('td:not(.o_list_record_selector)').first(); assert.doesNotHaveClass($td.parent(), 'o_selected_row', "td should not be in edit mode"); - testUtils.dom.click($td); + await testUtils.dom.click($td); assert.hasClass($td.parent(),'o_selected_row', "td should be in edit mode"); list.destroy(); }); - QUnit.test('editable list: add a line and discard', function (assert) { + QUnit.test('editable list: add a line and discard', async function (assert) { assert.expect(11); testUtils.mock.patch(basicFields.FieldChar, { @@ -641,7 +642,7 @@ QUnit.module('Views', { }, }); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -656,7 +657,7 @@ QUnit.module('Views', { assert.strictEqual(list.pager.$('.o_pager_value').text(), '1-1', "pager should be correct"); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.containsN(list, 'tbody tr', 4, "list should still contain 4 rows"); @@ -665,7 +666,7 @@ QUnit.module('Views', { assert.strictEqual(list.pager.$('.o_pager_value').text(), '1-2', "pager should be correct"); - testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); assert.containsN(list, 'tbody tr', 4, "list should still contain 4 rows"); @@ -680,10 +681,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('field changes are triggered correctly', function (assert) { + QUnit.test('field changes are triggered correctly', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -695,30 +696,31 @@ QUnit.module('Views', { testUtils.mock.intercept(list, "field_changed", function () { n += 1; }); - testUtils.dom.click($td); + await testUtils.dom.click($td); testUtils.fields.editInput($td.find('input'), 'abc'); assert.strictEqual(n, 1, "field_changed should have been triggered"); - testUtils.dom.click(list.$('td:not(.o_list_record_selector)').eq(2)); + await testUtils.dom.click(list.$('td:not(.o_list_record_selector)').eq(2)); assert.strictEqual(n, 1, "field_changed should not have been triggered"); list.destroy(); }); - QUnit.test('editable list view: basic char field edition', function (assert) { + QUnit.test('editable list view: basic char field edition', async function (assert) { assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, arch: '<tree editable="bottom"><field name="foo"/><field name="bar"/></tree>', }); + var $td = list.$('td:not(.o_list_record_selector)').first(); - testUtils.dom.click($td); - testUtils.fields.editInput($td.find('input'), 'abc'); + await testUtils.dom.click($td); + await testUtils.fields.editInput($td.find('input'), 'abc'); assert.strictEqual($td.find('input').val(), 'abc', "char field has been edited correctly"); var $next_row_td = list.$('tbody tr:eq(1) td:not(.o_list_record_selector)').first(); - testUtils.dom.click($next_row_td); + await testUtils.dom.click($next_row_td); assert.strictEqual(list.$('td:not(.o_list_record_selector)').first().text(), 'abc', 'changes should be saved correctly'); assert.doesNotHaveClass(list.$('tbody tr').first(), 'o_selected_row', @@ -728,12 +730,12 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list view: save data when list sorting in edit mode', function (assert) { + QUnit.test('editable list view: save data when list sorting in edit mode', async function (assert) { assert.expect(3); this.data.foo.fields.foo.sortable = true; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -744,27 +746,27 @@ QUnit.module('Views', { "should correctly save the edited record"); } return this._super.apply(this, arguments); - } + }, }); - testUtils.dom.click(list.$('.o_data_cell:first')); - testUtils.fields.editInput(list.$('input[name="foo"]'), 'xyz'); - testUtils.dom.click(list.$('.o_column_sortable')); + await testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.fields.editInput(list.$('input[name="foo"]'), 'xyz'); + await testUtils.dom.click(list.$('.o_column_sortable')); assert.hasClass(list.$('.o_data_row:first'),'o_selected_row', "first row should still be in edition"); - testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_save')); assert.doesNotHaveClass(list.$buttons, 'o-editing', "list buttons should be back to their readonly mode"); list.destroy(); }); - QUnit.test('selection changes are triggered correctly', function (assert) { + QUnit.test('selection changes are triggered correctly', async function (assert) { assert.expect(8); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -800,10 +802,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('selection is reset on reload', function (assert) { + QUnit.test('selection is reset on reload', async function (assert) { assert.expect(5); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -824,7 +826,7 @@ QUnit.module('Views', { "total should be 10 (first record selected)"); // reload - list.reload(); + await list.reload(); $firstRowSelector = list.$('tbody .o_list_record_selector input').first(); assert.notOk($firstRowSelector.is(':checked'), "first row should no longer be selected"); @@ -834,10 +836,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('selection is kept on render without reload', function (assert) { + QUnit.test('selection is kept on render without reload', async function (assert) { assert.expect(6); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -852,19 +854,19 @@ QUnit.module('Views', { assert.isNotVisible(list.sidebar); // open blip grouping and check all lines - testUtils.dom.click(list.$('.o_group_header:contains("blip (2)")')); - testUtils.dom.click(list.$('.o_data_row:first input')); + await testUtils.dom.click(list.$('.o_group_header:contains("blip (2)")')); + await testUtils.dom.click(list.$('.o_data_row:first input')); assert.isVisible(list.sidebar); // open yop grouping and verify blip are still checked - testUtils.dom.click(list.$('.o_group_header:contains("yop (1)")')); + await testUtils.dom.click(list.$('.o_group_header:contains("yop (1)")')); assert.containsOnce(list, '.o_data_row input:checked', "opening a grouping does not uncheck others"); assert.isVisible(list.sidebar); // close and open blip grouping and verify blip are unchecked - testUtils.dom.click(list.$('.o_group_header:contains("blip (2)")')); - testUtils.dom.click(list.$('.o_group_header:contains("blip (2)")')); + await testUtils.dom.click(list.$('.o_group_header:contains("blip (2)")')); + await testUtils.dom.click(list.$('.o_group_header:contains("blip (2)")')); assert.containsNone(list, '.o_data_row input:checked', "opening and closing a grouping uncheck its elements"); assert.isNotVisible(list.sidebar); @@ -872,10 +874,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('aggregates are computed correctly', function (assert) { + QUnit.test('aggregates are computed correctly', async function (assert) { assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -896,16 +898,16 @@ QUnit.module('Views', { "total should be 32 as all records are selected"); // Let's update the view to dislay NO records - list.update({domain: ['&', ['bar', '=', false], ['int_field', '>', 0]]}); + await list.update({domain: ['&', ['bar', '=', false], ['int_field', '>', 0]]}); assert.strictEqual(list.$('tfoot td:nth(2)').text(), "0", "total should have been recomputed to 0"); list.destroy(); }); - QUnit.test('aggregates are computed correctly in grouped lists', function (assert) { + QUnit.test('aggregates are computed correctly in grouped lists', async function (assert) { assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -923,17 +925,17 @@ QUnit.module('Views', { assert.strictEqual($groupHeader2.find('td:last()').text(), "9", "second group total should be 9"); assert.strictEqual(list.$('tfoot td:last()').text(), "32", "total should be 32"); - testUtils.dom.click($groupHeader1); - testUtils.dom.click(list.$('tbody .o_list_record_selector input').first()); + await testUtils.dom.click($groupHeader1); + await testUtils.dom.click(list.$('tbody .o_list_record_selector input').first()); assert.strictEqual(list.$('tfoot td:last()').text(), "10", "total should be 10 as first record of first group is selected"); list.destroy(); }); - QUnit.test('aggregates are updated when a line is edited', function (assert) { + QUnit.test('aggregates are updated when a line is edited', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -942,18 +944,18 @@ QUnit.module('Views', { assert.strictEqual(list.$('td[title="Sum"]').text(), "32", "current total should be 32"); - testUtils.dom.click(list.$('tr.o_data_row td.o_data_cell').first()); - testUtils.fields.editInput(list.$('td.o_data_cell input'), "15"); + await testUtils.dom.click(list.$('tr.o_data_row td.o_data_cell').first()); + await testUtils.fields.editInput(list.$('td.o_data_cell input'), "15"); assert.strictEqual(list.$('td[title="Sum"]').text(), "37", "current total should now be 37"); list.destroy(); }); - QUnit.test('aggregates are formatted according to field widget', function (assert) { + QUnit.test('aggregates are formatted according to field widget', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -969,9 +971,9 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('groups can be sorted on aggregates', function (assert) { + QUnit.test('groups can be sorted on aggregates', async function (assert) { assert.expect(10); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -989,12 +991,12 @@ QUnit.module('Views', { "initial order should be 10, 5, 17"); assert.strictEqual(list.$('tfoot td:last()').text(), '32', "total should be 32"); - testUtils.dom.click(list.$('.o_column_sortable')); + await testUtils.dom.click(list.$('.o_column_sortable')); assert.strictEqual(list.$('tfoot td:last()').text(), '32', "total should still be 32"); assert.strictEqual(list.$('tbody .o_list_number').text(), '51017', "order should be 5, 10, 17"); - testUtils.dom.click(list.$('.o_column_sortable')); + await testUtils.dom.click(list.$('.o_column_sortable')); assert.strictEqual(list.$('tbody .o_list_number').text(), '17105', "initial order should be 17, 10, 5"); assert.strictEqual(list.$('tfoot td:last()').text(), '32', "total should still be 32"); @@ -1004,14 +1006,14 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('groups cannot be sorted on non-aggregable fields', function (assert) { + QUnit.test('groups cannot be sorted on non-aggregable fields', async function (assert) { assert.expect(6); this.data.foo.fields.sort_field = {string: "sortable_field", type: "sting", sortable: true, default: "value"}; _.each(this.data.records, function(elem) { elem.sort_field = "value" + elem.id; }); this.data.foo.fields.foo.sortable= true; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1025,13 +1027,13 @@ QUnit.module('Views', { }, }); //we cannot sort by sort_field since it doesn't have a group_operator - testUtils.dom.click(list.$('.o_column_sortable:eq(2)')); + await testUtils.dom.click(list.$('.o_column_sortable:eq(2)')); //we can sort by int_field since it has a group_operator - testUtils.dom.click(list.$('.o_column_sortable:eq(1)')); + await testUtils.dom.click(list.$('.o_column_sortable:eq(1)')); //we keep previous order - testUtils.dom.click(list.$('.o_column_sortable:eq(2)')); + await testUtils.dom.click(list.$('.o_column_sortable:eq(2)')); //we can sort on foo since we are groupped by foo + previous order - testUtils.dom.click(list.$('.o_column_sortable:eq(0)')); + await testUtils.dom.click(list.$('.o_column_sortable:eq(0)')); assert.verifySteps([ 'default order', @@ -1045,7 +1047,7 @@ QUnit.module('Views', { }); - QUnit.test('properly apply onchange in simple case', function (assert) { + QUnit.test('properly apply onchange in simple case', async function (assert) { assert.expect(2); this.data.foo.onchanges = { @@ -1053,7 +1055,7 @@ QUnit.module('Views', { obj.int_field = obj.foo.length + 1000; }, }; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1065,19 +1067,19 @@ QUnit.module('Views', { assert.strictEqual($int_field_td.text(), '10', "should contain initial value"); - testUtils.dom.click($foo_td); - testUtils.fields.editInput($foo_td.find('input'), 'tralala'); + await testUtils.dom.click($foo_td); + await testUtils.fields.editInput($foo_td.find('input'), 'tralala'); assert.strictEqual($int_field_td.find('input').val(), "1007", "should contain input with onchange applied"); list.destroy(); }); - QUnit.test('column width should not change when switching mode', function (assert) { + QUnit.test('column width should not change when switching mode', async function (assert) { assert.expect(4); // Warning: this test is css dependant - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1093,13 +1095,13 @@ QUnit.module('Views', { var startWidth = list.$('table').addBack('table').width(); // start edition of first row - testUtils.dom.click(list.$('td:not(.o_list_record_selector)').first()); + await testUtils.dom.click(list.$('td:not(.o_list_record_selector)').first()); var editionWidths = _.pluck(list.$('thead th'), 'offsetWidth'); var editionWidth = list.$('table').addBack('table').width(); // leave edition - testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_save')); var readonlyWidths = _.pluck(list.$('thead th'), 'offsetWidth'); var readonlyWidth = list.$('table').addBack('table').width(); @@ -1116,10 +1118,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('deleting one record', function (assert) { + QUnit.test('deleting one record', async function (assert) { assert.expect(5); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1130,27 +1132,27 @@ QUnit.module('Views', { assert.isNotVisible(list.sidebar.$el, 'sidebar should be invisible'); assert.containsN(list, 'tbody td.o_list_record_selector', 4, "should have 4 records"); - testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input')); + await testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input')); assert.isVisible(list.sidebar.$el, 'sidebar should be visible'); - testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(list.sidebar.$('a:contains(Delete)')); + await testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(list.sidebar.$('a:contains(Delete)')); assert.hasClass($('body'),'modal-open', 'body should have modal-open clsss'); - testUtils.dom.click($('body .modal button span:contains(Ok)')); + await testUtils.dom.click($('body .modal button span:contains(Ok)')); assert.containsN(list, 'tbody td.o_list_record_selector', 3, "should have 3 records"); list.destroy(); }); - QUnit.test('archiving one record', function (assert) { + QUnit.test('archiving one record', async function (assert) { assert.expect(12); // add active field on foo model and make all records active this.data.foo.fields.active = {string: 'Active', type: 'boolean', default: true}; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1171,24 +1173,24 @@ QUnit.module('Views', { assert.verifySteps(['/web/dataset/search_read']); testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(list.sidebar.$('a:contains(Archive)')); + await testUtils.dom.click(list.sidebar.$('a:contains(Archive)')); assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed'); testUtils.dom.click($('.modal-footer .btn-secondary')); assert.containsN(list, 'tbody td.o_list_record_selector', 4, "still should have 4 records"); testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(list.sidebar.$('a:contains(Archive)')); + await testUtils.dom.click(list.sidebar.$('a:contains(Archive)')); assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed'); - testUtils.dom.click($('.modal-footer .btn-primary')); + await testUtils.dom.click($('.modal-footer .btn-primary')); assert.containsN(list, 'tbody td.o_list_record_selector', 3, "should have 3 records"); - assert.verifySteps(['/web/dataset/search_read', '/web/dataset/call_kw/foo/write', '/web/dataset/search_read']); + assert.verifySteps(['/web/dataset/call_kw/foo/write', '/web/dataset/search_read']); list.destroy(); }); - QUnit.test('pager (ungrouped and grouped mode), default limit', function (assert) { + QUnit.test('pager (ungrouped and grouped mode), default limit', async function (assert) { assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1203,18 +1205,18 @@ QUnit.module('Views', { assert.isVisible(list.pager.$el, "pager should be visible"); assert.strictEqual(list.pager.state.size, 4, "pager's size should be 4"); - list.update({ groupBy: ['bar']}); + await list.update({ groupBy: ['bar']}); assert.isNotVisible(list.pager.$el, "pager should be invisible"); list.destroy(); }); - QUnit.test('can sort records when clicking on header', function (assert) { + QUnit.test('can sort records when clicking on header', async function (assert) { assert.expect(9); this.data.foo.fields.foo.sortable = true; var nbSearchRead = 0; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1234,7 +1236,7 @@ QUnit.module('Views', { "record 3 should be first"); nbSearchRead = 0; - testUtils.dom.click(list.$('thead th:contains(Foo)')); + await testUtils.dom.click(list.$('thead th:contains(Foo)')); assert.strictEqual(nbSearchRead, 1, "should have done one search_read"); assert.ok(list.$('tbody tr:first td:contains(blip)').length, "record 3 should be first"); @@ -1242,7 +1244,7 @@ QUnit.module('Views', { "record 1 should be first"); nbSearchRead = 0; - testUtils.dom.click(list.$('thead th:contains(Foo)')); + await testUtils.dom.click(list.$('thead th:contains(Foo)')); assert.strictEqual(nbSearchRead, 1, "should have done one search_read"); assert.ok(list.$('tbody tr:first td:contains(yop)').length, "record 3 should be first"); @@ -1252,10 +1254,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('use default_order', function (assert) { + QUnit.test('use default_order', async function (assert) { assert.expect(3); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1277,10 +1279,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('use more complex default_order', function (assert) { + QUnit.test('use more complex default_order', async function (assert) { assert.expect(3); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1304,12 +1306,12 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('use default_order on editable tree: sort on save', function (assert) { + QUnit.test('use default_order on editable tree: sort on save', async function (assert) { assert.expect(8); this.data.foo.records[0].o2m = [1, 3]; - var form = createView({ + var form = await createView({ View: FormView, model: 'foo', data: this.data, @@ -1325,15 +1327,15 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.ok(form.$('tbody tr:first td:contains(Value 1)').length, "Value 1 should be first"); assert.ok(form.$('tbody tr:eq(1) td:contains(Value 3)').length, "Value 3 should be second"); var $o2m = form.$('.o_field_widget[name=o2m]'); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); - testUtils.fields.editInput($o2m.find('.o_field_widget'), "Value 2"); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.fields.editInput($o2m.find('.o_field_widget'), "Value 2"); assert.ok(form.$('tbody tr:first td:contains(Value 1)').length, "Value 1 should be first"); assert.ok(form.$('tbody tr:eq(1) td:contains(Value 3)').length, @@ -1341,7 +1343,7 @@ QUnit.module('Views', { assert.ok(form.$('tbody tr:eq(2) td input').val(), "Value 2 should be third (shouldn't be sorted)"); - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); assert.ok(form.$('tbody tr:first td:contains(Value 1)').length, "Value 1 should be first"); assert.ok(form.$('tbody tr:eq(1) td:contains(Value 2)').length, @@ -1352,7 +1354,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('use default_order on editable tree: sort on demand', function (assert) { + QUnit.test('use default_order on editable tree: sort on demand', async function (assert) { assert.expect(11); this.data.foo.records[0].o2m = [1, 3]; @@ -1360,7 +1362,7 @@ QUnit.module('Views', { this.data.bar.records[0].name = "Value 1"; this.data.bar.records[2].name = "Value 3"; - var form = createView({ + var form = await createView({ View: FormView, model: 'foo', data: this.data, @@ -1376,15 +1378,15 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.ok(form.$('tbody tr:first td:contains(Value 1)').length, "Value 1 should be first"); assert.ok(form.$('tbody tr:eq(1) td:contains(Value 3)').length, "Value 3 should be second"); var $o2m = form.$('.o_field_widget[name=o2m]'); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); - testUtils.fields.editInput($o2m.find('.o_field_widget'), "Value 2"); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.fields.editInput($o2m.find('.o_field_widget'), "Value 2"); assert.ok(form.$('tbody tr:first td:contains(Value 1)').length, "Value 1 should be first"); assert.ok(form.$('tbody tr:eq(1) td:contains(Value 3)').length, @@ -1392,9 +1394,9 @@ QUnit.module('Views', { assert.ok(form.$('tbody tr:eq(2) td input').val(), "Value 2 should be third (shouldn't be sorted)"); - testUtils.dom.click(form.$('.o_form_sheet_bg')); + await testUtils.dom.click(form.$('.o_form_sheet_bg')); - testUtils.dom.click($o2m.find('.o_column_sortable')); + await testUtils.dom.click($o2m.find('.o_column_sortable')); assert.strictEqual(form.$('tbody tr:first').text(), 'Value 1', "Value 1 should be first"); assert.strictEqual(form.$('tbody tr:eq(1)').text(), 'Value 2', @@ -1402,7 +1404,7 @@ QUnit.module('Views', { assert.strictEqual(form.$('tbody tr:eq(2)').text(), 'Value 3', "Value 3 should be third"); - testUtils.dom.click($o2m.find('.o_column_sortable')); + await testUtils.dom.click($o2m.find('.o_column_sortable')); assert.strictEqual(form.$('tbody tr:first').text(), 'Value 3', "Value 3 should be first"); assert.strictEqual(form.$('tbody tr:eq(1)').text(), 'Value 2', @@ -1413,7 +1415,7 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('use default_order on editable tree: sort on demand in page', function (assert) { + QUnit.test('use default_order on editable tree: sort on demand in page', async function (assert) { assert.expect(4); this.data.bar.fields = {name: {string: "Name", type: "char", sortable: true}}; @@ -1429,7 +1431,7 @@ QUnit.module('Views', { } this.data.foo.records[0].o2m = ids; - var form = createView({ + var form = await createView({ View: FormView, model: 'foo', data: this.data, @@ -1446,13 +1448,13 @@ QUnit.module('Views', { }); // Change page - testUtils.dom.click(form.$('.o_field_widget[name=o2m] .o_pager_next')); + await testUtils.dom.click(form.$('.o_field_widget[name=o2m] .o_pager_next')); assert.strictEqual(form.$('tbody tr:first').text(), 'Value 44', "record 44 should be first"); assert.strictEqual(form.$('tbody tr:eq(4)').text(), 'Value 48', "record 48 should be last"); - testUtils.dom.click(form.$('.o_column_sortable')); + await testUtils.dom.click(form.$('.o_column_sortable')); assert.strictEqual(form.$('tbody tr:first').text(), 'Value 08', "record 48 should be first"); assert.strictEqual(form.$('tbody tr:eq(4)').text(), 'Value 04', @@ -1461,10 +1463,10 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.test('can display button in edit mode', function (assert) { + QUnit.test('can display button in edit mode', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1478,10 +1480,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('can display a list with a many2many field', function (assert) { + QUnit.test('can display a list with a many2many field', async function (assert) { assert.expect(3); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1499,10 +1501,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('list without import button', function (assert) { + QUnit.test('list without import button', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1516,13 +1518,13 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('display a tooltip on a field', function (assert) { + QUnit.test('display a tooltip on a field', async function (assert) { assert.expect(2); var initialDebugMode = config.debug; config.debug = false; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1538,7 +1540,7 @@ QUnit.module('Views', { config.debug = true; // it is necessary to rerender the list so tooltips can be properly created - list.reload(); + await list.reload(); list.$('th:not(.o_list_record_selector)').tooltip('show', false); list.$('th:not(.o_list_record_selector)').trigger($.Event('mouseenter')); @@ -1548,10 +1550,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('support row decoration', function (assert) { + QUnit.test('support row decoration', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1567,12 +1569,12 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('support row decoration (with unset numeric values)', function (assert) { + QUnit.test('support row decoration (with unset numeric values)', async function (assert) { assert.expect(2); this.data.foo.records = []; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1581,22 +1583,22 @@ QUnit.module('Views', { '</tree>', }); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.containsNone(list, 'tr.o_data_row.text-danger', "the data row should not have .text-danger decoration (int_field is unset)"); - testUtils.fields.editInput(list.$('input[name="int_field"]'), '-3'); + await testUtils.fields.editInput(list.$('input[name="int_field"]'), '-3'); assert.containsOnce(list, 'tr.o_data_row.text-danger', "the data row should have .text-danger decoration (int_field is negative)"); list.destroy(); }); - QUnit.test('support row decoration with date', function (assert) { + QUnit.test('support row decoration with date', async function (assert) { assert.expect(3); this.data.foo.records[0].datetime = '2017-02-27 12:51:35'; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1615,14 +1617,14 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('no content helper when no data', function (assert) { + QUnit.test('no content helper when no data', async function (assert) { assert.expect(5); var records = this.data.foo.records; this.data.foo.records = []; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1643,7 +1645,7 @@ QUnit.module('Views', { "should have rendered no content helper from action"); this.data.foo.records = records; - list.reload(); + await list.reload(); assert.containsNone(list, '.o_view_nocontent', "should not display the no content helper"); @@ -1651,12 +1653,12 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('no nocontent helper when no data and no help', function (assert) { + QUnit.test('no nocontent helper when no data and no help', async function (assert) { assert.expect(3); this.data.foo.records = []; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1673,14 +1675,14 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('list view, editable, without data', function (assert) { + QUnit.test('list view, editable, without data', async function (assert) { assert.expect(13); this.data.foo.records = []; this.data.foo.fields.date.default = "2017-02-10"; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1710,7 +1712,7 @@ QUnit.module('Views', { "should not have a div.table-responsive"); assert.containsNone(list, 'table', "should not have rendered a table"); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.containsNone(list, '.o_view_nocontent', "should not have a no content helper displayed"); @@ -1728,7 +1730,7 @@ QUnit.module('Views', { assert.strictEqual(list.$('.o_list_button button').prop('disabled'), true, "buttons should be disabled while the record is not yet created"); - testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_save')); assert.strictEqual(list.$('tbody tr:eq(0) .o_list_record_selector input').prop('disabled'), false, "record selector checkbox should not be disabled once the record is created"); @@ -1738,12 +1740,12 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('list view, editable, with a button', function (assert) { + QUnit.test('list view, editable, with a button', async function (assert) { assert.expect(1); this.data.foo.records = []; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1753,17 +1755,17 @@ QUnit.module('Views', { '</tree>', }); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.containsOnce(list, 'table button.o_icon_button i.fa-phone', "should have rendered a button"); list.destroy(); }); - QUnit.test('list view with a button without icon', function (assert) { + QUnit.test('list view with a button without icon', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1778,10 +1780,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('list view, editable, can discard', function (assert) { + QUnit.test('list view, editable, can discard', async function (assert) { assert.expect(5); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1792,13 +1794,13 @@ QUnit.module('Views', { assert.strictEqual(list.$('td:not(.o_list_record_selector) input').length, 0, "no input should be in the table"); - testUtils.dom.click(list.$('tbody td:not(.o_list_record_selector):first')); + await testUtils.dom.click(list.$('tbody td:not(.o_list_record_selector):first')); assert.strictEqual(list.$('td:not(.o_list_record_selector) input').length, 1, "first cell should be editable"); assert.ok(list.$buttons.find('.o_list_button_discard').is(':visible'), "discard button should be visible"); - testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); assert.strictEqual(list.$('td:not(.o_list_record_selector) input').length, 0, "no input should be in the table"); @@ -1807,7 +1809,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list view, click on the list to save', function (assert) { + QUnit.test('editable list view, click on the list to save', async function (assert) { assert.expect(3); this.data.foo.fields.date.default = "2017-02-10"; @@ -1815,7 +1817,7 @@ QUnit.module('Views', { var createCount = 0; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1830,27 +1832,27 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); - testUtils.dom.click(list.$('div.table-responsive')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$('div.table-responsive')); assert.strictEqual(createCount, 1, "should have created a record"); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); - testUtils.dom.click(list.$('tfoot')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$('tfoot')); assert.strictEqual(createCount, 2, "should have created a record"); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); - testUtils.dom.click(list.$('tbody tr').last()); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$('tbody tr').last()); assert.strictEqual(createCount, 3, "should have created a record"); list.destroy(); }); - QUnit.test('click on a button in a list view', function (assert) { + QUnit.test('click on a button in a list view', async function (assert) { assert.expect(9); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1882,16 +1884,16 @@ QUnit.module('Views', { assert.containsOnce(list, '.o_list_button:first .o_icon_button .fa.fa-car', 'buttons should have correct icon'); - testUtils.dom.click(list.$('.o_list_button:first > button')); + await testUtils.dom.click(list.$('.o_list_button:first > button')); assert.verifySteps(['/web/dataset/search_read', '/web/dataset/search_read'], "should have reloaded the view (after the action is complete)"); list.destroy(); }); - QUnit.test('invisible attrs in readonly and editable list', function (assert) { + QUnit.test('invisible attrs in readonly and editable list', async function (assert) { assert.expect(5); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1910,28 +1912,28 @@ QUnit.module('Views', { "td that contains an invisible button should be empty"); // edit first row - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2)')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2)')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(4) input.o_invisible_modifier').length, 1, "td that contains an invisible field should not be empty in edition"); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > button.o_invisible_modifier').length, 1, "td that contains an invisible button should not be empty in edition"); - testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); // click on the invisible field's cell to edit first row - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(4)')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(4)')); assert.hasClass(list.$('tbody tr:nth(0)'),'o_selected_row', "first row should be in edition"); list.destroy(); }); - QUnit.test('monetary fields are properly rendered', function (assert) { + QUnit.test('monetary fields are properly rendered', async function (assert) { assert.expect(3); var currencies = {}; _.each(this.data.res_currency.records, function (currency) { currencies[currency.id] = currency; }); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1955,10 +1957,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('simple list with date and datetime', function (assert) { + QUnit.test('simple list with date and datetime', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1977,12 +1979,12 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('edit a row by clicking on a readonly field', function (assert) { + QUnit.test('edit a row by clicking on a readonly field', async function (assert) { assert.expect(9); this.data.foo.fields.foo.readonly = true; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -1993,7 +1995,7 @@ QUnit.module('Views', { "foo field cells should have class 'o_readonly_modifier'"); // edit the first row - testUtils.dom.click(list.$('.o_data_row:first td:nth(1)')); + await testUtils.dom.click(list.$('.o_data_row:first td:nth(1)')); assert.hasClass(list.$('.o_data_row:first'),'o_selected_row', "first row should be selected"); var $cell = list.$('.o_data_row:first td:nth(1)'); @@ -2008,7 +2010,7 @@ QUnit.module('Views', { "a widget for field 'int_field should have been rendered'"); // click again on readonly cell of first line: nothing should have changed - testUtils.dom.click(list.$('.o_data_row:first td:nth(1)')); + await testUtils.dom.click(list.$('.o_data_row:first td:nth(1)')); assert.hasClass(list.$('.o_data_row:first'),'o_selected_row', "first row should be selected"); assert.strictEqual(list.$('.o_data_row:first td:nth(2) input').length, 1, @@ -2017,7 +2019,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('list view with nested groups', function (assert) { + QUnit.test('list view with nested groups', async function (assert) { assert.expect(42); this.data.foo.records.push({id: 5, foo: "blip", int_field: -7, m2o: 1}); @@ -2026,7 +2028,7 @@ QUnit.module('Views', { var nbRPCs = {readGroup: 0, searchRead: 0}; var envIDs = []; // the ids that should be in the environment during this test - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2075,7 +2077,7 @@ QUnit.module('Views', { // open the first group nbRPCs = {readGroup: 0, searchRead: 0}; - testUtils.dom.click(list.$('.o_group_header:first')); + await testUtils.dom.click(list.$('.o_group_header:first')); assert.strictEqual(nbRPCs.readGroup, 1, "should have done one read_group"); assert.strictEqual(nbRPCs.searchRead, 0, "should have done no search_read"); assert.deepEqual(list.exportState().resIds, envIDs); @@ -2098,7 +2100,7 @@ QUnit.module('Views', { // open subgroup nbRPCs = {readGroup: 0, searchRead: 0}; envIDs = [4, 5]; // the opened subgroup contains these two records - testUtils.dom.click($openGroup.find('.o_group_header:nth(2)')); + await testUtils.dom.click($openGroup.find('.o_group_header:nth(2)')); assert.strictEqual(nbRPCs.readGroup, 0, "should have done no read_group"); assert.strictEqual(nbRPCs.searchRead, 1, "should have done one search_read"); assert.deepEqual(list.exportState().resIds, envIDs); @@ -2111,12 +2113,12 @@ QUnit.module('Views', { "first record in open subgroup should be res_id 4 (with int_field -4)"); // open a record (should trigger event 'open_record') - testUtils.dom.click($openSubGroup.find('.o_data_row:first')); + await testUtils.dom.click($openSubGroup.find('.o_data_row:first')); // sort by int_field (ASC) and check that open groups are still open nbRPCs = {readGroup: 0, searchRead: 0}; envIDs = [5, 4]; // order of the records changed - testUtils.dom.click(list.$('thead th:last')); + await testUtils.dom.click(list.$('thead th:last')); assert.strictEqual(nbRPCs.readGroup, 2, "should have done two read_groups"); assert.strictEqual(nbRPCs.searchRead, 1, "should have done one search_read"); assert.deepEqual(list.exportState().resIds, envIDs); @@ -2131,7 +2133,7 @@ QUnit.module('Views', { // close first level group nbRPCs = {readGroup: 0, searchRead: 0}; envIDs = []; // the group being closed, there is no more record in the environment - testUtils.dom.click(list.$('.o_group_header:nth(1)')); + await testUtils.dom.click(list.$('.o_group_header:nth(1)')); assert.strictEqual(nbRPCs.readGroup, 0, "should have done no read_group"); assert.strictEqual(nbRPCs.searchRead, 0, "should have done no search_read"); assert.deepEqual(list.exportState().resIds, envIDs); @@ -2145,7 +2147,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('grouped list on selection field at level 2', function (assert) { + QUnit.test('grouped list on selection field at level 2', async function (assert) { assert.expect(4); this.data.foo.fields.priority = { @@ -2157,7 +2159,7 @@ QUnit.module('Views', { this.data.foo.records.push({id: 5, foo: "blip", int_field: -7, m2o: 1, priority: 2}); this.data.foo.records.push({id: 6, foo: "blip", int_field: 5, m2o: 1, priority: 3}); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2169,7 +2171,7 @@ QUnit.module('Views', { "should contain 2 groups at first level"); // open the first group - testUtils.dom.click(list.$('.o_group_header:first')); + await testUtils.dom.click(list.$('.o_group_header:first')); var $openGroup = list.$('tbody:nth(1)'); assert.strictEqual($openGroup.find('tr').length, 3, @@ -2182,11 +2184,11 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('grouped list with a pager in a group', function (assert) { + QUnit.test('grouped list with a pager in a group', async function (assert) { assert.expect(6); this.data.foo.records[3].bar = true; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2200,7 +2202,7 @@ QUnit.module('Views', { var headerHeight = list.$('.o_group_header').css('height'); // basic rendering checks - testUtils.dom.click(list.$('.o_group_header')); + await testUtils.dom.click(list.$('.o_group_header')); assert.strictEqual(list.$('.o_group_header').css('height'), headerHeight, "height of group header shouldn't have changed"); assert.hasClass(list.$('.o_group_header td:last'),'o_group_pager', @@ -2211,7 +2213,7 @@ QUnit.module('Views', { "open group should display 3 records"); // go to next page - testUtils.dom.click(list.$('.o_group_header .o_pager_next')); + await testUtils.dom.click(list.$('.o_group_header .o_pager_next')); assert.strictEqual(list.$('.o_group_header .o_pager_value').text(), '4-4', "pager's value should be correct"); assert.containsOnce(list, '.o_data_row', @@ -2220,10 +2222,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('edition: create new line, then discard', function (assert) { + QUnit.test('edition: create new line, then discard', async function (assert) { assert.expect(8); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2236,12 +2238,12 @@ QUnit.module('Views', { "create button should be visible"); assert.strictEqual(list.$buttons.find('.o_list_button_discard:visible').length, 0, "discard button should be hidden"); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.strictEqual(list.$buttons.find('.o_list_button_add:visible').length, 0, "create button should be hidden"); assert.strictEqual(list.$buttons.find('.o_list_button_discard:visible').length, 1, "discard button should be visible"); - testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); assert.containsN(list, 'tr.o_data_row', 4, "should still have 4 records"); assert.strictEqual(list.$buttons.find('.o_list_button_add:visible').length, 1, @@ -2251,10 +2253,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('invisible attrs on fields are re-evaluated on field change', function (assert) { + QUnit.test('invisible attrs on fields are re-evaluated on field change', async function (assert) { assert.expect(7); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2269,26 +2271,26 @@ QUnit.module('Views', { "there should be 3 invisible foo cells in readonly mode"); // Make first line editable - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(1)')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(1)')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"].o_invisible_modifier').length, 1, "the foo field widget should have been rendered as invisible"); - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"]:not(.o_invisible_modifier)').length, 1, "the foo field widget should have been marked as non-invisible"); assert.containsN(list, 'tbody td.o_invisible_modifier', 2, "the foo field widget parent cell should not be invisible anymore"); - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"].o_invisible_modifier').length, 1, "the foo field widget should have been marked as invisible again"); assert.containsN(list, 'tbody td.o_invisible_modifier', 3, "the foo field widget parent cell should now be invisible again"); // Reswitch the cell to editable and save the row - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); - testUtils.dom.click(list.$('thead')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('thead')); assert.containsN(list, 'tbody td.o_invisible_modifier', 2, "there should be 2 invisible foo cells in readonly mode"); @@ -2296,10 +2298,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('readonly attrs on fields are re-evaluated on field change', function (assert) { + QUnit.test('readonly attrs on fields are re-evaluated on field change', async function (assert) { assert.expect(9); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2314,31 +2316,31 @@ QUnit.module('Views', { "there should be 3 readonly foo cells in readonly mode"); // Make first line editable - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(1)')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(1)')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > span[name="foo"]').length, 1, "the foo field widget should have been rendered as readonly"); - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"]').length, 1, "the foo field widget should have been rerendered as editable"); assert.containsN(list, 'tbody td.o_readonly_modifier', 2, "the foo field widget parent cell should not be readonly anymore"); - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > span[name="foo"]').length, 1, "the foo field widget should have been rerendered as readonly"); assert.containsN(list, 'tbody td.o_readonly_modifier', 3, "the foo field widget parent cell should now be readonly again"); - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"]').length, 1, "the foo field widget should have been rerendered as editable again"); assert.containsN(list, 'tbody td.o_readonly_modifier', 2, "the foo field widget parent cell should not be readonly again"); // Click outside to leave edition mode - testUtils.dom.click(list.$el); + await testUtils.dom.click(list.$el); assert.containsN(list, 'tbody td.o_readonly_modifier', 2, "there should be 2 readonly foo cells in readonly mode"); @@ -2346,10 +2348,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('required attrs on fields are re-evaluated on field change', function (assert) { + QUnit.test('required attrs on fields are re-evaluated on field change', async function (assert) { assert.expect(7); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2364,26 +2366,26 @@ QUnit.module('Views', { "there should be 3 required foo cells in readonly mode"); // Make first line editable - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(1)')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(1)')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"].o_required_modifier').length, 1, "the foo field widget should have been rendered as required"); - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"]:not(.o_required_modifier)').length, 1, "the foo field widget should have been marked as non-required"); assert.containsN(list, 'tbody td.o_required_modifier', 2, "the foo field widget parent cell should not be required anymore"); - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"].o_required_modifier').length, 1, "the foo field widget should have been marked as required again"); assert.containsN(list, 'tbody td.o_required_modifier', 3, "the foo field widget parent cell should now be required again"); // Reswitch the cell to editable and save the row - testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); - testUtils.dom.click(list.$('thead')); + await testUtils.dom.click(list.$('tbody tr:nth(0) td:nth(2) input')); + await testUtils.dom.click(list.$('thead')); assert.containsN(list, 'tbody td.o_required_modifier', 2, "there should be 2 required foo cells in readonly mode"); @@ -2391,11 +2393,11 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('leaving unvalid rows in edition', function (assert) { + QUnit.test('leaving unvalid rows in edition', async function (assert) { assert.expect(4); var warnings = 0; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2417,14 +2419,15 @@ QUnit.module('Views', { // Start first line edition var $firstFooTd = list.$('tbody tr:nth(0) td:nth(1)'); - testUtils.dom.click($firstFooTd); + await testUtils.dom.click($firstFooTd); // Remove required foo field value - testUtils.fields.editInput($firstFooTd.find('input'), "") + await testUtils.fields.editInput($firstFooTd.find('input'), ""); // Try starting other line edition var $secondFooTd = list.$('tbody tr:nth(1) td:nth(1)'); - testUtils.dom.click($secondFooTd); + await testUtils.dom.click($secondFooTd); + await testUtils.nextTick(); assert.strictEqual($firstFooTd.parent('.o_selected_row').length, 1, "first line should still be in edition as invalid"); @@ -2438,10 +2441,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('open a virtual id', function (assert) { + QUnit.test('open a virtual id', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'event', data: this.data, @@ -2461,10 +2464,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('pressing enter on last line of editable list view', function (assert) { + QUnit.test('pressing enter on last line of editable list view', async function (assert) { assert.expect(7); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2476,29 +2479,29 @@ QUnit.module('Views', { }); // click on 3rd line - testUtils.dom.click(list.$('td:contains(gnap)')); + await testUtils.dom.click(list.$('td:contains(gnap)')); assert.hasClass(list.$('tr.o_data_row:eq(2)'),'o_selected_row', "3rd row should be selected"); // press enter in input - list.$('tr.o_selected_row input').trigger({type: 'keydown', which: 13}); // enter + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input'), 'enter'); assert.hasClass(list.$('tr.o_data_row:eq(3)'),'o_selected_row', "4rd row should be selected"); assert.doesNotHaveClass(list.$('tr.o_data_row:eq(2)'), 'o_selected_row', "3rd row should no longer be selected"); // press enter on last row - list.$('tr.o_selected_row input').trigger({type: 'keydown', which: 13}); // enter + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input'), 'enter'); assert.containsN(list, 'tr.o_data_row', 5, "should have created a 5th row"); assert.verifySteps(['/web/dataset/search_read', '/web/dataset/call_kw/foo/default_get']); list.destroy(); }); - QUnit.test('pressing tab on last cell of editable list view', function (assert) { + QUnit.test('pressing tab on last cell of editable list view', async function (assert) { assert.expect(9); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2509,7 +2512,7 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(list.$('td:contains(blip)').last()); + await testUtils.dom.click(list.$('td:contains(blip)').last()); assert.strictEqual(document.activeElement.name, "foo", "focus should be on an input with name = foo"); @@ -2517,12 +2520,11 @@ QUnit.module('Views', { document.activeElement.value = "blip-changed"; $(document.activeElement).trigger({type: 'change'}); - list.$('tr.o_selected_row input[name="foo"]').trigger({type: 'keydown', which: 9}); // tab + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'tab'); assert.strictEqual(document.activeElement.name, "int_field", "focus should be on an input with name = int_field"); - list.$('tr.o_selected_row input[name="int_field"]').trigger({type: 'keydown', which: 9}); // tab - + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'tab'); assert.hasClass(list.$('tr.o_data_row:eq(4)'),'o_selected_row', "5th row should be selected"); assert.strictEqual(document.activeElement.name, "foo", @@ -2535,13 +2537,13 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('navigation with tab and read completes after default_get', function (assert) { + QUnit.test('navigation with tab and read completes after default_get', async function (assert) { assert.expect(8); - var defaultGetDef = $.Deferred(); - var readDef = $.Deferred(); + var defaultGetPromise = testUtils.makeTestPromise(); + var readPromise = testUtils.makeTestPromise(); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2552,24 +2554,30 @@ QUnit.module('Views', { } var result = this._super.apply(this, arguments); if (args.method === 'read') { - return readDef.then(_.constant(result)); + return readPromise.then(function () { + return result; + }); } if (args.method === 'default_get') { - return defaultGetDef.then(_.constant(result)); + return defaultGetPromise.then(function () { + return result; + }); } return result; }, }); - testUtils.dom.click(list.$('td:contains(-4)').last()); + await testUtils.dom.click(list.$('td:contains(-4)').last()); - testUtils.fields.editInput(list.$('tr.o_selected_row input[name="int_field"]'), '1234'); - list.$('tr.o_selected_row input[name="int_field"]').trigger({type: 'keydown', which: 9}); // tab + await testUtils.fields.editInput(list.$('tr.o_selected_row input[name="int_field"]'), '1234'); + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="int_field"]'), 'tab'); - defaultGetDef.resolve(); + defaultGetPromise.resolve(); assert.containsN(list, 'tbody tr.o_data_row', 4, "should have 4 data rows"); - readDef.resolve(); + + readPromise.resolve(); + await testUtils.nextTick(); assert.containsN(list, 'tbody tr.o_data_row', 5, "should have 5 data rows"); assert.strictEqual(list.$('td:contains(1234)').length, 1, @@ -2578,7 +2586,7 @@ QUnit.module('Views', { // we trigger a tab to move to the second cell in the current row. this // operation requires that this.currentRow is properly set in the // list editable renderer. - list.$('tr.o_selected_row input[name="foo"]').trigger({type: 'keydown', which: 9}); // tab + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'tab'); assert.hasClass(list.$('tr.o_data_row:eq(4)'),'o_selected_row', "5th row should be selected"); @@ -2586,10 +2594,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('display toolbar', function (assert) { + QUnit.test('display toolbar', async function (assert) { assert.expect(6); - var list = createView({ + var list = await createView({ View: ListView, model: 'event', data: this.data, @@ -2628,27 +2636,27 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('edit list line after line deletion', function (assert) { + QUnit.test('edit list line after line deletion', async function (assert) { assert.expect(5); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, arch: '<tree editable="top"><field name="foo"/><field name="int_field"/></tree>', }); - testUtils.dom.click(list.$('.o_data_row:nth(2) > td:not(.o_list_record_selector)').first()); + await testUtils.dom.click(list.$('.o_data_row:nth(2) > td:not(.o_list_record_selector)').first()); assert.ok(list.$('.o_data_row:nth(2)').is('.o_selected_row'), "third row should be in edition"); - testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.ok(list.$('.o_data_row:nth(0)').is('.o_selected_row'), "first row should be in edition (creation)"); - testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); assert.containsNone(list, '.o_selected_row', "no row should be selected"); - testUtils.dom.click(list.$('.o_data_row:nth(2) > td:not(.o_list_record_selector)').first()); + await testUtils.dom.click(list.$('.o_data_row:nth(2) > td:not(.o_list_record_selector)').first()); assert.ok(list.$('.o_data_row:nth(2)').is('.o_selected_row'), "third row should be in edition"); assert.containsOnce(list, '.o_selected_row', @@ -2657,14 +2665,14 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('navigation with tab and readonly field (no modification)', function (assert) { + QUnit.test('navigation with tab and readonly field (no modification)', async function (assert) { // This test makes sure that if we have 2 cells in a row, the first in // edit mode, and the second one readonly, then if we press TAB when the // focus is on the first, then the focus skip the readonly cells and // directly goes to the next line instead. assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2672,31 +2680,30 @@ QUnit.module('Views', { }); // click on first td and press TAB - testUtils.dom.click(list.$('td:contains(yop)').last()); + await testUtils.dom.click(list.$('td:contains(yop)').last()); - list.$('tr.o_selected_row input[name="foo"]').trigger({type: 'keydown', which: $.ui.keyCode.TAB}); + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'tab'); assert.hasClass(list.$('tr.o_data_row:eq(1)'),'o_selected_row', "2nd row should be selected"); // we do it again. This was broken because the this.currentRow variable // was not properly set, and the second TAB could cause a crash. - list.$('tr.o_selected_row input[name="foo"]').trigger({type: 'keydown', which: $.ui.keyCode.TAB}); + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'tab'); assert.hasClass(list.$('tr.o_data_row:eq(2)'),'o_selected_row', "3rd row should be selected"); list.destroy(); }); - - QUnit.test('navigation with tab and readonly field (with modification)', function (assert) { + QUnit.test('navigation with tab and readonly field (with modification)', async function (assert) { // This test makes sure that if we have 2 cells in a row, the first in // edit mode, and the second one readonly, then if we press TAB when the // focus is on the first, then the focus skips the readonly cells and // directly goes to the next line instead. assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2704,30 +2711,30 @@ QUnit.module('Views', { }); // click on first td and press TAB - testUtils.dom.click(list.$('td:contains(yop)').last()); + await testUtils.dom.click(list.$('td:contains(yop)')); //modity the cell content - document.activeElement.value = "blip-changed"; - $(document.activeElement).trigger({type: 'change'}); + testUtils.fields.editAndTrigger($(document.activeElement), + 'blip-changed', ['change']); - list.$('tr.o_selected_row input[name="foo"]').trigger({type: 'keydown', which: $.ui.keyCode.TAB}); + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'tab'); assert.hasClass(list.$('tr.o_data_row:eq(1)'),'o_selected_row', "2nd row should be selected"); // we do it again. This was broken because the this.currentRow variable // was not properly set, and the second TAB could cause a crash. - list.$('tr.o_selected_row input[name="foo"]').trigger({type: 'keydown', which: $.ui.keyCode.TAB}); + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'tab'); assert.hasClass(list.$('tr.o_data_row:eq(2)'),'o_selected_row', "3rd row should be selected"); list.destroy(); }); - QUnit.test('navigation with tab on a list with create="0"', function (assert) { + QUnit.test('navigation with tab on a list with create="0"', async function (assert) { assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2739,31 +2746,31 @@ QUnit.module('Views', { assert.containsN(list, '.o_data_row', 4, "the list should contain 4 rows"); - testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:first')); + await testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:first')); assert.hasClass(list.$('.o_data_row:nth(2)'),'o_selected_row', "third row should be in edition"); // Press 'Tab' -> should go to next line // add a value in the cell because the Tab on an empty first cell would activate the next widget in the view - testUtils.fields.editInput(list.$('.o_selected_row input').eq(1), 11); - list.$('.o_selected_row input').trigger({type: 'keydown', which: 9}); + await testUtils.fields.editInput(list.$('.o_selected_row input').eq(1), 11); + await testUtils.fields.triggerKeydown(list.$('.o_selected_row input'), 'tab'); assert.hasClass(list.$('.o_data_row:nth(3)'),'o_selected_row', "fourth row should be in edition"); // Press 'Tab' -> should go back to first line as the create action isn't available - testUtils.fields.editInput(list.$('.o_selected_row input').eq(1), 11); - list.$('.o_selected_row input').trigger({type: 'keydown', which: 9}); + await testUtils.fields.editInput(list.$('.o_selected_row input').eq(1), 11); + await testUtils.fields.triggerKeydown(list.$('.o_selected_row input'), 'tab'); assert.hasClass(list.$('.o_data_row:first'),'o_selected_row', "first row should be in edition"); list.destroy(); }); - QUnit.test('navigation with tab on a one2many list with create="0"', function (assert) { + QUnit.test('navigation with tab on a one2many list with create="0"', async function (assert) { assert.expect(4); this.data.foo.records[0].o2m = [1, 2]; - var form = createView({ + var form = await createView({ View: FormView, model: 'foo', data: this.data, @@ -2784,31 +2791,31 @@ QUnit.module('Views', { assert.containsN(form, '.o_field_widget[name=o2m] .o_data_row', 2, "there should be two records in the many2many"); - testUtils.dom.click(form.$('.o_field_widget[name=o2m] .o_data_cell:first')); + await testUtils.dom.click(form.$('.o_field_widget[name=o2m] .o_data_cell:first')); assert.hasClass(form.$('.o_field_widget[name=o2m] .o_data_row:first'),'o_selected_row', "first row should be in edition"); // Press 'Tab' -> should go to next line - form.$('.o_field_widget[name=o2m] .o_selected_row input').trigger({type: 'keydown', which: 9}); + await testUtils.fields.triggerKeydown(form.$('.o_field_widget[name=o2m] .o_selected_row input'), 'tab'); assert.hasClass(form.$('.o_field_widget[name=o2m] .o_data_row:nth(1)'),'o_selected_row', "second row should be in edition"); // Press 'Tab' -> should get out of the one to many and go to the next field of the form - form.$('.o_field_widget[name=o2m] .o_selected_row input').trigger({type: 'keydown', which: 9}); + await testUtils.fields.triggerKeydown(form.$('.o_field_widget[name=o2m] .o_selected_row input'), 'tab'); assert.strictEqual(document.activeElement, form.$('input[name="foo"]')[0], "the next field should be selected"); form.destroy(); }); - QUnit.test('edition, then navigation with tab (with a readonly field)', function (assert) { + QUnit.test('edition, then navigation with tab (with a readonly field)', async function (assert) { // This test makes sure that if we have 2 cells in a row, the first in // edit mode, and the second one readonly, then if we edit and press TAB, // (before debounce), the save operation is properly done (before // selecting the next row) assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2823,9 +2830,9 @@ QUnit.module('Views', { }); // click on first td and press TAB - testUtils.dom.click(list.$('td:contains(yop)')); - testUtils.fields.editSelect(list.$('tr.o_selected_row input[name="foo"]'), 'new value'); - list.$('tr.o_selected_row input[name="foo"]').trigger({type: 'keydown', which: $.ui.keyCode.TAB}); + await testUtils.dom.click(list.$('td:contains(yop)')); + await testUtils.fields.editSelect(list.$('tr.o_selected_row input[name="foo"]'), 'new value'); + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'tab'); assert.strictEqual(list.$('tbody tr:first td:contains(new value)').length, 1, "should have the new value visible in dom"); @@ -2833,10 +2840,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('skip invisible fields when navigating list view with TAB', function (assert) { + QUnit.test('skip invisible fields when navigating list view with TAB', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2848,20 +2855,20 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.dom.click(list.$('td:contains(gnap)')); + await testUtils.dom.click(list.$('td:contains(gnap)')); assert.strictEqual(list.$('input[name="foo"]')[0], document.activeElement, "foo should be focused"); - list.$('input[name="foo"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB})); + await testUtils.fields.triggerKeydown(list.$('input[name="foo"]'), 'tab'); assert.strictEqual(list.$('input[name="int_field"]')[0], document.activeElement, "int_field should be focused"); list.destroy(); }); - QUnit.test('skip buttons when navigating list view with TAB (end)', function (assert) { + QUnit.test('skip buttons when navigating list view with TAB (end)', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2872,20 +2879,20 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.dom.click(list.$('tbody tr:eq(2) td:eq(1)')); + await testUtils.dom.click(list.$('tbody tr:eq(2) td:eq(1)')); assert.strictEqual(list.$('tbody tr:eq(2) input[name="foo"]')[0], document.activeElement, "foo should be focused"); - list.$('tbody tr:eq(2) input[name="foo"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB})); + await testUtils.fields.triggerKeydown(list.$('tbody tr:eq(2) input[name="foo"]'), 'tab'); assert.strictEqual(list.$('tbody tr:eq(3) input[name="foo"]')[0], document.activeElement, "next line should be selected"); list.destroy(); }); - QUnit.test('skip buttons when navigating list view with TAB (middle)', function (assert) { + QUnit.test('skip buttons when navigating list view with TAB (middle)', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2899,40 +2906,40 @@ QUnit.module('Views', { res_id: 1, }); - testUtils.dom.click(list.$('tbody tr:eq(2) td:eq(2)')); + await testUtils.dom.click(list.$('tbody tr:eq(2) td:eq(2)')); assert.strictEqual(list.$('tbody tr:eq(2) input[name="foo"]')[0], document.activeElement, "foo should be focused"); - list.$('tbody tr:eq(2) input[name="foo"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB})); + await testUtils.fields.triggerKeydown(list.$('tbody tr:eq(2) input[name="foo"]'), 'tab'); assert.strictEqual(list.$('tbody tr:eq(2) input[name="int_field"]')[0], document.activeElement, "int_field should be focused"); list.destroy(); }); - QUnit.test('navigation: not moving down with keydown', function (assert) { + QUnit.test('navigation: not moving down with keydown', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, arch: '<tree editable="bottom"><field name="foo"/></tree>', }); - testUtils.dom.click(list.$('td:contains(yop)')); + await testUtils.dom.click(list.$('td:contains(yop)')); assert.hasClass(list.$('tr.o_data_row:eq(0)'),'o_selected_row', "1st row should be selected"); - list.$('tr.o_selected_row input[name="foo"]').trigger({type: 'keydown', which: $.ui.keyCode.DOWN}); + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input[name="foo"]'), 'down'); assert.hasClass(list.$('tr.o_data_row:eq(0)'),'o_selected_row', "1st row should still be selected"); list.destroy(); }); - QUnit.test('navigation: moving right with keydown from text field does not move the focus', function (assert) { + QUnit.test('navigation: moving right with keydown from text field does not move the focus', async function (assert) { assert.expect(6); this.data.foo.fields.foo.type = 'text'; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2943,7 +2950,7 @@ QUnit.module('Views', { '</tree>', }); - testUtils.dom.click(list.$('td:contains(yop)')); + await testUtils.dom.click(list.$('td:contains(yop)')); var textarea = list.$('textarea[name="foo"]')[0]; assert.strictEqual(document.activeElement, textarea, "textarea should be focused"); @@ -2956,16 +2963,16 @@ QUnit.module('Views', { "textarea should still be focused"); assert.ok(textarea.selectionStart === 3 && textarea.selectionEnd === 3, "textarea value ('yop') should not be selected and cursor should be at the end"); - $(textarea).trigger({type: 'keydown', which: $.ui.keyCode.RIGHT}); + await testUtils.fields.triggerKeydown($(textarea), 'right'); assert.strictEqual(document.activeElement, list.$('textarea[name="foo"]')[0], "next field (checkbox) should now be focused"); list.destroy(); }); - QUnit.test('discarding changes in a row properly updates the rendering', function (assert) { + QUnit.test('discarding changes in a row properly updates the rendering', async function (assert) { assert.expect(3); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -2978,27 +2985,27 @@ QUnit.module('Views', { assert.strictEqual(list.$('.o_data_cell:first').text(), "yop", "first cell should contain 'yop'"); - testUtils.dom.click(list.$('.o_data_cell:first')); - testUtils.fields.editInput(list.$('input[name="foo"]'), "hello"); - testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); + await testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.fields.editInput(list.$('input[name="foo"]'), "hello"); + await testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); assert.strictEqual($('.modal:visible').length, 1, "a modal to ask for discard should be visible"); - testUtils.dom.click($('.modal:visible .btn-primary')); + await testUtils.dom.click($('.modal:visible .btn-primary')); assert.strictEqual(list.$('.o_data_cell:first').text(), "yop", "first cell should still contain 'yop'"); list.destroy(); }); - QUnit.test('numbers in list are right-aligned', function (assert) { + QUnit.test('numbers in list are right-aligned', async function (assert) { assert.expect(2); var currencies = {}; _.each(this.data.res_currency.records, function (currency) { currencies[currency.id] = currency; }); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3021,7 +3028,7 @@ QUnit.module('Views', { assert.strictEqual(nbCellRight, 2, "there should be two right-aligned cells"); - testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.dom.click(list.$('.o_data_cell:first')); var nbInputRight = _.filter(list.$('.o_data_row:first > .o_data_cell input'), function (el) { var style = window.getComputedStyle(el); @@ -3033,7 +3040,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('grouped list are not editable (ungrouped first)', function (assert) { + QUnit.test('grouped list are not editable (ungrouped first)', async function (assert) { // Editable grouped list views are not supported, so the purpose of this // test is to check that when a list view is grouped, its editable // attribute is ignored @@ -3042,7 +3049,7 @@ QUnit.module('Views', { // editable assert.expect(5); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3055,28 +3062,28 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.dom.click(list.$('.o_data_cell:first')); assert.verifySteps([], 'no switch view should have been requested'); assert.containsOnce(list, '.o_selected_row', "a row should be in edition"); - testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_discard')); // reload with groupBy - list.reload({groupBy: ['bar']}); + await list.reload({groupBy: ['bar']}); // clicking on record should open the form view - testUtils.dom.click(list.$('.o_group_header:first')); - testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.dom.click(list.$('.o_group_header:first')); + await testUtils.dom.click(list.$('.o_data_cell:first')); // clicking on create button should open the form view - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.verifySteps(['switch view form 1', 'switch view form false'], 'two switch view to form should have been requested'); list.destroy(); }); - QUnit.test('grouped list are not editable (grouped first)', function (assert) { + QUnit.test('grouped list are not editable (grouped first)', async function (assert) { // Editable grouped list views are not supported, so the purpose of this // test is to check that when a list view is grouped, its editable // attribute is ignored @@ -3084,7 +3091,7 @@ QUnit.module('Views', { // editable, and then it is reloaded with no groupBy and becomes editable assert.expect(6); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3100,42 +3107,40 @@ QUnit.module('Views', { // the view being grouped, it is not editable, so clicking on a record // should open the form view - testUtils.dom.click(list.$('.o_group_header:first')); - testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.dom.click(list.$('.o_group_header:first')); + await testUtils.dom.click(list.$('.o_data_cell:first')); // for the same reason, clicking on 'Create' should open the form view - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); assert.verifySteps(['switch view form 1', 'switch view form false'], "two switch view to form should have been requested"); // reload without groupBy - list.reload({groupBy: []}); + await list.reload({groupBy: []}); // as the view is no longer grouped, it is editable, so clicking on a // row should switch it in edition - testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.dom.click(list.$('.o_data_cell:first')); - assert.verifySteps(['switch view form 1', 'switch view form false'], - "no more switch view should have been requested"); - assert.containsOnce(list, '.o_selected_row', - "a row should be in edition"); + assert.verifySteps([], "no more switch view should have been requested"); + assert.containsOnce(list, '.o_selected_row', "a row should be in edition"); // clicking on the body should leave the edition - testUtils.dom.click($('body')); + await testUtils.dom.click($('body')); assert.containsNone(list, '.o_selected_row', "the row should no longer be in edition"); list.destroy(); }); - QUnit.test('field values are escaped', function (assert) { + QUnit.test('field values are escaped', async function (assert) { assert.expect(1); var value = '<script>throw Error();</script>'; this.data.foo.records[0].foo = value; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3148,21 +3153,23 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('pressing ESC discard the current line changes', function (assert) { - assert.expect(3); + QUnit.test('pressing ESC discard the current line changes', async function (assert) { + assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, arch: '<tree editable="top"><field name="foo"/></tree>', }); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + assert.containsN(list, 'tr.o_data_row', 5, + "should currently adding a 5th data row"); - list.$('input[name="foo"]').trigger({type: 'keydown', which: $.ui.keyCode.ESCAPE}); + await testUtils.fields.triggerKeydown(list.$('input[name="foo"]'), 'escape'); assert.containsN(list, 'tr.o_data_row', 4, - "should have 4 data row in list"); + "should have only 4 data row after escape"); assert.containsNone(list, 'tr.o_data_row.o_selected_row', "no rows should be selected"); assert.ok(!list.$buttons.find('.o_list_button_save').is(':visible'), @@ -3170,21 +3177,23 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('pressing ESC discard the current line changes (with required)', function (assert) { - assert.expect(3); + QUnit.test('pressing ESC discard the current line changes (with required)', async function (assert) { + assert.expect(4); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, arch: '<tree editable="top"><field name="foo" required="1"/></tree>', }); - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + assert.containsN(list, 'tr.o_data_row', 5, + "should currently adding a 5th data row"); - list.$('input[name="foo"]').trigger({type: 'keydown', which: $.ui.keyCode.ESCAPE}); + await testUtils.fields.triggerKeydown(list.$('input[name="foo"]'), 'escape'); assert.containsN(list, 'tr.o_data_row', 4, - "should have 4 data row in list"); + "should have only 4 data row after escape"); assert.containsNone(list, 'tr.o_data_row.o_selected_row', "no rows should be selected"); assert.ok(!list.$buttons.find('.o_list_button_save').is(':visible'), @@ -3192,10 +3201,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('field with password attribute', function (assert) { + QUnit.test('field with password attribute', async function (assert) { assert.expect(2); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3210,10 +3219,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('list with handle widget', function (assert) { + QUnit.test('list with handle widget', async function (assert) { assert.expect(11); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3229,7 +3238,7 @@ QUnit.module('Views', { "should write the right field as sequence"); assert.deepEqual(args.ids, [4, 2 , 3], "should write the sequence in correct order"); - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, @@ -3245,7 +3254,7 @@ QUnit.module('Views', { "default fourth record should have amount 0"); // Drag and drop the fourth line in second position - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(3), list.$('tbody tr').first(), {position: 'bottom'} @@ -3263,7 +3272,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('result of consecutive resequences is correctly sorted', function (assert) { + QUnit.test('result of consecutive resequences is correctly sorted', async function (assert) { assert.expect(9); this.data = { // we want the data to be minimal to have a minimal test foo: { @@ -3277,7 +3286,7 @@ QUnit.module('Views', { } }; var moves = 0; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3326,28 +3335,31 @@ QUnit.module('Views', { }); assert.strictEqual(list.$('tbody tr td.o_list_number').text(), '1234', "default should be sorted by id"); - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(3), list.$('tbody tr').eq(2), {position: 'top'} ); assert.strictEqual(list.$('tbody tr td.o_list_number').text(), '1243', "the int_field (sequence) should have been correctly updated"); - testUtils.dom.dragAndDrop( + + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(2), list.$('tbody tr').eq(1), {position: 'top'} ); assert.deepEqual(list.$('tbody tr td.o_list_number').text(), '1423', "the int_field (sequence) should have been correctly updated"); - testUtils.dom.dragAndDrop( + + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(1), list.$('tbody tr').eq(3), {position: 'top'} ); assert.deepEqual(list.$('tbody tr td.o_list_number').text(), '1243', "the int_field (sequence) should have been correctly updated"); - testUtils.dom.dragAndDrop( + + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(2), list.$('tbody tr').eq(1), {position: 'top'} @@ -3357,7 +3369,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list with handle widget', function (assert) { + QUnit.test('editable list with handle widget', async function (assert) { assert.expect(12); // resequence makes sense on a sequence field, not on arbitrary fields @@ -3366,7 +3378,7 @@ QUnit.module('Views', { this.data.foo.records[2].int_field = 2; this.data.foo.records[3].int_field = 3; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3397,7 +3409,7 @@ QUnit.module('Views', { "default fourth record should have amount 0"); // Drag and drop the fourth line in second position - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(3), list.$('tbody tr').first(), {position: 'bottom'} @@ -3412,7 +3424,7 @@ QUnit.module('Views', { assert.strictEqual(list.$('tbody tr:eq(3) td:last').text(), '300', "new fourth record should have amount 300"); - testUtils.dom.click(list.$('tbody tr:eq(1) td:last')); + await testUtils.dom.click(list.$('tbody tr:eq(1) td:last')); assert.strictEqual(list.$('tbody tr:eq(1) td:last input').val(), '0', "the edited record should be the good one"); @@ -3420,7 +3432,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list, handle widget locks and unlocks on sort', function (assert) { + QUnit.test('editable list, handle widget locks and unlocks on sort', async function (assert) { assert.expect(6); // we need another sortable field to lock/unlock the handle @@ -3431,7 +3443,7 @@ QUnit.module('Views', { this.data.foo.records[2].int_field = 2; this.data.foo.records[3].int_field = 3; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3445,7 +3457,7 @@ QUnit.module('Views', { "default should be sorted by int_field"); // Drag and drop the fourth line in second position - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(3), list.$('tbody tr').first(), {position: 'bottom'} @@ -3456,13 +3468,13 @@ QUnit.module('Views', { "drag and drop should have succeeded, as the handle is unlocked"); // Sorting by a field different for int_field should lock the handle - testUtils.dom.click(list.$('.o_column_sortable').eq(1)); + await testUtils.dom.click(list.$('.o_column_sortable').eq(1)); assert.strictEqual(list.$('tbody span[name="amount"]').text(), '0.00300.00500.001200.00', "should have been sorted by amount"); // Drag and drop the fourth line in second position (not) - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(3), list.$('tbody tr').first(), {position: 'bottom'} @@ -3472,13 +3484,13 @@ QUnit.module('Views', { "drag and drop should have failed as the handle is locked"); // Sorting by int_field should unlock the handle - testUtils.dom.click(list.$('.o_column_sortable').eq(0)); + await testUtils.dom.click(list.$('.o_column_sortable').eq(0)); assert.strictEqual(list.$('tbody span[name="amount"]').text(), '1200.000.00500.00300.00', "records should be ordered as per the previous resequence"); // Drag and drop the fourth line in second position - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(3), list.$('tbody tr').first(), {position: 'bottom'} @@ -3490,7 +3502,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list with handle widget with slow network', function (assert) { + QUnit.test('editable list with handle widget with slow network', async function (assert) { assert.expect(15); // resequence makes sense on a sequence field, not on arbitrary fields @@ -3499,9 +3511,9 @@ QUnit.module('Views', { this.data.foo.records[2].int_field = 2; this.data.foo.records[3].int_field = 3; - var def = $.Deferred(); + var prom = testUtils.makeTestPromise(); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3518,14 +3530,13 @@ QUnit.module('Views', { "should write the right field as sequence"); assert.deepEqual(args.ids, [4, 2, 3], "should write the sequence in correct order"); - return $.when(def).then(function () { + return prom.then(function () { return _super(route, args); }); } return this._super.apply(this, arguments); }, }); - assert.strictEqual(list.$('tbody tr:eq(0) td:last').text(), '1200', "default first record should have amount 1200"); assert.strictEqual(list.$('tbody tr:eq(1) td:last').text(), '500', @@ -3536,19 +3547,21 @@ QUnit.module('Views', { "default fourth record should have amount 0"); // drag and drop the fourth line in second position - testUtils.dom.dragAndDrop( + await testUtils.dom.dragAndDrop( list.$('.ui-sortable-handle').eq(3), list.$('tbody tr').first(), {position: 'bottom'} ); // edit moved row before the end of resequence - testUtils.dom.click(list.$('tbody tr:eq(3) td:last')); + await testUtils.dom.click(list.$('tbody tr:eq(3) td:last')); + await testUtils.nextTick(); assert.strictEqual(list.$('tbody tr:eq(3) td:last input').length, 0, "shouldn't edit the line before resequence"); - def.resolve(); + prom.resolve(); + await testUtils.nextTick(); assert.strictEqual(list.$('tbody tr:eq(3) td:last input').length, 1, "should edit the line after resequence"); @@ -3556,10 +3569,10 @@ QUnit.module('Views', { assert.strictEqual(list.$('tbody tr:eq(3) td:last input').val(), '300', "fourth record should have amount 300"); - testUtils.fields.editInput(list.$('tbody tr:eq(3) td:last input'), 301); - testUtils.dom.click(list.$('tbody tr:eq(0) td:last')); + await testUtils.fields.editInput(list.$('tbody tr:eq(3) td:last input'), 301); + await testUtils.dom.click(list.$('tbody tr:eq(0) td:last')); - testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_save')); assert.strictEqual(list.$('tbody tr:eq(0) td:last').text(), '1200', "first record should have amount 1200"); @@ -3570,22 +3583,22 @@ QUnit.module('Views', { assert.strictEqual(list.$('tbody tr:eq(3) td:last').text(), '301', "fourth record should have amount 301"); - testUtils.dom.click(list.$('tbody tr:eq(3) td:last')); + await testUtils.dom.click(list.$('tbody tr:eq(3) td:last')); assert.strictEqual(list.$('tbody tr:eq(3) td:last input').val(), '301', "fourth record should have amount 301"); list.destroy(); }); - QUnit.test('multiple clicks on Add do not create invalid rows', function (assert) { + QUnit.test('multiple clicks on Add do not create invalid rows', async function (assert) { assert.expect(2); this.data.foo.onchanges = { m2o: function () {}, }; - var def = $.Deferred(); - var list = createView({ + var prom = testUtils.makeTestPromise(); + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3593,7 +3606,9 @@ QUnit.module('Views', { mockRPC: function (route, args) { var result = this._super.apply(this, arguments); if (args.method === 'onchange') { - return $.when(def).then(_.constant(result)); + return prom.then(function () { + return result; + }); } return result; }, @@ -3606,7 +3621,8 @@ QUnit.module('Views', { testUtils.dom.click(list.$buttons.find('.o_list_button_add')); testUtils.dom.click(list.$buttons.find('.o_list_button_add')); - def.resolve(); + prom.resolve(); + await testUtils.nextTick(); assert.containsN(list, '.o_data_row', 5, "only one record should have been created"); @@ -3614,7 +3630,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('reference field rendering', function (assert) { + QUnit.test('reference field rendering', async function (assert) { assert.expect(4); this.data.foo.records.push({ @@ -3622,7 +3638,7 @@ QUnit.module('Views', { reference: 'res_currency,2', }); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3641,10 +3657,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list view: contexts are correctly sent', function (assert) { + QUnit.test('editable list view: contexts are correctly sent', async function (assert) { assert.expect(6); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3670,17 +3686,17 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(list.$('.o_data_cell:first')); - testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), 'abc'); - testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + await testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), 'abc'); + await testUtils.dom.click(list.$buttons.find('.o_list_button_save')); list.destroy(); }); - QUnit.test('list grouped by date:month', function (assert) { + QUnit.test('list grouped by date:month', async function (assert) { assert.expect(1); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3694,10 +3710,10 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('grouped list edition with toggle_button widget', function (assert) { + QUnit.test('grouped list edition with toggle_button widget', async function (assert) { assert.expect(3); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3712,17 +3728,17 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(list.$('.o_group_header:first')); + await testUtils.dom.click(list.$('.o_group_header:first')); assert.containsOnce(list, '.o_data_row:first .o_toggle_button_success', "boolean value of the first record should be true"); - testUtils.dom.click(list.$('.o_data_row:first .o_icon_button')); + await testUtils.dom.click(list.$('.o_data_row:first .o_icon_button')); assert.strictEqual(list.$('.o_data_row:first .text-muted:not(.o_toggle_button_success)').length, 1, "boolean button should have been updated"); list.destroy(); }); - QUnit.test('grouped list view, indentation for empty group', function (assert) { + QUnit.test('grouped list view, indentation for empty group', async function (assert) { assert.expect(3); this.data.foo.fields.priority = { @@ -3734,7 +3750,7 @@ QUnit.module('Views', { this.data.foo.records.push({id: 5, foo: "blip", int_field: -7, m2o: 1, priority: 2}); this.data.foo.records.push({id: 6, foo: "blip", int_field: 5, m2o: 1, priority: 3}); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3744,7 +3760,7 @@ QUnit.module('Views', { // Override of the read_group to display the row even if there is no record in it, // to mock the behavihour of some fields e.g stage_id on the sale order. if (args.method === 'read_group' && args.kwargs.groupby[0] === "m2o") { - return $.when([ + return Promise.resolve([ { id: 8, m2o:[1,"Value 1"], @@ -3761,7 +3777,7 @@ QUnit.module('Views', { }); // open the first group - testUtils.dom.click(list.$('.o_group_header:first')); + await testUtils.dom.click(list.$('.o_group_header:first')); assert.strictEqual(list.$('th.o_group_name').eq(1).children().length, 1, "There should be an empty element creating the indentation for the subgroup."); assert.hasClass(list.$('th.o_group_name').eq(1).children().eq(0), 'fa', @@ -3771,7 +3787,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('basic support for widgets', function (assert) { + QUnit.test('basic support for widgets', async function (assert) { assert.expect(1); var MyWidget = Widget.extend({ @@ -3784,7 +3800,7 @@ QUnit.module('Views', { }); widgetRegistry.add('test', MyWidget); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3798,10 +3814,10 @@ QUnit.module('Views', { delete widgetRegistry.map.test; }); - QUnit.test('use the limit attribute in arch', function (assert) { + QUnit.test('use the limit attribute in arch', async function (assert) { assert.expect(3); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3821,7 +3837,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('check if the view destroys all widgets and instances', function (assert) { + QUnit.test('check if the view destroys all widgets and instances', async function (assert) { assert.expect(1); var instanceNumber = 0; @@ -3858,13 +3874,13 @@ QUnit.module('Views', { '</tree>', }; - var list = createView(params); + var list = await createView(params); list.destroy(); var initialInstanceNumber = instanceNumber; instanceNumber = 0; - list = createView(params); + list = await createView(params); // call destroy function of controller to ensure that it correctly destroys everything list.__destroy(); @@ -3878,12 +3894,12 @@ QUnit.module('Views', { testUtils.mock.unpatch(mixins.ParentedMixin); }); - QUnit.test('concurrent reloads finishing in inverse order', function (assert) { - assert.expect(3); + QUnit.test('concurrent reloads finishing in inverse order', async function (assert) { + assert.expect(4); var blockSearchRead = false; - var def = $.Deferred(); - var list = createView({ + var prom = testUtils.makeTestPromise(); + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3891,7 +3907,7 @@ QUnit.module('Views', { mockRPC: function (route) { var result = this._super.apply(this, arguments); if (route === '/web/dataset/search_read' && blockSearchRead) { - return $.when(def).then(_.constant(result)); + return prom.then(_.constant(result)); } return result; }, @@ -3903,6 +3919,7 @@ QUnit.module('Views', { // reload with a domain (this request is blocked) blockSearchRead = true; list.reload({domain: [['foo', '=', 'yop']]}); + await testUtils.nextTick(); assert.containsN(list, '.o_list_view .o_data_row', 4, "list view should still contain 4 records (search_read being blocked)"); @@ -3910,23 +3927,29 @@ QUnit.module('Views', { // reload without the domain blockSearchRead = false; list.reload({domain: []}); + await testUtils.nextTick(); + + assert.containsN(list, '.o_list_view .o_data_row', 4, + "list view should still contain 4 records"); // unblock the RPC - def.resolve(); + prom.resolve(); + await testUtils.nextTick(); + assert.containsN(list, '.o_list_view .o_data_row', 4, "list view should still contain 4 records"); list.destroy(); }); - QUnit.test('list view on a "noCache" model', function (assert) { - assert.expect(8); + QUnit.test('list view on a "noCache" model', async function (assert) { + assert.expect(9); testUtils.mock.patch(BasicModel, { noCacheModels: BasicModel.prototype.noCacheModels.concat(['foo']), }); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3946,20 +3969,20 @@ QUnit.module('Views', { core.bus.on('clear_cache', list, assert.step.bind(assert, 'clear_cache')); // create a new record - testUtils.dom.click(list.$buttons.find('.o_list_button_add')); - testUtils.fields.editInput(list.$('.o_selected_row .o_field_widget'), 'some value'); - testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + await testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + await testUtils.fields.editInput(list.$('.o_selected_row .o_field_widget'), 'some value'); + await testUtils.dom.click(list.$buttons.find('.o_list_button_save')); // edit an existing record - testUtils.dom.click(list.$('.o_data_cell:first')); - testUtils.fields.editInput(list.$('.o_selected_row .o_field_widget'), 'new value'); - testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + await testUtils.dom.click(list.$('.o_data_cell:first')); + await testUtils.fields.editInput(list.$('.o_selected_row .o_field_widget'), 'new value'); + await testUtils.dom.click(list.$buttons.find('.o_list_button_save')); // delete a record - testUtils.dom.click(list.$('.o_data_row:first .o_list_record_selector input')); - testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); - testUtils.dom.click(list.sidebar.$('a:contains(Delete)')); - testUtils.dom.click($('.modal-footer .btn-primary')); + await testUtils.dom.click(list.$('.o_data_row:first .o_list_record_selector input')); + await testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(list.sidebar.$('a:contains(Delete)')); + await testUtils.dom.click($('.modal-footer .btn-primary')); assert.verifySteps([ 'create', @@ -3972,12 +3995,14 @@ QUnit.module('Views', { list.destroy(); testUtils.mock.unpatch(BasicModel); + + assert.verifySteps(['clear_cache']); // triggered by the test environment on destroy }); - QUnit.test('list should ask to scroll to top on page changes', function (assert) { + QUnit.test('list should ask to scroll to top on page changes', async function (assert) { assert.expect(10); - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -3994,36 +4019,34 @@ QUnit.module('Views', { }); // switch pages (should ask to scroll) - testUtils.dom.click(list.pager.$('.o_pager_next')); - testUtils.dom.click(list.pager.$('.o_pager_previous')); - + await testUtils.dom.click(list.pager.$('.o_pager_next')); + await testUtils.dom.click(list.pager.$('.o_pager_previous')); assert.verifySteps(['scroll', 'scroll'], "should ask to scroll when switching pages"); // change the limit (should not ask to scroll) - testUtils.dom.click(list.pager.$('.o_pager_value')); - list.pager.$('.o_pager_value input').val('1-2').blur(); + await testUtils.dom.click(list.pager.$('.o_pager_value')); + await testUtils.fields.editAndTrigger(list.pager.$('.o_pager_value input'), + '1-2', ['blur']); assert.strictEqual(list.pager.$('.o_pager_value').text(), '1-2', "should have changed the limit"); - assert.verifySteps(['scroll', 'scroll'], - "should not ask to scroll when changing the limit"); + assert.verifySteps([], "should not ask to scroll when changing the limit"); // switch pages again (should still ask to scroll) - testUtils.dom.click(list.pager.$('.o_pager_next')); + await testUtils.dom.click(list.pager.$('.o_pager_next')); - assert.verifySteps(['scroll', 'scroll', 'scroll'], - "this is still working after a limit change"); + assert.verifySteps(['scroll'], "this is still working after a limit change"); list.destroy(); }); - QUnit.test('list with handle field, override default_get, bottom when inline', function (assert) { + QUnit.test('list with handle field, override default_get, bottom when inline', async function (assert) { assert.expect(2); this.data.foo.fields.int_field.default = 10; - var list = createView({ + var list = await createView({ View: ListView, model: 'foo', data: this.data, @@ -4042,15 +4065,55 @@ QUnit.module('Views', { // check line is at the correct place var inputText = 'ninja'; - testUtils.dom.click($('.o_list_button_add')); - testUtils.fields.editInput(list.$('.o_input[name="foo"]'), inputText); - testUtils.dom.click($('.o_list_button_save')); - testUtils.dom.click($('.o_list_button_add')); + await testUtils.dom.click($('.o_list_button_add')); + await testUtils.fields.editInput(list.$('.o_input[name="foo"]'), inputText); + await testUtils.dom.click($('.o_list_button_save')); + await testUtils.dom.click($('.o_list_button_add')); assert.strictEqual($('.o_data_cell').text(), "blipblipyopgnap" + inputText); list.destroy(); }); + + QUnit.test('grouped list with async widget', async function (assert) { + assert.expect(4); + + var prom = testUtils.makeTestPromise(); + var AsyncWidget = Widget.extend({ + willStart: function () { + return prom; + }, + start: function () { + this.$el.text('ready'); + }, + }); + widgetRegistry.add('asyncWidget', AsyncWidget); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree><widget name="asyncWidget"/></tree>', + groupBy: ['int_field'], + }); + + assert.containsNone(list, '.o_data_row', "no group should be open"); + + await testUtils.dom.click(list.$('.o_group_header:first')); + + assert.containsNone(list, '.o_data_row', + "should wait for async widgets before opening the group"); + + prom.resolve(); + await testUtils.nextTick(); + + assert.containsN(list, '.o_data_row', 1, "group should be open"); + assert.strictEqual(list.$('.o_data_row .o_data_cell').text(), 'ready', + "async widget should be correctly displayed"); + + list.destroy(); + delete widgetRegistry.map.asyncWidget; + }); }); }); diff --git a/addons/web/static/tests/views/mobile_control_panel_tests.js b/addons/web/static/tests/views/mobile_control_panel_tests.js index 05e2cd09a73..19226741474 100644 --- a/addons/web/static/tests/views/mobile_control_panel_tests.js +++ b/addons/web/static/tests/views/mobile_control_panel_tests.js @@ -39,12 +39,12 @@ QUnit.module('Mobile Search view Screen', { }; }, }, function () { - QUnit.test('can activate a filter with mobile search view', function (assert) { + QUnit.test('can activate a filter with mobile search view', async function (assert) { assert.expect(3); var filterActiveFlag = false; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -57,23 +57,23 @@ QUnit.module('Mobile Search view Screen', { }, }); - actionManager.doAction(1); + await actionManager.doAction(1); assert.ok(!$('.o_mobile_search').is(':visible'), 'mobile search view is not visible'); // open the search view - testUtils.dom.click($('button.o_enable_searchview')); + await testUtils.dom.click($('button.o_enable_searchview')); assert.ok($('.o_mobile_search').is(':visible'), 'mobile search view is visible'); // open filter sub menu - testUtils.dom.click($('button.o_dropdown_toggler_btn').first()); + await testUtils.dom.click($('button.o_dropdown_toggler_btn').first()); filterActiveFlag = true; // click on Active filter - testUtils.dom.click($('.o_filters_menu a:contains(Active)')); + await testUtils.dom.click($('.o_filters_menu a:contains(Active)')); actionManager.destroy(); }); diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js index 0e7e7ff0a2d..c098ecd7867 100644 --- a/addons/web/static/tests/views/pivot_tests.js +++ b/addons/web/static/tests/views/pivot_tests.js @@ -89,10 +89,10 @@ QUnit.module('Views', { }, function () { QUnit.module('PivotView'); - QUnit.test('simple pivot rendering', function (assert) { + QUnit.test('simple pivot rendering', async function (assert) { assert.expect(3); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -113,10 +113,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot rendering with widget', function (assert) { + QUnit.test('pivot rendering with widget', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -130,12 +130,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot rendering with string attribute on field', function (assert) { + QUnit.test('pivot rendering with string attribute on field', async function (assert) { assert.expect(1); this.data.partner.fields.foo = {string: "Foo", type: "integer", store: true}; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -149,12 +149,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot rendering with string attribute on non stored field', function (assert) { + QUnit.test('pivot rendering with string attribute on non stored field', async function (assert) { assert.expect(1); this.data.partner.fields.fubar = {string: "Fubar", type: "integer", store:false}; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -166,7 +166,7 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot rendering with invisible attribute on field', function (assert) { + QUnit.test('pivot rendering with invisible attribute on field', async function (assert) { assert.expect(2); // when invisible, a field should neither be an active measure, // nor be a selectable measure. @@ -175,7 +175,7 @@ QUnit.module('Views', { foo2: {string: "Foo2", type: "integer", store: true} }); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -193,10 +193,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot view without "string" attribute', function (assert) { + QUnit.test('pivot view without "string" attribute', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -210,10 +210,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot view add computed fields explicitly defined as measure', function (assert) { + QUnit.test('pivot view add computed fields explicitly defined as measure', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -226,10 +226,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('clicking on a cell triggers a do_action', function (assert) { + QUnit.test('clicking on a cell triggers a do_action', async function (assert) { assert.expect(2); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -271,15 +271,15 @@ QUnit.module('Views', { assert.hasClass(pivot.$el,'o_enable_linking', "root node should have classname 'o_enable_linking'"); - testUtils.dom.click(pivot.$('.o_pivot_cell_value:contains(12)')); // should trigger a do_action + await testUtils.dom.click(pivot.$('.o_pivot_cell_value:contains(12)')); // should trigger a do_action pivot.destroy(); }); - QUnit.test('pivot view with disable_linking="True"', function (assert) { + QUnit.test('pivot view with disable_linking="True"', async function (assert) { assert.expect(2); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -297,16 +297,16 @@ QUnit.module('Views', { "root node should not have classname 'o_enable_linking'"); assert.containsOnce(pivot, '.o_pivot_cell_value', "should have one cell"); - testUtils.dom.click(pivot.$('.o_pivot_cell_value')); // should not trigger a do_action + await testUtils.dom.click(pivot.$('.o_pivot_cell_value')); // should not trigger a do_action pivot.destroy(); }); - QUnit.test('pivot view grouped by date field', function (assert) { + QUnit.test('pivot view grouped by date field', async function (assert) { assert.expect(2); var data = this.data; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -325,10 +325,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('without measures, pivot view uses __count by default', function (assert) { + QUnit.test('without measures, pivot view uses __count by default', async function (assert) { assert.expect(2); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -347,11 +347,11 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot view can be reloaded', function (assert) { + QUnit.test('pivot view can be reloaded', async function (assert) { assert.expect(4); var readGroupCount = 0; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -368,17 +368,17 @@ QUnit.module('Views', { "should contain a pivot cell with the number of all records"); assert.strictEqual(readGroupCount, 1, "should have done 1 rpc"); - testUtils.pivot.reload(pivot, {domain: [['foo', '>', 10]]}); + await testUtils.pivot.reload(pivot, {domain: [['foo', '>', 10]]}); assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(2)').length, 1, "should contain a pivot cell with the number of remaining records"); assert.strictEqual(readGroupCount, 2, "should have done 2 rpcs"); pivot.destroy(); }); - QUnit.test('pivot view grouped by many2one field', function (assert) { + QUnit.test('pivot view grouped by many2one field', async function (assert) { assert.expect(3); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -397,12 +397,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('basic folding/unfolding', function (assert) { + QUnit.test('basic folding/unfolding', async function (assert) { assert.expect(7); var rpcCount = 0; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -420,12 +420,12 @@ QUnit.module('Views', { "should have 3 rows: 1 for the opened header, and 2 for data"); // click on the opened header to close it - testUtils.dom.click(pivot.$('.o_pivot_header_cell_opened')); + await testUtils.dom.click(pivot.$('.o_pivot_header_cell_opened')); assert.containsOnce(pivot, 'tbody tr', "should have 1 row"); // click on closed header to open dropdown - testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); assert.containsOnce(pivot, '.o_pivot_field_menu .dropdown-item[data-field="date"]:first', "should have the date field as proposition"); @@ -434,8 +434,7 @@ QUnit.module('Views', { assert.containsNone(pivot, '.o_field_selection .dropdown-item[data-field="non_stored_m2o"]:first', "should not have the non_stored_m2o field as proposition"); - - testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"]:first')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"]:first')); assert.containsN(pivot, 'tbody tr', 4, "should have 4 rows: one for header, 3 for data"); @@ -444,10 +443,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('more folding/unfolding', function (assert) { + QUnit.test('more folding/unfolding', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -458,25 +457,26 @@ QUnit.module('Views', { }); // open dropdown to zoom into first row - testUtils.dom.clickFirst(pivot.$('tbody .o_pivot_header_cell_closed')); + await testUtils.dom.clickFirst(pivot.$('tbody .o_pivot_header_cell_closed')); // click on date by day pivot.$('.dropdown-menu.show .o_inline_dropdown .dropdown-menu').toggle(); // unfold inline dropdown - testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"][data-interval="day"]')); + await testUtils.nextTick(); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"][data-interval="day"]')); // open dropdown to zoom into second row - testUtils.dom.clickLast(pivot.$('tbody td.o_pivot_header_cell_closed')); + await testUtils.dom.clickLast(pivot.$('tbody td.o_pivot_header_cell_closed')); assert.containsN(pivot, 'tbody tr', 7, "should have 7 rows (1 for total, 1 for xphone, 1 for xpad, 4 for data)"); pivot.destroy(); }); - QUnit.test('pivot view can be flipped', function (assert) { + QUnit.test('pivot view can be flipped', async function (assert) { assert.expect(3); var rpcCount = 0; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -493,7 +493,7 @@ QUnit.module('Views', { "should have 3 rows: 1 for the open header, and 2 for data"); rpcCount = 0; - testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button')); + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button')); assert.strictEqual(rpcCount, 0, "should not have done any rpc"); assert.containsOnce(pivot, 'tbody tr', @@ -501,12 +501,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('can toggle extra measure', function (assert) { + QUnit.test('can toggle extra measure', async function (assert) { assert.expect(8); var rpcCount = 0; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -526,8 +526,8 @@ QUnit.module('Views', { "the __count measure should not be selected"); rpcCount = 0; - testUtils.pivot.toggleMeasuresDropdown(pivot); - testUtils.pivot.clickMeasure(pivot, '__count'); + await testUtils.pivot.toggleMeasuresDropdown(pivot); + await testUtils.pivot.clickMeasure(pivot, '__count'); assert.hasClass(pivot.$buttons.find('.dropdown-item[data-field=__count]:first'),'selected', "the __count measure should be selected"); @@ -536,7 +536,7 @@ QUnit.module('Views', { assert.strictEqual(rpcCount, 2, "should have done 2 rpcs to reload data"); - testUtils.pivot.clickMeasure(pivot, '__count'); + await testUtils.pivot.clickMeasure(pivot, '__count'); assert.doesNotHaveClass(pivot.$buttons.find('.dropdown-item[data-field=__count]:first'), 'selected', "the __count measure should not be selected"); @@ -548,10 +548,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('no content helper when no active measure', function (assert) { + QUnit.test('no content helper when no active measure', async function (assert) { assert.expect(4); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -564,8 +564,8 @@ QUnit.module('Views', { assert.containsOnce(pivot, 'table', "should have a table in DOM"); - testUtils.pivot.toggleMeasuresDropdown(pivot); - testUtils.pivot.clickMeasure(pivot, '__count'); + await testUtils.pivot.toggleMeasuresDropdown(pivot); + await testUtils.pivot.clickMeasure(pivot, '__count'); assert.containsOnce(pivot, '.o_view_nocontent', "should have a no_content_helper"); @@ -574,10 +574,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('no content helper when no data', function (assert) { + QUnit.test('no content helper when no data', async function (assert) { assert.expect(4); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -590,7 +590,7 @@ QUnit.module('Views', { assert.containsOnce(pivot, 'table', "should have a table in DOM"); - testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); + await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); assert.containsOnce(pivot, '.o_view_nocontent', "should have a no_content_helper"); @@ -599,12 +599,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('no content helper when no data, part 2', function (assert) { + QUnit.test('no content helper when no data, part 2', async function (assert) { assert.expect(1); this.data.partner.records = []; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -616,10 +616,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('no content helper when no data, part 3', function (assert) { + QUnit.test('no content helper when no data, part 3', async function (assert) { assert.expect(4); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -631,27 +631,27 @@ QUnit.module('Views', { assert.containsOnce(pivot, '.o_view_nocontent', "should have a no_content_helper"); - testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); + await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); assert.containsOnce(pivot, '.o_view_nocontent', "should still have a no_content_helper"); - testUtils.pivot.reload(pivot, {domain: []}); + await testUtils.pivot.reload(pivot, {domain: []}); assert.containsNone(pivot, '.o_view_nocontent', "should not have a no_content_helper"); // tries to open a field selection menu, to make sure it was not // removed from the dom. - testUtils.dom.clickFirst(pivot.$('.o_pivot_header_cell_closed')); + await testUtils.dom.clickFirst(pivot.$('.o_pivot_header_cell_closed')); assert.containsOnce(pivot, '.o_pivot_field_menu', "the field selector menu exists"); pivot.destroy(); }); - QUnit.test('tries to restore previous state after domain change', function (assert) { + QUnit.test('tries to restore previous state after domain change', async function (assert) { assert.expect(5); var rpcCount = 0; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -670,10 +670,10 @@ QUnit.module('Views', { assert.strictEqual(pivot.$('.o_pivot_measure_row:contains(Foo)').length, 1, "should have 1 row for measure Foo"); - testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); + await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); rpcCount = 0; - testUtils.pivot.reload(pivot, {domain: []}); + await testUtils.pivot.reload(pivot, {domain: []}); assert.equal(rpcCount, 2, "should have reloaded data"); assert.containsN(pivot, '.o_pivot_cell_value', 3, @@ -683,10 +683,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('can be grouped with the update function', function (assert) { + QUnit.test('can be grouped with the update function', async function (assert) { assert.expect(4); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -700,7 +700,7 @@ QUnit.module('Views', { assert.containsOnce(pivot, 'tbody tr', "should have 1 rows"); - testUtils.pivot.reload(pivot, {groupBy: ['product_id']}); + await testUtils.pivot.reload(pivot, {groupBy: ['product_id']}); assert.containsN(pivot, '.o_pivot_cell_value', 3, "should have 3 cells"); @@ -709,10 +709,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('can sort data in a column by clicking on header', function (assert) { + QUnit.test('can sort data in a column by clicking on header', async function (assert) { assert.expect(3); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -725,12 +725,12 @@ QUnit.module('Views', { assert.strictEqual($('td.o_pivot_cell_value').text(), "321220", "should have proper values in cells (total, result 1, result 2"); - testUtils.dom.click(pivot.$('th.o_pivot_measure_row')); + await testUtils.dom.click(pivot.$('th.o_pivot_measure_row')); assert.strictEqual($('td.o_pivot_cell_value').text(), "322012", "should have proper values in cells (total, result 2, result 1"); - testUtils.dom.click(pivot.$('th.o_pivot_measure_row')); + await testUtils.dom.click(pivot.$('th.o_pivot_measure_row')); assert.strictEqual($('td.o_pivot_cell_value').text(), "321220", "should have proper values in cells (total, result 1, result 2"); @@ -738,11 +738,11 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('can expand all rows', function (assert) { + QUnit.test('can expand all rows', async function (assert) { assert.expect(7); var nbReadGroups = 0; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -764,22 +764,22 @@ QUnit.module('Views', { // expand on date:days, product nbReadGroups = 0; - testUtils.pivot.reload(pivot, {groupBy: ['date:days', 'product_id']}); + await testUtils.pivot.reload(pivot, {groupBy: ['date:days', 'product_id']}); assert.strictEqual(nbReadGroups, 3, "should have done 3 read_group RPCS"); assert.containsN(pivot, 'tbody tr', 8, "should have 7 rows (total + 3 for December and 2 for October and April)"); // collapse the last two rows - testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened')); - testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened')); + await testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened')); + await testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened')); assert.containsN(pivot, 'tbody tr', 6, "should have 6 rows now"); // expand all nbReadGroups = 0; - testUtils.dom.click(pivot.$buttons.find('.o_pivot_expand_button')); + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_expand_button')); assert.strictEqual(nbReadGroups, 3, "should have done 3 read_group RPCS"); assert.containsN(pivot, 'tbody tr', 8, @@ -788,11 +788,11 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('expand all with a delay', function (assert) { + QUnit.test('expand all with a delay', async function (assert) { assert.expect(3); var def; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -803,40 +803,40 @@ QUnit.module('Views', { mockRPC: function (route, args) { var result = this._super.apply(this, arguments); if (args.method === 'read_group') { - return $.when(def).then(_.constant(result)); + return Promise.resolve(def).then(_.constant(result)); } return result; }, }); // expand on date:days, product - testUtils.pivot.reload(pivot, {groupBy: ['date:days', 'product_id']}); + await testUtils.pivot.reload(pivot, {groupBy: ['date:days', 'product_id']}); assert.containsN(pivot, 'tbody tr', 8, "should have 7 rows (total + 3 for December and 2 for October and April)"); // collapse the last two rows - testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened')); - testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened')); + await testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened')); + await testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened')); assert.containsN(pivot, 'tbody tr', 6, "should have 6 rows now"); // expand all - def = $.Deferred(); - testUtils.dom.click(pivot.$buttons.find('.o_pivot_expand_button')); + def = testUtils.makeTestPromise(); + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_expand_button')); def.resolve(); - + await testUtils.nextTick(); assert.containsN(pivot, 'tbody tr', 8, "should have 8 rows again"); pivot.destroy(); }); - QUnit.test('can download a file', function (assert) { + QUnit.test('can download a file', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -853,16 +853,16 @@ QUnit.module('Views', { }, }); - testUtils.dom.click(pivot.$buttons.find('.o_pivot_download')); + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_download')); pivot.destroy(); }); - QUnit.test('download button is disabled when there is no data', function (assert) { + QUnit.test('download button is disabled when there is no data', async function (assert) { assert.expect(1); this.data.partner.records = []; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -877,10 +877,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('getOwnedQueryParams correctly returns measures and groupbys', function (assert) { + QUnit.test('getOwnedQueryParams correctly returns measures and groupbys', async function (assert) { assert.expect(3); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -899,8 +899,8 @@ QUnit.module('Views', { }, "context should be correct"); // expand header on field customer - testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed:nth(1)')); - testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="customer"]:first')); + await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed:nth(1)')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="customer"]:first')); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { pivot_column_groupby: ['date:day', 'customer'], @@ -910,8 +910,8 @@ QUnit.module('Views', { }, "context should be correct"); // expand row on field product_id - testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); - testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first')); + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first')); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { pivot_column_groupby: ['date:day', 'customer'], @@ -923,13 +923,13 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('correctly remove pivot_ keys from the context', function (assert) { + QUnit.test('correctly remove pivot_ keys from the context', async function (assert) { assert.expect(5); this.data.partner.fields.amount = {string: "Amount", type: "float"}; // Equivalent to loading with default filter - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -961,7 +961,7 @@ QUnit.module('Views', { }, "context should be correct"); // Let's get rid of the rows groupBy - pivot.$('tbody .o_pivot_header_cell_opened').click(); + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_opened')); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { @@ -972,7 +972,7 @@ QUnit.module('Views', { }, "context should be correct"); // And now, get rid of the col groupby - pivot.$('thead .o_pivot_header_cell_opened').click(); + await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_opened')); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { @@ -982,8 +982,8 @@ QUnit.module('Views', { }, }, "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(); + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); + await testUtils.dom.click( pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first')); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { @@ -993,8 +993,8 @@ QUnit.module('Views', { }, }, "context should be correct"); - pivot.$('thead .o_pivot_header_cell_closed').click(); - pivot.$('.o_pivot_field_menu .dropdown-item[data-field="customer"]:first').click(); + await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="customer"]:first')); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { @@ -1007,10 +1007,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('Unload Filter, reset display, load another filter', function (assert) { + QUnit.test('Unload Filter, reset display, load another filter', async function (assert) { assert.expect(18); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1046,10 +1046,10 @@ QUnit.module('Views', { var reloadParams = { context: {}, }; - pivot.reload(reloadParams); + await pivot.reload(reloadParams); // collapse all headers - pivot.$('.o_pivot_header_cell_opened').click(); - pivot.$('.o_pivot_header_cell_opened').click(); + await testUtils.dom.click(pivot.$('.o_pivot_header_cell_opened:first')); + await testUtils.dom.click(pivot.$('.o_pivot_header_cell_opened')); // Check Columns assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed').length, 1, @@ -1075,7 +1075,7 @@ QUnit.module('Views', { pivot_row_groupby: ['product_id'], }, }; - pivot.reload(reloadParams); + await pivot.reload(reloadParams); // Check Columns assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1, @@ -1096,12 +1096,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('correctly uses pivot_ keys from the context', function (assert) { + QUnit.test('correctly uses pivot_ keys from the context', async function (assert) { assert.expect(7); this.data.partner.fields.amount = {string: "Amount", type: "float"}; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1138,12 +1138,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('correctly uses pivot_ keys from the context (at reload)', function (assert) { + QUnit.test('correctly uses pivot_ keys from the context (at reload)', async function (assert) { assert.expect(8); this.data.partner.fields.amount = {string: "Amount", type: "float"}; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1163,7 +1163,7 @@ QUnit.module('Views', { pivot_row_groupby: ['product_id'], }, }; - testUtils.pivot.reload(pivot, reloadParams); + await testUtils.pivot.reload(pivot, reloadParams); assert.containsOnce(pivot, 'thead .o_pivot_header_cell_opened', "column: should have one opened header"); @@ -1185,10 +1185,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('correctly use group_by key from the context', function (assert) { + QUnit.test('correctly use group_by key from the context', async function (assert) { assert.expect(7); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: 'partner', data: this.data, @@ -1219,12 +1219,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot still handles __count__ measure', function (assert) { + QUnit.test('pivot still handles __count__ measure', async function (assert) { // for retro-compatibility reasons, the pivot view still handles // '__count__' measure. assert.expect(2); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1249,10 +1249,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('not use a many2one as a measure by default', function (assert) { + QUnit.test('not use a many2one as a measure by default', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1266,10 +1266,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('use a many2one as a measure with specified additional measure', function (assert) { + QUnit.test('use a many2one as a measure with specified additional measure', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1286,10 +1286,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot view with many2one field as a measure', function (assert) { + QUnit.test('pivot view with many2one field as a measure', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1304,10 +1304,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('m2o as measure, drilling down into data', function (assert) { + QUnit.test('m2o as measure, drilling down into data', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1315,20 +1315,20 @@ QUnit.module('Views', { '<field name="product_id" type="measure"/>' + '</pivot>', }); - testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); // click on date by month pivot.$('.dropdown-menu.show .o_inline_dropdown .dropdown-menu').toggle(); // unfold inline dropdown - testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"][data-interval="month"]')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"][data-interval="month"]')); assert.strictEqual(pivot.$('.o_pivot_cell_value').text(), '2211', 'should have loaded the proper data'); pivot.destroy(); }); - QUnit.test('pivot view with same many2one field as a measure and grouped by', function (assert) { + QUnit.test('pivot view with same many2one field as a measure and grouped by', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1340,17 +1340,17 @@ QUnit.module('Views', { }, }); - testUtils.pivot.toggleMeasuresDropdown(pivot); - testUtils.pivot.clickMeasure(pivot, 'product_id'); + await testUtils.pivot.toggleMeasuresDropdown(pivot); + await testUtils.pivot.clickMeasure(pivot, 'product_id'); assert.strictEqual(pivot.$('.o_pivot_cell_value').text(), '421131', 'should have loaded the proper data'); pivot.destroy(); }); - QUnit.test('pivot view with same many2one field as a measure and grouped by (and drill down)', function (assert) { + QUnit.test('pivot view with same many2one field as a measure and grouped by (and drill down)', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1359,19 +1359,19 @@ QUnit.module('Views', { '</pivot>', }); - testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); - testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first')); assert.strictEqual(pivot.$('.o_pivot_cell_value').text(), '211', 'should have loaded the proper data'); pivot.destroy(); }); - QUnit.test('Row and column groupbys plus a domain', function (assert) { + QUnit.test('Row and column groupbys plus a domain', async function (assert) { assert.expect(3); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1381,15 +1381,15 @@ QUnit.module('Views', { }); // Set a column groupby - testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - testUtils.dom.click(pivot.$('.o_field_selection .dropdown-item[data-field=customer]:first')); + await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('.o_field_selection .dropdown-item[data-field=customer]:first')); // Set a Row groupby - testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); - testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]:first')); + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]:first')); // Set a domain - testUtils.pivot.reload(pivot, {domain: [['product_id', '=', 41]]}); + await testUtils.pivot.reload(pivot, {domain: [['product_id', '=', 41]]}); var expectedContext = { context: { @@ -1413,12 +1413,12 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('parallel data loading should discard all but the last one', function (assert) { + QUnit.test('parallel data loading should discard all but the last one', async function (assert) { assert.expect(2); var def; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1428,17 +1428,17 @@ QUnit.module('Views', { mockRPC: function (route, args) { var result = this._super.apply(this, arguments); if (args.method === 'read_group') { - return $.when(def).then(_.constant(result)); + return Promise.resolve(def).then(_.constant(result)); } return result; }, }); - def = $.Deferred(); + def = testUtils.makeTestPromise(); testUtils.pivot.reload(pivot, {groupBy: ['product_id']}); testUtils.pivot.reload(pivot, {groupBy: ['product_id', 'customer']}); - def.resolve(); - + await def.resolve(); + await testUtils.nextTick(); assert.containsN(pivot, '.o_pivot_cell_value', 6, "should have 6 cells"); assert.containsN(pivot, 'tbody tr', 6, @@ -1446,7 +1446,7 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot measures should be alphabetically sorted', function (assert) { + QUnit.test('pivot measures should be alphabetically sorted', async function (assert) { assert.expect(2); var data = this.data; @@ -1456,7 +1456,7 @@ QUnit.module('Views', { data.partner.fields.modd = {string: "modd", type: "integer"}; data.partner.fields.zip = {string: "Zip", type: "integer"}; - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: data, @@ -1475,10 +1475,10 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('pivot view should use default order for auto sorting', function (assert) { + QUnit.test('pivot view should use default order for auto sorting', async function (assert) { assert.expect(1); - var pivot = createView({ + var pivot = await createView({ View: PivotView, model: "partner", data: this.data, @@ -1493,7 +1493,7 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('rendering of pivot view with comparison', function (assert) { + QUnit.test('rendering of pivot view with comparison', async function (assert) { assert.expect(91); this.data.partner.records[0].date = '2016-12-15'; @@ -1522,7 +1522,7 @@ QUnit.module('Views', { } // create an action manager to test the interactions with the search view - var actionManager = createActionManager({ + var actionManager = await createActionManager({ data: this.data, archs: { 'partner,false,pivot': '<pivot>' + @@ -1533,7 +1533,7 @@ QUnit.module('Views', { } }); - actionManager.doAction({ + await actionManager.doAction({ res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'pivot']], @@ -1547,17 +1547,17 @@ QUnit.module('Views', { // with no data - testUtils.dom.click($('.o_time_range_menu_button')); - testUtils.dom.click($('.o_time_range_menu .o_comparison_checkbox')); + await testUtils.dom.click($('.o_time_range_menu_button')); + await testUtils.dom.click($('.o_time_range_menu .o_comparison_checkbox')); $('.o_time_range_selector').val('today'); - testUtils.dom.click($('.o_time_range_menu .o_apply_range')); + await testUtils.dom.click($('.o_time_range_menu .o_apply_range')); assert.strictEqual($('.o_pivot p.o_view_nocontent_empty_folder').length, 1); // with data, no row groupby - testUtils.dom.click($('.o_time_range_menu_button')); + await testUtils.dom.click($('.o_time_range_menu_button')); $('.o_time_range_selector').val('this_month'); - testUtils.dom.click($('.o_time_range_menu .o_apply_range')); + await testUtils.dom.click($('.o_time_range_menu .o_apply_range')); results = [ "13", "0", "100%", "0", "19", "-100%", "13", "19", "-31.58%" ]; @@ -1565,8 +1565,8 @@ QUnit.module('Views', { // with data, with row groupby - testUtils.dom.click($('.o_pivot .o_pivot_header_cell_closed').eq(2)); - testUtils.dom.click($('.o_pivot .o_field_selection a[data-field="product_id"]')); + await testUtils.dom.click($('.o_pivot .o_pivot_header_cell_closed').eq(2)); + await testUtils.dom.click($('.o_pivot .o_field_selection a[data-field="product_id"]')); results = [ "13", "0", "100%", "0", "19", "-100%", "13", "19", "-31.58%" , "12", "0", "100%", "12", "0" , "100%" , @@ -1574,9 +1574,9 @@ QUnit.module('Views', { ]; checkCellValues(results); - testUtils.pivot.toggleMeasuresDropdown(actionManager.getCurrentController().widget); - testUtils.dom.click($('.o_control_panel div.o_pivot_measures_list a[data-field="foo"]')); - testUtils.dom.click($('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"]')); + await testUtils.pivot.toggleMeasuresDropdown(actionManager.getCurrentController().widget); + await testUtils.dom.click($('.o_control_panel div.o_pivot_measures_list a[data-field="foo"]')); + await testUtils.dom.click($('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"]')); results = [ "2", "0", "100%", "0", "1", "-100%", "2", "1", "100%" , "1", "0", "100%", "1", "0" , "100%" , @@ -1584,8 +1584,8 @@ QUnit.module('Views', { ]; checkCellValues(results); - testUtils.dom.click($('.o_control_panel div.o_pivot_measures_list a[data-field="__count"]')); - testUtils.dom.click($('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"]')); + await testUtils.dom.click($('.o_control_panel div.o_pivot_measures_list a[data-field="__count"]')); + await testUtils.dom.click($('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"]')); results = [ "2", "0", "100%", "0", "2", "-100%", "2", "2", "0%" , "1", "0", "100%", "1", "0" , "100%" , @@ -1593,7 +1593,7 @@ QUnit.module('Views', { ]; checkCellValues(results); - testUtils.dom.clickFirst($('.o_pivot .o_pivot_header_cell_opened')); + await testUtils.dom.clickFirst($('.o_pivot .o_pivot_header_cell_opened')); results = [ "2", "2", "0%" , "1", "0", "100%" , @@ -1611,7 +1611,7 @@ QUnit.module('Views', { actionManager.destroy(); }); - QUnit.test('export data in excel with comparison', function (assert) { + QUnit.test('export data in excel with comparison', async function (assert) { assert.expect(10); this.data.partner.records[0].date = '2016-12-15'; @@ -1622,7 +1622,7 @@ QUnit.module('Views', { var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); // create an action manager to test the interactions with the search view - var actionManager = createActionManager({ + var actionManager = await createActionManager({ data: this.data, archs: { 'partner,false,pivot': '<pivot>' + @@ -1635,11 +1635,14 @@ QUnit.module('Views', { get_file: function (args) { var data = JSON.parse(args.data.data); _.each(data.headers, function (l) { - assert.step(l.map(function (o) {return o.title;})); + var titles = l.map(function (o) {return o.title;}); + assert.step(JSON.stringify(titles)); }); - assert.step(data.measure_row.map(function (o) {return o.measure;})); - assert.step(data.nbr_measures); - assert.step(data.rows.map(function (o) {return o.values.length;})); + var measures = data.measure_row.map(function (o) {return o.measure;}); + assert.step(JSON.stringify(measures)); + assert.step(String(data.nbr_measures)); + var valuesLength = data.rows.map(function (o) {return o.values.length;}); + assert.step(JSON.stringify(valuesLength)); assert.strictEqual(args.url, '/web/pivot/export_xls', "should call get_file with correct parameters"); args.complete(); @@ -1647,20 +1650,20 @@ QUnit.module('Views', { }, }); - actionManager.doAction({ + await actionManager.doAction({ res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'pivot']], }); // open time range menu - testUtils.dom.click($('.o_control_panel .o_time_range_menu_button')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu_button')); // select 'Today' as range $('.o_control_panel .o_time_range_selector').val('today'); // check checkbox 'Compare To' - testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_comparison_checkbox')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_comparison_checkbox')); // Click on 'Apply' button - testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range')); // the time range menu configuration is by now: Date, Today, checkbox checked, Previous Period // With the data above, the time ranges contain no record. @@ -1670,38 +1673,35 @@ QUnit.module('Views', { assert.ok($('.o_control_panel button.o_pivot_download').prop('disabled')); // open time range menu - testUtils.dom.click($('.o_control_panel .o_time_range_menu_button')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu_button')); // select 'This Month' as date range $('.o_control_panel .o_time_range_selector').val('this_month'); // Click on 'Apply' button - testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range')); // the time range menu configuration is by now: Date, This Month, checkbox checked, Previous Period // With the data above, the time ranges contain some records. // export data. Should execute 'get_file' - testUtils.dom.click($('.o_control_panel button.o_pivot_download')); + await testUtils.dom.click($('.o_control_panel button.o_pivot_download')); assert.verifySteps([ // Headers - ["Total", ""], - ["December 2016" , "November 2016"], - ["Foo", "Foo", "Foo"], - [ - "This Month", "Previous Period", "Variation", - "This Month", "Previous Period", "Variation", - "This Month", "Previous Period", "Variation" - ], + '["Total",""]', + '["December 2016","November 2016"]', + '["Foo","Foo","Foo"]', + '["This Month","Previous Period","Variation","This Month","Previous Period"' + + ',"Variation","This Month","Previous Period","Variation"]', // number of 'measures' - 3, + '3', // rows values length - [9] + '[9]', ]); unpatchDate(); actionManager.destroy(); }); - QUnit.test('rendering of pivot view with comparison and count measure', function (assert) { + QUnit.test('rendering of pivot view with comparison and count measure', async function (assert) { assert.expect(10); var mockMock = false; @@ -1715,7 +1715,7 @@ QUnit.module('Views', { var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); // create an action manager to test the interactions with the search view - var actionManager = createActionManager({ + var actionManager = await createActionManager({ data: this.data, archs: { 'partner,false,pivot': '<pivot>' + @@ -1731,14 +1731,14 @@ QUnit.module('Views', { // this modification is necessary because mockReadGroup does not // properly reflect the server response when there is no record // and a groupby list of length at least one. - return $.when([{}]); + return Promise.resolve([{}]); } } return result; }, }); - actionManager.doAction({ + await actionManager.doAction({ res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'pivot']], @@ -1747,10 +1747,10 @@ QUnit.module('Views', { mockMock = true; // activate 'This Month' and 'Previous Period' in time range menu - testUtils.dom.click($('.o_control_panel .o_time_range_menu_button')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu_button')); $('.o_control_panel .o_time_range_selector').val('this_month'); - testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_comparison_checkbox')); - testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_comparison_checkbox')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range')); var results = [ "4", "0", "100%", @@ -1768,7 +1768,7 @@ QUnit.module('Views', { }); QUnit.module('Sort in comparison mode', { - beforeEach: function () { + beforeEach: async function () { this.data.partner.records[0].date = '2016-12-15'; this.data.partner.records[1].date = '2016-12-17'; this.data.partner.records[2].date = '2016-11-22'; @@ -1792,7 +1792,7 @@ QUnit.module('Views', { }; // create an action manager to test the interactions with the search view - this.actionManager = createActionManager({ + this.actionManager = await createActionManager({ data: this.data, archs: { 'partner,false,pivot': '<pivot>' + @@ -1802,10 +1802,9 @@ QUnit.module('Views', { '</pivot>', 'partner,false,search': '<search></search>', }, - debug: 1, }); - this.actionManager.doAction({ + await this.actionManager.doAction({ res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'pivot']], @@ -1817,13 +1816,13 @@ QUnit.module('Views', { }); // open time range menu - testUtils.dom.click($('.o_control_panel .o_time_range_menu_button')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu_button')); // select 'Today' as range - testUtils.fields.editInput($('.o_control_panel .o_time_range_selector'), 'this_month'); + await testUtils.fields.editInput($('.o_control_panel .o_time_range_selector'), 'this_month'); // check checkbox 'Compare To' - testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_comparison_checkbox')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_comparison_checkbox')); // Click on 'Apply' button - testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range')); + await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range')); // We are in comparison mode }, afterEach: function () { @@ -1831,11 +1830,11 @@ QUnit.module('Views', { this.actionManager.destroy(); }, }, function () { - QUnit.test('when clicking on measure, sort by "This Month" (period of interest)', function (assert) { + QUnit.test('when clicking on measure, sort by "This Month" (period of interest)', async function (assert) { assert.expect(1); // click on 'Foo' in column Total/Company - testUtils.dom.click($('.o_pivot_measure_row').eq(0)); + await testUtils.dom.click($('.o_pivot_measure_row').eq(0)); var results = [ "12", "17", "-29.41%", "1", "2", "-50%", "13", "19", "-31.58%", "12", "0", "100%", "12", "0" , "100%", @@ -1849,11 +1848,11 @@ QUnit.module('Views', { QUnit.test( 'click on a period of interest header sort according to the appropriate column ' + 'first in ascending order then in descending order', - function (assert) { + async function (assert) { assert.expect(2); // click on 'This Month' in column Total/Individual/Foo - testUtils.dom.click($('.o_pivot_measure_row').eq(6)); + await testUtils.dom.click($('.o_pivot_measure_row').eq(6)); var results = [ "12", "17", "-29.41%", "1", "2", "-50%", "13", "19", "-31.58%", "1", "0", "100%", "1" , "0", "100%", @@ -1865,7 +1864,7 @@ QUnit.module('Views', { assert.strictEqual(actualResult, results.join('')); // click once again on 'This Month' in column Total/Individual/Foo - testUtils.dom.click($('.o_pivot_measure_row').eq(6)); + await testUtils.dom.click($('.o_pivot_measure_row').eq(6)); results = [ "12", "17", "-29.41%", "1", "2", "-50%", "13", "19", "-31.58%", "12", "0", "100%", "12", "0" , "100%", @@ -1877,11 +1876,11 @@ QUnit.module('Views', { }); QUnit.test('click on a period of comparison header sort according to appropriate column', - function (assert) { + async function (assert) { assert.expect(1); // click on 'Previous Period' in column Total/Individual/Foo - testUtils.dom.click($('.o_pivot_measure_row').eq(7)); + await testUtils.dom.click($('.o_pivot_measure_row').eq(7)); var results = [ "12", "17", "-29.41%", "1", "2", "-50%", "13", "19", "-31.58%", "0", "2", "-100%", "0" , "2" , "-100%", @@ -1893,11 +1892,11 @@ QUnit.module('Views', { }); QUnit.test('click on a variation header sort according to appropriate column', - function (assert) { + async function (assert) { assert.expect(1); // click on 'Variation' in column Total/Individual/Foo - testUtils.dom.click($('.o_pivot_measure_row').eq(11)); + await testUtils.dom.click($('.o_pivot_measure_row').eq(11)); var results = [ "12", "17", "-29.41%", "1", "2", "-50%", "13", "19", "-31.58%", "12", "0", "100%", "12", "0" , "100%", diff --git a/addons/web/static/tests/views/qweb_tests.js b/addons/web/static/tests/views/qweb_tests.js index e9b7398fd09..c7872690e49 100644 --- a/addons/web/static/tests/views/qweb_tests.js +++ b/addons/web/static/tests/views/qweb_tests.js @@ -8,9 +8,9 @@ QUnit.module("Views", { }, function () { QUnit.module("QWeb"); - QUnit.test("basic", function (assert) { - assert.expect(13); - var am = utils.createActionManager({ + QUnit.test("basic", async function (assert) { + assert.expect(14); + var am = await utils.createActionManager({ data: { test: { fields: {}, @@ -27,7 +27,7 @@ QUnit.module("Views", { case 'test.qweb_render_view': assert.step('fetch'); assert.equal(args.kwargs.view_id, 5); - return $.when( + return Promise.resolve( '<div>foo' + '<div data-model="test" data-method="wheee" data-id="42" data-other="5">' + '<a type="toggle" class="fa fa-caret-right">Unfold</a>' + @@ -38,7 +38,7 @@ QUnit.module("Views", { assert.step('unfold'); assert.deepEqual(args.args, [42]); assert.deepEqual(args.kwargs, {other: 5}); - return $.when('<div id="sub">ok</div>'); + return Promise.resolve('<div id="sub">ok</div>'); } } return this._super.apply(this, arguments); @@ -51,15 +51,18 @@ QUnit.module("Views", { views: [[false, 'qweb']], res_model: 'test', }).then(function () { resolved = true; }); - assert.ok(resolved, "Action should have resolved synchronously"); + assert.ok(!resolved, "Action cannot be resolved synchronously"); + + await utils.nextTick(); + assert.ok(resolved, "Action is resolved asynchronously"); var $content = am.$('.o_content'); assert.ok(/^\s*foo/.test($content.text())); - utils.dom.click($content.find('[type=toggle]')); + await utils.dom.click($content.find('[type=toggle]')); assert.equal($content.find('div#sub').text(), 'ok', 'should have unfolded the sub-item'); - utils.dom.click($content.find('[type=toggle]')); + await utils.dom.click($content.find('[type=toggle]')); assert.equal($content.find('div#sub').length, 0, "should have removed the sub-item"); - utils.dom.click($content.find('[type=toggle]')); + await utils.dom.click($content.find('[type=toggle]')); assert.verifySteps(['fetch', 'unfold', 'unfold']); } finally { diff --git a/addons/web/static/tests/views/search_filter_menu_tests.js b/addons/web/static/tests/views/search_filter_menu_tests.js index 92d49cc23ae..62cfaccdc93 100644 --- a/addons/web/static/tests/views/search_filter_menu_tests.js +++ b/addons/web/static/tests/views/search_filter_menu_tests.js @@ -11,8 +11,9 @@ function createFilterMenu(filters, fields, params) { var target = params.debug ? document.body : $('#qunit-fixture'); var menu = new FilterMenu(null, filters, fields); testUtils.mock.addMockEnvironment(menu, params); - menu.appendTo(target); - return menu; + return menu.appendTo(target).then(function() { + return menu; + }); } QUnit.module('FilterMenu', { @@ -33,11 +34,11 @@ QUnit.module('FilterMenu', { }, }, function () { - QUnit.test('simple rendering with no filter', function (assert) { + QUnit.test('simple rendering with no filter', async function (assert) { assert.expect(3); - var filterMenu = createFilterMenu([], this.fields); - testUtils.dom.click(filterMenu.$('span.fa-filter')); + var filterMenu = await createFilterMenu([], this.fields); + await testUtils.dom.click(filterMenu.$('span.fa-filter')); assert.containsNone(filterMenu, '.dropdown-divider'); assert.containsNone(filterMenu, '.o_add_filter_menu'); assert.containsOnce(filterMenu, '.o_add_custom_filter', @@ -45,24 +46,24 @@ QUnit.module('FilterMenu', { filterMenu.destroy(); }); - QUnit.test('simple rendering with a filter', function (assert) { + QUnit.test('simple rendering with a filter', async function (assert) { assert.expect(2); - var filterMenu = createFilterMenu(this.filters, this.fields); + var filterMenu = await createFilterMenu(this.filters, this.fields); assert.containsN(filterMenu, '.dropdown-divider, .dropdown-item, .o_add_custom_filter', 4, 'should have 4 elements: a hidden separator, a filter, a separator, a add custom filter item'); assert.containsOnce(filterMenu, '.o_add_custom_filter'); filterMenu.destroy(); }); - QUnit.test('click on add custom filter opens the submenu', function (assert) { + QUnit.test('click on add custom filter opens the submenu', async function (assert) { assert.expect(3); - var filterMenu = createFilterMenu([], this.fields); + var filterMenu = await createFilterMenu([], this.fields); // open menu dropdown - testUtils.dom.click(filterMenu.$('span.fa-filter')); + await testUtils.dom.click(filterMenu.$('span.fa-filter')); // open add custom filter submenu - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); assert.containsNone(filterMenu, '.dropdown-divider'); assert.isVisible(filterMenu.$('.o_add_filter_menu')); assert.containsN(filterMenu, '.dropdown-item, .dropdown-item-text', 3, @@ -71,16 +72,16 @@ QUnit.module('FilterMenu', { filterMenu.destroy(); }); - QUnit.test('removing last prop disable the apply button', function (assert) { + QUnit.test('removing last prop disable the apply button', async function (assert) { assert.expect(2); - var filterMenu = createFilterMenu([], this.fields); + var filterMenu = await createFilterMenu([], this.fields); // open menu dropdown and custom filter submenu - testUtils.dom.click(filterMenu.$('span.fa-filter')); - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.dom.click(filterMenu.$('span.fa-filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); // remove the current unique proposition - testUtils.dom.click(filterMenu.$('.o_searchview_extended_delete_prop')); + await testUtils.dom.click(filterMenu.$('.o_searchview_extended_delete_prop')); assert.containsNone(filterMenu, '.dropdown-divider'); assert.containsN(filterMenu, '.dropdown-item, .dropdown-item-text', 2, @@ -89,26 +90,26 @@ QUnit.module('FilterMenu', { filterMenu.destroy(); }); - QUnit.test('readding a proposition reenable apply button', function (assert) { + QUnit.test('readding a proposition reenable apply button', async function (assert) { assert.expect(1); - var filterMenu = createFilterMenu([], this.fields); + var filterMenu = await createFilterMenu([], this.fields); // open menu dropdown and custom filter submenu, remove existing prop - testUtils.dom.click(filterMenu.$('span.fa-filter')); - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); - testUtils.dom.click(filterMenu.$('.o_searchview_extended_delete_prop')); + await testUtils.dom.click(filterMenu.$('span.fa-filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.dom.click(filterMenu.$('.o_searchview_extended_delete_prop')); // read a proposition - testUtils.dom.click(filterMenu.$('.o_add_condition')); + await testUtils.dom.click(filterMenu.$('.o_add_condition')); assert.ok(!filterMenu.$('.o_apply_filter').attr('disabled')); filterMenu.destroy(); }); - QUnit.test('adding a simple filter works', function (assert) { + QUnit.test('adding a simple filter works', async function (assert) { assert.expect(6); delete this.fields.date_field; - var filterMenu = createFilterMenu([], this.fields, { + var filterMenu = await createFilterMenu([], this.fields, { intercepts: { new_filters: function (ev) { var filter = ev.data.filters[0]; @@ -125,17 +126,17 @@ QUnit.module('FilterMenu', { }, }); // open menu dropdown and custom filter submenu, remove existing prop - testUtils.dom.click(filterMenu.$('span.fa-filter')); - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.dom.click(filterMenu.$('span.fa-filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); // click on apply to activate filter - testUtils.dom.click(filterMenu.$('.o_apply_filter')); + await testUtils.dom.click(filterMenu.$('.o_apply_filter')); assert.containsNone(filterMenu, '.o_filter_condition'); assert.containsN(filterMenu, '.dropdown-divider', 2); assert.isNotVisible(filterMenu.$('.dropdown-divider').eq(0)); filterMenu.destroy(); }); - QUnit.test('commit search with an extended proposition with field char does not cause a crash', function (assert) { + QUnit.test('commit search with an extended proposition with field char does not cause a crash', async function (assert) { assert.expect(6); this.fields = {many2one_field: {string: "Trululu", type: "many2one", relation: 'partner', selectable: true, searchable: true}}; @@ -149,7 +150,7 @@ QUnit.module('FilterMenu', { [['many2one_field','ilike', `\\`]], ]; - var filterMenu = createFilterMenu([], this.fields, { + var filterMenu = await createFilterMenu([], this.fields, { intercepts: { new_filters: function (ev) { var filter = ev.data.filters[0]; @@ -162,42 +163,42 @@ QUnit.module('FilterMenu', { }); // open menu dropdown and custom filter submenu, select trululu field and enter string `a`, then click apply - testUtils.dom.click(filterMenu.$('span.fa-filter')); - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); - testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); - testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `a`); - testUtils.dom.click(filterMenu.$('.o_apply_filter')); + await testUtils.dom.click(filterMenu.$('span.fa-filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); + await testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `a`); + await testUtils.dom.click(filterMenu.$('.o_apply_filter')); // open custom filter submenu, select trululu field and enter string `"a"`, then click apply - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); - testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); - testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `"a"`); - testUtils.dom.click(filterMenu.$('.o_apply_filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); + await testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `"a"`); + await testUtils.dom.click(filterMenu.$('.o_apply_filter')); // open custom filter submenu, select trululu field and enter string `'a'`, then click apply - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); - testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); - testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `'a'`); - testUtils.dom.click(filterMenu.$('.o_apply_filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); + await testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `'a'`); + await testUtils.dom.click(filterMenu.$('.o_apply_filter')); // open custom filter submenu, select trululu field and enter string `'`, then click apply - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); - testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); - testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `'`); - testUtils.dom.click(filterMenu.$('.o_apply_filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); + await testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `'`); + await testUtils.dom.click(filterMenu.$('.o_apply_filter')); // open custom filter submenu, select trululu field and enter string `"`, then click apply - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); - testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); - testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `"`); - testUtils.dom.click(filterMenu.$('.o_apply_filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); + await testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `"`); + await testUtils.dom.click(filterMenu.$('.o_apply_filter')); // open custom filter submenu, select trululu field and enter string `\`, then click apply - testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); - testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); - testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `\\`); - testUtils.dom.click(filterMenu.$('.o_apply_filter')); + await testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); + await testUtils.fields.editSelect(filterMenu.$('.o_filter_condition select.o_input.o_searchview_extended_prop_field'), 'many2one_field'); + await testUtils.fields.editInput(filterMenu.$('.o_filter_condition .o_searchview_extended_prop_value input'), `\\`); + await testUtils.dom.click(filterMenu.$('.o_apply_filter')); filterMenu.destroy(); }); diff --git a/addons/web/static/tests/views/search_groupby_menu_tests.js b/addons/web/static/tests/views/search_groupby_menu_tests.js index efeb74103d5..b25eae37e8f 100644 --- a/addons/web/static/tests/views/search_groupby_menu_tests.js +++ b/addons/web/static/tests/views/search_groupby_menu_tests.js @@ -11,8 +11,9 @@ function createGroupByMenu(groupBys, fields, params) { var target = params.debug ? document.body : $('#qunit-fixture'); var menu = new GroupByMenu(null, groupBys, fields); testUtils.mock.addMockEnvironment(menu, params); - menu.appendTo(target); - return menu; + return menu.appendTo(target).then(function() { + return menu; + }); } QUnit.module('GroupByMenu', { @@ -30,10 +31,10 @@ QUnit.module('GroupByMenu', { }, }, function () { - QUnit.test('simple rendering with no filter and no field', function (assert) { + QUnit.test('simple rendering with no filter and no field', async function (assert) { assert.expect(1); - var groupByMenu = createGroupByMenu([], {}); + var groupByMenu = await createGroupByMenu([], {}); // open groupBy menu testUtils.dom.click(groupByMenu.$('button:first')); assert.containsNone(groupByMenu, '.dropdown-divider, .dropdown-item, .dropdown-item-text', @@ -42,13 +43,12 @@ QUnit.module('GroupByMenu', { groupByMenu.destroy(); }); - QUnit.test('simple rendering', function (assert) { + QUnit.test('simple rendering', async function (assert) { assert.expect(2); - var groupByMenu = createGroupByMenu(this.groupBys, this.fields); + var groupByMenu = await createGroupByMenu(this.groupBys, this.fields); // open groupBy menu - testUtils.dom.click(groupByMenu.$('button:first')); - groupByMenu.$('button:first').click(); + await testUtils.dom.click(groupByMenu.$('button:first')); assert.containsN(groupByMenu, '.dropdown-divider, .dropdown-item', 4); assert.strictEqual(groupByMenu.$('.o_menu_item').text().trim(), 'some group by', 'should have proper filter name'); @@ -56,39 +56,40 @@ QUnit.module('GroupByMenu', { groupByMenu.destroy(); }); - QUnit.test('simple rendering with no filter but fields', function (assert) { + QUnit.test('simple rendering with no filter but fields', async function (assert) { assert.expect(1); - var groupByMenu = createGroupByMenu( + var groupByMenu = await createGroupByMenu( [], {fieldName: {sortable: true, string: 'Super Date', type: 'date'}} ); - testUtils.dom.click(groupByMenu.$('button:first')); + await testUtils.nextTick(); + await testUtils.dom.click(groupByMenu.$('button:first')); assert.containsOnce(groupByMenu, '.dropdown-divider, .dropdown-item, .dropdown-item-text', 'should have 1 element'); groupByMenu.destroy(); }); - QUnit.test('click on add custom group toggle group selector', function (assert) { + QUnit.test('click on add custom group toggle group selector', async function (assert) { assert.expect(2); - var groupByMenu = createGroupByMenu([], + var groupByMenu = await createGroupByMenu([], {fieldName: {sortable: true, string: 'Super Date', type: 'date'}} ); - testUtils.dom.click(groupByMenu.$('button:first')); + await testUtils.dom.click(groupByMenu.$('button:first')); var selector = groupByMenu.$('select.o_group_selector'); assert.ok(!selector.is(":visible"), 'should be invisible'); - testUtils.dom.click(groupByMenu.$('.o_add_custom_group')); + await testUtils.dom.click(groupByMenu.$('.o_add_custom_group')); selector = groupByMenu.$('select.o_group_selector'); assert.ok(selector.is(":visible"), 'should be visible'); groupByMenu.destroy(); }); - QUnit.test('select a groupBy of no date type in Add Custom Group menu add properly that groupBy to menu', function (assert) { + QUnit.test('select a groupBy of no date type in Add Custom Group menu add properly that groupBy to menu', async function (assert) { assert.expect(6); - var groupByMenu = createGroupByMenu( + var groupByMenu = await createGroupByMenu( [], { fieldName: {sortable: true, name: 'candlelight', string: 'Candlelight', type: 'boolean'}, @@ -109,23 +110,24 @@ QUnit.module('GroupByMenu', { }, } ); - testUtils.dom.click(groupByMenu.$('button:first')); - testUtils.dom.click(groupByMenu.$('.o_add_custom_group')); + await testUtils.nextTick(); + await testUtils.dom.click(groupByMenu.$('button:first')); + await testUtils.dom.click(groupByMenu.$('.o_add_custom_group')); assert.strictEqual(groupByMenu.$('select').val(), 'fieldName', 'the select value should be "fieldName"'); - testUtils.dom.click(groupByMenu.$('button.o_apply_group')); + await testUtils.dom.click(groupByMenu.$('button.o_apply_group')); assert.containsOnce(groupByMenu, '.o_menu_item > .dropdown-item.selected', 'there should be a groupby selected'); groupByMenu.destroy(); }); - QUnit.test('select a groupBy of date type in Add Custom Group menu add properly that groupBy to menu', function (assert) { + QUnit.test('select a groupBy of date type in Add Custom Group menu add properly that groupBy to menu', async function (assert) { assert.expect(13); INTERVAL_OPTIONS = INTERVAL_OPTIONS.map(function (option) { return _.extend(option, {description: option.description.toString()}); }); - var groupByMenu = createGroupByMenu( + var groupByMenu = await createGroupByMenu( [], this.fields, { @@ -153,20 +155,20 @@ QUnit.module('GroupByMenu', { } ); // open groupBy menu - testUtils.dom.click(groupByMenu.$('button:first')); + await testUtils.dom.click(groupByMenu.$('button:first')); // open Add Custom Group submenu - testUtils.dom.click(groupByMenu.$('.o_add_custom_group')); + await testUtils.dom.click(groupByMenu.$('.o_add_custom_group')); // select fieldName assert.strictEqual(groupByMenu.$('select').val(), 'fieldName', 'the select value should be "fieldName"'); // create new groupBy of type date - groupByMenu.$('button.o_apply_group').click(); + await testUtils.dom.click(groupByMenu.$('button.o_apply_group')); assert.strictEqual(groupByMenu.$('.o_menu_item > .dropdown-item.selected').length, 1, 'there should be a groupby selected'); assert.strictEqual(groupByMenu.$('.o_menu_item .o_submenu_switcher').length, 1, 'there should be options available'); // open options submenu - groupByMenu.$('.o_menu_item .o_submenu_switcher').click(); + await testUtils.dom.click(groupByMenu.$('.o_menu_item .o_submenu_switcher')); assert.strictEqual(groupByMenu.$('.o_item_option').length, 5, 'there should be five options available'); assert.strictEqual(groupByMenu.$('.o_add_custom_group').length, 0, diff --git a/addons/web/static/tests/views/search_panel_tests.js b/addons/web/static/tests/views/search_panel_tests.js index 9814a887b39..f564ccc0d6b 100644 --- a/addons/web/static/tests/views/search_panel_tests.js +++ b/addons/web/static/tests/views/search_panel_tests.js @@ -59,10 +59,10 @@ QUnit.module('Views', { QUnit.module('SearchPanel in Kanban views'); - QUnit.test('basic rendering', function (assert) { + QUnit.test('basic rendering', async function (assert) { assert.expect(17); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -115,10 +115,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('sections with custom icon and color', function (assert) { + QUnit.test('sections with custom icon and color', async function (assert) { assert.expect(4); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -144,12 +144,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('sections with attr invisible="1" are ignored', function (assert) { + QUnit.test('sections with attr invisible="1" are ignored', async function (assert) { // 'groups' attributes are converted server-side into invisible="1" when the user doesn't // belong to the given group assert.expect(4); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -181,10 +181,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('categories and filters order is kept', function (assert) { + QUnit.test('categories and filters order is kept', async function (assert) { assert.expect(4); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -214,10 +214,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('specify active category value in context', function (assert) { + QUnit.test('specify active category value in context', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -248,10 +248,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('use category (on many2one) to refine search', function (assert) { + QUnit.test('use category (on many2one) to refine search', async function (assert) { assert.expect(14); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -276,21 +276,21 @@ QUnit.module('Views', { }); // select 'asustek' - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2); // select 'agrolait' - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1); // select 'All' - testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:first .active'); @@ -306,10 +306,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('use category (on selection) to refine search', function (assert) { + QUnit.test('use category (on selection) to refine search', async function (assert) { assert.expect(14); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -333,21 +333,21 @@ QUnit.module('Views', { }); // select 'abc' - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1); // select 'ghi' - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(3) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(3) header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(3) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2); // select 'All' again - testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:first .active'); @@ -363,7 +363,7 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('store and retrieve active category value', function (assert) { + QUnit.test('store and retrieve active category value', async function (assert) { assert.expect(8); var Storage = RamStorage.extend({ @@ -380,7 +380,7 @@ QUnit.module('Views', { }); var expectedActiveId = 3; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -412,7 +412,7 @@ QUnit.module('Views', { // select 'agrolait' expectedActiveId = 5; - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header')); assert.verifySteps([ 'getItem searchpanel_partner_company_id', @@ -422,8 +422,8 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('retrieved category value does not exist', function (assert) { - assert.expect(5); + QUnit.test('retrieved category value does not exist', async function (assert) { + assert.expect(6); var Storage = RamStorage.extend({ getItem: function (key) { @@ -435,7 +435,7 @@ QUnit.module('Views', { storage: new Storage(), }); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -465,13 +465,15 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_search_panel_category_value:first .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 4); + assert.verifySteps(['getItem searchpanel_partner_company_id']); + kanban.destroy(); }); - QUnit.test('use two categories to refine search', function (assert) { + QUnit.test('use two categories to refine search', async function (assert) { assert.expect(14); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -500,23 +502,23 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3); // select 'asustek' - testUtils.dom.click(kanban.$('.o_search_panel_category_value[data-id=3] header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value[data-id=3] header')); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2); // select 'abc' - testUtils.dom.click(kanban.$('.o_search_panel_category_value[data-id=abc] header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value[data-id=abc] header')); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1); // select 'ghi' - testUtils.dom.click(kanban.$('.o_search_panel_category_value[data-id=ghi] header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value[data-id=ghi] header')); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1); // select 'All' in first category (company_id) - testUtils.dom.click(kanban.$('.o_search_panel_section:first .o_search_panel_category_value:first header')); + await testUtils.dom.click(kanban.$('.o_search_panel_section:first .o_search_panel_category_value:first header')); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1); // select 'All' in second category (state) - testUtils.dom.click(kanban.$('.o_search_panel_section:nth(1) .o_search_panel_category_value:first header')); + await testUtils.dom.click(kanban.$('.o_search_panel_section:nth(1) .o_search_panel_category_value:first header')); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3); assert.verifySteps([ @@ -531,14 +533,14 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('category with parent_field', function (assert) { + QUnit.test('category with parent_field', async function (assert) { assert.expect(24); this.data.company.records.push({id: 40, name: 'child company 1', parent_id: 5}); this.data.company.records.push({id: 41, name: 'child company 2', parent_id: 5}); this.data.partner.records[1].company_id = 40; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -569,7 +571,7 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_search_panel_category_value .o_toggle_fold'); // unfold parent category - testUtils.dom.click(kanban.$('.o_search_panel_category_value .o_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value .o_toggle_fold')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:first .active'); @@ -578,21 +580,21 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_search_panel_category_value .o_search_panel_category_value', 2); // click on first child company - testUtils.dom.click(kanban.$('.o_search_panel_category_value .o_search_panel_category_value:first header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value .o_search_panel_category_value:first header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value .o_search_panel_category_value:first .active'); assert.containsOnce(kanban, '.o_kanban_record:not(.o_kanban_ghost)'); // click on parent company - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) > header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) > header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1); // fold category with children - testUtils.dom.click(kanban.$('.o_search_panel_category_value .o_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value .o_toggle_fold')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active'); @@ -608,14 +610,14 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('can (un)fold parent category values', function (assert) { + QUnit.test('can (un)fold parent category values', async function (assert) { assert.expect(7); this.data.company.records.push({id: 40, name: 'child company 1', parent_id: 5}); this.data.company.records.push({id: 41, name: 'child company 2', parent_id: 5}); this.data.partner.records[1].company_id = 40; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -639,13 +641,13 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_search_panel_category_value', 3); // unfold agrolait - testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold')); assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold'), 'fa-caret-down', "'agrolait' should be open"); assert.containsN(kanban, '.o_search_panel_category_value', 5); // fold agrolait - testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold')); assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold'), 'fa-caret-left', "'agrolait' should be folded"); assert.containsN(kanban, '.o_search_panel_category_value', 3); @@ -653,14 +655,14 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('fold status is kept at reload', function (assert) { + QUnit.test('fold status is kept at reload', async function (assert) { assert.expect(4); this.data.company.records.push({id: 40, name: 'child company 1', parent_id: 5}); this.data.company.records.push({id: 41, name: 'child company 2', parent_id: 5}); this.data.partner.records[1].company_id = 40; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -678,12 +680,12 @@ QUnit.module('Views', { }); // unfold agrolait - testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold')); assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold'), 'fa-caret-down', "'agrolait' should be open"); assert.containsN(kanban, '.o_search_panel_category_value', 5); - kanban.reload({}); + await kanban.reload({}); assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold'), 'fa-caret-down', "'agrolait' should be open"); @@ -692,11 +694,11 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('concurrency: delayed search_reads', function (assert) { + QUnit.test('concurrency: delayed search_reads', async function (assert) { assert.expect(19); var def; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -704,7 +706,7 @@ QUnit.module('Views', { var result = this._super.apply(this, arguments); if (route === '/web/dataset/search_read') { assert.step(JSON.stringify(args.domain)); - return $.when(def).then(_.constant(result)); + return Promise.resolve(def).then(_.constant(result)); } return result; }, @@ -728,9 +730,9 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3); // select 'asustek' (delay the reload) - def = $.Deferred(); + def = testUtils.makeTestPromise(); var asustekDef = def; - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); // 'asustek' should be selected, but there should still be 3 records assert.containsOnce(kanban, '.o_search_panel_category_value .active'); @@ -738,9 +740,9 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3); // select 'agrolait' (delay the reload) - def = $.Deferred(); + def = testUtils.makeTestPromise(); var agrolaitDef = def; - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header')); // 'agrolait' should be selected, but there should still be 3 records assert.containsOnce(kanban, '.o_search_panel_category_value .active'); @@ -749,12 +751,14 @@ QUnit.module('Views', { // unlock asustek search (should be ignored, so there should still be 3 records) asustekDef.resolve(); + await testUtils.nextTick(); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3); // unlock agrolait search, there should now be 1 record agrolaitDef.resolve(); + await testUtils.nextTick(); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1); @@ -768,18 +772,18 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('concurrency: misordered get_filters', function (assert) { + QUnit.test('concurrency: misordered get_filters', async function (assert) { assert.expect(15); var def; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, mockRPC: function (route, args) { var result = this._super.apply(this, arguments); if (args.method === 'search_panel_select_multi_range') { - return $.when(def).then(_.constant(result)); + return Promise.resolve(def).then(_.constant(result)); } return result; }, @@ -803,9 +807,9 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 4); // select 'abc' (delay the reload) - def = $.Deferred(); + def = testUtils.makeTestPromise(); var abcDef = def; - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); // 'All' should still be selected, and there should still be 4 records assert.containsOnce(kanban, '.o_search_panel_category_value .active'); @@ -813,9 +817,9 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 4); // select 'ghi' (delay the reload) - def = $.Deferred(); + def = testUtils.makeTestPromise(); var ghiDef = def; - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(3) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(3) header')); // 'All' should still be selected, and there should still be 4 records assert.containsOnce(kanban, '.o_search_panel_category_value .active'); @@ -824,12 +828,14 @@ QUnit.module('Views', { // unlock ghi search ghiDef.resolve(); + await testUtils.nextTick(); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(3) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2); // unlock abc search (should be ignored) abcDef.resolve(); + await testUtils.nextTick(); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(3) .active'); assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2); @@ -837,18 +843,18 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('concurrency: delayed get_filter', function (assert) { + QUnit.test('concurrency: delayed get_filter', async function (assert) { assert.expect(3); var def; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, mockRPC: function (route, args) { var result = this._super.apply(this, arguments); if (args.method === 'search_panel_select_multi_range') { - return $.when(def).then(_.constant(result)); + return Promise.resolve(def).then(_.constant(result)); } return result; }, @@ -868,22 +874,24 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 4); // trigger a reload and delay the get_filter - def = $.Deferred(); + def = testUtils.makeTestPromise(); kanban.reload({domain: [['id', '=', 1]]}); + await testUtils.nextTick(); assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 4); def.resolve(); + await testUtils.nextTick(); assert.containsOnce(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)'); kanban.destroy(); }); - QUnit.test('use filter (on many2one) to refine search', function (assert) { + QUnit.test('use filter (on many2one) to refine search', async function (assert) { assert.expect(32); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -927,7 +935,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3); // check 'asustek' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked'); assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''), @@ -935,7 +943,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 2); // check 'agrolait' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2); assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''), @@ -943,7 +951,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3); // uncheck 'asustek' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked'); assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''), @@ -951,7 +959,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 1); // uncheck 'agrolait' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); assert.containsNone(kanban, '.o_search_panel_filter_value input:checked'); assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''), @@ -963,26 +971,26 @@ QUnit.module('Views', { '[]', '[["bar","=",true]]', // 'asustek' checked - '[["bar","=",true],["company_id","in",[3]]]', '[["company_id","in",[3]]]', + '[["bar","=",true],["company_id","in",[3]]]', // 'asustek' and 'agrolait' checked - '[["bar","=",true],["company_id","in",[3,5]]]', '[["company_id","in",[3,5]]]', + '[["bar","=",true],["company_id","in",[3,5]]]', // 'agrolait' checked - '[["bar","=",true],["company_id","in",[5]]]', '[["company_id","in",[5]]]', + '[["bar","=",true],["company_id","in",[5]]]', // nothing checked - '[["bar","=",true]]', '[]', + '[["bar","=",true]]', ]); kanban.destroy(); }); - QUnit.test('use filter (on selection) to refine search', function (assert) { + QUnit.test('use filter (on selection) to refine search', async function (assert) { assert.expect(32); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1026,7 +1034,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3); // check 'abc' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked'); assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''), @@ -1034,7 +1042,7 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 1); // check 'def' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2); assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''), @@ -1042,7 +1050,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 2); // uncheck 'abc' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked'); assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''), @@ -1050,7 +1058,7 @@ QUnit.module('Views', { assert.containsOnce(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)'); // uncheck 'def' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); assert.containsNone(kanban, '.o_search_panel_filter_value input:checked'); assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''), @@ -1062,26 +1070,26 @@ QUnit.module('Views', { '[]', '[["bar","=",true]]', // 'asustek' checked - '[["bar","=",true],["state","in",["abc"]]]', '[["state","in",["abc"]]]', + '[["bar","=",true],["state","in",["abc"]]]', // 'asustek' and 'agrolait' checked - '[["bar","=",true],["state","in",["abc","def"]]]', '[["state","in",["abc","def"]]]', + '[["bar","=",true],["state","in",["abc","def"]]]', // 'agrolait' checked - '[["bar","=",true],["state","in",["def"]]]', '[["state","in",["def"]]]', + '[["bar","=",true],["state","in",["def"]]]', // nothing checked - '[["bar","=",true]]', '[]', + '[["bar","=",true]]', ]); kanban.destroy(); }); - QUnit.test('only reload filters when domains change', function (assert) { + QUnit.test('only reload filters when domains change', async function (assert) { assert.expect(11); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1112,47 +1120,37 @@ QUnit.module('Views', { ]); // go to page 2 (the domain doesn't change, so the filters should not be reloaded) - testUtils.dom.click(kanban.$('.o_pager_next')); + await testUtils.dom.click(kanban.$('.o_pager_next')); assert.verifySteps([ - 'search_panel_select_multi_range', - '/web/dataset/search_read', '/web/dataset/search_read', ]); // reload with another domain, so the filters should be reloaded - kanban.reload({domain: [['id', '<', 5]]}); + await kanban.reload({domain: [['id', '<', 5]]}); assert.verifySteps([ 'search_panel_select_multi_range', '/web/dataset/search_read', - '/web/dataset/search_read', - '/web/dataset/search_read', - 'search_panel_select_multi_range', ]); // change category value, so the filters should be reloaded - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); assert.verifySteps([ 'search_panel_select_multi_range', '/web/dataset/search_read', - '/web/dataset/search_read', - '/web/dataset/search_read', - 'search_panel_select_multi_range', - '/web/dataset/search_read', - 'search_panel_select_multi_range', ]); kanban.destroy(); }); - QUnit.test('filter with groupby', function (assert) { + QUnit.test('filter with groupby', async function (assert) { assert.expect(42); this.data.company.records.push({id: 11, name: 'camptocamp', category_id: 7}); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1200,7 +1198,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3); // check 'asustek' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input')); assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked'); var firstGroupCheckbox = kanban.$('.o_search_panel_filter_group:first > div > input').get(0); @@ -1211,7 +1209,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 2); // check 'agrolait' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2); var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0); @@ -1224,7 +1222,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 0); // check 'camptocamp' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(2) input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(2) input')); assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 3); secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0); @@ -1237,7 +1235,7 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 0); // uncheck second group - testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) > div > input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) > div > input')); assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked'); secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0); @@ -1254,28 +1252,28 @@ QUnit.module('Views', { '[]', '[["bar","=",true]]', // 'asustek' checked - '[["bar","=",true],["company_id","in",[3]]]', '[["company_id","in",[3]]]', + '[["bar","=",true],["company_id","in",[3]]]', // 'asustek' and 'agrolait' checked - '[["bar","=",true],["company_id","in",[3]],["company_id","in",[5]]]', '[["company_id","in",[3]],["company_id","in",[5]]]', + '[["bar","=",true],["company_id","in",[3]],["company_id","in",[5]]]', // 'asustek', 'agrolait' and 'camptocamp' checked - '[["bar","=",true],["company_id","in",[3]],["company_id","in",[5,11]]]', '[["company_id","in",[3]],["company_id","in",[5,11]]]', + '[["bar","=",true],["company_id","in",[3]],["company_id","in",[5,11]]]', // 'asustek' checked - '[["bar","=",true],["company_id","in",[3]]]', '[["company_id","in",[3]]]', + '[["bar","=",true],["company_id","in",[3]]]', ]); kanban.destroy(); }); - QUnit.test('filter with domain', function (assert) { + QUnit.test('filter with domain', async function (assert) { assert.expect(3); this.data.company.records.push({id: 40, name: 'child company 1', parent_id: 3}); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1313,12 +1311,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('(un)fold filter group', function (assert) { + QUnit.test('(un)fold filter group', async function (assert) { assert.expect(13); this.data.company.records.push({id: 11, name: 'camptocamp', category_id: 7}); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1340,13 +1338,13 @@ QUnit.module('Views', { assert.containsN(kanban, '.o_search_panel_filter_value', 3); // check 'agrolait' - testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input')); var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0); assert.strictEqual(secondGroupCheckbox.indeterminate, true); assert.hasAttrValue(kanban.$('.o_search_panel_filter_value:nth(1) input'), 'checked', 'checked'); // fold second group - testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) .o_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) .o_toggle_fold')); assert.containsN(kanban, '.o_search_panel_filter_group', 2); assert.containsOnce(kanban, '.o_search_panel_filter_value'); @@ -1354,13 +1352,13 @@ QUnit.module('Views', { assert.strictEqual(secondGroupCheckbox.indeterminate, true); // fold first group - testUtils.dom.click(kanban.$('.o_search_panel_filter_group:first .o_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:first .o_toggle_fold')); assert.containsN(kanban, '.o_search_panel_filter_group', 2); assert.containsNone(kanban, '.o_search_panel_filter_value'); // unfold second group - testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) .o_toggle_fold')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) .o_toggle_fold')); assert.containsN(kanban, '.o_search_panel_filter_group', 2); assert.containsN(kanban, '.o_search_panel_filter_value', 2); @@ -1371,10 +1369,10 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('filter with domain depending on category', function (assert) { + QUnit.test('filter with domain depending on category', async function (assert) { assert.expect(22); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1408,7 +1406,7 @@ QUnit.module('Views', { }); // select 'gold' category - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header')); assert.containsOnce(kanban, '.o_search_panel_category_value .active'); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active'); @@ -1417,7 +1415,7 @@ QUnit.module('Views', { "asustek1"); // select 'silver' category - testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header')); assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active'); assert.containsOnce(kanban, '.o_search_panel_filter_value'); @@ -1425,7 +1423,7 @@ QUnit.module('Views', { "agrolait2"); // select All - testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header')); + await testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header')); assert.containsOnce(kanban, '.o_search_panel_category_value:first .active'); assert.containsNone(kanban, '.o_search_panel_filter_value'); @@ -1444,14 +1442,14 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('specify active filter values in context', function (assert) { + QUnit.test('specify active filter values in context', async function (assert) { assert.expect(4); var expectedDomain = [ ['company_id', 'in', [5]], ['state', 'in', ['abc', 'ghi']], ]; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1483,17 +1481,17 @@ QUnit.module('Views', { // manually untick a default value expectedDomain = [['state', 'in', ['abc', 'ghi']]]; - testUtils.dom.click(kanban.$('.o_search_panel_filter:first .o_search_panel_filter_value:nth(1) input')); + await testUtils.dom.click(kanban.$('.o_search_panel_filter:first .o_search_panel_filter_value:nth(1) input')); assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2); kanban.destroy(); }); - QUnit.test('retrieved filter value from context does not exist', function (assert) { + QUnit.test('retrieved filter value from context does not exist', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -1522,12 +1520,12 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.test('filter with groupby and default values in context', function (assert) { + QUnit.test('filter with groupby and default values in context', async function (assert) { assert.expect(2); this.data.company.records.push({id: 11, name: 'camptocamp', category_id: 7}); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, diff --git a/addons/web/static/tests/views/search_view_tests.js b/addons/web/static/tests/views/search_view_tests.js index 4916834e1bc..e57194ef19c 100644 --- a/addons/web/static/tests/views/search_view_tests.js +++ b/addons/web/static/tests/views/search_view_tests.js @@ -266,34 +266,34 @@ QUnit.module('Search View', { ]; }, }, function () { - QUnit.test('basic rendering', function (assert) { + QUnit.test('basic rendering', async function (assert) { assert.expect(1); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(1); + await actionManager.doAction(1); assert.strictEqual($('.o_searchview input.o_searchview_input')[0], document.activeElement, "searchview input should be focused"); actionManager.destroy(); }); - QUnit.test('navigation with facets', function (assert) { + QUnit.test('navigation with facets', async function (assert) { assert.expect(4); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(1); + await actionManager.doAction(1); // add a facet - testUtils.dom.click(actionManager.$('.o_dropdown_toggler_btn:contains(Group By)')); - testUtils.dom.click(actionManager.$('.o_menu_item a')); + await testUtils.dom.click(actionManager.$('.o_dropdown_toggler_btn:contains(Group By)')); + await testUtils.dom.click(actionManager.$('.o_menu_item a')); assert.strictEqual(actionManager.$('.o_searchview .o_searchview_facet').length, 1, "there should be one facet"); assert.strictEqual(actionManager.$('.o_searchview input.o_searchview_input')[0], document.activeElement, @@ -320,110 +320,111 @@ QUnit.module('Search View', { QUnit.module('GroupByMenu'); - QUnit.test('click on groupby filter adds a facet', function (assert) { + QUnit.test('click on groupby filter adds a facet', async function (assert) { assert.expect(1); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(1); - testUtils.dom.click($('.o_dropdown_toggler_btn:contains(Group By)')); - testUtils.dom.click($('.o_menu_item a')); + + await actionManager.doAction(1); + await testUtils.dom.click($('.o_dropdown_toggler_btn:contains(Group By)')); + await testUtils.dom.click($('.o_menu_item a')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').text().trim(), 'candle', 'should have a facet with candle name'); actionManager.destroy(); }); - QUnit.test('remove a "Group By" facet properly unchecks groupbys in groupby menu', function (assert) { + QUnit.test('remove a "Group By" facet properly unchecks groupbys in groupby menu', async function (assert) { assert.expect(2); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(1); - testUtils.dom.click($('.o_dropdown_toggler_btn:contains(Group By)')); - testUtils.dom.click($('.o_menu_item a')); + await actionManager.doAction(1); + await testUtils.dom.click($('.o_dropdown_toggler_btn:contains(Group By)')); + await testUtils.dom.click($('.o_menu_item a')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').text().trim(), 'candle', 'should have a facet with candle name'); - testUtils.dom.click($('.o_facet_remove:first')); + await testUtils.dom.click($('.o_facet_remove:first')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').length, 0, 'there should be no facet'); actionManager.destroy(); }); - QUnit.test('change option of a "Group By" does not remove groupy in facet "Group By"', function (assert) { + QUnit.test('change option of a "Group By" does not remove groupy in facet "Group By"', async function (assert) { assert.expect(3); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(2); - testUtils.dom.click($('span.fa-bars')); - testUtils.dom.click($('.o_submenu_switcher')); + await actionManager.doAction(2); + await testUtils.dom.click($('span.fa-bars')); + await testUtils.dom.click($('.o_submenu_switcher')); // Don't forget there is a hidden li.divider element at first place among children - testUtils.dom.click($('.o_item_option:nth-child(2)')); + await testUtils.dom.click($('.o_item_option:nth-child(2)')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').length, 1, 'should have a facet'); - testUtils.dom.click($('.o_item_option:nth-child(3)')); + await testUtils.dom.click($('.o_item_option:nth-child(3)')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').length, 1, 'should have a facet'); - testUtils.dom.click($('.o_item_option:nth-child(3)')); + await testUtils.dom.click($('.o_item_option:nth-child(3)')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').length, 0, 'should have no facet'); actionManager.destroy(); }); - QUnit.test('select and unselect quickly groupby does not crash', function (assert) { + QUnit.test('select and unselect quickly groupby does not crash', async function (assert) { assert.expect(1); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(3); - testUtils.dom.click($('span.fa-bars')); - testUtils.dom.click($('.o_menu_item:first')); - testUtils.dom.click($('.o_menu_item:first')); + await actionManager.doAction(3); + await testUtils.dom.click($('span.fa-bars')); + await testUtils.dom.click($('.o_menu_item:first')); + await testUtils.dom.click($('.o_menu_item:first')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').length, 0, 'should have a facet'); actionManager.destroy(); }); - QUnit.test('groupby selected within graph subview are not deleted when modifying search view content', function (assert) { + QUnit.test('groupby selected within graph subview are not deleted when modifying search view content', async function (assert) { assert.expect(2); this.actions[3].flags = {isEmbedded: true}; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(4); - testUtils.dom.click($('.o_graph_controller .o_control_panel .o_cp_buttons button').eq(1)); - testUtils.dom.click($('.o_graph_controller .o_group_by_menu .o_menu_item').eq(1)); - testUtils.dom.click($('.o_graph_controller .o_group_by_menu .o_menu_item .o_item_option > .dropdown-item').first()); + await actionManager.doAction(4); + await testUtils.dom.click($('.o_graph_controller .o_control_panel .o_cp_buttons button').eq(1)); + await testUtils.dom.click($('.o_graph_controller .o_group_by_menu .o_menu_item').eq(1)); + await testUtils.dom.click($('.o_graph_controller .o_group_by_menu .o_menu_item .o_item_option > .dropdown-item').first()); assert.doesNotHaveClass($('.o_graph_controller .o_group_by_menu .o_menu_item > .dropdown-item').eq(1), 'selected', 'groupby should be unselected'); - testUtils.dom.click($('.o_search_options button span.fa-filter')); - testUtils.dom.click($('.o_filters_menu .o_menu_item a').first()); + await testUtils.dom.click($('.o_search_options button span.fa-filter')); + await testUtils.dom.click($('.o_filters_menu .o_menu_item a').first()); assert.doesNotHaveClass($('.o_graph_controller .o_group_by_menu .o_menu_item > .dropdown-item').eq(1), 'selected', 'groupby should be still unselected'); actionManager.destroy(); }); - QUnit.test('group by a date field using interval works', function (assert) { + QUnit.test('group by a date field using interval works', async function (assert) { assert.expect(13); @@ -434,7 +435,7 @@ QUnit.module('Search View', { ["birthday:year"], ]; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -446,67 +447,67 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(5); + await actionManager.doAction(5); // open menu 'Group By' - testUtils.dom.click($('.o_search_options .fa-bars')); + await testUtils.dom.click($('.o_search_options .fa-bars')); // Activate the groupby 'Date' - testUtils.dom.click($('.o_group_by_menu .o_menu_item')); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item')); // // select option 'day' - testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option[data-option_id="day"]')); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option[data-option_id="day"]')); assert.strictEqual($('div.o_facet_values span').text().trim(),'Date: Day'); assert.strictEqual($('.o_content tr.o_group_header').length, 5); // // select option 'month' - testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option[data-option_id="month"]')); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option[data-option_id="month"]')); // // data should be grouped by the field 'Date' using the interval 'month' assert.strictEqual($('div.o_facet_values span').text().trim(),'Date: Month'); assert.strictEqual($('.o_content tr.o_group_header').length, 3); // // deactivate option 'month' - testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option[data-option_id="month"]')); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option[data-option_id="month"]')); // // no groupby is applied assert.strictEqual($('div.o_facet_values span').length, 0); // // open 'Add custom Groupby' menu - testUtils.dom.click($('.o_group_by_menu .o_add_custom_group')); + await testUtils.dom.click($('.o_group_by_menu .o_add_custom_group')); // // click on 'Apply' button - testUtils.dom.click($('.o_group_by_menu .o_generator_menu button')); + await testUtils.dom.click($('.o_group_by_menu .o_generator_menu button')); // // data should be grouped by the field 'Birthday' using the interval 'month' assert.strictEqual($('div.o_facet_values span').text().trim(),'Birthday: Month'); assert.strictEqual($('.o_content tr.o_group_header').length, 5); // // open submenu with interval options - testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_submenu_switcher').eq(1)); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_submenu_switcher').eq(1)); // // select option 'year' - testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option').eq(9)); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option').eq(9)); // // data should be grouped by the field 'Birthday' using the interval 'year' assert.strictEqual($('div.o_facet_values span').text().trim(),'Birthday: Year'); assert.strictEqual($('.o_content tr.o_group_header').length, 4); actionManager.destroy(); }); - QUnit.test('a separator in groupbys does not cause problems', function (assert) { + QUnit.test('a separator in groupbys does not cause problems', async function (assert) { assert.expect(6); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(7); + await actionManager.doAction(7); // open menu 'Group By' - testUtils.dom.click($('span.fa-bars')); + await testUtils.dom.click($('span.fa-bars')); // open options menu - testUtils.dom.click($('.o_group_by_menu .o_menu_item a:first')); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item a:first')); // activate groupby with 'day' option - testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option[data-option_id="day"]')); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item .o_item_option[data-option_id="day"]')); // activate the second groupby - testUtils.dom.click($('.o_group_by_menu .o_menu_item > a').eq(1)); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item > a').eq(1)); assert.strictEqual($('.o_group_by_menu .o_menu_item').length, 2); assert.strictEqual($('.o_group_by_menu .o_menu_item > .dropdown-item.selected').length, 2); // deactivate second groupby - testUtils.dom.click($('.o_group_by_menu .o_menu_item > a').eq(1)); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item > a').eq(1)); assert.hasClass($('.o_group_by_menu .o_menu_item > .dropdown-item').eq(0), 'selected'); assert.doesNotHaveClass($('.o_group_by_menu .o_menu_item > .dropdown-item').eq(1), 'selected'); // remove facet - testUtils.dom.click($('.o_facet_remove')); + await testUtils.dom.click($('.o_facet_remove')); assert.doesNotHaveClass($('.o_group_by_menu .o_menu_item > .dropdown-item').eq(0), 'selected'); assert.doesNotHaveClass($('.o_group_by_menu .o_menu_item > .dropdown-item').eq(1), 'selected'); actionManager.destroy(); @@ -514,22 +515,24 @@ QUnit.module('Search View', { QUnit.module('FilterMenu'); - QUnit.test('Search date and datetime fields. Support of timezones', function (assert) { + QUnit.test('Search date and datetime fields. Support of timezones', async function (assert) { assert.expect(4); this.data.partner.fields.birth_datetime = {string: "Birth DateTime", type: "datetime", store: true, sortable: true}; this.data.partner.records = this.data.partner.records.slice(0,-1); // exclude wrong date record - function stringToEvent ($element, string) { + async function stringToEvent ($element, string) { for (var i = 0; i < string.length; i++) { var keyAscii = string.charCodeAt(i); $element.val($element.val()+string[i]); + await testUtils.nextTick(); $element.trigger($.Event('keyup', {which: keyAscii, keyCode:keyAscii})); + await testUtils.nextTick(); } } var searchReadSequence = 0; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: [{ id: 11, name: 'Partners Action 11', @@ -571,31 +574,23 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(11); + await actionManager.doAction(11); // Date case var $autocomplete = $('.o_searchview_input'); - stringToEvent($autocomplete, '07/15/1983'); - - $autocomplete.trigger($.Event('keyup', { - which: $.ui.keyCode.ENTER, - keyCode: $.ui.keyCode.ENTER, - })); + await stringToEvent($autocomplete, '07/15/1983'); + await testUtils.fields.triggerKey('up', $autocomplete, 'enter'); assert.equal($('.o_searchview_facet .o_facet_values').text().trim(), '07/15/1983', 'The format of the date in the facet should be in locale'); // Close Facet - $('.o_searchview_facet .o_facet_remove').click(); + await testUtils.dom.click($('.o_searchview_facet .o_facet_remove')); // DateTime case $autocomplete = $('.o_searchview_input'); - stringToEvent($autocomplete, '07/15/1983 00:00:00'); - - $autocomplete.trigger($.Event('keyup', { - which: $.ui.keyCode.ENTER, - keyCode: $.ui.keyCode.ENTER, - })); + await stringToEvent($autocomplete, '07/15/1983 00:00:00'); + await testUtils.fields.triggerKey('up', $autocomplete, 'enter'); assert.equal($('.o_searchview_facet .o_facet_values').text().trim(), '07/15/1983 00:00:00', 'The format of the datetime in the facet should be in locale'); @@ -603,44 +598,44 @@ QUnit.module('Search View', { actionManager.destroy(); }); - QUnit.test('add a custom filter works', function (assert) { + QUnit.test('add a custom filter works', async function (assert) { assert.expect(1); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(1); - testUtils.dom.click($('span.fa-filter')); - testUtils.dom.click($('.o_add_custom_filter')); - testUtils.dom.click($('.o_apply_filter')); + await actionManager.doAction(1); + await testUtils.dom.click($('span.fa-filter')); + await testUtils.dom.click($('.o_add_custom_filter')); + await testUtils.dom.click($('.o_apply_filter')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').text().trim(), 'ID is \"0\"', 'should have a facet with candle name'); actionManager.destroy(); }); - QUnit.test('deactivate a new custom filter works', function (assert) { + QUnit.test('deactivate a new custom filter works', async function (assert) { assert.expect(1); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(1); - testUtils.dom.click($('span.fa-filter')); - testUtils.dom.click($('.o_add_custom_filter')); - testUtils.dom.click($('.o_apply_filter')); - testUtils.dom.click($('.o_menu_item')); + await actionManager.doAction(1); + await testUtils.dom.click($('span.fa-filter')); + await testUtils.dom.click($('.o_add_custom_filter')); + await testUtils.dom.click($('.o_apply_filter')); + await testUtils.dom.click($('.o_menu_item')); assert.strictEqual($('.o_searchview .o_searchview_facet .o_facet_values span').length, 0, 'no facet should be in the search view'); actionManager.destroy(); }); - QUnit.test('filter by a date field using period works', function (assert) { + QUnit.test('filter by a date field using period works', async function (assert) { assert.expect(15); var self = this; @@ -659,7 +654,7 @@ QUnit.module('Search View', { return new Date(2017,2,22); }; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -671,12 +666,12 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(5); + await actionManager.doAction(5); // open menu 'Filter' - testUtils.dom.click($('.o_search_options .fa-filter')); + await testUtils.dom.click($('.o_search_options .fa-filter')); // open menu options - testUtils.dom.click($('.o_menu_item')); + await testUtils.dom.click($('.o_menu_item')); var periodOptions = $('.o_menu_item .o_item_option').map(function () { return $(this).data('option_id'); @@ -685,33 +680,33 @@ QUnit.module('Search View', { assert.deepEqual(periodOptions, PERIOD_OPTIONS_IDS, "13 period options should be available:"); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_7_days"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_30_days"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_365_days"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_5_years"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="today"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="this_week"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="this_month"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="this_quarter"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="this_year"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="yesterday"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_week"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_month"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_quarter"]')); - testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_year"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_7_days"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_30_days"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_365_days"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_5_years"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="today"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="this_week"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="this_month"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="this_quarter"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="this_year"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="yesterday"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_week"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_month"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_quarter"]')); + await testUtils.dom.click($('.o_menu_item .o_item_option[data-option_id="last_year"]')); actionManager.destroy(); window.Date = RealDate; }); - QUnit.test('`context` key in <filter> is used', function (assert) { + QUnit.test('`context` key in <filter> is used', async function (assert) { assert.expect(3); this.archs['partner,4,search'] = '<search>'+ '<filter string="AAA" name="some_filter" context="{\'coucou_1\': 1}"></filter>' + '</search>'; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -723,11 +718,11 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(5); + await actionManager.doAction(5); // select filter - testUtils.dom.click($('.o_search_options .fa-filter')); - testUtils.dom.click($('.o_menu_item:contains(AAA)')); + await testUtils.dom.click($('.o_search_options .fa-filter')); + await testUtils.dom.click($('.o_menu_item:contains(AAA)')); assert.verifySteps([ "{}", @@ -739,10 +734,10 @@ QUnit.module('Search View', { QUnit.module('Favorites Menu'); - QUnit.test('dynamic filters are saved dynamic', function (assert) { + QUnit.test('dynamic filters are saved dynamic', async function (assert) { assert.expect(1); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -758,18 +753,18 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(6); - testUtils.dom.click($('span.fa-filter')); - testUtils.dom.click($('.o_filters_menu .o_menu_item a')); - testUtils.dom.click($('.o_item_option[data-option_id="today"]')); - testUtils.dom.click($('span.fa-star')); - testUtils.dom.click($('.o_favorites_menu .o_add_favorite')); - testUtils.fields.editInput($('div.o_favorite_name input'), 'name for favorite'); - testUtils.dom.click($('.o_favorites_menu .o_save_favorite button')); + await actionManager.doAction(6); + await testUtils.dom.click($('span.fa-filter')); + await testUtils.dom.click($('.o_filters_menu .o_menu_item a')); + await testUtils.dom.click($('.o_item_option[data-option_id="today"]')); + await testUtils.dom.click($('span.fa-star')); + await testUtils.dom.click($('.o_favorites_menu .o_add_favorite')); + await testUtils.fields.editInput($('div.o_favorite_name input'), 'name for favorite'); + await testUtils.dom.click($('.o_favorites_menu .o_save_favorite button')); actionManager.destroy(); }); - QUnit.test('save search filter in modal', function (assert) { + QUnit.test('save search filter in modal', async function (assert) { assert.expect(5); this.data.partner.records.push({ id: 7, @@ -785,7 +780,7 @@ QUnit.module('Search View', { display_name: "Partner 9", }); this.data.partner.fields.date_field.searchable = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -803,39 +798,39 @@ QUnit.module('Search View', { res_id: 1, }); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); - testUtils.fields.many2one.clickOpenDropdown('bar'); - testUtils.fields.many2one.clickItem('bar','Search'); + await testUtils.fields.many2one.clickOpenDropdown('bar'); + await testUtils.fields.many2one.clickItem('bar','Search'); assert.strictEqual($('tr.o_data_row').length, 9, "should display 9 records"); - testUtils.dom.click($('button:contains(Filters)')); - testUtils.dom.click($('.o_add_custom_filter:visible')); + await testUtils.dom.click($('button:contains(Filters)')); + await testUtils.dom.click($('.o_add_custom_filter:visible')); assert.strictEqual($('.o_filter_condition select.o_searchview_extended_prop_field').val(), 'date_field', "date field should be selected"); - testUtils.dom.click($('.o_apply_filter')); + await testUtils.dom.click($('.o_apply_filter')); assert.strictEqual($('tr.o_data_row').length, 0, "should display 0 records"); // Save this search - testUtils.mock.intercept(form, 'create_filter', function (event) { + await testUtils.mock.intercept(form, 'create_filter', function (event) { assert.strictEqual(event.data.filter.name, "Awesome Test Customer Filter", "filter name should be correct"); }); - testUtils.dom.click($('button:contains(Favorites)')); - testUtils.dom.click($('.o_add_favorite')); + await testUtils.dom.click($('button:contains(Favorites)')); + await testUtils.dom.click($('.o_add_favorite')); var filterNameInput = $('.o_favorite_name .o_input[type="text"]:visible'); assert.strictEqual(filterNameInput.length, 1, "should display an input field for the filter name"); - testUtils.fields.editInput(filterNameInput, 'Awesome Test Customer Filter'); - testUtils.dom.click($('.o_save_favorite button')); + await testUtils.fields.editInput(filterNameInput, 'Awesome Test Customer Filter'); + await testUtils.dom.click($('.o_save_favorite button')); form.destroy(); }); - QUnit.test('save filters created via autocompletion works', function (assert) { + QUnit.test('save filters created via autocompletion works', async function (assert) { assert.expect(2); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -846,37 +841,40 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(10); + await actionManager.doAction(10); $('.o_searchview_input').trigger($.Event('keypress', { which: 97, })); - + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keyup', { which: $.ui.keyCode.ENTER, keyCode: $.ui.keyCode.ENTER, })); + await testUtils.nextTick(); + + await testUtils.nextTick(); assert.strictEqual($('.o_searchview_input_container .o_facet_values span').text().trim(), "a"); - testUtils.dom.click($('button .fa-star')); - testUtils.dom.click($('.o_favorites_menu .o_add_favorite')); - testUtils.fields.editInput($('div.o_favorite_name input'), 'name for favorite'); - testUtils.dom.click($('.o_favorites_menu div.o_save_favorite button')); + await testUtils.dom.click($('button .fa-star')); + await testUtils.dom.click($('.o_favorites_menu .o_add_favorite')); + await testUtils.fields.editInput($('div.o_favorite_name input'), 'name for favorite'); + await testUtils.dom.click($('.o_favorites_menu div.o_save_favorite button')); actionManager.destroy(); }); - QUnit.test('delete an active favorite remove it both in list of favorite and in search bar', function (assert) { + QUnit.test('delete an active favorite remove it both in list of favorite and in search bar', async function (assert) { assert.expect(2); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, intercepts: { load_filters: function (event) { - return $.when([{ + return Promise.resolve([{ context: "{}", domain: "[]", id: 7, @@ -892,11 +890,11 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(6); - testUtils.dom.click(actionManager.$('.o_control_panel .o_search_options button.o_favorites_menu_button')); + await actionManager.doAction(6); + await testUtils.dom.click(actionManager.$('.o_control_panel .o_search_options button.o_favorites_menu_button')); assert.containsOnce(actionManager, '.o_control_panel .o_searchview_input_container .o_facet_values'); - testUtils.dom.click(actionManager.$('.o_control_panel .o_search_options .o_favorites_menu span.o_trash_button')); - testUtils.modal.clickButton('Ok'); + await testUtils.dom.click(actionManager.$('.o_control_panel .o_search_options .o_favorites_menu span.o_trash_button')); + await testUtils.modal.clickButton('Ok'); assert.containsNone(actionManager, '.o_control_panel .o_searchview_input_container .o_facet_values'); actionManager.destroy(); }); @@ -910,7 +908,7 @@ QUnit.module('Search View', { data: this.data, intercepts: { load_filters: function (event) { - return $.when([{ + return Promise.resolve([{ context: "{}", domain: "[]", id: 7, @@ -932,17 +930,17 @@ QUnit.module('Search View', { QUnit.module('Search Arch'); - QUnit.test('arch order of groups of filters preserved', function (assert) { + QUnit.test('arch order of groups of filters preserved', async function (assert) { assert.expect(12); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(8); - testUtils.dom.click($('span.fa-filter')); + await actionManager.doAction(8); + await testUtils.dom.click($('span.fa-filter')); assert.strictEqual($('.o_filters_menu .o_menu_item').length, 11); for (var i = 0; i < 11; i++) { assert.strictEqual($('.o_filters_menu .o_menu_item').eq(i).text().trim(), (i+1).toString()); @@ -952,34 +950,21 @@ QUnit.module('Search View', { QUnit.module('Autocompletion'); - QUnit.test('selection via autocompletion modifies appropriately submenus', function (assert) { + QUnit.test('selection via autocompletion modifies appropriately submenus', async function (assert) { assert.expect(4); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(9); + await actionManager.doAction(9); - $('.o_searchview_input').trigger($.Event('keypress', { - which: 97, - })); - - $('.o_searchview_input').trigger($.Event('keyup', { - which: $.ui.keyCode.ENTER, - keyCode: $.ui.keyCode.ENTER, - })); - - $('.o_searchview_input').trigger($.Event('keypress', { - which: 103, - })); - - $('.o_searchview_input').trigger($.Event('keyup', { - which: $.ui.keyCode.ENTER, - keyCode: $.ui.keyCode.ENTER, - })); + await testUtils.fields.triggerKey('press', $('.o_searchview_input'), 97); + await testUtils.fields.triggerKey('up', $('.o_searchview_input'), 'enter'); + await testUtils.fields.triggerKey('press', $('.o_searchview_input'), 103); + await testUtils.fields.triggerKey('up', $('.o_searchview_input'), 'enter'); assert.strictEqual($('.o_searchview_input_container .o_facet_values').eq(0).text().trim(), "Date Field Filter: This Month", @@ -988,28 +973,27 @@ QUnit.module('Search View', { "Date Field Groupby: Day", "There should be a filter facet with label 'Date Field Groupby: Day'"); - testUtils.dom.click($('button .fa-filter')); - testUtils.dom.click($('.o_filters_menu .o_menu_item').eq(0)); + await testUtils.dom.click($('button .fa-filter')); + await testUtils.dom.click($('.o_filters_menu .o_menu_item').eq(0)); assert.strictEqual($('.o_filters_menu .o_item_option a.selected').text().trim(), "This Month", "The item 'This Month' should be selected in the filters menu"); - testUtils.dom.click($('button .fa-bars')); - testUtils.dom.click($('.o_group_by_menu .o_menu_item').eq(0)); + await testUtils.dom.click($('button .fa-bars')); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item').eq(0)); assert.strictEqual($('.o_group_by_menu .o_item_option a.selected').text().trim(), "Day", "The item 'Day' should be selected in the groupby menu"); actionManager.destroy(); }); - QUnit.test('select an autocomplete field with `context` key', function (assert) { + QUnit.test('select an autocomplete field with `context` key', async function (assert) { assert.expect(9); var searchRead = 0; - var actionManager = createActionManager({ + var actionManager = await 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) { @@ -1025,17 +1009,24 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(11); + await actionManager.doAction(11); + await testUtils.nextTick(); assert.strictEqual(searchRead, 1, "there should be 1 search_read"); // 'r' key to filter on bar "First Record" $('.o_searchview_input').val('r'); $('.o_searchview_input').trigger($.Event('keypress', { which: 82, keyCode: 82 })); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN })); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.RIGHT, keyCode: $.ui.keyCode.RIGHT })); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN })); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.ENTER, keyCode: $.ui.keyCode.ENTER })); + await testUtils.nextTick(); + await testUtils.nextTick(); 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"); @@ -1043,11 +1034,16 @@ QUnit.module('Search View', { // 'r' key to filter on bar "Second Record" $('.o_searchview_input').val('r'); $('.o_searchview_input').trigger($.Event('keypress', { which: 82, keyCode: 82 })); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN })); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.RIGHT, keyCode: $.ui.keyCode.RIGHT })); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.DOWN, keyCode: $.ui.keyCode.DOWN })); + await testUtils.nextTick(); $('.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 })); + await testUtils.nextTick(); assert.strictEqual($('.o_searchview_input_container .o_facet_values').eq(0).text().trim(), "First record or Second record", "the autocompletion facet should be correct"); @@ -1056,10 +1052,10 @@ QUnit.module('Search View', { actionManager.destroy(); }); - QUnit.test('no search text triggers a reload', function (assert) { + QUnit.test('no search text triggers a reload', async function (assert) { assert.expect(2); var rpcs = 0; - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -1069,16 +1065,19 @@ QUnit.module('Search View', { }, }); - actionManager.doAction(10); + await actionManager.doAction(10); rpcs = 0; $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.ENTER, keyCode: $.ui.keyCode.ENTER, })); + await testUtils.nextTick(); + $('.o_searchview_input').trigger($.Event('keyup', { which: $.ui.keyCode.ENTER, keyCode: $.ui.keyCode.ENTER, })); + await testUtils.nextTick(); assert.containsNone(actionManager, '.o_searchview_facet_label'); assert.strictEqual(rpcs, 2, "should have reloaded"); @@ -1088,16 +1087,16 @@ QUnit.module('Search View', { QUnit.module('TimeRangeMenu'); - QUnit.test('time range menu stays hidden', function (assert) { + QUnit.test('time range menu stays hidden', async function (assert) { assert.expect(4); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, }); - actionManager.doAction(1); + await actionManager.doAction(1); // check that there is no time range menu assert.containsNone(actionManager, '.o_control_panel .o_search_options .o_time_range_menu'); @@ -1105,8 +1104,8 @@ QUnit.module('Search View', { assert.strictEqual($('.o_facet_values').length, 0); // activate groupby - testUtils.dom.click($('button .fa-bars')); - testUtils.dom.click($('.o_menu_item a').eq(0)); + await testUtils.dom.click($('button .fa-bars')); + await testUtils.dom.click($('.o_menu_item a').eq(0)); // check that there is a facet assert.strictEqual($('div.o_facet_values').length, 1); // check that there is still no time range menu @@ -1114,7 +1113,7 @@ QUnit.module('Search View', { actionManager.destroy(); }); - QUnit.test('time range menu in comparison mode', function (assert) { + QUnit.test('time range menu in comparison mode', async function (assert) { assert.expect(46); var self = this; @@ -1123,7 +1122,7 @@ QUnit.module('Search View', { var periodOptionText, periodOptionValue; var unpatchDate = patchDate(2017, 2, 22, 1, 0, 0); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -1149,7 +1148,7 @@ QUnit.module('Search View', { }, }); // time range menu should be available in graph view - actionManager.doAction(4); + await actionManager.doAction(4); var $timeRangeMenu = $('.o_time_range_menu'); assert.strictEqual($timeRangeMenu.not('.o_hidden').length, 1, @@ -1164,35 +1163,34 @@ QUnit.module('Search View', { assert.deepEqual(periodOptions, PERIOD_OPTIONS_IDS, "13 period options should be available:"); - $periodOptions.each(function () { - periodOptionText = $(this).text().trim(); - periodOptionValue = $(this).val(); + for (var option of $periodOptions) { + periodOptionText = $(option).text().trim(); + periodOptionValue = $(option).val(); // opens time range menu dropdown - testUtils.dom.click($('.o_time_range_menu_button')); + await testUtils.dom.click($('.o_time_range_menu_button')); var $timeRangeMenu = $('.o_time_range_menu'); // comparison is not checked by default if (!$timeRangeMenu.find('.o_comparison_checkbox').is(':checked')) { - testUtils.dom.click($timeRangeMenu.find('.o_comparison_checkbox')); + await testUtils.dom.click($timeRangeMenu.find('.o_comparison_checkbox')); assert.strictEqual($('.o_comparison_time_range_selector:visible').length, 1, "Comparison has to be checked (only at the first time)"); } // select one period option to test it $timeRangeMenu.find('.o_time_range_selector').val(periodOptionValue); // apply - testUtils.dom.click($timeRangeMenu.find('.o_apply_range')); + await testUtils.dom.click($timeRangeMenu.find('.o_apply_range')); assert.strictEqual($('.o_facet_values').text().trim(), "Date: " + periodOptionText + " / Previous Period", "Facet should be updated with this period: " + periodOptionValue); - }); - + } unpatchDate(); actionManager.destroy(); }); - QUnit.test('a default time range only in context is taken into account', function (assert) { + QUnit.test('a default time range only in context is taken into account', async function (assert) { assert.expect(2); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, data: this.data, @@ -1206,7 +1204,7 @@ QUnit.module('Search View', { }, }); - actionManager.doAction({ + await actionManager.doAction({ res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'pivot']], @@ -1215,7 +1213,7 @@ QUnit.module('Search View', { actionManager.destroy(); }); - QUnit.test('Customizing filter does not close the filter dropdown', function (assert) { + QUnit.test('Customizing filter does not close the filter dropdown', async function (assert) { assert.expect(3); var self = this; @@ -1225,7 +1223,7 @@ QUnit.module('Search View', { }); this.data.partner.fields.date_field.searchable = true; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1242,25 +1240,26 @@ QUnit.module('Search View', { res_id: 1, }); - testUtils.fields.many2one.clickOpenDropdown('bar'); - testUtils.fields.many2one.clickItem('bar', 'Search More'); + await testUtils.fields.many2one.clickOpenDropdown('bar'); + await testUtils.fields.many2one.clickItem('bar', 'Search More'); assert.containsOnce(document.body, '.modal'); - testUtils.dom.click($('.modal .o_filters_menu_button')); + await testUtils.dom.click($('.modal .o_filters_menu_button')); var $filterDropdown = $('.modal .o_filters_menu'); - testUtils.dom.click($filterDropdown.find('.o_add_custom_filter')); + await testUtils.dom.click($filterDropdown.find('.o_add_custom_filter')); assert.containsN($filterDropdown, '.o_input', 3); // 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($filterDropdown.find('input'), function (input) { + _.each($filterDropdown.find('input'), async function (input) { var $input = $(input); $input.click(); $input.click(); + await testUtils.nextTick(); }); assert.isVisible($filterDropdown); diff --git a/addons/web/static/tests/views/view_dialogs_tests.js b/addons/web/static/tests/views/view_dialogs_tests.js index 27275e7fd80..b17112c8978 100644 --- a/addons/web/static/tests/views/view_dialogs_tests.js +++ b/addons/web/static/tests/views/view_dialogs_tests.js @@ -52,7 +52,6 @@ QUnit.module('Views', { }, }; }, - }, function () { QUnit.module('view_dialogs'); @@ -64,7 +63,7 @@ QUnit.module('Views', { return widget; } - QUnit.test('formviewdialog buttons in footer are positioned properly', function (assert) { + QUnit.test('formviewdialog buttons in footer are positioned properly', async function (assert) { assert.expect(2); var parent = createParent({ @@ -84,6 +83,7 @@ QUnit.module('Views', { res_model: 'partner', res_id: 1, }).open(); + await testUtils.nextTick(); assert.notOk($('.modal-body button').length, "should not have any button in body"); @@ -92,7 +92,7 @@ QUnit.module('Views', { parent.destroy(); }); - QUnit.test('formviewdialog buttons in footer are not duplicated', function (assert) { + QUnit.test('formviewdialog buttons in footer are not duplicated', async function (assert) { assert.expect(2); this.data.partner.fields.poney_ids = {string: "Poneys", type: "one2many", relation: 'partner'}; this.data.partner.records[0].poney_ids = []; @@ -112,22 +112,20 @@ QUnit.module('Views', { res_model: 'partner', res_id: 1, }).open(); + await testUtils.nextTick(); assert.strictEqual($('.modal button.btn-primary').length, 1, "should have 1 buttons in modal"); - testUtils.dom.click($('.o_field_x2many_list_row_add a')); - $('input.o_input').trigger($.Event('keydown', { - which: $.ui.keyCode.ESCAPE, - keyCode: $.ui.keyCode.ESCAPE, - })); + await testUtils.dom.click($('.o_field_x2many_list_row_add a')); + await testUtils.fields.triggerKeydown($('input.o_input'), 'escape'); assert.strictEqual($('.modal button.btn-primary').length, 1, "should still have 1 buttons in modal"); parent.destroy(); }); - QUnit.test('SelectCreateDialog use domain, group_by and search default', function (assert) { + QUnit.test('SelectCreateDialog use domain, group_by and search default', async function (assert) { assert.expect(3); var search = 0; @@ -183,7 +181,8 @@ QUnit.module('Views', { }, }); - var dialog = new dialogs.SelectCreateDialog(parent, { + var dialog; + new dialogs.SelectCreateDialog(parent, { no_create: true, readonly: true, res_model: 'partner', @@ -192,15 +191,18 @@ QUnit.module('Views', { search_default_groupby_bar: true, search_default_foo: 'piou', }, - }).open(); + }).open().then(function (result) { + dialog = result; + }); + await testUtils.nextTick(); - testUtils.dom.click(dialog.$('.o_searchview_facet:contains(groupby_bar) .o_facet_remove')); - testUtils.dom.click(dialog.$('.o_searchview_facet .o_facet_remove')); + await testUtils.dom.click(dialog.$('.o_searchview_facet:contains(groupby_bar) .o_facet_remove')); + await testUtils.dom.click(dialog.$('.o_searchview_facet .o_facet_remove')); parent.destroy(); }); - QUnit.test('SelectCreateDialog correctly evaluates domains', function (assert) { + QUnit.test('SelectCreateDialog correctly evaluates domains', async function (assert) { assert.expect(1); var parent = createParent({ @@ -234,11 +236,12 @@ QUnit.module('Views', { res_model: 'partner', domain: "[['id', '=', uid]]", }).open(); + await testUtils.nextTick(); parent.destroy(); }); - QUnit.test('SelectCreateDialog list view in readonly', function (assert) { + QUnit.test('SelectCreateDialog list view in readonly', async function (assert) { assert.expect(1); var parent = createParent({ @@ -254,12 +257,16 @@ QUnit.module('Views', { }, }); - var dialog = new dialogs.SelectCreateDialog(parent, { + var dialog; + new dialogs.SelectCreateDialog(parent, { res_model: 'partner', - }).open(); + }).open().then(function (result) { + dialog = result; + }); + await testUtils.nextTick(); // click on the first row to see if the list is editable - testUtils.dom.click(dialog.$('.o_list_view tbody tr:first td:not(.o_list_record_selector):first')); + await testUtils.dom.click(dialog.$('.o_list_view tbody tr:first td:not(.o_list_record_selector):first')); assert.equal(dialog.$('.o_list_view tbody tr:first td:not(.o_list_record_selector):first input').length, 0, "list view should not be editable in a SelectCreateDialog"); @@ -267,10 +274,10 @@ QUnit.module('Views', { parent.destroy(); }); - QUnit.test('SelectCreateDialog cascade x2many in create mode', function (assert) { + QUnit.test('SelectCreateDialog cascade x2many in create mode', async function (assert) { assert.expect(5); - var form = createView({ + var form = await createView({ View: FormView, model: 'product', data: this.data, @@ -310,31 +317,31 @@ QUnit.module('Views', { mockRPC: function(route, args) { if (route === '/web/dataset/call_kw/partner/get_formview_id') { - return $.when(false); + return Promise.resolve(false); } if (route === '/web/dataset/call_kw/instrument/get_formview_id') { - return $.when(false); + return Promise.resolve(false); } if (route === '/web/dataset/call_kw/instrument/create') { assert.deepEqual(args.args, [{badassery: [[6, false, [1]]], name: false}], 'The method create should have been called with the right arguments'); - return $.when(false); + return Promise.resolve(false); } return this._super(route, args); }, }); - testUtils.form.clickEdit(form); - testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); - testUtils.dom.click(form.$('.o_field_widget .o_field_many2one[name=instrument] input')); - testUtils.dom.click($('ul.ui-autocomplete.ui-front.ui-menu.ui-widget.ui-widget-content li.o_m2o_dropdown_option').first()); + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.dom.click(form.$('.o_field_widget .o_field_many2one[name=instrument] input')); + await testUtils.dom.click($('ul.ui-autocomplete.ui-front.ui-menu.ui-widget.ui-widget-content li.o_m2o_dropdown_option').first()); var $modal = $('.modal-lg'); assert.equal($modal.length, 1, 'There should be one modal'); - testUtils.dom.click($modal.find('.o_field_x2many_list_row_add a')); + await testUtils.dom.click($modal.find('.o_field_x2many_list_row_add a')); var $modals = $('.modal-lg'); @@ -342,9 +349,9 @@ QUnit.module('Views', { 'There should be two modals'); var $second_modal = $modals.not($modal); - testUtils.dom.click($second_modal.find('.o_list_view.table.table-sm.table-striped.o_list_view_ungrouped .o_data_row input[type=checkbox]')); + await testUtils.dom.click($second_modal.find('.o_list_view.table.table-sm.table-striped.o_list_view_ungrouped .o_data_row input[type=checkbox]')); - testUtils.dom.click($second_modal.find('.o_select_button')); + await testUtils.dom.click($second_modal.find('.o_select_button')); $modal = $('.modal-lg'); @@ -354,18 +361,18 @@ QUnit.module('Views', { assert.equal($modal.find('.o_data_cell').text(), 'Awsome', 'There should be one item in the list of the modal'); - testUtils.dom.click($modal.find('.btn.btn-primary')); + await testUtils.dom.click($modal.find('.btn.btn-primary')); form.destroy(); }); - QUnit.test('Form dialog and subview with _view_ref contexts', function (assert) { + QUnit.test('Form dialog and subview with _view_ref contexts', async function (assert) { assert.expect(2); this.data.instrument.records = [{id: 1, name: 'Tromblon', badassery: [1]}]; this.data.partner.records[0].instrument = 1; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -390,7 +397,7 @@ QUnit.module('Views', { mockRPC: function(route, args) { if (args.method === 'get_formview_id') { - return $.when(false); + return Promise.resolve(false); } return this._super(route, args); }, @@ -410,11 +417,11 @@ QUnit.module('Views', { }, }); - form.$('.o_field_widget[name="instrument"] button.o_external_button').click(); + await testUtils.dom.click(form.$('.o_field_widget[name="instrument"] button.o_external_button')); form.destroy(); }); - QUnit.test('SelectCreateDialog: save current search', function (assert) { + QUnit.test('SelectCreateDialog: save current search', async function (assert) { assert.expect(4); testUtils.mock.patch(ListController, { @@ -451,32 +458,34 @@ QUnit.module('Views', { }, }); - var dialog = new dialogs.SelectCreateDialog(parent, { + var dialog; + new dialogs.SelectCreateDialog(parent, { context: {shouldNotBeInFilterContext: false}, res_model: 'partner', - }).open(); + }).open().then(function (result) { + dialog = result; + }); + await testUtils.nextTick(); - assert.containsN(dialog, '.o_data_row', 3, - "should contain 3 records"); + assert.containsN(dialog, '.o_data_row', 3, "should contain 3 records"); // filter on bar - testUtils.dom.click(dialog.$('.o_dropdown_toggler_btn:contains(Filters)')); - testUtils.dom.click(dialog.$('.o_filters_menu a:contains(Bar)')); + await testUtils.dom.click(dialog.$('.o_dropdown_toggler_btn:contains(Filters)')); + await testUtils.dom.click(dialog.$('.o_filters_menu a:contains(Bar)')); - assert.containsN(dialog, '.o_data_row', 2, - "should contain 2 records"); + assert.containsN(dialog, '.o_data_row', 2, "should contain 2 records"); // save filter - testUtils.dom.click(dialog.$('.o_dropdown_toggler_btn:contains(Favorites)')); - testUtils.dom.click(dialog.$('.o_add_favorite')); - dialog.$('.o_favorite_name input[type=text]').val('some name'); // name the filter - testUtils.dom.click(dialog.$('.o_save_favorite button')); + await testUtils.dom.click(dialog.$('.o_dropdown_toggler_btn:contains(Favorites)')); + await testUtils.dom.click(dialog.$('.o_add_favorite')); + await testUtils.fields.editInput(dialog.$('.o_favorite_name input[type=text]'), 'some name'); // name the filter + await testUtils.dom.click(dialog.$('.o_save_favorite button')); testUtils.mock.unpatch(ListController); parent.destroy(); }); - QUnit.test('propagate can_create onto the search popup o2m', function (assert) { + QUnit.test('propagate can_create onto the search popup o2m', async function (assert) { assert.expect(3); this.data.instrument.records = [ @@ -490,7 +499,7 @@ QUnit.module('Views', { {id: 8, name: 'Tromblon8'}, ]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -513,18 +522,19 @@ QUnit.module('Views', { mockRPC: function(route, args) { if (args.method === 'get_formview_id') { - return $.when(false); + return Promise.resolve(false); } return this._super(route, args); }, }); - form.$('.o_field_widget[name="instrument"] .o_input').click(); + await testUtils.dom.click(form.$('.o_field_widget[name="instrument"] .o_input')); 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'); + await testUtils.dom.triggerEvents($('.ui-autocomplete a:contains(Search More)'), + ['mouseenter', 'click']); var $modal = $('.modal-dialog.modal-lg'); |
