diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/tests/views/pivot_tests.js | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/web/static/tests/views/pivot_tests.js')
| -rw-r--r-- | addons/web/static/tests/views/pivot_tests.js | 3294 |
1 files changed, 3294 insertions, 0 deletions
diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js new file mode 100644 index 00000000..9760cfbc --- /dev/null +++ b/addons/web/static/tests/views/pivot_tests.js @@ -0,0 +1,3294 @@ +odoo.define('web.pivot_tests', function (require) { +"use strict"; + +var core = require('web.core'); +var PivotView = require('web.PivotView'); +const PivotController = require("web.PivotController"); +var testUtils = require('web.test_utils'); +var testUtilsDom = require('web.test_utils_dom'); + +var _t = core._t; +const cpHelpers = testUtils.controlPanel; +var createActionManager = testUtils.createActionManager; +var createView = testUtils.createView; +var patchDate = testUtils.mock.patchDate; + +/** + * Helper function that returns, given a pivot instance, the values of the + * table, separated by ','. + * + * @returns {string} + */ +var getCurrentValues = function (pivot) { + return pivot.$('.o_pivot_cell_value div').map(function () { + return $(this).text(); + }).get().join(); +}; + + +QUnit.module('Views', { + beforeEach: function () { + this.data = { + partner: { + fields: { + foo: {string: "Foo", type: "integer", searchable: true, group_operator: 'sum'}, + bar: {string: "bar", type: "boolean", store: true, sortable: true}, + date: {string: "Date", type: "date", store: true, sortable: true}, + product_id: {string: "Product", type: "many2one", relation: 'product', store: true}, + other_product_id: {string: "Other Product", type: "many2one", relation: 'product', store: true}, + non_stored_m2o: {string: "Non Stored M2O", type: "many2one", relation: 'product'}, + customer: {string: "Customer", type: "many2one", relation: 'customer', store: true}, + computed_field: {string: "Computed and not stored", type: 'integer', compute: true, group_operator: 'sum'}, + company_type: { + string: "Company Type", type: "selection", + selection: [["company", "Company"], ["individual", "individual"]], + searchable: true, sortable: true, store: true, + }, + }, + records: [ + { + id: 1, + foo: 12, + bar: true, + date: '2016-12-14', + product_id: 37, + customer: 1, + computed_field: 19, + company_type: 'company', + }, { + id: 2, + foo: 1, + bar: true, + date: '2016-10-26', + product_id: 41, + customer: 2, + computed_field: 23, + company_type: 'individual', + }, { + id: 3, + foo: 17, + bar: true, + date: '2016-12-15', + product_id: 41, + customer: 2, + computed_field: 26, + company_type: 'company', + }, { + id: 4, + foo: 2, + bar: false, + date: '2016-04-11', + product_id: 41, + customer: 1, + computed_field: 19, + company_type: 'individual', + }, + ] + }, + product: { + fields: { + name: {string: "Product Name", type: "char"} + }, + records: [{ + id: 37, + display_name: "xphone", + }, { + id: 41, + display_name: "xpad", + }] + }, + customer: { + fields: { + name: {string: "Customer Name", type: "char"} + }, + records: [{ + id: 1, + display_name: "First", + }, { + id: 2, + display_name: "Second", + }] + }, + }; + }, +}, function () { + QUnit.module('PivotView'); + + QUnit.test('simple pivot rendering', async function (assert) { + assert.expect(3); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners">' + + '<field name="foo" type="measure"/>' + + '</pivot>', + mockRPC: function (route, args) { + assert.strictEqual(args.kwargs.lazy, false, + "the read_group should be done with the lazy=false option"); + return this._super.apply(this, arguments); + }, + }); + + assert.hasClass(pivot.$('table'), 'o_enable_linking', + "table should have classname 'o_enable_linking'"); + assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(32)').length, 1, + "should contain a pivot cell with the sum of all records"); + pivot.destroy(); + }); + + QUnit.test('pivot rendering with widget', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners">' + + '<field name="foo" type="measure" widget="float_time"/>' + + '</pivot>', + }); + + assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(32:00)').length, 1, + "should contain a pivot cell with the sum of all records"); + pivot.destroy(); + }); + + 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, group_operator: 'sum'}; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners">' + + '<field name="foo" string="BAR" type="measure"/>' + + '</pivot>', + }); + + assert.strictEqual(pivot.$('.o_pivot_measure_row').text(), "BAR", + "the displayed name should be the one set in the string attribute"); + pivot.destroy(); + }); + + 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, group_operator: 'sum'}; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners">' + + '<field name="fubar" string="fubar" type="measure"/>' + + '</pivot>', + }); + assert.containsOnce(pivot, '.o_pivot', 'Non stored fields can have a string attribute'); + pivot.destroy(); + }); + + QUnit.test('pivot rendering with invisible attribute on field', async function (assert) { + assert.expect(3); + // when invisible, a field should neither be an active measure, + // nor be a selectable measure. + _.extend(this.data.partner.fields, { + foo: {string: "Foo", type: "integer", store: true, group_operator: 'sum'}, + foo2: {string: "Foo2", type: "integer", store: true, group_operator: 'sum'} + }); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners">' + + '<field name="foo" type="measure"/>' + + '<field name="foo2" type="measure" invisible="True"/>' + + '</pivot>', + }); + + // there should be only one displayed measure as the other one is invisible + assert.containsOnce(pivot, '.o_pivot_measure_row'); + // there should be only one measure besides count, as the other one is invisible + assert.containsN(pivot, '.o_cp_bottom_left .dropdown-item', 2); + // the invisible field souldn't be in the groupable fields neither + await testUtils.dom.click(pivot.$('.o_pivot_header_cell_closed:first')); + assert.containsNone(pivot, '.o_pivot_field_menu a[data-field="foo2"]'); + + pivot.destroy(); + }); + + QUnit.test('pivot view without "string" attribute', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + // this is important for export functionality. + assert.strictEqual(pivot.title, _t("Untitled"), "should have a valid title"); + pivot.destroy(); + }); + + QUnit.test('group headers should have a tooltip', async function (assert) { + assert.expect(2); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="col"/>' + + '<field name="date" type="row"/>' + + '</pivot>', + }); + + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:first').attr('data-original-title'), 'Date'); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:first').attr('data-original-title'), 'Product'); + + pivot.destroy(); + }); + + QUnit.test('pivot view add computed fields explicitly defined as measure', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="computed_field" type="measure"/>' + + '</pivot>', + }); + + assert.ok(pivot.measures.computed_field, "measures contains the field 'computed_field'"); + pivot.destroy(); + }); + + QUnit.test('clicking on a cell triggers a do_action', async function (assert) { + assert.expect(2); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + intercepts: { + do_action: function (ev) { + assert.deepEqual(ev.data.action, { + context: {someKey: true, userContextKey: true}, + domain: [['product_id', '=', 37]], + name: 'Partners', + res_model: 'partner', + target: 'current', + type: 'ir.actions.act_window', + view_mode: 'list', + views: [[false, 'list'], [2, 'form']], + }, "should trigger do_action with the correct args"); + }, + }, + session: { + user_context: {userContextKey: true}, + }, + viewOptions: { + action: { + views: [ + { viewID: 2, type: 'form' }, + { viewID: 5, type: 'kanban' }, + { viewID: false, type: 'list' }, + { viewID: false, type: 'pivot' }, + ], + }, + context: {someKey: true, search_default_test: 3}, + title: 'Partners', + } + }); + + assert.hasClass(pivot.$('table'), 'o_enable_linking', + "table should have classname 'o_enable_linking'"); + await testUtils.dom.click(pivot.$('.o_pivot_cell_value:contains(12)')); // should trigger a do_action + + pivot.destroy(); + }); + + QUnit.test('row and column are highlighted when hovering a cell', async function (assert) { + assert.expect(11); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners">' + + '<field name="foo" type="col"/>' + + '<field name="product_id" type="row"/>' + + '</pivot>', + }); + + // check row highlighting + assert.hasClass(pivot.$('table'), 'table-hover', + "with className 'table-hover', rows are highlighted (bootstrap)"); + + // check column highlighting + // hover third measure + await testUtils.dom.triggerEvents(pivot.$('th.o_pivot_measure_row:nth(2)'), 'mouseover'); + assert.containsN(pivot, '.o_cell_hover', 3); + for (var i = 0; i < 3; i++) { + assert.hasClass(pivot.$('tbody tr:nth(' + i + ') td:nth(2)'), 'o_cell_hover'); + } + await testUtils.dom.triggerEvents(pivot.$('th.o_pivot_measure_row:nth(2)'), 'mouseout'); + assert.containsNone(pivot, '.o_cell_hover'); + + // hover second cell, second row + await testUtils.dom.triggerEvents(pivot.$('tbody tr:nth(1) td:nth(1)'), 'mouseover'); + assert.containsN(pivot, '.o_cell_hover', 3); + for (i = 0; i < 3; i++) { + assert.hasClass(pivot.$('tbody tr:nth(' + i + ') td:nth(1)'), 'o_cell_hover'); + } + await testUtils.dom.triggerEvents(pivot.$('tbody tr:nth(1) td:nth(1)'), 'mouseout'); + assert.containsNone(pivot, '.o_cell_hover'); + + pivot.destroy(); + }); + + QUnit.test('pivot view with disable_linking="True"', async function (assert) { + assert.expect(2); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot disable_linking="True">' + + '<field name="foo" type="measure"/>' + + '</pivot>', + intercepts: { + do_action: function () { + assert.ok(false, "should not trigger do_action"); + }, + }, + }); + + assert.doesNotHaveClass(pivot.$('table'), 'o_enable_linking', + "table should not have classname 'o_enable_linking'"); + assert.containsOnce(pivot, '.o_pivot_cell_value', + "should have one cell"); + await testUtils.dom.click(pivot.$('.o_pivot_cell_value')); // should not trigger a do_action + + pivot.destroy(); + }); + + QUnit.test('clicking on the "Total" Cell with time range activated gives the right action domain', async function (assert) { + assert.expect(2); + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot/>', + archs: { + 'partner,false,search': ` + <search> + <filter name="date_filter" date="date" domain="[]" default_period='this_month'/> + </search> + `, + }, + intercepts: { + do_action: function (ev) { + assert.deepEqual( + ev.data.action.domain, + ["&",["date",">=","2016-12-01"],["date","<=","2016-12-31"]], + "should trigger do_action with the correct action domain" + ); + }, + }, + viewOptions: { + context: { search_default_date_filter: true, }, + title: 'Partners', + }, + }); + + assert.hasClass(pivot.$('table'), 'o_enable_linking', + "root node should have classname 'o_enable_linking'"); + await testUtilsDom.click(pivot.$('.o_pivot_cell_value')); + + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('clicking on a fake cell value ("empty group") in comparison mode gives an action domain equivalent to [[0,"=",1]]', async function (assert) { + assert.expect(3); + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + + this.data.partner.records[0].date = '2016-11-15'; + this.data.partner.records[1].date = '2016-11-17'; + this.data.partner.records[2].date = '2016-11-22'; + this.data.partner.records[3].date = '2016-11-03'; + + var first_do_action = true; + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '</pivot>', + intercepts: { + do_action: function (ev) { + if (first_do_action) { + assert.deepEqual( + ev.data.action.domain, + ["&",["date",">=","2016-12-01"],["date","<=","2016-12-31"]], + "should trigger do_action with the correct action domain" + ); + } else { + assert.deepEqual( + ev.data.action.domain, + [[0, "=", 1]], + "should trigger do_action with the correct action domain" + ); + } + first_do_action = false; + }, + }, + archs: { + 'partner,false,search': ` + <search> + <filter name="date_filter" date="date" domain="[]" default_period='this_month'/> + </search> + `, + }, + viewOptions: { + context: { search_default_date_filter: true, }, + title: 'Partners', + }, + }); + + await cpHelpers.toggleComparisonMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period'); + + assert.hasClass(pivot.$('table'), 'o_enable_linking', + "root node should have classname 'o_enable_linking'"); + // here we click on the group corresponding to Total/Total/This Month + pivot.$('.o_pivot_cell_value').eq(1).click(); // should trigger a do_action with appropriate domain + // here we click on the group corresponding to xphone/Total/This Month + pivot.$('.o_pivot_cell_value').eq(4).click(); // should trigger a do_action with appropriate domain + + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('pivot view grouped by date field', async function (assert) { + assert.expect(2); + + var data = this.data; + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="month" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + mockRPC: function (route, params) { + var wrong_fields = _.filter(params.kwargs.fields, function (field) { + return !(field.split(':')[0] in data.partner.fields); + }); + assert.ok(!wrong_fields.length, 'fields given to read_group should exist on the model'); + return this._super.apply(this, arguments); + }, + }); + pivot.destroy(); + }); + + QUnit.test('without measures, pivot view uses __count by default', async function (assert) { + assert.expect(2); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot></pivot>', + mockRPC: function (route, args) { + if (args.method === 'read_group') { + assert.deepEqual(args.kwargs.fields, ['__count'], + "should make a read_group with no valid fields"); + } + return this._super(route, args); + } + }); + + var $countMeasure = pivot.$buttons.find('.dropdown-item[data-field=__count]:first'); + assert.hasClass($countMeasure, 'selected', "The count measure should be activated"); + pivot.destroy(); + }); + + QUnit.test('pivot view can be reloaded', async function (assert) { + assert.expect(4); + var readGroupCount = 0; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot></pivot>', + mockRPC: function (route, args) { + if (args.method === 'read_group') { + readGroupCount++; + } + return this._super(route, args); + } + }); + + assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(4)').length, 1, + "should contain a pivot cell with the number of all records"); + assert.strictEqual(readGroupCount, 1, "should have done 1 rpc"); + + 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', async function (assert) { + assert.expect(3); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + assert.containsOnce(pivot, '.o_pivot_header_cell_opened', + "should have one opened header"); + assert.strictEqual(pivot.$('.o_pivot_header_cell_closed:contains(xphone)').length, 1, + "should display one header with 'xphone'"); + assert.strictEqual(pivot.$('.o_pivot_header_cell_closed:contains(xpad)').length, 1, + "should display one header with 'xpad'"); + pivot.destroy(); + }); + + QUnit.test('basic folding/unfolding', async function (assert) { + assert.expect(7); + + var rpcCount = 0; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + mockRPC: function () { + rpcCount++; + return this._super.apply(this, arguments); + }, + }); + assert.containsN(pivot, 'tbody tr', 3, + "should have 3 rows: 1 for the opened header, and 2 for data"); + + // click on the opened header to close it + 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 + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed')); + assert.containsN(pivot, '.o_pivot_field_menu .dropdown-item[data-field="date"]', 6, + "should have the date field as proposition (Date, Day, Week, Month, Quarter and Year)"); + assert.containsOnce(pivot, '.o_pivot_field_menu .dropdown-item[data-field="product_id"]', + "should have the product_id field as proposition"); + assert.containsNone(pivot, '.o_pivot_field_menu .dropdown-item[data-field="non_stored_m2o"]', + "should not have the non_stored_m2o field as proposition"); + + 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"); + assert.strictEqual(rpcCount, 3, + "should have done 3 rpcs (initial load) + open header with different groupbys"); + + pivot.destroy(); + }); + + QUnit.test('more folding/unfolding', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + // open dropdown to zoom into first row + 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 + await testUtils.nextTick(); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"]:contains("Day")')); + + // open dropdown to zoom into second row + await testUtils.dom.clickLast(pivot.$('tbody th.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('fold and unfold header group', async function (assert) { + assert.expect(3); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + assert.containsN(pivot, 'thead tr', 3); + + // fold opened col group + await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_opened')); + assert.containsN(pivot, 'thead tr', 2); + + // unfold it + await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]')); + assert.containsN(pivot, 'thead tr', 3); + + pivot.destroy(); + }); + + QUnit.test('unfold second header group', async function (assert) { + assert.expect(4); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + assert.containsN(pivot, 'thead tr', 3); + var values = ['12', '20', '32']; + assert.strictEqual(getCurrentValues(pivot), values.join(',')); + + // unfold it + 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="company_type"]')); + assert.containsN(pivot, 'thead tr', 4); + values = ['12', '3', '17', '32']; + assert.strictEqual(getCurrentValues(pivot), values.join(',')); + + pivot.destroy(); + }); + + QUnit.test('can toggle extra measure', async function (assert) { + assert.expect(8); + + var rpcCount = 0; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + mockRPC: function () { + rpcCount++; + return this._super.apply(this, arguments); + }, + }); + + assert.containsN(pivot, '.o_pivot_cell_value', 3, + "should have 3 cells: 1 for the open header, and 2 for data"); + assert.doesNotHaveClass(pivot.$buttons.find('.dropdown-item[data-field=__count]:first'), 'selected', + "the __count measure should not be selected"); + + rpcCount = 0; + 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"); + assert.containsN(pivot, '.o_pivot_cell_value', 6, + "should have 6 cells: 2 for the open header, and 4 for data"); + assert.strictEqual(rpcCount, 2, + "should have done 2 rpcs to reload data"); + + 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"); + assert.containsN(pivot, '.o_pivot_cell_value', 3, + "should have 3 cells: 1 for the open header, and 2 for data"); + assert.strictEqual(rpcCount, 2, + "should not have done any extra rpcs"); + + pivot.destroy(); + }); + + QUnit.test('no content helper when no active measure', async function (assert) { + assert.expect(4); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners">' + + '</pivot>', + }); + + assert.containsNone(pivot, '.o_view_nocontent', + "should not have a no_content_helper"); + assert.containsOnce(pivot, 'table', + "should have a table in DOM"); + + await testUtils.pivot.toggleMeasuresDropdown(pivot); + await testUtils.pivot.clickMeasure(pivot, '__count'); + + assert.containsOnce(pivot, '.o_view_nocontent', + "should have a no_content_helper"); + assert.containsNone(pivot, 'table', + "should not have a table in DOM"); + pivot.destroy(); + }); + + QUnit.test('no content helper when no data', async function (assert) { + assert.expect(4); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners">' + + '</pivot>', + }); + + assert.containsNone(pivot, '.o_view_nocontent', + "should not have a no_content_helper"); + assert.containsOnce(pivot, 'table', + "should have a table in DOM"); + + await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); + + assert.containsOnce(pivot, '.o_view_nocontent', + "should have a no_content_helper"); + assert.containsNone(pivot, 'table', + "should not have a table in DOM"); + pivot.destroy(); + }); + + QUnit.test('no content helper when no data, part 2', async function (assert) { + assert.expect(1); + + this.data.partner.records = []; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners"></pivot>', + }); + + assert.containsOnce(pivot, '.o_view_nocontent', + "should have a no_content_helper"); + pivot.destroy(); + }); + + QUnit.test('no content helper when no data, part 3', async function (assert) { + assert.expect(4); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot string="Partners"></pivot>', + viewOptions: { + domain: [['foo', '=', 12345]] + }, + }); + + assert.containsOnce(pivot, '.o_view_nocontent', + "should have a no_content_helper"); + await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); + assert.containsOnce(pivot, '.o_view_nocontent', + "should still have a no_content_helper"); + 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. + 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', async function (assert) { + assert.expect(5); + + var rpcCount = 0; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + mockRPC: function () { + rpcCount++; + return this._super.apply(this, arguments); + }, + }); + + assert.containsN(pivot, '.o_pivot_cell_value', 3, + "should have 3 cells: 1 for the open header, and 2 for data"); + assert.strictEqual(pivot.$('.o_pivot_measure_row:contains(Foo)').length, 1, + "should have 1 row for measure Foo"); + + await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]}); + + rpcCount = 0; + await testUtils.pivot.reload(pivot, {domain: []}); + + assert.equal(rpcCount, 2, "should have reloaded data"); + assert.containsN(pivot, '.o_pivot_cell_value', 3, + "should still have 3 cells: 1 for the open header, and 2 for data"); + assert.strictEqual(pivot.$('.o_pivot_measure_row:contains(Foo)').length, 1, + "should still have 1 row for measure Foo"); + pivot.destroy(); + }); + + QUnit.test('can be grouped with the update function', async function (assert) { + assert.expect(4); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + assert.containsOnce(pivot, '.o_pivot_cell_value', + "should have only 1 cell"); + assert.containsOnce(pivot, 'tbody tr', + "should have 1 rows"); + + await testUtils.pivot.reload(pivot, {groupBy: ['product_id']}); + + assert.containsN(pivot, '.o_pivot_cell_value', 3, + "should have 3 cells"); + assert.containsN(pivot, 'tbody tr', 3, + "should have 3 rows"); + pivot.destroy(); + }); + + QUnit.test('can sort data in a column by clicking on header', async function (assert) { + assert.expect(3); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '<field name="product_id" type="row"/>' + + '</pivot>', + }); + + assert.strictEqual($('td.o_pivot_cell_value').text(), "321220", + "should have proper values in cells (total, result 1, result 2"); + + 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"); + + 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"); + + pivot.destroy(); + }); + + QUnit.test('can expand all rows', async function (assert) { + assert.expect(7); + + var nbReadGroups = 0; + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '<field name="product_id" type="row"/>' + + '</pivot>', + mockRPC: function (route, args) { + if (args.method === 'read_group') { + nbReadGroups++; + } + return this._super.apply(this, arguments); + }, + }); + + assert.strictEqual(nbReadGroups, 2, "should have done 2 read_group RPCS"); + assert.strictEqual(pivot.$('td.o_pivot_cell_value').text(), "321220", + "should have proper values in cells (total, result 1, result 2)"); + + // expand on date:days, product + nbReadGroups = 0; + 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 + 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; + 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, + "should have 8 rows again"); + + pivot.destroy(); + }); + + QUnit.test('expand all with a delay', async function (assert) { + assert.expect(3); + + var def; + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '<field name="product_id" type="row"/>' + + '</pivot>', + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'read_group') { + return Promise.resolve(def).then(_.constant(result)); + } + return result; + }, + }); + + // expand on date:days, product + 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 + 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 = testUtils.makeTestPromise(); + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_expand_button')); + await testUtils.nextTick(); + def.resolve(); + // await testUtils.returnAfterNextAnimationFrame(); + await testUtils.nextTick(); + assert.containsN(pivot, 'tbody tr', 8, + "should have 8 rows again"); + + pivot.destroy(); + }); + + QUnit.test('can download a file', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="month" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + session: { + get_file: function (args) { + assert.strictEqual(args.url, '/web/pivot/export_xlsx', + "should call get_file with correct parameters"); + args.complete(); + }, + }, + }); + + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_download')); + pivot.destroy(); + }); + + QUnit.test('download a file with single measure, measure row displayed in table', async function (assert) { + assert.expect(1); + + const pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="month" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + session: { + get_file: function (args) { + const data = JSON.parse(args.data.data); + assert.strictEqual(data.measure_headers.length, 4, + "should have measure_headers in data"); + args.complete(); + }, + }, + }); + + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_download')); + pivot.destroy(); + }); + + QUnit.test('download button is disabled when there is no data', async function (assert) { + assert.expect(1); + + this.data.partner.records = []; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="month" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + assert.hasAttrValue(pivot.$buttons.find('.o_pivot_download'), 'disabled', 'disabled', + "download button should be disabled"); + pivot.destroy(); + }); + + QUnit.test('getOwnedQueryParams correctly returns measures and groupbys', async function (assert) { + assert.expect(3); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="day" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + assert.deepEqual(pivot.getOwnedQueryParams(), { + context: { + pivot_column_groupby: ['date:day'], + pivot_measures: ['foo'], + pivot_row_groupby: [], + }, + }, "context should be correct"); + + // expand header on field customer + 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'], + pivot_measures: ['foo'], + pivot_row_groupby: [], + }, + }, "context should be correct"); + + // expand row on field product_id + 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'], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id'], + }, + }, "context should be correct"); + + pivot.destroy(); + }); + + QUnit.test('correctly remove pivot_ keys from the context', async function (assert) { + assert.expect(5); + + this.data.partner.fields.amount = {string: "Amount", type: "float", group_operator: 'sum'}; + + // Equivalent to loading with default filter + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="day" type="col"/>' + + '<field name="amount" type="measure"/>' + + '</pivot>', + viewOptions: { + context: { + pivot_measures: ['foo'], + pivot_column_groupby: ['customer'], + pivot_row_groupby: ['product_id'], + }, + }, + }); + + // Equivalent to unload the filter + var reloadParams = { + context: {}, + }; + await pivot.reload(reloadParams); + + assert.deepEqual(pivot.getOwnedQueryParams(), { + context: { + pivot_column_groupby: ['customer'], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id'], + }, + }, "context should be correct"); + + // Let's get rid of the rows groupBy + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_opened')); + + assert.deepEqual(pivot.getOwnedQueryParams(), { + context: { + pivot_column_groupby: ['customer'], + pivot_measures: ['foo'], + pivot_row_groupby: [], + }, + }, "context should be correct"); + + // And now, get rid of the col groupby + await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_opened')); + + assert.deepEqual(pivot.getOwnedQueryParams(), { + context: { + pivot_column_groupby: [], + pivot_measures: ['foo'], + pivot_row_groupby: [], + }, + }, "context should be correct"); + + 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: [], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id'], + }, + }, "context should be correct"); + + 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: { + pivot_column_groupby: ['customer'], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id'], + }, + }, "context should be correct"); + + pivot.destroy(); + }); + + QUnit.test('Unload Filter, reset display, load another filter', async function (assert) { + assert.expect(18); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + viewOptions: { + context: { + pivot_measures: ['foo'], + pivot_column_groupby: ['customer'], + pivot_row_groupby: ['product_id'], + }, + }, + }); + + // Check Columns + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1, + 'The column should be grouped'); + assert.strictEqual(pivot.$('thead tr:contains("First")').length, 1, + 'There should be a column "First"'); + assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 1, + 'There should be a column "Second"'); + + // Check Rows + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_opened').length, 1, + 'The row should be grouped'); + assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 1, + 'There should be a row "xphone"'); + assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 1, + 'There should be a row "xpad"'); + + // Equivalent to unload the filter + var reloadParams = { + context: {}, + }; + await pivot.reload(reloadParams); + // collapse all headers + 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, + 'The column should not be grouped'); + assert.strictEqual(pivot.$('thead tr:contains("First")').length, 0, + 'There should not be a column "First"'); + assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 0, + 'There should not be a column "Second"'); + + // Check Rows + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed').length, 1, + 'The row should not be grouped'); + assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 0, + 'There should not be a row "xphone"'); + assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 0, + 'There should not be a row "xpad"'); + + // Equivalent to load another filter + reloadParams = { + context: { + pivot_measures: ['foo'], + pivot_column_groupby: ['customer'], + pivot_row_groupby: ['product_id'], + }, + }; + await pivot.reload(reloadParams); + + // Check Columns + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1, + 'The column should be grouped'); + assert.strictEqual(pivot.$('thead tr:contains("First")').length, 1, + 'There should be a column "First"'); + assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 1, + 'There should be a column "Second"'); + + // Check Rows + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_opened').length, 1, + 'The row should be grouped'); + assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 1, + 'There should be a row "xphone"'); + assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 1, + 'There should be a row "xpad"'); + + pivot.destroy(); + }); + + QUnit.test('Reload, group by columns, reload', async function (assert) { + assert.expect(2); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot/>', + }); + + // Set a column groupby + 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]')); + + // Set a domain + await pivot.update({domain: [['product_id', '=', 37]], groupBy: [], context: {}}); + + var expectedContext = {pivot_column_groupby: ['customer'], + pivot_measures: ['__count'], + pivot_row_groupby: []}; + + // Check that column groupbys were not lost + assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext}, + 'Column groupby not lost after first reload'); + + // Set a column groupby + await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]')); + + // Set a domain + await pivot.update({domain: [['product_id', '=', 41]], groupBy: [], context: {}}); + + expectedContext = {pivot_column_groupby: ['customer', 'product_id'], + pivot_measures: ['__count'], + pivot_row_groupby: []}; + + assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext}, + 'Column groupby not lost after second reload'); + + pivot.destroy(); + }); + + QUnit.test('folded groups remain folded at reload', async function (assert) { + assert.expect(5); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '<field name="company_type" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + var values = [ + "29", "3", "32", + "12", "12", + "17", "3", "20", + ]; + assert.strictEqual(getCurrentValues(pivot), values.join(',')); + + // expand a col group + 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"]')); + + values = [ + "29", "1", "2", "32", + "12", "12", + "17", "1", "2", "20", + ]; + assert.strictEqual(getCurrentValues(pivot), values.join(',')); + + // expand a row group + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed:nth(1)')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="other_product_id"]')); + + values = [ + "29", "1", "2", "32", + "12", "12", + "17", "1", "2", "20", + "17", "1", "2", "20", + ]; + assert.strictEqual(getCurrentValues(pivot), values.join(',')); + + // reload (should keep folded groups folded as col/row groupbys didn't change) + await testUtils.pivot.reload(pivot, {context: {}, domain: [], groupBy: []}); + + assert.strictEqual(getCurrentValues(pivot), values.join(',')); + + await testUtils.dom.click(pivot.$('.o_pivot_expand_button')); + + // sanity check of what the table should look like if all groups are + // expanded, to ensure that the former asserts are pertinent + values = [ + "12", "17", "1", "2", "32", + "12", "12", + "12", "12", + "17", "1", "2", "20", + "17", "1", "2", "20", + ]; + assert.strictEqual(getCurrentValues(pivot), values.join(',')); + + pivot.destroy(); + }); + + QUnit.test('Empty results keep groupbys', async function (assert) { + assert.expect(2); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot/>', + }); + + // Set a column groupby + 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]')); + + // Set a domain for empty results + await pivot.update({domain: [['id', '=', false]]}); + + var expectedContext = {pivot_column_groupby: ['customer'], + pivot_measures: ['__count'], + pivot_row_groupby: []}; + assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext}, + 'Column groupby not lost after empty results'); + + // Set a domain for not empty results + await pivot.update({domain: [['product_id', '=', 37]]}); + + assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext}, + 'Column groupby not lost after reload after empty results'); + + pivot.destroy(); + }); + + QUnit.test('correctly uses pivot_ keys from the context', async function (assert) { + assert.expect(7); + + this.data.partner.fields.amount = {string: "Amount", type: "float", group_operator: 'sum'}; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="day" type="col"/>' + + '<field name="amount" type="measure"/>' + + '</pivot>', + viewOptions: { + context: { + pivot_measures: ['foo'], + pivot_column_groupby: ['customer'], + pivot_row_groupby: ['product_id'], + }, + }, + }); + + assert.containsOnce(pivot, 'thead .o_pivot_header_cell_opened', + "column: should have one opened header"); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(First)').length, 1, + "column: should display one closed header with 'First'"); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(Second)').length, 1, + "column: should display one closed header with 'Second'"); + + assert.containsOnce(pivot, 'tbody .o_pivot_header_cell_opened', + "row: should have one opened header"); + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xphone)').length, 1, + "row: should display one closed header with 'xphone'"); + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xpad)').length, 1, + "row: should display one closed header with 'xpad'"); + + assert.strictEqual(pivot.$('tbody tr:first td:nth(2)').text(), '32', + "selected measure should be foo, with total 32"); + + pivot.destroy(); + }); + + QUnit.test('clear table cells data after closeGroup', async function (assert) { + assert.expect(2); + + const pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot/>', + groupBy: ['product_id'], + }); + + await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.el.querySelectorAll('.o_pivot_field_menu .dropdown-item[data-field="date"]')[0]); + + // close and reopen row groupings after changing value + this.data.partner.records.find(r => r.product_id === 37).date = '2016-10-27'; + await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_opened')); + await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.el.querySelector('.o_pivot_field_menu .dropdown-item[data-field="product_id"]')); + assert.strictEqual(pivot.el.querySelectorAll('.o_pivot_cell_value')[4].innerText, ''); // xphone December 2016 + + // invert axis, and reopen column groupings + await testUtils.dom.click(pivot.el.querySelector('.o_cp_buttons .o_pivot_flip_button')); + await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_opened')); + await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.el.querySelector('.o_pivot_field_menu .dropdown-item[data-field="product_id"]')); + assert.strictEqual(pivot.el.querySelectorAll('.o_pivot_cell_value')[3].innerText, ''); // December 2016 xphone + + pivot.destroy(); + }); + + QUnit.test('correctly group data after flip (1)', async function (assert) { + assert.expect(4); + const actionManager = await createActionManager({ + data: this.data, + archs: { + 'partner,false,pivot': "<pivot/>", + 'partner,false,search': `<search><filter name="bayou" string="Bayou" domain="[(1,'=',1)]"/></search>`, + 'partner,false,list': '<tree><field name="foo"/></tree>', + 'partner,false,form': '<form><field name="foo"/></form>', + }, + }); + + await actionManager.doAction({ + res_model: 'partner', + type: 'ir.actions.act_window', + views: [[false, 'pivot']], + context: { group_by: ["product_id"] }, + }); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + "xphone", + "xpad" + ] + ); + + // flip axis + await testUtils.dom.click(actionManager.el.querySelector('.o_cp_buttons .o_pivot_flip_button')); + await testUtils.nextTick(); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + ] + ); + + // select filter "Bayou" in control panel + await cpHelpers.toggleFilterMenu(actionManager); + await cpHelpers.toggleMenuItem(actionManager, "Bayou"); + await testUtils.nextTick(); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + "xphone", + "xpad" + ] + ); + + // close row header "Total" + await testUtils.dom.click(actionManager.el.querySelector('tbody .o_pivot_header_cell_opened')); + await testUtils.nextTick(); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total" + ] + ); + + actionManager.destroy(); + }); + + QUnit.test('correctly group data after flip (2)', async function (assert) { + assert.expect(5); + const actionManager = await createActionManager({ + data: this.data, + archs: { + 'partner,false,pivot': "<pivot/>", + 'partner,false,search': `<search><filter name="bayou" string="Bayou" domain="[(1,'=',1)]"/></search>`, + 'partner,false,list': '<tree><field name="foo"/></tree>', + 'partner,false,form': '<form><field name="foo"/></form>', + }, + }); + + await actionManager.doAction({ + res_model: 'partner', + type: 'ir.actions.act_window', + views: [[false, 'pivot']], + context: { group_by: ["product_id"] }, + }); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + "xphone", + "xpad" + ] + ); + + // select filter "Bayou" in control panel + await cpHelpers.toggleFilterMenu(actionManager); + await cpHelpers.toggleMenuItem(actionManager, "Bayou"); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + "xphone", + "xpad" + ] + ); + + // flip axis + await testUtils.dom.click(actionManager.el.querySelector('.o_cp_buttons .o_pivot_flip_button')); + await testUtils.nextTick(); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total" + ] + ); + + // unselect filter "Bayou" in control panel + await cpHelpers.toggleFilterMenu(actionManager); + await cpHelpers.toggleMenuItem(actionManager, "Bayou"); + await testUtils.nextTick(); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + "xphone", + "xpad" + ] + ); + + // close row header "Total" + await testUtils.dom.click(actionManager.el.querySelector('tbody .o_pivot_header_cell_opened')); + await testUtils.nextTick(); + + assert.deepEqual( + [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total" + ] + ); + + actionManager.destroy(); + }); + + QUnit.test('correctly group data after flip (3))', async function (assert) { + assert.expect(10); + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: ` + <pivot> + <field name="product_id" type="row"/> + <field name="company_type" type="col"/> + </pivot> + `, + archs: { + 'partner,false,search': `<search><filter name="bayou" string="Bayou" domain="[(1,'=',1)]"/></search>`, + } + }); + + assert.deepEqual( + [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText), + [ + "", "Total", "", + "Company", "individual", + "Count", "Count", "Count" + ] + ); + + assert.deepEqual( + [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + "xphone", + "xpad" + ] + ); + + // close col header "Total" + await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_opened')); + await testUtils.nextTick(); + + assert.deepEqual( + [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText), + [ + "", "Total", + "Count" + ] + ); + assert.deepEqual( + [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + "xphone", + "xpad" + ] + ); + + // flip axis + await testUtils.dom.click(pivot.el.querySelector('.o_cp_buttons .o_pivot_flip_button')); + await testUtils.nextTick(); + + assert.deepEqual( + [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText), + [ + "", "Total", "", + "xphone", "xpad", + "Count", "Count", "Count" + ] + ); + assert.deepEqual( + [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total" + ] + ); + + // select filter "Bayou" in control panel + await cpHelpers.toggleFilterMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, "Bayou"); + await testUtils.nextTick(); + + assert.deepEqual( + [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText), + [ + "", "Total", "", + "xphone", "xpad", + "Count", "Count", "Count" + ] + ); + assert.deepEqual( + [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total", + "xphone", + "xpad" + ] + ); + + // close row header "Total" + await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_opened')); + await testUtils.nextTick(); + + assert.deepEqual( + [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText), + [ + "", "Total", "", + "xphone", "xpad", + "Count", "Count", "Count" + ] + ); + assert.deepEqual( + [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText), + [ + "Total" + ] + ); + + pivot.destroy(); + }); + + 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", group_operator: 'sum'}; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="day" type="col"/>' + + '<field name="amount" type="measure"/>' + + '</pivot>', + }); + + assert.strictEqual(pivot.$('tbody tr:first td.o_pivot_cell_value:last').text(), '0.00', + "the active measure should be amount"); + + var reloadParams = { + context: { + pivot_measures: ['foo'], + pivot_column_groupby: ['customer'], + pivot_row_groupby: ['product_id'], + }, + }; + await testUtils.pivot.reload(pivot, reloadParams); + + assert.containsOnce(pivot, 'thead .o_pivot_header_cell_opened', + "column: should have one opened header"); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(First)').length, 1, + "column: should display one closed header with 'First'"); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(Second)').length, 1, + "column: should display one closed header with 'Second'"); + + assert.containsOnce(pivot, 'tbody .o_pivot_header_cell_opened', + "row: should have one opened header"); + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xphone)').length, 1, + "row: should display one closed header with 'xphone'"); + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xpad)').length, 1, + "row: should display one closed header with 'xpad'"); + + assert.strictEqual(pivot.$('tbody tr:first td:nth(2)').text(), '32', + "selected measure should be foo, with total 32"); + + pivot.destroy(); + }); + + QUnit.test('correctly use group_by key from the context', async function (assert) { + assert.expect(7); + + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot>' + + '<field name="customer" type="col" />' + + '<field name="foo" type="measure" />' + + '</pivot>', + groupBy: ['product_id'], + }); + + assert.containsOnce(pivot, 'thead .o_pivot_header_cell_opened', + 'column: should have one opened header'); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(First)').length, 1, + 'column: should display one closed header with "First"'); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(Second)').length, 1, + 'column: should display one closed header with "Second"'); + + assert.containsOnce(pivot, 'tbody .o_pivot_header_cell_opened', + 'row: should have one opened header'); + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xphone)').length, 1, + 'row: should display one closed header with "xphone"'); + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xpad)').length, 1, + 'row: should display one closed header with "xpad"'); + + assert.strictEqual(pivot.$('tbody tr:first td:nth(2)').text(), '32', + 'selected measure should be foo, with total 32'); + + pivot.destroy(); + }); + + QUnit.test('correctly uses pivot_row_groupby key with default groupBy from the context', async function (assert) { + assert.expect(6); + + this.data.partner.fields.amount = {string: "Amount", type: "float", group_operator: 'sum'}; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="customer" type="col"/>' + + '<field name="date" interval="day" type="row"/>' + + '</pivot>', + groupBy: ['customer'], + viewOptions: { + context: { + pivot_row_groupby: ['product_id'], + }, + }, + }); + + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1, + "column: should have one opened header"); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(First)').length, 1, + "column: should display one closed header with 'First'"); + assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(Second)').length, 1, + "column: should display one closed header with 'Second'"); + + // With pivot_row_groupby, groupBy customer should replace and eventually display product_id + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_opened').length, 1, + "row: should have one opened header"); + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xphone)').length, 1, + "row: should display one closed header with 'xphone'"); + assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xpad)').length, 1, + "row: should display one closed header with 'xpad'"); + + pivot.destroy(); + }); + + 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 = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot></pivot>', + mockRPC: function (route, args) { + if (args.method === 'read_group') { + assert.deepEqual(args.kwargs.fields, ['__count'], + "should make a read_group with field __count"); + } + return this._super(route, args); + }, + viewOptions: { + context: { + pivot_measures: ['__count__'], + }, + }, + }); + + var $countMeasure = pivot.$buttons.find('.dropdown-item[data-field=__count]:first'); + assert.hasClass($countMeasure,'selected', "The count measure should be activated"); + + pivot.destroy(); + }); + + QUnit.test('not use a many2one as a measure by default', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id"/>' + + '<field name="date" interval="month" type="col"/>' + + '</pivot>', + }); + assert.notOk(pivot.measures.product_id, + "should not have product_id as measure"); + pivot.destroy(); + }); + + QUnit.test('use a many2one as a measure with specified additional measure', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id"/>' + + '<field name="date" interval="month" type="col"/>' + + '</pivot>', + viewOptions: { + additionalMeasures: ['product_id'], + }, + }); + assert.ok(pivot.measures.product_id, + "should have product_id as measure"); + pivot.destroy(); + }); + + QUnit.test('pivot view with many2one field as a measure', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="measure"/>' + + '<field name="date" interval="month" type="col"/>' + + '</pivot>', + }); + + assert.strictEqual(pivot.$('table tbody tr').text().trim(), "Total2112", + "should display product_id count as measure"); + pivot.destroy(); + }); + + QUnit.test('m2o as measure, drilling down into data', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="measure"/>' + + '</pivot>', + }); + 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 + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"]:contains("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', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '</pivot>', + viewOptions: { + additionalMeasures: ['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)', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="measure"/>' + + '</pivot>', + }); + + 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.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', async function (assert) { + assert.expect(3); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + // Set a column groupby + 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')); + + // Set a Row groupby + 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 + await testUtils.pivot.reload(pivot, {domain: [['product_id', '=', 41]]}); + + var expectedContext = { + context: { + pivot_column_groupby: ['customer'], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id'], + }, + }; + + // Mock 'save as favorite' + assert.deepEqual(pivot.getOwnedQueryParams(), expectedContext, + 'The pivot view should have the right context'); + + var $xpadHeader = pivot.$('tbody .o_pivot_header_cell_closed[data-original-title=Product]'); + assert.equal($xpadHeader.length, 1, + 'There should be only one product line because of the domain'); + + assert.equal($xpadHeader.text(), 'xpad', + 'The product should be the right one'); + + pivot.destroy(); + }); + + QUnit.test('parallel data loading should discard all but the last one', async function (assert) { + assert.expect(2); + + var def; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'read_group') { + return Promise.resolve(def).then(_.constant(result)); + } + return result; + }, + }); + + def = testUtils.makeTestPromise(); + testUtils.pivot.reload(pivot, {groupBy: ['product_id']}); + testUtils.pivot.reload(pivot, {groupBy: ['product_id', 'customer']}); + await def.resolve(); + await testUtils.nextTick(); + assert.containsN(pivot, '.o_pivot_cell_value', 6, + "should have 6 cells"); + assert.containsN(pivot, 'tbody tr', 6, + "should have 6 rows"); + pivot.destroy(); + }); + + QUnit.test('pivot measures should be alphabetically sorted', async function (assert) { + assert.expect(2); + + var data = this.data; + // It's important to compare capitalized and lowercased words + // to be sure the sorting is effective with both of them + data.partner.fields.bouh = {string: "bouh", type: "integer", group_operator: 'sum'}; + data.partner.fields.modd = {string: "modd", type: "integer", group_operator: 'sum'}; + data.partner.fields.zip = {string: "Zip", type: "integer", group_operator: 'sum'}; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: data, + arch: '<pivot>' + + '<field name="zip" type="measure"/>' + + '<field name="foo" type="measure"/>' + + '<field name="bouh" type="measure"/>' + + '<field name="modd" type="measure"/>' + + '</pivot>', + }); + assert.strictEqual(pivot.$buttons.find('.o_pivot_measures_list .dropdown-item:first').data('field'), 'bouh', + "Bouh should be the first measure"); + assert.strictEqual(pivot.$buttons.find('.o_pivot_measures_list .dropdown-item:last').data('field'), '__count', + "Count should be the last measure"); + + pivot.destroy(); + }); + + QUnit.test('pivot view should use default order for auto sorting', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot default_order="foo asc">' + + '<field name="foo" type="measure"/>' + + '</pivot>', + }); + + assert.hasClass(pivot.$('thead tr:last th:last'), 'o_pivot_sort_order_asc', + "Last thead should be sorted in ascending order"); + + pivot.destroy(); + }); + + QUnit.test('pivot view can be flipped', async function (assert) { + assert.expect(5); + + var rpcCount = 0; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="product_id" type="row"/>' + + '</pivot>', + mockRPC: function () { + rpcCount++; + return this._super.apply(this, arguments); + }, + }); + + assert.containsN(pivot, 'tbody tr', 3, + "should have 3 rows: 1 for the open header, and 2 for data"); + var values = [ + "4", + "1", + "3" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + rpcCount = 0; + 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', + "should have 1 rows: 1 for the main header"); + + values = [ + "1", "3", "4" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + pivot.destroy(); + }); + + QUnit.test('rendering of pivot view with comparison', async function (assert) { + assert.expect(8); + + 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'; + this.data.partner.records[3].date = '2016-11-03'; + + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="month" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + archs: { + 'partner,false,search': ` + <search> + <filter name="date_filter" date="date" domain="[]" default_period='last_year'/> + </search> + `, + }, + viewOptions: { + additionalMeasures: ['product_id'], + context: { search_default_date_filter: 1 }, + }, + mockRPC: function () { + return this._super.apply(this, arguments); + }, + env: { + dataManager: { + create_filter: async function (filter) { + assert.deepEqual(filter.context, { + pivot_measures: ['__count'], + pivot_column_groupby: [], + pivot_row_groupby: ['product_id'], + group_by: [], + comparison: { + comparisonId: "previous_period", + comparisonRange: "[\"&\", [\"date\", \">=\", \"2016-11-01\"], [\"date\", \"<=\", \"2016-11-30\"]]", + comparisonRangeDescription: "November 2016", + fieldDescription: "Date", + fieldName: "date", + range: "[\"&\", [\"date\", \">=\", \"2016-12-01\"], [\"date\", \"<=\", \"2016-12-31\"]]", + rangeDescription: "December 2016" + }, + }); + } + } + }, + }); + + // with no data + await cpHelpers.toggleComparisonMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period'); + + assert.strictEqual(pivot.$('.o_pivot p.o_view_nocontent_empty_folder').length, 1); + + await cpHelpers.toggleFilterMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date'); + await cpHelpers.toggleMenuItemOption(pivot, 'Date', 'December'); + await cpHelpers.toggleMenuItemOption(pivot, 'Date', '2016'); + await cpHelpers.toggleMenuItemOption(pivot, 'Date', '2015'); + + assert.containsN(pivot, '.o_pivot thead tr:last th', 9, + "last header row should contains 9 cells (3*[December 2016, November 2016, Variation]"); + var values = [ + "19", "0", "-100%", "0", "13", "100%", "19", "13", "-31.58%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + // with data, with row groupby + await testUtils.dom.click(pivot.$('.o_pivot .o_pivot_header_cell_closed').eq(2)); + await testUtils.dom.click(pivot.$('.o_pivot .o_pivot_field_menu a[data-field="product_id"]')); + values = [ + "19", "0", "-100%", "0", "13", "100%", "19", "13", "-31.58%", + "19", "0", "-100%", "0", "1" , "100%", "19", "1", "-94.74%", + "0", "12", "100%", "0" , "12", "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + await testUtils.pivot.toggleMeasuresDropdown(pivot); + await testUtils.dom.click(pivot.$('.o_control_panel div.o_pivot_measures_list a[data-field="foo"]')); + await testUtils.dom.click(pivot.$('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"]')); + values = [ + "1", "0", "-100%", "0", "2", "100%", "1", "2", "100%", + "1", "0", "-100%", "0", "1", "100%", "1", "1", "0%", + "0", "1", "100%", "0", "1", "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + await testUtils.dom.click(pivot.$('.o_control_panel div.o_pivot_measures_list a[data-field="__count"]')); + await testUtils.dom.click(pivot.$('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"]')); + values = [ + "2", "0", "-100%", "0", "2", "100%", "2", "2", "0%", + "2", "0", "-100%", "0", "1", "100%", "2", "1", "-50%", + "0", "1", "100%", "0", "1", "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + await testUtils.dom.clickFirst(pivot.$('.o_pivot .o_pivot_header_cell_opened')); + values = [ + "2", "2", "0%", + "2", "1", "-50%", + "0", "1", "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + await cpHelpers.toggleFavoriteMenu(pivot); + await cpHelpers.toggleSaveFavorite(pivot); + await cpHelpers.editFavoriteName(pivot, 'Fav'); + await cpHelpers.saveFavorite(pivot); + + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('export data in excel with comparison', async function (assert) { + assert.expect(11); + + 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'; + this.data.partner.records[3].date = '2016-11-03'; + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="month" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + archs: { + 'partner,false,search': ` + <search> + <filter name="date_filter" date="date" domain="[]" default_period='antepenultimate_month'/> + </search> + `, + }, + viewOptions: { + context: { search_default_date_filter: 1 }, + }, + session: { + get_file: function (args) { + var data = JSON.parse(args.data.data); + _.each(data.col_group_headers, function (l) { + var titles = l.map(function (o) { + return o.title; + }); + assert.step(JSON.stringify(titles)); + }); + var measures = data.measure_headers.map(function (o) { + return o.title; + }); + assert.step(JSON.stringify(measures)); + var origins = data.origin_headers.map(function (o) { + return o.title; + }); + assert.step(JSON.stringify(origins)); + assert.step(String(data.measure_count)); + assert.step(String(data.origin_count)); + var valuesLength = data.rows.map(function (o) { + return o.values.length; + }); + assert.step(JSON.stringify(valuesLength)); + assert.strictEqual(args.url, '/web/pivot/export_xlsx', + "should call get_file with correct parameters"); + args.complete(); + }, + }, + }); + + // open comparison menu + await cpHelpers.toggleComparisonMenu(pivot); + // compare October 2016 to September 2016 + await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period'); + + // With the data above, the time ranges contain no record. + assert.strictEqual(pivot.$('.o_pivot p.o_view_nocontent_empty_folder').length, 1, "there should be no data"); + // export data should be impossible since the pivot buttons + // are deactivated (exception: the 'Measures' button). + assert.ok(pivot.$('.o_control_panel button.o_pivot_download').prop('disabled')); + + await cpHelpers.toggleFilterMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date'); + await cpHelpers.toggleMenuItemOption(pivot, 'Date', 'December'); + await cpHelpers.toggleMenuItemOption(pivot, 'Date', 'October'); + + // With the data above, the time ranges contain some records. + // export data. Should execute 'get_file' + await testUtils.dom.click(pivot.$('.o_control_panel button.o_pivot_download')); + + assert.verifySteps([ + // col group headers + '["Total",""]', + '["November 2016","December 2016"]', + // measure headers + '["Foo","Foo","Foo"]', + // origin headers + '["November 2016","December 2016","Variation","November 2016","December 2016"' + + ',"Variation","November 2016","December 2016","Variation"]', + // number of 'measures' + '1', + // number of 'origins' + '2', + // rows values length + '[9]', + ]); + + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('rendering of pivot view with comparison and count measure', async function (assert) { + assert.expect(2); + + var mockMock = false; + var nbReadGroup = 0; + + 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-12-22'; + this.data.partner.records[3].date = '2016-12-03'; + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot><field name="customer" type="row"/></pivot>', + archs: { + 'partner,false,search': ` + <search> + <filter name="date_filter" date="date" domain="[]" default_period='this_month'/> + </search> + `, + }, + viewOptions: { + context: { search_default_date_filter: 1 }, + }, + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'read_group' && mockMock) { + nbReadGroup++; + if (nbReadGroup === 4) { + // 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 Promise.resolve([{}]); + } + } + return result; + }, + }); + + mockMock = true; + + // compare December 2016 to November 2016 + await cpHelpers.toggleComparisonMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period'); + + var values = [ + "0", "4", "100%", + "0", "2", "100%", + "0", "2", "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join(',')); + assert.strictEqual(pivot.$('.o_pivot_header_cell_closed').length, 3, + "there should be exactly three closed header ('Total','First', 'Second')"); + + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('can sort a pivot view with comparison by clicking on header', async function (assert) { + assert.expect(6); + + 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'; + this.data.partner.records[3].date = '2016-11-03'; + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="day" type="row"/>' + + '<field name="company_type" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + archs: { + 'partner,false,search': ` + <search> + <filter name="date_filter" date="date" domain="[]" default_period='this_month'/> + </search> + `, + }, + viewOptions: { + additionalMeasures: ['product_id'], + context: { search_default_date_filter: 1 }, + }, + }); + + // compare December 2016 to November 2016 + await cpHelpers.toggleComparisonMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period'); + + // initial sanity check + var values = [ + "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%", + "17", "0", "-100%", "17", "0", "-100%", + "2", "0", "-100%", "2", "0", "-100%", + "0", "12" , "100%", "0", "12" , "100%", + "0", "1", "100%" , "0" , "1" , "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + // click on 'Foo' in column Total/Company (should sort by the period of interest, ASC) + await testUtils.dom.click(pivot.$('.o_pivot_measure_row').eq(0)); + values = [ + "17", "12", "-29.41%", "2", "1", "-50%" , "19", "13", "-31.58%", + "2", "0", "-100%", "2", "0", "-100%", + "0", "12", "100%", "0", "12", "100%", + "0", "1", "100%", "0", "1", "100%", + "17", "0", "-100%", "17", "0", "-100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + // click again on 'Foo' in column Total/Company (should sort by the period of interest, DESC) + await testUtils.dom.click(pivot.$('.o_pivot_measure_row').eq(0)); + values = [ + "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%", + "17", "0", "-100%", "17", "0", "-100%", + "2", "0", "-100%", "2", "0", "-100%", + "0", "12", "100%", "0", "12", "100%", + "0", "1", "100%", "0", "1", "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + // click on 'This Month' in column Total/Individual/Foo + await testUtils.dom.click(pivot.$('.o_pivot_origin_row').eq(3)); + values = [ + "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%", + "17", "0", "-100%", "17", "0", "-100%", + "0", "12", "100%", "0", "12" , "100%", + "0", "1", "100%", "0", "1", "100%", + "2", "0", "-100%", "2", "0", "-100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + // click on 'Previous Period' in column Total/Individual/Foo + await testUtils.dom.click(pivot.$('.o_pivot_origin_row').eq(4)); + values = [ + "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%", + "17", "0", "-100%", "17", "0", "-100%", + "2", "0", "-100%", "2", "0", "-100%", + "0", "12", "100%", "0", "12", "100%", + "0", "1", "100%", "0", "1", "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + // click on 'Variation' in column Total/Foo + await testUtils.dom.click(pivot.$('.o_pivot_origin_row').eq(8)); + values = [ + "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%", + "17", "0", "-100%", "17", "0", "-100%", + "2", "0", "-100%", "2" , "0" , "-100%", + "0", "12", "100%", "0", "12" , "100%", + "0", "1", "100%", "0" , "1", "100%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('Click on the measure list but not on a menu item', async function (assert) { + assert.expect(2); + + const pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: `<pivot/>`, + }); + + // open the "Measures" menu + await testUtils.dom.click(pivot.el.querySelector('.o_cp_buttons button')); + + // click on the divider in the "Measures" menu does not crash + await testUtils.dom.click(pivot.el.querySelector('.o_pivot_measures_list .dropdown-divider')); + // the menu should still be open + assert.isVisible(pivot.el.querySelector('.o_pivot_measures_list')); + + // click on the measure list but not on a menu item or the separator + await testUtils.dom.click(pivot.el.querySelector('.o_pivot_measures_list')); + // the menu should still be open + assert.isVisible(pivot.el.querySelector('.o_pivot_measures_list')); + + pivot.destroy(); + }); + + QUnit.test('Navigation list view for a group and back with breadcrumbs', async function (assert) { + assert.expect(16); + // create an action manager to test the interactions with the search view + var readGroupCount = 0; + + var actionManager = await createActionManager({ + data: this.data, + archs: { + 'partner,false,pivot': '<pivot>' + + '<field name="customer" type="row"/>' + + '</pivot>', + 'partner,false,search': '<search><filter name="bayou" string="Bayou" domain="[(\'foo\',\'=\', 12)]"/></search>', + 'partner,false,list': '<tree><field name="foo"/></tree>', + 'partner,false,form': '<form><field name="foo"/></form>', + }, + intercepts: { + do_action: function (event) { + var action = event.data.action; + actionManager.doAction(action); + } + }, + mockRPC: function (route, args) { + if (args.method === 'read_group') { + assert.step('read_group'); + const domain = args.kwargs.domain; + if ([0,1].indexOf(readGroupCount) !== -1) { + assert.deepEqual(domain, [], 'domain empty'); + } else if ([2,3,4,5].indexOf(readGroupCount) !== -1) { + assert.deepEqual(domain, [['foo', '=', 12]], + 'domain conserved when back with breadcrumbs'); + } + readGroupCount++; + } + if (route === '/web/dataset/search_read') { + assert.step('search_read'); + const domain = args.domain; + assert.deepEqual(domain, ['&', ['customer', '=', 1], ['foo', '=', 12]], + 'list domain is correct'); + } + return this._super.apply(this, arguments); + }, + }); + + await actionManager.doAction({ + res_model: 'partner', + type: 'ir.actions.act_window', + views: [[false, 'pivot']], + }); + + + await cpHelpers.toggleFilterMenu(actionManager); + await cpHelpers.toggleMenuItem(actionManager, 0); + await testUtils.nextTick(); + + await testUtilsDom.click(actionManager.$('.o_pivot_cell_value:nth(1)')); + await testUtils.nextTick(); + + assert.containsOnce(actionManager, '.o_list_view'); + + await testUtilsDom.click(actionManager.$('.o_control_panel ol.breadcrumb li.breadcrumb-item').eq(0)); + + assert.verifySteps([ + 'read_group', 'read_group', + 'read_group', 'read_group', + 'search_read', + 'read_group', 'read_group']); + actionManager.destroy(); + }); + + QUnit.test('Cell values are kept when flippin a pivot view in comparison mode', async function (assert) { + assert.expect(2); + + 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'; + this.data.partner.records[3].date = '2016-11-03'; + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="day" type="row"/>' + + '<field name="company_type" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + archs: { + 'partner,false,search': ` + <search> + <filter name="date_filter" date="date" domain="[]" default_period='this_month'/> + </search> + `, + }, + viewOptions: { + additionalMeasures: ['product_id'], + context: { search_default_date_filter: 1 }, + }, + }); + + // compare December 2016 to November 2016 + await cpHelpers.toggleComparisonMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period'); + + // initial sanity check + var values = [ + "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%", + "17", "0", "-100%", "17", "0", "-100%", + "2", "0", "-100%", "2", "0", "-100%", + "0", "12", "100%", "0", "12", "100%", + "0", "1", "100%", "0", "1", "100%", + + + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + // flip table + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button')); + + values = [ + "17", "0", "-100%", "2", "0", "-100%", "0", "12", "100%", "0", "1", "100%", "19", "13", "-31.58%", + "17", "0", "-100%", "0", "12", "100%", "17", "12", "-29.41%", + "2", "0", "-100%", "0", "1", "100%", "2", "1", "-50%" + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('Flip then compare, table col groupbys are kept', async function (assert) { + assert.expect(6); + + 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'; + this.data.partner.records[3].date = '2016-11-03'; + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="day" type="row"/>' + + '<field name="company_type" type="col"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + archs: { + 'partner,false,search': ` + <search> + <filter name="date_filter" date="date" domain="[]" default_period='this_month'/> + </search> + `, + }, + viewOptions: { + additionalMeasures: ['product_id'], + }, + }); + + + assert.strictEqual( + pivot.$('th').slice(0, 5).text(), + [ + '', 'Total', '', + 'Company', 'individual', + ].join(''), + "The col headers should be as expected" + ); + assert.strictEqual( + pivot.$('th').slice(8).text(), + [ + 'Total', + '2016-12-15', + '2016-12-17', + '2016-11-22', + '2016-11-03' + ].join(''), + "The row headers should be as expected" + ); + + // flip + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button')); + + assert.strictEqual( + pivot.$('th').slice(0, 7).text(), + [ + '', 'Total', '', + '2016-12-15', '2016-12-17', '2016-11-22', '2016-11-03', + ].join(''), + "The col headers should be as expected" + ); + assert.strictEqual( + pivot.$('th').slice(12).text(), + [ + 'Total', + 'Company', + 'individual' + + ].join(''), + "The row headers should be as expected" + ); + + // Filter on December 2016 + await cpHelpers.toggleFilterMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date'); + await cpHelpers.toggleMenuItemOption(pivot, 'Date', 'December'); + + // compare December 2016 to November 2016 + await cpHelpers.toggleComparisonMenu(pivot); + await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period'); + + assert.strictEqual( + pivot.$('th').slice(0, 7).text(), + [ + '', 'Total', '', + '2016-11-22', '2016-11-03', '2016-12-15', '2016-12-17', + ].join(''), + "The col headers should be as expected" + ); + assert.strictEqual( + pivot.$('th').slice(27).text(), + [ + 'Total', + 'Company', + 'individual' + + ].join(''), + "The row headers should be as expected" + ); + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('correctly compute group domain when a date field has false value', async function (assert) { + assert.expect(1); + + this.data.partner.records.forEach(r => r.date = false); + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot o_enable_linking="1">' + + '<field name="date" interval="day" type="row"/>' + + '</pivot>', + intercepts: { + do_action: function (ev) { + assert.deepEqual(ev.data.action.domain, [['date', '=', false]]); + }, + }, + }); + + await testUtils.dom.click($('div .o_value')[1]); + + unpatchDate(); + pivot.destroy(); + }); + QUnit.test('Does not identify "false" with false as keys when creating group trees', async function (assert) { + assert.expect(2); + + this.data.partner.fields.favorite_animal = {string: "Favorite animal", type: "char", store: true}; + this.data.partner.records[0].favorite_animal = 'Dog'; + this.data.partner.records[1].favorite_animal = 'false'; + this.data.partner.records[2].favorite_animal = 'Undefined'; + + var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot o_enable_linking="1">' + + '<field name="favorite_animal" type="row"/>' + + '</pivot>', + + }); + + assert.strictEqual( + pivot.$('th').slice(0, 2).text(), + [ + '', 'Total', '', + ].join(''), + "The col headers should be as expected" + ); + assert.strictEqual( + pivot.$('th').slice(3).text(), + [ + 'Total', + 'Dog', + 'false', + 'Undefined', + 'Undefined' + + ].join(''), + "The row headers should be as expected" + ); + + unpatchDate(); + pivot.destroy(); + }); + + QUnit.test('group bys added via control panel and expand Header do not stack', async function (assert) { + assert.expect(8); + + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + viewOptions: { + additionalMeasures: ['product_id'], + }, + }); + + assert.strictEqual( + pivot.$('th').slice(0, 2).text(), + [ + '', 'Total', + ].join(''), + "The col headers should be as expected" + ); + assert.strictEqual( + pivot.$('th').slice(3).text(), + [ + 'Total', + ].join(''), + "The row headers should be as expected" + ); + + + // open group by menu and add new groupby + await cpHelpers.toggleGroupByMenu(pivot); + await cpHelpers.toggleAddCustomGroup(pivot); + await cpHelpers.applyGroup(pivot); + + assert.strictEqual( + pivot.$('th').slice(0, 2).text(), + [ + '', 'Total', + ].join(''), + "The col headers should be as expected" + ); + assert.strictEqual( + pivot.$('th').slice(3).text(), + [ + 'Total', + 'Company', + 'individual' + ].join(''), + "The row headers should be as expected" + ); + + // Set a Row groupby + await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed').eq(0)); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]:first')); + + assert.strictEqual( + pivot.$('th').slice(0, 2).text(), + [ + '', 'Total', + ].join(''), + "The col headers should be as expected" + ); + assert.strictEqual( + pivot.$('th').slice(3).text(), + [ + 'Total', + 'Company', + 'xphone', + 'xpad', + 'individual' + ].join(''), + "The row headers should be as expected" + ); + + // open groupby menu generator and add a new groupby + await cpHelpers.toggleGroupByMenu(pivot); + await cpHelpers.toggleAddCustomGroup(pivot); + await cpHelpers.selectGroup(pivot, 'bar'); + await cpHelpers.applyGroup(pivot); + + assert.strictEqual( + pivot.$('th').slice(0, 2).text(), + [ + '', 'Total', + ].join(''), + "The col headers should be as expected" + ); + assert.strictEqual( + pivot.$('th').slice(3).text(), + [ + 'Total', + 'Company', + 'true', + 'individual', + 'true', + 'Undefined' + ].join(''), + "The row headers should be as expected" + ); + + pivot.destroy(); + }); + + QUnit.test('display only one dropdown menu', async function (assert) { + assert.expect(1); + + var pivot = await createView({ + View: PivotView, + model: 'partner', + data: this.data, + arch: '<pivot>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + viewOptions: { + additionalMeasures: ['product_id'], + }, + }); + await testUtils.dom.clickFirst(pivot.$('th.o_pivot_header_cell_closed')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]:first')); + + // Click on the two dropdown + await testUtils.dom.click(pivot.$('th.o_pivot_header_cell_closed')[0]); + await testUtils.dom.click(pivot.$('th.o_pivot_header_cell_closed')[1]); + + assert.containsOnce(pivot, '.o_pivot_field_menu', 'Only one dropdown should be displayed at a time'); + + pivot.destroy(); + }); + + QUnit.test('Server order is kept by default', async function (assert) { + assert.expect(1); + + let isSecondReadGroup = false; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="customer" type="row"/>' + + '<field name="foo" type="measure"/>' + + '</pivot>', + mockRPC: function (route, args) { + if (args.method === 'read_group' && isSecondReadGroup) { + return Promise.resolve([ + { + customer: [2, 'Second'], + foo: 18, + __count: 2, + __domain :[["customer", "=", 2]], + }, + { + customer: [1, 'First'], + foo: 14, + __count: 2, + __domain :[["customer", "=", 1]], + } + ]); + } + var result = this._super.apply(this, arguments); + isSecondReadGroup = true; + return result; + }, + }); + + const values = [ + "32", // Total Value + "18", // Second + "14", // First + ]; + assert.strictEqual(getCurrentValues(pivot), values.join()); + + pivot.destroy(); + }); + + QUnit.test('pivot rendering with boolean field', async function (assert) { + assert.expect(4); + + this.data.partner.fields.bar = {string: "bar", type: "boolean", store: true, searchable: true, group_operator: 'bool_or'}; + this.data.partner.records = [{id: 1, bar: true, date: '2019-12-14'}, {id: 2, bar: false, date: '2019-05-14'}]; + + var pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" type="row" interval="day"/>' + + '<field name="bar" type="col"/>' + + '<field name="bar" string="SLA status Failed" type="measure"/>' + + '</pivot>', + }); + + assert.strictEqual(pivot.$('tbody tr:contains("2019-12-14")').length, 1, 'There should be a first column'); + assert.ok(pivot.$('tbody tr:contains("2019-12-14") [type="checkbox"]').is(':checked'), 'first column contains checkbox and value should be ticked'); + assert.strictEqual(pivot.$('tbody tr:contains("2019-05-14")').length, 1, 'There should be a second column'); + assert.notOk(pivot.$('tbody tr:contains("2019-05-14") [type="checkbox"]').is(':checked'), "second column should have checkbox that is not checked by default"); + + pivot.destroy(); + }); + + QUnit.test('Allow to add behaviour to buttons on pivot', async function (assert) { + assert.expect(2); + + let _testButtons = (ev) => { + if ($(ev.target).hasClass("o_pivot_flip_button")) { + assert.step("o_pivot_flip_button") + } + } + + PivotController.include({ + _addIncludedButtons: async function (ev) { + await this._super(...arguments); + _testButtons(ev); + }, + }); + + const pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" type="row" interval="day"/>' + + '<field name="bar" type="col"/>' + + '</pivot>', + }); + await testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button')); + assert.verifySteps(["o_pivot_flip_button"]); + _testButtons = () => true; + pivot.destroy(); + }); + + QUnit.test('empty pivot view with action helper', async function (assert) { + assert.expect(4); + + const pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: ` + <pivot> + <field name="product_id" type="measure"/> + <field name="date" interval="month" type="col"/> + </pivot>`, + domain: [['id', '<', 0]], + viewOptions: { + action: { + help: '<p class="abc">click to add a foo</p>' + } + }, + }); + + assert.containsOnce(pivot, '.o_view_nocontent .abc'); + assert.containsNone(pivot, 'table'); + + await pivot.reload({ domain: [] }); + + assert.containsNone(pivot, '.o_view_nocontent .abc'); + assert.containsOnce(pivot, 'table'); + + pivot.destroy(); + }); + + QUnit.test('empty pivot view with sample data', async function (assert) { + assert.expect(7); + + const pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: ` + <pivot sample="1"> + <field name="product_id" type="measure"/> + <field name="date" interval="month" type="col"/> + </pivot>`, + domain: [['id', '<', 0]], + viewOptions: { + action: { + help: '<p class="abc">click to add a foo</p>' + } + }, + }); + + assert.hasClass(pivot.el, 'o_view_sample_data'); + assert.containsOnce(pivot, '.o_view_nocontent .abc'); + assert.containsOnce(pivot, 'table.o_sample_data_disabled'); + + await pivot.reload({ domain: [] }); + + assert.doesNotHaveClass(pivot.el, 'o_view_sample_data'); + assert.containsNone(pivot, '.o_view_nocontent .abc'); + assert.containsOnce(pivot, 'table'); + assert.doesNotHaveClass(pivot.$('table'), 'o_sample_data_disabled'); + + pivot.destroy(); + }); + + QUnit.test('non empty pivot view with sample data', async function (assert) { + assert.expect(7); + + const pivot = await createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: ` + <pivot sample="1"> + <field name="product_id" type="measure"/> + <field name="date" interval="month" type="col"/> + </pivot>`, + viewOptions: { + action: { + help: '<p class="abc">click to add a foo</p>' + } + }, + }); + + assert.doesNotHaveClass(pivot.el, 'o_view_sample_data'); + assert.containsNone(pivot, '.o_view_nocontent .abc'); + assert.containsOnce(pivot, 'table'); + assert.doesNotHaveClass(pivot.$('table'), 'o_sample_data_disabled'); + + await pivot.reload({ domain: [['id', '<', 0]] }); + + assert.doesNotHaveClass(pivot.el, 'o_view_sample_data'); + assert.containsOnce(pivot, '.o_view_nocontent .abc'); + assert.containsNone(pivot, 'table'); + + pivot.destroy(); + }); +}); +}); |
