summaryrefslogtreecommitdiff
path: root/addons/web/static/tests/views/search_panel_tests.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/tests/views/search_panel_tests.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/web/static/tests/views/search_panel_tests.js')
-rw-r--r--addons/web/static/tests/views/search_panel_tests.js4173
1 files changed, 4173 insertions, 0 deletions
diff --git a/addons/web/static/tests/views/search_panel_tests.js b/addons/web/static/tests/views/search_panel_tests.js
new file mode 100644
index 00000000..cf2cd4b4
--- /dev/null
+++ b/addons/web/static/tests/views/search_panel_tests.js
@@ -0,0 +1,4173 @@
+odoo.define("web/static/tests/views/search_panel_tests.js", function (require) {
+"use strict";
+
+const FormView = require('web.FormView');
+const KanbanView = require('web.KanbanView');
+const ListView = require('web.ListView');
+const testUtils = require('web.test_utils');
+const SearchPanel = require("web/static/src/js/views/search_panel.js");
+
+const cpHelpers = testUtils.controlPanel;
+const createActionManager = testUtils.createActionManager;
+const createView = testUtils.createView;
+
+/**
+ * Return the list of counters displayed in the search panel (if any).
+ * @param {Widget} view, view controller
+ * @returns {number[]}
+ */
+function getCounters(view) {
+ return [...view.el.querySelectorAll('.o_search_panel_counter')].map(
+ counter => Number(counter.innerText.trim())
+ );
+}
+
+/**
+ * Fold/unfold the category value (with children)
+ * @param {Widget} widget
+ * @param {string} text
+ * @returns {Promise}
+ */
+function toggleFold(widget, text) {
+ const headers = [...widget.el.querySelectorAll(".o_search_panel_category_value header")];
+ const target = headers.find(
+ (header) => header.innerText.trim().startsWith(text)
+ );
+ return testUtils.dom.click(target);
+}
+
+QUnit.module('Views', {
+ beforeEach: function () {
+ this.data = {
+ partner: {
+ fields: {
+ foo: {string: "Foo", type: 'char'},
+ bar: {string: "Bar", type: 'boolean'},
+ int_field: {string: "Int Field", type: 'integer', group_operator: 'sum'},
+ company_id: {string: "company", type: 'many2one', relation: 'company'},
+ company_ids: { string: "Companies", type: 'many2many', relation: 'company' },
+ category_id: { string: "category", type: 'many2one', relation: 'category' },
+ state: { string: "State", type: 'selection', selection: [['abc', "ABC"], ['def', "DEF"], ['ghi', "GHI"]]},
+ },
+ records: [
+ {id: 1, bar: true, foo: "yop", int_field: 1, company_ids: [3], company_id: 3, state: 'abc', category_id: 6},
+ {id: 2, bar: true, foo: "blip", int_field: 2, company_ids: [3], company_id: 5, state: 'def', category_id: 7},
+ {id: 3, bar: true, foo: "gnap", int_field: 4, company_ids: [], company_id: 3, state: 'ghi', category_id: 7},
+ {id: 4, bar: false, foo: "blip", int_field: 8, company_ids: [5], company_id: 5, state: 'ghi', category_id: 7},
+ ]
+ },
+ company: {
+ fields: {
+ name: {string: "Display Name", type: 'char'},
+ parent_id: {string: 'Parent company', type: 'many2one', relation: 'company'},
+ category_id: {string: 'Category', type: 'many2one', relation: 'category'},
+ },
+ records: [
+ {id: 3, name: "asustek", category_id: 6},
+ {id: 5, name: "agrolait", category_id: 7},
+ ],
+ },
+ category: {
+ fields: {
+ name: {string: "Category Name", type: 'char'},
+ },
+ records: [
+ {id: 6, name: "gold"},
+ {id: 7, name: "silver"},
+ ]
+ },
+ };
+
+ this.actions = [{
+ id: 1,
+ name: 'Partners',
+ res_model: 'partner',
+ type: 'ir.actions.act_window',
+ views: [[false, 'kanban'], [false, 'list'], [false, 'pivot'], [false, 'form']],
+ }, {
+ id: 2,
+ name: 'Partners',
+ res_model: 'partner',
+ type: 'ir.actions.act_window',
+ views: [[false, 'form']],
+ }];
+
+ this.archs = {
+ 'partner,false,list': '<tree><field name="foo"/></tree>',
+ 'partner,false,kanban':
+ `<kanban>
+ <templates>
+ <div t-name="kanban-box" class="oe_kanban_global_click">
+ <field name="foo"/>
+ </div>
+ </templates>
+ </kanban>`,
+ 'partner,false,form':
+ `<form>
+ <button name="1" type="action" string="multi view"/>
+ <field name="foo"/>
+ </form>`,
+ 'partner,false,pivot': '<pivot><field name="int_field" type="measure"/></pivot>',
+ 'partner,false,search':
+ `<search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1" expand="1"/>
+ <field name="category_id" select="multi" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ };
+ },
+}, function () {
+
+ QUnit.module('SearchPanel');
+
+ QUnit.test('basic rendering', async function (assert) {
+ assert.expect(16);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method + ' on ' + args.model);
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ <field name="category_id" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.containsOnce(kanban, '.o_content.o_controller_with_searchpanel > .o_search_panel');
+ assert.containsOnce(kanban, '.o_content.o_controller_with_searchpanel > .o_kanban_view');
+
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ assert.containsN(kanban, '.o_search_panel_section', 2);
+
+ var $firstSection = kanban.$('.o_search_panel_section:first');
+ assert.hasClass($firstSection.find('.o_search_panel_section_header i'), 'fa-folder');
+ assert.containsOnce($firstSection, '.o_search_panel_section_header:contains(company)');
+ assert.containsN($firstSection, '.o_search_panel_category_value', 3);
+ assert.containsOnce($firstSection, '.o_search_panel_category_value:first .active');
+ assert.strictEqual($firstSection.find('.o_search_panel_category_value').text().replace(/\s/g, ''),
+ 'Allasustek2agrolait2');
+
+ var $secondSection = kanban.$('.o_search_panel_section:nth(1)');
+ assert.hasClass($secondSection.find('.o_search_panel_section_header i'), 'fa-filter');
+ assert.containsOnce($secondSection, '.o_search_panel_section_header:contains(category)');
+ assert.containsN($secondSection, '.o_search_panel_filter_value', 2);
+ assert.strictEqual($secondSection.find('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'gold1silver3');
+
+ assert.verifySteps([
+ 'search_panel_select_range on partner',
+ 'search_panel_select_multi_range on partner',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('sections with custom icon and color', async function (assert) {
+ assert.expect(4);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" icon="fa-car" color="blue" enable_counters="1"/>
+ <field name="state" select="multi" icon="fa-star" color="#000" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.hasClass(kanban.$('.o_search_panel_section_header:first i'), 'fa-car');
+ assert.hasAttrValue(kanban.$('.o_search_panel_section_header:first i'), 'style="{color: blue}"');
+ assert.hasClass(kanban.$('.o_search_panel_section_header:nth(1) i'), 'fa-star');
+ assert.hasAttrValue(kanban.$('.o_search_panel_section_header:nth(1) i'), 'style="{color: #000}"');
+
+ kanban.destroy();
+ });
+
+ QUnit.test('sections with attr invisible="1" are ignored', async function (assert) {
+ // 'groups' attributes are converted server-side into invisible="1" when the user doesn't
+ // belong to the given group
+ assert.expect(3);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `<kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ <field name="state" select="multi" invisible="1" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method || route);
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ assert.containsOnce(kanban, '.o_search_panel_section');
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('categories and filters order is kept', async function (assert) {
+ assert.expect(4);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ <field name="category_id" select="multi" enable_counters="1"/>
+ <field name="state" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ }
+ });
+
+ assert.containsN(kanban, '.o_search_panel_section', 3);
+ assert.strictEqual(kanban.$('.o_search_panel_section_header:nth(0)').text().trim(),
+ 'company');
+ assert.strictEqual(kanban.$('.o_search_panel_section_header:nth(1)').text().trim(),
+ 'category');
+ assert.strictEqual(kanban.$('.o_search_panel_section_header:nth(2)').text().trim(),
+ 'State');
+
+ kanban.destroy();
+ });
+
+ QUnit.test('specify active category value in context and manually change category', async function (assert) {
+ assert.expect(5);
+
+ const kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ <field name="state" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return this._super.apply(this, arguments);
+ },
+ context: {
+ searchpanel_default_company_id: false,
+ searchpanel_default_state: 'ghi',
+ },
+ });
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value header.active label')].map(
+ el => el.innerText
+ ),
+ ['All', 'GHI']
+ );
+
+ // select 'ABC' in the category 'state'
+ await testUtils.dom.click(kanban.el.querySelectorAll('.o_search_panel_category_value header')[4]);
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value header.active label')].map(
+ el => el.innerText
+ ),
+ ['All', 'ABC']
+ );
+
+ assert.verifySteps([
+ '[["state","=","ghi"]]',
+ '[["state","=","abc"]]'
+ ]);
+ kanban.destroy();
+ });
+
+ QUnit.test('use category (on many2one) to refine search', async function (assert) {
+ assert.expect(14);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ domain: [['bar', '=', true]],
+ });
+
+ // select 'asustek'
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ // select 'agrolait'
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // select 'All'
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:first .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ assert.verifySteps([
+ '[["bar","=",true]]',
+ '["&",["bar","=",true],["company_id","child_of",3]]',
+ '["&",["bar","=",true],["company_id","child_of",5]]',
+ '[["bar","=",true]]',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('use category (on selection) to refine search', async function (assert) {
+ assert.expect(14);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ // select 'abc'
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // select 'ghi'
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(3) header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(3) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ // select 'All' again
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:first .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ assert.verifySteps([
+ '[]',
+ '[["state","=","abc"]]',
+ '[["state","=","ghi"]]',
+ '[]',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('category has been archived', async function (assert) {
+ assert.expect(2);
+
+ this.data.company.fields.active = {type: 'boolean', string: 'Archived'};
+ this.data.company.records = [
+ {
+ name: 'Company 5',
+ id: 5,
+ active: true,
+ }, {
+ name: 'child of 5 archived',
+ parent_id: 5,
+ id: 666,
+ active: false,
+ }, {
+ name: 'child of 666',
+ parent_id: 666,
+ id: 777,
+ active: true,
+ }
+ ];
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ mockRPC: async function (route, args) {
+ if (route === '/web/dataset/call_kw/partner/search_panel_select_range') {
+ var results = await this._super.apply(this, arguments);
+ results.values = results.values.filter(rec => rec.active !== false);
+ return Promise.resolve(results);
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ assert.containsN(kanban, '.o_search_panel_category_value', 2,
+ 'The number of categories should be 2: All and Company 5');
+
+ assert.containsNone(kanban, '.o_toggle_fold > i',
+ 'None of the categories should have children');
+
+ kanban.destroy();
+ });
+
+ QUnit.test('use two categories to refine search', async function (assert) {
+ assert.expect(14);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ <field name="state" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ domain: [['bar', '=', true]],
+ });
+
+ assert.containsN(kanban, '.o_search_panel_section', 2);
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // select 'asustek'
+ await testUtils.dom.click(
+ [
+ ...kanban.el.querySelectorAll('.o_search_panel_category_value header .o_search_panel_label_title')
+ ]
+ .filter(el => el.innerText === 'asustek')
+ );
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ // select 'abc'
+ await testUtils.dom.click(
+ [
+ ...kanban.el.querySelectorAll('.o_search_panel_category_value header .o_search_panel_label_title')
+ ]
+ .filter(el => el.innerText === 'ABC')
+ );
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // select 'ghi'
+ await testUtils.dom.click(
+ [
+ ...kanban.el.querySelectorAll('.o_search_panel_category_value header .o_search_panel_label_title')
+ ]
+ .filter(el => el.innerText === 'GHI')
+ );
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // select 'All' in first category (company_id)
+ await testUtils.dom.click(kanban.$('.o_search_panel_section:first .o_search_panel_category_value:first header'));
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // select 'All' in second category (state)
+ await testUtils.dom.click(kanban.$('.o_search_panel_section:nth(1) .o_search_panel_category_value:first header'));
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ assert.verifySteps([
+ '[["bar","=",true]]',
+ '["&",["bar","=",true],["company_id","child_of",3]]',
+ '["&",["bar","=",true],"&",["company_id","child_of",3],["state","=","abc"]]',
+ '["&",["bar","=",true],"&",["company_id","child_of",3],["state","=","ghi"]]',
+ '["&",["bar","=",true],["state","=","ghi"]]',
+ '[["bar","=",true]]',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('category with parent_field', async function (assert) {
+ assert.expect(33);
+
+ this.data.company.records.push({id: 40, name: 'child company 1', parent_id: 5});
+ this.data.company.records.push({id: 41, name: 'child company 2', parent_id: 5});
+ this.data.partner.records[1].company_id = 40;
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ // 'All' is selected by default
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:first .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+ assert.containsN(kanban, '.o_search_panel_category_value', 3);
+ assert.containsOnce(kanban, '.o_search_panel_category_value .o_toggle_fold > i');
+
+ // unfold parent category and select 'All' again
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) > header'));
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:first > header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:first .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+ assert.containsN(kanban, '.o_search_panel_category_value', 5);
+ assert.containsN(kanban, '.o_search_panel_category_value .o_search_panel_category_value', 2);
+
+ // click on first child company
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value .o_search_panel_category_value:first header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value .o_search_panel_category_value:first .active');
+ assert.containsOnce(kanban, '.o_kanban_record:not(.o_kanban_ghost)');
+
+ // click on parent company
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) > header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsOnce(kanban, '.o_kanban_record:not(.o_kanban_ghost)');
+
+ // fold parent company by clicking on it
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) > header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // parent company should be folded
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsN(kanban, '.o_search_panel_category_value', 3);
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // fold category with children
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) > header'));
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) > header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsN(kanban, '.o_search_panel_category_value', 3);
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ assert.verifySteps([
+ '[]',
+ '[["company_id","child_of",5]]',
+ '[]',
+ '[["company_id","child_of",40]]',
+ '[["company_id","child_of",5]]',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('category with no parent_field', async function (assert) {
+ assert.expect(10);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="category_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ // 'All' is selected by default
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:first .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+ assert.containsN(kanban, '.o_search_panel_category_value', 3);
+
+ // click on 'gold' category
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active');
+ assert.containsOnce(kanban, '.o_kanban_record:not(.o_kanban_ghost)');
+
+ assert.verifySteps([
+ '[]',
+ '[["category_id","=",6]]', // must use '=' operator (instead of 'child_of')
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('can (un)fold parent category values', async function (assert) {
+ assert.expect(7);
+
+ this.data.company.records.push({id: 40, name: 'child company 1', parent_id: 5});
+ this.data.company.records.push({id: 41, name: 'child company 2', parent_id: 5});
+ this.data.partner.records[1].company_id = 40;
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.strictEqual(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold > i').length, 1,
+ "'agrolait' should be displayed as a parent category value");
+ assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold > i'), 'fa-caret-right',
+ "'agrolait' should be folded");
+ assert.containsN(kanban, '.o_search_panel_category_value', 3);
+
+ // unfold agrolait
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold > i'));
+ assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold > i'), 'fa-caret-down',
+ "'agrolait' should be open");
+ assert.containsN(kanban, '.o_search_panel_category_value', 5);
+
+ // fold agrolait
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold > i'));
+ assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold > i'), 'fa-caret-right',
+ "'agrolait' should be folded");
+ assert.containsN(kanban, '.o_search_panel_category_value', 3);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('fold status is kept at reload', async function (assert) {
+ assert.expect(4);
+
+ this.data.company.records.push({id: 40, name: 'child company 1', parent_id: 5});
+ this.data.company.records.push({id: 41, name: 'child company 2', parent_id: 5});
+ this.data.partner.records[1].company_id = 40;
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ // unfold agrolait
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:contains(agrolait) > header'));
+ assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold > i'), 'fa-caret-down',
+ "'agrolait' should be open");
+ assert.containsN(kanban, '.o_search_panel_category_value', 5);
+
+ await kanban.reload({});
+
+ assert.hasClass(kanban.$('.o_search_panel_category_value:contains(agrolait) .o_toggle_fold > i'), 'fa-caret-down',
+ "'agrolait' should be open");
+ assert.containsN(kanban, '.o_search_panel_category_value', 5);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('concurrency: delayed search_reads', async function (assert) {
+ assert.expect(19);
+
+ var def;
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ return Promise.resolve(def).then(_.constant(result));
+ }
+ return result;
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ domain: [['bar', '=', true]],
+ });
+
+ // 'All' should be selected by default
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:first .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // select 'asustek' (delay the reload)
+ def = testUtils.makeTestPromise();
+ var asustekDef = def;
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ // 'asustek' should be selected, but there should still be 3 records
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // select 'agrolait' (delay the reload)
+ def = testUtils.makeTestPromise();
+ var agrolaitDef = def;
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header'));
+
+ // 'agrolait' should be selected, but there should still be 3 records
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // unlock asustek search (should be ignored, so there should still be 3 records)
+ asustekDef.resolve();
+ await testUtils.nextTick();
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // unlock agrolait search, there should now be 1 record
+ agrolaitDef.resolve();
+ await testUtils.nextTick();
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ assert.verifySteps([
+ '[["bar","=",true]]',
+ '["&",["bar","=",true],["company_id","child_of",3]]',
+ '["&",["bar","=",true],["company_id","child_of",5]]',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("concurrency: single category", async function (assert) {
+ assert.expect(12);
+
+ let prom = testUtils.makeTestPromise();
+ const kanbanPromise = createView({
+ arch: `
+ <kanban>
+ <templates>
+ <div t-name="kanban-box">
+ <field name="foo"/>
+ </div>
+ </templates>
+ </kanban>`,
+ archs: {
+ "partner,false,search": `
+ <search>
+ <filter name="Filter" domain="[('id', '=', 1)]"/>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ context: {
+ searchpanel_default_company_id: [5],
+ },
+ data: this.data,
+ async mockRPC(route, args) {
+ const _super = this._super.bind(this);
+ if (route !== "/web/dataset/search_read") {
+ await prom;
+ }
+ assert.step(args.method || route);
+ return _super(...arguments);
+ },
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ // Case 1: search panel is awaited to build the query with search defaults
+ await testUtils.nextTick();
+ assert.verifySteps([]);
+
+ prom.resolve();
+ const kanban = await kanbanPromise;
+
+ assert.verifySteps([
+ "search_panel_select_range",
+ "/web/dataset/search_read",
+ ]);
+
+ // Case 2: search domain changed so we wait for the search panel once again
+ prom = testUtils.makeTestPromise();
+
+ await testUtils.controlPanel.toggleFilterMenu(kanban);
+ await testUtils.controlPanel.toggleMenuItem(kanban, 0);
+
+ assert.verifySteps([]);
+
+ prom.resolve();
+ await testUtils.nextTick();
+
+ assert.verifySteps([
+ "search_panel_select_range",
+ "/web/dataset/search_read",
+ ]);
+
+ // Case 3: search domain is the same and default values do not matter anymore
+ prom = testUtils.makeTestPromise();
+
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ // The search read is executed right away in this case
+ assert.verifySteps(["/web/dataset/search_read"]);
+
+ prom.resolve();
+ await testUtils.nextTick();
+
+ assert.verifySteps(["search_panel_select_range"]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("concurrency: category and filter", async function (assert) {
+ assert.expect(5);
+
+ let prom = testUtils.makeTestPromise();
+ const kanbanPromise = createView({
+ arch: `
+ <kanban>
+ <templates>
+ <div t-name="kanban-box">
+ <field name="foo"/>
+ </div>
+ </templates>
+ </kanban>`,
+ archs: {
+ "partner,false,search": `
+ <search>
+ <searchpanel>
+ <field name="category_id" enable_counters="1"/>
+ <field name="company_id" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ async mockRPC(route, args) {
+ const _super = this._super.bind(this);
+ if (route !== "/web/dataset/search_read") {
+ await prom;
+ }
+ assert.step(args.method || route);
+ return _super(...arguments);
+ },
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ await testUtils.nextTick();
+ assert.verifySteps(["/web/dataset/search_read"]);
+
+ prom.resolve();
+ const kanban = await kanbanPromise;
+
+ assert.verifySteps([
+ "search_panel_select_range",
+ "search_panel_select_multi_range",
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("concurrency: category and filter with a domain", async function (assert) {
+ assert.expect(5);
+
+ let prom = testUtils.makeTestPromise();
+ const kanbanPromise = createView({
+ arch: `
+ <kanban>
+ <templates>
+ <div t-name="kanban-box">
+ <field name="foo"/>
+ </div>
+ </templates>
+ </kanban>`,
+ archs: {
+ "partner,false,search": `
+ <search>
+ <searchpanel>
+ <field name="category_id" enable_counters="1"/>
+ <field name="company_id" select="multi" domain="[['category_id', '=', category_id]]" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ async mockRPC(route, args) {
+ const _super = this._super.bind(this);
+ if (route !== "/web/dataset/search_read") {
+ await prom;
+ }
+ assert.step(args.method || route);
+ return _super(...arguments);
+ },
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ await testUtils.nextTick();
+ assert.verifySteps([]);
+
+ prom.resolve();
+ const kanban = await kanbanPromise;
+
+ assert.verifySteps([
+ "search_panel_select_range",
+ "search_panel_select_multi_range",
+ "/web/dataset/search_read",
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('concurrency: misordered get_filters', async function (assert) {
+ assert.expect(15);
+
+ var def;
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'search_panel_select_multi_range') {
+ return Promise.resolve(def).then(_.constant(result));
+ }
+ return result;
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" enable_counters="1"/>
+ <field name="company_id" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ // 'All' should be selected by default
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:first .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ // select 'abc' (delay the reload)
+ def = testUtils.makeTestPromise();
+ var abcDef = def;
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ // 'All' should still be selected, and there should still be 4 records
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // select 'ghi' (delay the reload)
+ def = testUtils.makeTestPromise();
+ var ghiDef = def;
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(3) header'));
+
+ // 'All' should still be selected, and there should still be 4 records
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(3) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ // unlock ghi search
+ ghiDef.resolve();
+ await testUtils.nextTick();
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(3) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ // unlock abc search (should be ignored)
+ abcDef.resolve();
+ await testUtils.nextTick();
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(3) .active');
+ assert.containsN(kanban, '.o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('concurrency: delayed get_filter', async function (assert) {
+ assert.expect(3);
+
+ var def;
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'search_panel_select_multi_range') {
+ return Promise.resolve(def).then(_.constant(result));
+ }
+ return result;
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="Filter" domain="[('id', '=', 1)]"/>
+ <searchpanel>
+ <field name="company_id" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ // trigger a reload and delay the get_filter
+ def = testUtils.makeTestPromise();
+ await cpHelpers.toggleFilterMenu(kanban);
+ await cpHelpers.toggleMenuItem(kanban, 0);
+ await testUtils.nextTick();
+
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ def.resolve();
+ await testUtils.nextTick();
+
+ assert.containsOnce(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)');
+
+ kanban.destroy();
+ });
+
+ QUnit.test('use filter (on many2one) to refine search', async function (assert) {
+ assert.expect(32);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'search_panel_select_multi_range') {
+ // the following keys should have same value for all calls to this route
+ var keys = ['field_name', 'group_by', 'comodel_domain', 'search_domain', 'category_domain'];
+ assert.deepEqual(_.pick(args.kwargs, keys), {
+ group_by: false,
+ comodel_domain: [],
+ search_domain: [['bar', '=', true]],
+ category_domain: [],
+ });
+ // the filter_domain depends on the filter selection
+ assert.step(JSON.stringify(args.kwargs.filter_domain));
+ }
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return result;
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ domain: [['bar', '=', true]],
+ });
+
+ assert.containsN(kanban, '.o_search_panel_filter_value', 2);
+ assert.containsNone(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolait1');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // check 'asustek'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input'));
+
+ assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolait1');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ // check 'agrolait'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input'));
+
+ assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2);
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolait1');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // uncheck 'asustek'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input'));
+
+ assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolait1');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // uncheck 'agrolait'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input'));
+
+ assert.containsNone(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolait1');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ assert.verifySteps([
+ // nothing checked
+ '[]',
+ '[["bar","=",true]]',
+ // 'asustek' checked
+ '[]',
+ '["&",["bar","=",true],["company_id","in",[3]]]',
+ // 'asustek' and 'agrolait' checked
+ '[]',
+ '["&",["bar","=",true],["company_id","in",[3,5]]]',
+ // 'agrolait' checked
+ '[]',
+ '["&",["bar","=",true],["company_id","in",[5]]]',
+ // nothing checked
+ '[]',
+ '[["bar","=",true]]',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('use filter (on selection) to refine search', async function (assert) {
+ assert.expect(32);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'search_panel_select_multi_range') {
+ // the following keys should have same value for all calls to this route
+ var keys = ['group_by', 'comodel_domain', 'search_domain', 'category_domain'];
+ assert.deepEqual(_.pick(args.kwargs, keys), {
+ group_by: false,
+ comodel_domain: [],
+ search_domain: [['bar', '=', true]],
+ category_domain: [],
+ });
+ // the filter_domain depends on the filter selection
+ assert.step(JSON.stringify(args.kwargs.filter_domain));
+ }
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return result;
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" select="multi" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ domain: [['bar', '=', true]],
+ });
+
+ assert.containsN(kanban, '.o_search_panel_filter_value', 3);
+ assert.containsNone(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'ABC1DEF1GHI1');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // check 'abc'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input'));
+
+ assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'ABC1DEF1GHI1');
+ assert.containsOnce(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // check 'def'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input'));
+
+ assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2);
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'ABC1DEF1GHI1');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ // uncheck 'abc'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input'));
+
+ assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'ABC1DEF1GHI1');
+ assert.containsOnce(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)');
+
+ // uncheck 'def'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input'));
+
+ assert.containsNone(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'ABC1DEF1GHI1');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ assert.verifySteps([
+ // nothing checked
+ '[]',
+ '[["bar","=",true]]',
+ // 'asustek' checked
+ '[]',
+ '["&",["bar","=",true],["state","in",["abc"]]]',
+ // 'asustek' and 'agrolait' checked
+ '[]',
+ '["&",["bar","=",true],["state","in",["abc","def"]]]',
+ // 'agrolait' checked
+ '[]',
+ '["&",["bar","=",true],["state","in",["def"]]]',
+ // nothing checked
+ '[]',
+ '[["bar","=",true]]',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("only reload categories and filters when domains change (counters disabled, selection)", async function (assert) {
+ assert.expect(8);
+
+ const kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method);
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="Filter" domain="[('id', '&lt;', 5)]"/>
+ <searchpanel>
+ <field name="state" expand="1"/>
+ <field name="company_id" select="multi" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ viewOptions: {
+ limit: 2,
+ },
+ });
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ 'search_panel_select_multi_range',
+ ]);
+
+ // go to page 2 (the domain doesn't change, so the filters should not be reloaded)
+ await cpHelpers.pagerNext(kanban);
+
+ assert.verifySteps([]);
+
+ // reload with another domain, so the filters should be reloaded
+ await cpHelpers.toggleFilterMenu(kanban);
+ await cpHelpers.toggleMenuItem(kanban, 0);
+
+ assert.verifySteps([
+ 'search_panel_select_multi_range',
+ ]);
+
+ // change category value, so the filters should be reloaded
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.verifySteps([
+ 'search_panel_select_multi_range',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("only reload categories and filters when domains change (counters disabled, many2one)", async function (assert) {
+ assert.expect(8);
+
+ const kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method);
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="domain" domain="[('id', '&lt;', 5)]"/>
+ <searchpanel>
+ <field name="category_id" expand="1"/>
+ <field name="company_id" select="multi" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ viewOptions: {
+ limit: 2,
+ },
+ });
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ 'search_panel_select_multi_range',
+ ]);
+
+ // go to page 2 (the domain doesn't change, so the filters should not be reloaded)
+ await cpHelpers.pagerNext(kanban);
+
+ assert.verifySteps([]);
+
+ // reload with another domain, so the filters should be reloaded
+ await cpHelpers.toggleFilterMenu(kanban);
+ await cpHelpers.toggleMenuItem(kanban, 0);
+
+ assert.verifySteps([
+ 'search_panel_select_multi_range',
+ ]);
+
+ // change category value, so the filters should be reloaded
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.verifySteps([
+ 'search_panel_select_multi_range',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('category counters', async function (assert) {
+ assert.expect(16);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method);
+ }
+ if (route === "/web/dataset/call_kw/partner/search_panel_select_range") {
+ assert.step(args.args[0]);
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="Filter" domain="[('id', '&lt;', 3)]"/>
+ <searchpanel>
+ <field name="state" enable_counters="1" expand="1"/>
+ <field name="company_id" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ viewOptions: {
+ limit: 2,
+ },
+ });
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ 'state',
+ 'search_panel_select_range',
+ 'company_id',
+ ]);
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value')].map(
+ e => e.innerText.replace(/\s/g, '')
+ ),
+ [ "All", "ABC1", "DEF1", "GHI2", "All", "asustek", "agrolait"]
+ );
+
+ // go to page 2 (the domain doesn't change, so the categories should not be reloaded)
+ await cpHelpers.pagerNext(kanban);
+
+ assert.verifySteps([]);
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value')].map(
+ e => e.innerText.replace(/\s/g, '')
+ ),
+ [ "All", "ABC1", "DEF1", "GHI2", "All", "asustek", "agrolait"]
+ );
+
+ // reload with another domain, so the categories 'state' and 'company_id' should be reloaded
+ await cpHelpers.toggleFilterMenu(kanban);
+ await cpHelpers.toggleMenuItem(kanban, 0);
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ 'state',
+ ]);
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value')].map(
+ e => e.innerText.replace(/\s/g, '')
+ ),
+ [ "All", "ABC1", "DEF1", "GHI", "All", "asustek", "agrolait"]
+ );
+
+ // change category value, so the category 'state' should be reloaded
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value')].map(
+ e => e.innerText.replace(/\s/g, '')
+ ),
+ [ "All", "ABC1", "DEF1", "GHI", "All", "asustek", "agrolait"]
+ );
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ 'state',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('category selection without counters', async function (assert) {
+ assert.expect(10);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method);
+ }
+ if (route === "/web/dataset/call_kw/partner/search_panel_select_range") {
+ assert.step(args.args[0]);
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="Filter" domain="[('id', '&lt;', 3)]"/>
+ <searchpanel>
+ <field name="state" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ viewOptions: {
+ limit: 2,
+ },
+ });
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ 'state',
+ ]);
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value')].map(
+ e => e.innerText.replace(/\s/g, '')
+ ),
+ [ "All", "ABC", "DEF", "GHI"]
+ );
+
+ // go to page 2 (the domain doesn't change, so the categories should not be reloaded)
+ await cpHelpers.pagerNext(kanban);
+
+ assert.verifySteps([]);
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value')].map(
+ e => e.innerText.replace(/\s/g, '')
+ ),
+ [ "All", "ABC", "DEF", "GHI"]
+ );
+
+ // reload with another domain, so the category 'state' should be reloaded
+ await cpHelpers.toggleFilterMenu(kanban);
+ await cpHelpers.toggleMenuItem(kanban, 0);
+
+ assert.verifySteps([]);
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value')].map(
+ e => e.innerText.replace(/\s/g, '')
+ ),
+ [ "All", "ABC", "DEF", "GHI"]
+ );
+
+ // change category value, so the category 'state' should be reloaded
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value')].map(
+ e => e.innerText.replace(/\s/g, '')
+ ),
+ [ "All", "ABC", "DEF", "GHI"]
+ );
+
+ assert.verifySteps([]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('filter with groupby', async function (assert) {
+ assert.expect(42);
+
+ this.data.company.records.push({id: 11, name: 'camptocamp', category_id: 7});
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'search_panel_select_multi_range') {
+ // the following keys should have same value for all calls to this route
+ var keys = ['group_by', 'comodel_domain', 'search_domain', 'category_domain'];
+ assert.deepEqual(_.pick(args.kwargs, keys), {
+ group_by: 'category_id',
+ comodel_domain: [],
+ search_domain: [['bar', '=', true]],
+ category_domain: [],
+ });
+ // the filter_domain depends on the filter selection
+ assert.step(JSON.stringify(args.kwargs.filter_domain));
+ }
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return result;
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" groupby="category_id" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ domain: [['bar', '=', true]],
+ });
+
+ assert.containsN(kanban, '.o_search_panel_filter_group', 2);
+ assert.containsOnce(kanban, '.o_search_panel_filter_group:first .o_search_panel_filter_value');
+ assert.containsN(kanban, '.o_search_panel_filter_group:nth(1) .o_search_panel_filter_value', 2);
+ assert.containsNone(kanban, '.o_search_panel_filter_value input:checked');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_group > header > div > label').text().replace(/\s/g, ''),
+ 'goldsilver');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolait1camptocamp');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3);
+
+ // check 'asustek'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input'));
+
+ assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked');
+ var firstGroupCheckbox = kanban.$('.o_search_panel_filter_group:first > header > div > input').get(0);
+ assert.strictEqual(firstGroupCheckbox.checked, true,
+ "first group checkbox should be checked");
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolaitcamptocamp');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ // check 'agrolait'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input'));
+
+ assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2);
+ var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input').get(0);
+ assert.strictEqual(secondGroupCheckbox.checked, false,
+ "second group checkbox should not be checked");
+ assert.strictEqual(secondGroupCheckbox.indeterminate, true,
+ "second group checkbox should be indeterminate");
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustekagrolaitcamptocamp');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 0);
+
+ // check 'camptocamp'
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(2) input'));
+
+ assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 3);
+ secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input').get(0);
+ assert.strictEqual(secondGroupCheckbox.checked, true,
+ "second group checkbox should be checked");
+ assert.strictEqual(secondGroupCheckbox.indeterminate, false,
+ "second group checkbox should not be indeterminate");
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustekagrolaitcamptocamp');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 0);
+
+ // uncheck second group
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input'));
+
+ assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked');
+ secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input').get(0);
+ assert.strictEqual(secondGroupCheckbox.checked, false,
+ "second group checkbox should not be checked");
+ assert.strictEqual(secondGroupCheckbox.indeterminate, false,
+ "second group checkbox should not be indeterminate");
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolaitcamptocamp');
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ assert.verifySteps([
+ // nothing checked
+ '[]',
+ '[["bar","=",true]]',
+ // 'asustek' checked
+ '[]',
+ '["&",["bar","=",true],["company_id","in",[3]]]',
+ // 'asustek' and 'agrolait' checked
+ '[]',
+ '["&",["bar","=",true],"&",["company_id","in",[3]],["company_id","in",[5]]]',
+ // 'asustek', 'agrolait' and 'camptocamp' checked
+ '[]',
+ '["&",["bar","=",true],"&",["company_id","in",[3]],["company_id","in",[5,11]]]',
+ // 'asustek' checked
+ '[]',
+ '["&",["bar","=",true],["company_id","in",[3]]]',
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('filter with domain', async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({id: 40, name: 'child company 1', parent_id: 3});
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'search_panel_select_multi_range') {
+ assert.deepEqual(args.kwargs, {
+ group_by: false,
+ category_domain: [],
+ expand: true,
+ filter_domain: [],
+ search_domain: [],
+ comodel_domain: [['parent_id', '=', false]],
+ group_domain: [],
+ enable_counters: true,
+ limit: 200,
+ });
+ }
+ return result;
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" domain="[('parent_id','=',False)]" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.containsN(kanban, '.o_search_panel_filter_value', 2);
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'asustek2agrolait2');
+
+ kanban.destroy();
+ });
+
+ QUnit.test('filter with domain depending on category', async function (assert) {
+ assert.expect(22);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'search_panel_select_multi_range') {
+ // the following keys should have same value for all calls to this route
+ var keys = ['group_by', 'search_domain', 'filter_domain'];
+ assert.deepEqual(_.pick(args.kwargs, keys), {
+ group_by: false,
+ filter_domain: [],
+ search_domain: [],
+ });
+ assert.step(JSON.stringify(args.kwargs.category_domain));
+ assert.step(JSON.stringify(args.kwargs.comodel_domain));
+ }
+ return result;
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="category_id" enable_counters="1"/>
+ <field name="company_id" select="multi" domain="[['category_id', '=', category_id]]" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ // select 'gold' category
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value .active');
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(1) .active');
+ assert.containsOnce(kanban, '.o_search_panel_filter_value');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ "asustek1");
+
+ // select 'silver' category
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(2) header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value:nth(2) .active');
+ assert.containsOnce(kanban, '.o_search_panel_filter_value');
+ assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ "agrolait2");
+
+ // select All
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:first header'));
+
+ assert.containsOnce(kanban, '.o_search_panel_category_value:first .active');
+ assert.containsNone(kanban, '.o_search_panel_filter_value');
+
+ assert.verifySteps([
+ '[]', // category_domain (All)
+ '[["category_id","=",false]]', // comodel_domain (All)
+ '[["category_id","=",6]]', // category_domain ('gold')
+ '[["category_id","=",6]]', // comodel_domain ('gold')
+ '[["category_id","=",7]]', // category_domain ('silver')
+ '[["category_id","=",7]]', // comodel_domain ('silver')
+ '[]', // category_domain (All)
+ '[["category_id","=",false]]', // comodel_domain (All)
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('specify active filter values in context', async function (assert) {
+ assert.expect(4);
+
+ var expectedDomain = [
+ "&",
+ ['company_id', 'in', [5]],
+ ['state', 'in', ['abc', 'ghi']],
+ ];
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" enable_counters="1"/>
+ <field name="state" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.deepEqual(args.domain, expectedDomain);
+ }
+ return this._super.apply(this, arguments);
+ },
+ context: {
+ searchpanel_default_company_id: [5],
+ searchpanel_default_state: ['abc', 'ghi'],
+ },
+ });
+
+ assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 3);
+
+ // manually untick a default value
+ expectedDomain = [['state', 'in', ['abc', 'ghi']]];
+ await testUtils.dom.click(kanban.$('.o_search_panel_filter:first .o_search_panel_filter_value:nth(1) input'));
+
+ assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('retrieved filter value from context does not exist', async function (assert) {
+ assert.expect(1);
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.deepEqual(args.domain, [["company_id", "in", [3]]]);
+ }
+ return this._super.apply(this, arguments);
+ },
+ context: {
+ searchpanel_default_company_id: [1, 3],
+ },
+ });
+
+ kanban.destroy();
+ });
+
+ QUnit.test('filter with groupby and default values in context', async function (assert) {
+ assert.expect(2);
+
+ this.data.company.records.push({id: 11, name: 'camptocamp', category_id: 7});
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" groupby="category_id" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.deepEqual(args.domain, [['company_id', 'in', [5]]]);
+ }
+ return this._super.apply(this, arguments);
+ },
+ context: {
+ searchpanel_default_company_id: [5],
+ },
+ });
+
+ var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input').get(0);
+ assert.strictEqual(secondGroupCheckbox.indeterminate, true);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('Does not confuse false and "false" groupby values', async function (assert) {
+ assert.expect(6);
+
+ this.data.company.fields.char_field = {string: "Char Field", type: 'char'};
+
+ this.data.company.records = [
+ {id: 3, name: 'A', char_field: false, },
+ {id: 5, name: 'B', char_field: 'false', }
+ ];
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `<kanban>
+ <templates><t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t></templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" groupby="char_field"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.containsOnce(kanban, '.o_search_panel_section');
+ var $firstSection = kanban.$('.o_search_panel_section');
+
+ // There should be a group 'false' displayed with only value B inside it.
+ assert.containsOnce($firstSection, '.o_search_panel_filter_group');
+ assert.strictEqual($firstSection.find('.o_search_panel_filter_group').text().replace(/\s/g, ''),
+ 'falseB');
+ assert.containsOnce($firstSection.find('.o_search_panel_filter_group'), '.o_search_panel_filter_value');
+
+ // Globally, there should be two values, one displayed in the group 'false', and one at the end of the section
+ // (the group false is not displayed and its values are displayed at the first level)
+ assert.containsN($firstSection, '.o_search_panel_filter_value', 2);
+ assert.strictEqual($firstSection.find('.o_search_panel_filter_value').text().replace(/\s/g, ''),
+ 'BA');
+
+ kanban.destroy();
+ });
+
+ QUnit.test('tests conservation of category record order', async function (assert) {
+ assert.expect(1);
+
+ this.data.company.records.push({id: 56, name: 'highID', category_id: 6});
+ this.data.company.records.push({id: 2, name: 'lowID', category_id: 6});
+
+ var kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1" expand="1"/>
+ <field name="category_id" select="multi" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ var $firstSection = kanban.$('.o_search_panel_section:first');
+ assert.strictEqual($firstSection.find('.o_search_panel_category_value').text().replace(/\s/g, ''),
+ 'Allasustek2agrolait2highIDlowID');
+ kanban.destroy();
+ });
+
+ QUnit.test('search panel is available on list and kanban by default', async function (assert) {
+ assert.expect(8);
+
+ var actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ });
+ await actionManager.doAction(1);
+
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_kanban_view');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel');
+
+ await cpHelpers.switchView(actionManager, 'pivot');
+ await testUtils.nextTick();
+ assert.containsOnce(actionManager, '.o_content .o_pivot');
+ assert.containsNone(actionManager, '.o_content .o_search_panel');
+
+ await cpHelpers.switchView(actionManager, 'list');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_list_view');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel');
+
+ await testUtils.dom.click(actionManager.$('.o_data_row .o_data_cell:first'));
+ assert.containsOnce(actionManager, '.o_content .o_form_view');
+ assert.containsNone(actionManager, '.o_content .o_search_panel');
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('search panel with view_types attribute', async function (assert) {
+ assert.expect(6);
+
+ this.archs['partner,false,search'] =
+ `<search>
+ <searchpanel view_types="kanban,pivot">
+ <field name="company_id" enable_counters="1"/>
+ <field name="category_id" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`;
+
+
+ var actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ });
+ await actionManager.doAction(1);
+
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_kanban_view');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel');
+
+ await cpHelpers.switchView(actionManager, 'list');
+ assert.containsOnce(actionManager, '.o_content .o_list_view');
+ assert.containsNone(actionManager, '.o_content .o_search_panel');
+
+ await cpHelpers.switchView(actionManager, 'pivot');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_pivot');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel');
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('search panel state is shared between views', async function (assert) {
+ assert.expect(16);
+
+ var actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+ await actionManager.doAction(1);
+
+ assert.hasClass(actionManager.$('.o_search_panel_category_value:first header'), 'active');
+ assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ // select 'asustek' company
+ await testUtils.dom.click(actionManager.$('.o_search_panel_category_value:nth(1) header'));
+ assert.hasClass(actionManager.$('.o_search_panel_category_value:nth(1) header'), 'active');
+ assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ await cpHelpers.switchView(actionManager, 'list');
+ assert.hasClass(actionManager.$('.o_search_panel_category_value:nth(1) header'), 'active');
+ assert.containsN(actionManager, '.o_data_row', 2);
+
+ // select 'agrolait' company
+ await testUtils.dom.click(actionManager.$('.o_search_panel_category_value:nth(2) header'));
+ assert.hasClass(actionManager.$('.o_search_panel_category_value:nth(2) header'), 'active');
+ assert.containsN(actionManager, '.o_data_row', 2);
+
+ await cpHelpers.switchView(actionManager, 'kanban');
+ assert.hasClass(actionManager.$('.o_search_panel_category_value:nth(2) header'), 'active');
+ assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 2);
+
+ assert.verifySteps([
+ '[]', // initial search_read
+ '[["company_id","child_of",3]]', // kanban, after selecting the first company
+ '[["company_id","child_of",3]]', // list
+ '[["company_id","child_of",5]]', // list, after selecting the other company
+ '[["company_id","child_of",5]]', // kanban
+ ]);
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('search panel filters are kept between switch views', async function (assert) {
+ assert.expect(17);
+
+ const actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ assert.step(JSON.stringify(args.domain));
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+ await actionManager.doAction(1);
+
+ assert.containsNone(actionManager, '.o_search_panel_filter_value input:checked');
+ assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ // select gold filter
+ await testUtils.dom.click(actionManager.$('.o_search_panel_filter input[type="checkbox"]:nth(0)'));
+ assert.containsOnce(actionManager, '.o_search_panel_filter_value input:checked');
+ assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ await cpHelpers.switchView(actionManager, 'list');
+ assert.containsOnce(actionManager, '.o_search_panel_filter_value input:checked');
+ assert.containsN(actionManager, '.o_data_row', 1);
+
+ // select silver filter
+ await testUtils.dom.click(actionManager.$('.o_search_panel_filter input[type="checkbox"]:nth(1)'));
+ assert.containsN(actionManager, '.o_search_panel_filter_value input:checked', 2);
+ assert.containsN(actionManager, '.o_data_row', 4);
+
+ await cpHelpers.switchView(actionManager, 'kanban');
+ assert.containsN(actionManager, '.o_search_panel_filter_value input:checked', 2);
+ assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ await testUtils.dom.click(actionManager.$(".o_kanban_record:nth(0)"));
+ await testUtils.dom.click(actionManager.$(".breadcrumb-item:nth(0)"));
+
+ assert.verifySteps([
+ '[]', // initial search_read
+ '[["category_id","in",[6]]]', // kanban, after selecting the gold filter
+ '[["category_id","in",[6]]]', // list
+ '[["category_id","in",[6,7]]]', // list, after selecting the silver filter
+ '[["category_id","in",[6,7]]]', // kanban
+ '[["category_id","in",[6,7]]]', // kanban, after switching back from form view
+ ]);
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('search panel filters are kept when switching to a view with no search panel', async function (assert) {
+ assert.expect(13);
+
+ var actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ });
+ await actionManager.doAction(1);
+
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_kanban_view');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel');
+ assert.containsNone(actionManager, '.o_search_panel_filter_value input:checked');
+ assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 4);
+
+ // select gold filter
+ await testUtils.dom.click(actionManager.$('.o_search_panel_filter input[type="checkbox"]:nth(0)'));
+ assert.containsOnce(actionManager, '.o_search_panel_filter_value input:checked');
+ assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ // switch to pivot
+ await cpHelpers.switchView(actionManager, 'pivot');
+ assert.containsOnce(actionManager, '.o_content .o_pivot');
+ assert.containsNone(actionManager, '.o_content .o_search_panel');
+ assert.strictEqual(actionManager.$('.o_pivot_cell_value').text(), '15');
+
+ // switch to list
+ await cpHelpers.switchView(actionManager, 'list');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_list_view');
+ assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel');
+ assert.containsOnce(actionManager, '.o_search_panel_filter_value input:checked');
+ assert.containsN(actionManager, '.o_data_row', 1);
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('after onExecuteAction, selects "All" as default category value', async function (assert) {
+ assert.expect(4);
+
+ var actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ });
+
+ await actionManager.doAction(2);
+ await testUtils.dom.click(actionManager.$('.o_form_view button:contains("multi view")'));
+
+ assert.containsOnce(actionManager, '.o_kanban_view');
+ assert.containsOnce(actionManager, '.o_search_panel');
+ assert.containsOnce(actionManager, '.o_search_panel_category_value:first .active');
+
+ assert.verifySteps([]); // should not communicate with localStorage
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('search panel is not instantiated if stated in context', async function (assert) {
+ assert.expect(2);
+
+ this.actions[0].context = {search_panel: false};
+ var actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ });
+
+ await actionManager.doAction(2);
+ await testUtils.dom.click(actionManager.$('.o_form_view button:contains("multi view")'));
+
+ assert.containsOnce(actionManager, '.o_kanban_view');
+ assert.containsNone(actionManager, '.o_search_panel');
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('categories and filters are not reloaded when switching between views', async function (assert) {
+ assert.expect(3);
+
+ var actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method);
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+ await actionManager.doAction(1);
+
+ await cpHelpers.switchView(actionManager, 'list');
+ await cpHelpers.switchView(actionManager, 'kanban');
+
+ assert.verifySteps([
+ 'search_panel_select_range', // kanban: categories
+ 'search_panel_select_multi_range', // kanban: filters
+ ]);
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('scroll position is kept when switching between controllers', async function (assert) {
+ assert.expect(6);
+
+ const originalDebounce = SearchPanel.scrollDebounce;
+ SearchPanel.scrollDebounce = 0;
+
+ for (var i = 10; i < 20; i++) {
+ this.data.category.records.push({id: i, name: "Cat " + i});
+ }
+
+ var actionManager = await createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ });
+ actionManager.$el.css('max-height', 300);
+
+ async function scroll(top) {
+ actionManager.el.querySelector(".o_search_panel").scrollTop = top;
+ await testUtils.nextTick();
+ }
+
+ await actionManager.doAction(1);
+
+ assert.containsOnce(actionManager, '.o_content .o_kanban_view');
+ assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 0);
+
+ // simulate a scroll in the search panel and switch into list
+ await scroll(50);
+ await cpHelpers.switchView(actionManager, 'list');
+ assert.containsOnce(actionManager, '.o_content .o_list_view');
+ assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 50);
+
+ // simulate another scroll and switch back to kanban
+ await scroll(30);
+ await cpHelpers.switchView(actionManager, 'kanban');
+ assert.containsOnce(actionManager, '.o_content .o_kanban_view');
+ assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 30);
+
+ actionManager.destroy();
+ SearchPanel.scrollDebounce = originalDebounce;
+ });
+
+ QUnit.test('search panel is not instantiated in dialogs', async function (assert) {
+ assert.expect(2);
+
+ this.data.company.records = [
+ {id: 1, name: 'Company1'},
+ {id: 2, name: 'Company2'},
+ {id: 3, name: 'Company3'},
+ {id: 4, name: 'Company4'},
+ {id: 5, name: 'Company5'},
+ {id: 6, name: 'Company6'},
+ {id: 7, name: 'Company7'},
+ {id: 8, name: 'Company8'},
+ ];
+
+ var form = await createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form><field name="company_id"/></form>',
+ archs: {
+ 'company,false,list': '<tree><field name="name"/></tree>',
+ 'company,false,search':
+ `<search>
+ <field name="name"/>
+ <searchpanel>
+ <field name="category_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ await testUtils.fields.many2one.clickOpenDropdown('company_id');
+ await testUtils.fields.many2one.clickItem('company_id', 'Search More');
+
+ assert.containsOnce(document.body, '.modal .o_list_view');
+ assert.containsNone(document.body, '.modal .o_search_panel');
+
+ form.destroy();
+ });
+
+
+ QUnit.test("Reload categories with counters when filter values are selected", async function (assert) {
+ assert.expect(8);
+
+ const kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method);
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="category_id" enable_counters="1"/>
+ <field name="state" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ "search_panel_select_multi_range",
+ ]);
+
+ assert.deepEqual(getCounters(kanban), [
+ 1, 3, // category counts (in order)
+ 1, 1, 2 // filter counts
+ ]);
+
+ await testUtils.dom.click(kanban.el.querySelector('.o_search_panel_filter_value input'));
+
+ assert.deepEqual(getCounters(kanban), [
+ 1, // category counts (for silver: 0 is not displayed)
+ 1, 1, 2 // filter counts
+ ]);
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ "search_panel_select_multi_range",
+ ]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select one, expand, hierarchize, counters", async function (assert) {
+ assert.expect(5);
+
+ this.data.company.records.push({ id: 50, name: 'agrobeurre', parent_id: 5 });
+ this.data.company.records.push({ id: 51, name: 'agrocrèmefraiche', parent_id: 5 });
+ this.data.partner.records[1].company_id = 50;
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 3);
+ assert.containsOnce(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2 ,1]);
+
+ await toggleFold(kanban, "agrolait");
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 5);
+ assert.deepEqual(getCounters(kanban), [2, 1, 1]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select one, no expand, hierarchize, counters", async function (assert) {
+ assert.expect(5);
+
+ this.data.company.records.push({ id: 50, name: 'agrobeurre', parent_id: 5 });
+ this.data.company.records.push({ id: 51, name: 'agrocrèmefraiche', parent_id: 5 });
+ this.data.partner.records[1].company_id = 50;
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 3);
+ assert.containsOnce(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 1]);
+
+ await toggleFold(kanban, "agrolait");
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 4);
+ assert.deepEqual(getCounters(kanban), [2, 1, 1]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select one, expand, no hierarchize, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 50, name: 'agrobeurre', parent_id: 5 });
+ this.data.company.records.push({ id: 51, name: 'agrocrèmefraiche', parent_id: 5 });
+ this.data.partner.records[1].company_id = 50;
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" hierarchize="0" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 5);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 1, 1]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select one, no expand, no hierarchize, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 50, name: 'agrobeurre', parent_id: 5 });
+ this.data.company.records.push({ id: 51, name: 'agrocrèmefraiche', parent_id: 5 });
+ this.data.partner.records[1].company_id = 50;
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" hierarchize="0" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 4);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 1, 1]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select one, expand, hierarchize, no counters", async function (assert) {
+ assert.expect(5);
+
+ this.data.company.records.push({ id: 50, name: 'agrobeurre', parent_id: 5 });
+ this.data.company.records.push({ id: 51, name: 'agrocrèmefraiche', parent_id: 5 });
+ this.data.partner.records[1].company_id = 50;
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 3);
+ assert.containsOnce(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ await toggleFold(kanban, "agrolait");
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 5);
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select one, no expand, hierarchize, no counters", async function (assert) {
+ assert.expect(5);
+
+ this.data.company.records.push({ id: 50, name: 'agrobeurre', parent_id: 5 });
+ this.data.company.records.push({ id: 51, name: 'agrocrèmefraiche', parent_id: 5 });
+ this.data.partner.records[1].company_id = 50;
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 3);
+ assert.containsOnce(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ await toggleFold(kanban, "agrolait");
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 4);
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select one, expand, no hierarchize, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 50, name: 'agrobeurre', parent_id: 5 });
+ this.data.company.records.push({ id: 51, name: 'agrocrèmefraiche', parent_id: 5 });
+ this.data.partner.records[1].company_id = 50;
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" hierarchize="0" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 5);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select one, no expand, no hierarchize, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 50, name: 'agrobeurre', parent_id: 5 });
+ this.data.company.records.push({ id: 51, name: 'agrocrèmefraiche', parent_id: 5 });
+ this.data.partner.records[1].company_id = 50;
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" hierarchize="0"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 4);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select multi, expand, groupby, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" groupby="category_id" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 5);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 2]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select multi, no expand, groupby, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" groupby="category_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 4);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 2]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select multi, expand, no groupby, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 3);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 2]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select multi, no expand, no groupby, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 2);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 2]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select multi, expand, groupby, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" groupby="category_id" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 5);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select multi, no expand, groupby, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" groupby="category_id"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 4);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select multi, expand, no groupby, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 3);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2one: select multi, no expand, no groupby, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 2);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2many: select multi, expand, groupby, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_ids" select="multi" groupby="category_id" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 5);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 1]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2many: select multi, no expand, groupby, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_ids" select="multi" groupby="category_id" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 4);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 1]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2many: select multi, expand, no groupby, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_ids" select="multi" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 3);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 1]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2many: select multi, no expand, no groupby, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_ids" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 2);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [2, 1]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2many: select multi, expand, groupby, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_ids" select="multi" groupby="category_id" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 5);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2many: select multi, no expand, groupby, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_ids" select="multi" groupby="category_id"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 4);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2many: select multi, expand, no groupby, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_ids" select="multi" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 3);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("many2many: select multi, no expand, no groupby, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.company.records.push({ id: 666, name: "Mordor Inc.", category_id: 6 });
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_ids" select="multi"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 2);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("selection: select one, expand, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 4);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [1, 2]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("selection: select one, no expand, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 3);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [1, 2]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("selection: select one, expand, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 4);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("selection: select one, no expand, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_field .o_search_panel_category_value', 3);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("selection: select multi, expand, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" select="multi" enable_counters="1" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 3);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [1, 2]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("selection: select multi, no expand, counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 2);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [1, 2]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("selection: select multi, expand, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" select="multi" expand="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 3);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("selection: select multi, no expand, no counters", async function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="state" select="multi"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 2);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), []);
+
+ kanban.destroy();
+ });
+
+ //-------------------------------------------------------------------------
+ // Model domain and count domain distinction
+ //-------------------------------------------------------------------------
+
+ QUnit.test("selection: select multi, no expand, counters, extra_domain", async function (assert) {
+ assert.expect(5);
+
+ this.data.partner.records.shift();
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id"/>
+ <field name="state" select="multi" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsN(kanban, '.o_search_panel_label', 5);
+ assert.containsNone(kanban, '.o_toggle_fold > i');
+ assert.deepEqual(getCounters(kanban), [1, 2]);
+
+ await toggleFold(kanban, "asustek");
+
+ assert.containsN(kanban, '.o_search_panel_label', 5);
+ assert.deepEqual(getCounters(kanban), [1]);
+
+ kanban.destroy();
+ });
+
+ //-------------------------------------------------------------------------
+ // Limit
+ //-------------------------------------------------------------------------
+
+ QUnit.test("reached limit for a category", async function (assert) {
+ assert.expect(6);
+
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" limit="2"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsOnce(kanban, '.o_search_panel_section');
+ assert.containsOnce(kanban, '.o_search_panel_section_header');
+ assert.strictEqual(kanban.el.querySelector('.o_search_panel_section_header').innerText, "COMPANY");
+ assert.containsOnce(kanban, 'section div.alert.alert-warning');
+ assert.strictEqual(kanban.el.querySelector('section div.alert.alert-warning').innerText, "Too many items to display.");
+ assert.containsNone(kanban, '.o_search_panel_category_value');
+
+ kanban.destroy();
+ });
+
+ QUnit.test("reached limit for a filter", async function (assert) {
+ assert.expect(6);
+
+ const kanban = await createView({
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="company_id" select="multi" limit="2"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: 'partner',
+ View: KanbanView,
+ });
+
+ assert.containsOnce(kanban, '.o_search_panel_section');
+ assert.containsOnce(kanban, '.o_search_panel_section_header');
+ assert.strictEqual(kanban.el.querySelector('.o_search_panel_section_header').innerText, "COMPANY");
+ assert.containsOnce(kanban, 'section div.alert.alert-warning');
+ assert.strictEqual(kanban.el.querySelector('section div.alert.alert-warning').innerText, "Too many items to display.");
+ assert.containsNone(kanban, '.o_search_panel_filter_value');
+
+ kanban.destroy();
+ });
+
+ QUnit.test("a selected value becomming invalid should no more impact the view", async function (assert) {
+ assert.expect(13);
+
+ const kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method);
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="filter_on_def" string="DEF" domain="[('state', '=', 'def')]"/>
+ <searchpanel>
+ <field name="state" enable_counters="1"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ ]);
+
+ assert.containsN(kanban, '.o_kanban_record span', 4);
+
+ // select 'ABC' in search panel
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ ]);
+
+ assert.containsOnce(kanban, '.o_kanban_record span');
+ assert.strictEqual(kanban.el.querySelector('.o_kanban_record span').innerText, 'yop' );
+
+ // select DEF in filter menu
+ await testUtils.controlPanel.toggleFilterMenu(kanban);
+ await testUtils.controlPanel.toggleMenuItem(kanban, 'DEF');
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ ]);
+
+ const firstCategoryValue = kanban.el.querySelector('.o_search_panel_category_value header');
+ assert.strictEqual(firstCategoryValue.innerText, 'All');
+ assert.hasClass(
+ firstCategoryValue, 'active',
+ "the value 'All' should be selected since ABC is no longer a valid value with respect to search domain"
+ );
+ assert.containsOnce(kanban, '.o_kanban_record span');
+ assert.strictEqual(kanban.el.querySelector('.o_kanban_record span').innerText, 'blip' );
+
+ kanban.destroy();
+ });
+
+ QUnit.test("Categories with default attributes should be udpated when external domain changes", async function (assert) {
+ assert.expect(8);
+
+ const kanban = await createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method && args.method.includes('search_panel_')) {
+ assert.step(args.method);
+ }
+ return this._super.apply(this, arguments);
+ },
+ arch: `
+ <kanban>
+ <templates>
+ <t t-name="kanban-box">
+ <div>
+ <field name="foo"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>`,
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="filter_on_def" string="DEF" domain="[('state', '=', 'def')]"/>
+ <searchpanel>
+ <field name="state"/>
+ </searchpanel>
+ </search>`,
+ },
+ });
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ ]);
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value header label')].map(el => el.innerText),
+ ['All', 'ABC', 'DEF', 'GHI']
+ );
+
+ // select 'ABC' in search panel --> no need to update the category value
+ await testUtils.dom.click(kanban.$('.o_search_panel_category_value:nth(1) header'));
+
+ assert.verifySteps([]);
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value header label')].map(el => el.innerText),
+ ['All', 'ABC', 'DEF', 'GHI']
+ );
+
+ // select DEF in filter menu --> the external domain changes --> the values should be updated
+ await testUtils.controlPanel.toggleFilterMenu(kanban);
+ await testUtils.controlPanel.toggleMenuItem(kanban, 'DEF');
+
+ assert.verifySteps([
+ 'search_panel_select_range',
+ ]);
+ assert.deepEqual(
+ [...kanban.el.querySelectorAll('.o_search_panel_category_value header label')].map(el => el.innerText),
+ ['All', 'DEF']
+ );
+
+ kanban.destroy();
+ });
+
+ QUnit.test("Category with counters and filter with domain", async function (assert) {
+ assert.expect(2);
+
+ const list = await createView({
+ arch: '<tree><field name="foo"/></tree>',
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <searchpanel>
+ <field name="category_id" enable_counters="1"/>
+ <field name="company_id" select="multi" domain="[['category_id', '=', category_id]]"/>
+ </searchpanel>
+ </search>`,
+ },
+ data: this.data,
+ model: "partner",
+ services: this.services,
+ View: ListView,
+ });
+
+ assert.containsN(list, ".o_data_row", 4);
+ assert.strictEqual(
+ list.$(".o_search_panel_category_value").text().replace(/\s/g, ""),
+ "Allgoldsilver",
+ "Category counters should be empty if a filter has a domain attribute"
+ );
+
+ list.destroy();
+ });
+});
+});