Odoo18-Base/addons/web/static/tests/legacy/views/search_panel_tests.js
2025-03-10 11:12:23 +07:00

4187 lines
157 KiB
JavaScript

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.searchPanel");
const legacyViewRegistry = require('web.view_registry');
const createView = testUtils.createView;
const { makeFakeUserService } = require("@web/../tests/helpers/mock_services");
const { createWebClient, doAction } = require('@web/../tests/webclient/helpers');
const { getFixture, legacyExtraNextTick } = require("@web/../tests/helpers/utils");
const { registry } = require("@web/core/registry");
const { PivotView } = require("@web/views/pivot/pivot_view");
const {
switchView,
toggleFilterMenu,
toggleMenuItem,
} = require("@web/../tests/search/helpers");
/**
* 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);
}
let serverData;
let target;
QUnit.module('Views', {
beforeEach: function () {
registry.category("views").remove("list"); // remove new list from registry
registry.category("views").remove("kanban"); // remove new kanban from registry
registry.category("views").remove("form"); // remove new form from registry
legacyViewRegistry.add("list", ListView); // add legacy list -> will be wrapped and added to new registry
legacyViewRegistry.add("kanban", KanbanView); // add legacy kanban -> will be wrapped and added to new registry
legacyViewRegistry.add("form", FormView); // add legacy form -> will be wrapped and added to new registry
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>`,
};
// map legacy test data
const actions = {};
this.actions.forEach((act) => {
actions[act.xmlId || act.id] = act;
});
serverData = {
actions,
models: this.data,
views: this.archs,
};
target = getFixture();
},
}, 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_legacy_kanban_view');
assert.containsN(kanban, '.o_legacy_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 .o_search_panel_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 .o_search_panel_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 toggleFilterMenu(kanban.$el[0]);
await toggleMenuItem(kanban.$el[0], 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 rpcDonePromise = 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);
rpcDonePromise.resolve();
return _super(...arguments);
},
model: 'partner',
View: KanbanView,
});
await rpcDonePromise;
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"/>
<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_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 4);
// trigger a reload and delay the get_filter
def = testUtils.makeTestPromise();
await toggleFilterMenu(kanban.$el[0]);
await toggleMenuItem(kanban.$el[0], 0);
await testUtils.nextTick();
assert.containsN(kanban, '.o_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 4);
def.resolve();
await testUtils.nextTick();
assert.containsOnce(kanban, '.o_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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 testUtils.controlPanel.pagerNext(kanban);
assert.verifySteps([]);
// reload with another domain, so the filters should be reloaded
await toggleFilterMenu(kanban.$el[0]);
await toggleMenuItem(kanban.$el[0], 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 testUtils.controlPanel.pagerNext(kanban);
assert.verifySteps([]);
// reload with another domain, so the filters should be reloaded
await toggleFilterMenu(kanban.$el[0]);
await toggleMenuItem(kanban.$el[0], 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 testUtils.controlPanel.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 toggleFilterMenu(kanban.$el[0]);
await toggleMenuItem(kanban.$el[0], 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 testUtils.controlPanel.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 toggleFilterMenu(kanban.$el[0]);
await toggleMenuItem(kanban.$el[0], 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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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: [],
context: {},
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"/>
<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);
registry.category("services").add("user", makeFakeUserService());
const webClient = await createWebClient({ serverData });
await doAction(webClient, 1);
assert.containsOnce(target, '.o_content.o_controller_with_searchpanel .o_legacy_kanban_view');
assert.containsOnce(target, '.o_content.o_controller_with_searchpanel .o_search_panel');
await switchView(target, 'pivot');
await testUtils.nextTick();
assert.containsOnce(target, '.o_content .o_pivot');
assert.containsNone(target, '.o_content .o_search_panel');
await switchView(target, 'list');
await legacyExtraNextTick();
assert.containsOnce(target, '.o_content.o_controller_with_searchpanel .o_legacy_list_view');
assert.containsOnce(target, '.o_content.o_controller_with_searchpanel .o_search_panel');
await testUtils.dom.click($(target).find('.o_data_row .o_data_cell:first'));
await legacyExtraNextTick();
assert.containsOnce(target, '.o_content .o_legacy_form_view');
assert.containsNone(target, '.o_content .o_search_panel');
});
QUnit.skip('search panel with view_types attribute', async function (assert) {
assert.expect(6);
serverData.views['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>`;
registry.category("views").add("pivot", PivotView, { force: true });
const webClient = await createWebClient({ serverData });
await doAction(webClient, 1);
assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_legacy_kanban_view');
assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel');
await switchView(webClient, 'list');
await legacyExtraNextTick();
assert.containsOnce(webClient, '.o_content .o_legacy_list_view');
assert.containsNone(webClient, '.o_content .o_search_panel');
await switchView(webClient, 'pivot');
assert.containsOnce(webClient, '.o_content.o_component_with_search_panel .o_legacy_pivot');
assert.containsOnce(webClient, '.o_content.o_component_with_search_panel .o_search_panel');
});
QUnit.test('search panel state is shared between views', async function (assert) {
assert.expect(16);
const mockRPC = (route, args) => {
if (route === '/web/dataset/search_read') {
assert.step(JSON.stringify(args.domain));
}
};
const webClient = await createWebClient({ serverData , mockRPC });
await doAction(webClient, 1);
assert.hasClass($(target).find('.o_search_panel_category_value:first header'), 'active');
assert.containsN(target, '.o_kanban_record:not(.o_kanban_ghost)', 4);
// select 'asustek' company
await testUtils.dom.click($(target).find('.o_search_panel_category_value:nth(1) header'));
await legacyExtraNextTick();
assert.hasClass($(target).find('.o_search_panel_category_value:nth(1) header'), 'active');
assert.containsN(target, '.o_kanban_record:not(.o_kanban_ghost)', 2);
await switchView(target, 'list');
await legacyExtraNextTick();
assert.hasClass($(target).find('.o_search_panel_category_value:nth(1) header'), 'active');
assert.containsN(target, '.o_data_row', 2);
// select 'agrolait' company
await testUtils.dom.click($(target).find('.o_search_panel_category_value:nth(2) header'));
await legacyExtraNextTick();
assert.hasClass($(target).find('.o_search_panel_category_value:nth(2) header'), 'active');
assert.containsN(target, '.o_data_row', 2);
await switchView(target, 'kanban');
await legacyExtraNextTick();
assert.hasClass($(target).find('.o_search_panel_category_value:nth(2) header'), 'active');
assert.containsN(target, '.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
]);
});
QUnit.test('search panel filters are kept between switch views', async function (assert) {
assert.expect(17);
const mockRPC = (route, args) => {
if (route === '/web/dataset/search_read') {
assert.step(JSON.stringify(args.domain));
}
};
const webClient = await createWebClient({ serverData , mockRPC });
await doAction(webClient, 1);
assert.containsNone(target, '.o_search_panel_filter_value input:checked');
assert.containsN(target, '.o_kanban_record:not(.o_kanban_ghost)', 4);
// select gold filter
await testUtils.dom.click($(target).find('.o_search_panel_filter input[type="checkbox"]:nth(0)'));
await legacyExtraNextTick();
assert.containsOnce(target, '.o_search_panel_filter_value input:checked');
assert.containsN(target, '.o_kanban_record:not(.o_kanban_ghost)', 1);
await switchView(target, 'list');
await legacyExtraNextTick();
assert.containsOnce(target, '.o_search_panel_filter_value input:checked');
assert.containsN(target, '.o_data_row', 1);
// select silver filter
await testUtils.dom.click($(target).find('.o_search_panel_filter input[type="checkbox"]:nth(1)'));
await legacyExtraNextTick();
assert.containsN(target, '.o_search_panel_filter_value input:checked', 2);
assert.containsN(target, '.o_data_row', 4);
await switchView(target, 'kanban');
await legacyExtraNextTick();
assert.containsN(target, '.o_search_panel_filter_value input:checked', 2);
assert.containsN(target, '.o_kanban_record:not(.o_kanban_ghost)', 4);
await testUtils.dom.click($(target).find(".o_kanban_record:nth(0)"));
await legacyExtraNextTick();
await testUtils.dom.click($(target).find(".breadcrumb-item:nth(0)"));
await legacyExtraNextTick();
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
]);
});
QUnit.skip('search panel filters are kept when switching to a view with no search panel', async function (assert) {
assert.expect(13);
registry.category("views").add("pivot", PivotView, { force: true });
const webClient = await createWebClient({ serverData });
await doAction(webClient, 1);
assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_legacy_kanban_view');
assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel');
assert.containsNone(webClient, '.o_search_panel_filter_value input:checked');
assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 4);
// select gold filter
await testUtils.dom.click($(webClient.el).find('.o_search_panel_filter input[type="checkbox"]:nth(0)'));
await legacyExtraNextTick();
assert.containsOnce(webClient, '.o_search_panel_filter_value input:checked');
assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 1);
// switch to pivot
await switchView(webClient, 'pivot');
await legacyExtraNextTick();
assert.containsOnce(webClient, '.o_content .o_legacy_pivot');
assert.containsNone(webClient, '.o_content .o_search_panel');
assert.strictEqual($(webClient.el).find('.o_pivot_cell_value').text(), '15');
// switch to list
await switchView(webClient, 'list');
await legacyExtraNextTick();
assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_legacy_list_view');
assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel');
assert.containsOnce(webClient, '.o_search_panel_filter_value input:checked');
assert.containsN(webClient, '.o_data_row', 1);
});
QUnit.test('after onExecuteAction, selects "All" as default category value', async function (assert) {
assert.expect(4);
const webClient = await createWebClient({ serverData });
await doAction(webClient, 2);
await testUtils.dom.click($(target).find('.o_legacy_form_view button:contains("multi view")'));
await legacyExtraNextTick();
assert.containsOnce(target, '.o_legacy_kanban_view');
assert.containsOnce(target, '.o_search_panel');
assert.containsOnce(target, '.o_search_panel_category_value:first .active');
assert.verifySteps([]); // should not communicate with localStorage
});
QUnit.test('search panel is not instantiated if stated in context', async function (assert) {
assert.expect(2);
serverData.actions[2].context = {search_panel: false};
const webClient = await createWebClient({ serverData });
await doAction(webClient, 2);
await testUtils.dom.click($(target).find('.o_legacy_form_view button:contains("multi view")'));
await legacyExtraNextTick();
assert.containsOnce(target, '.o_legacy_kanban_view');
assert.containsNone(target, '.o_search_panel');
});
QUnit.test('categories and filters are not reloaded when switching between views', async function (assert) {
assert.expect(3);
const mockRPC = (route, args) => {
if (args.method && args.method.includes('search_panel_')) {
assert.step(args.method);
}
};
const webClient = await createWebClient({ serverData, mockRPC });
await doAction(webClient, 1);
await switchView(target, 'list');
await legacyExtraNextTick();
await switchView(target, 'kanban');
await legacyExtraNextTick();
assert.verifySteps([
'search_panel_select_range', // kanban: categories
'search_panel_select_multi_range', // kanban: filters
]);
});
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++) {
serverData.models.category.records.push({id: i, name: "Cat " + i});
}
const container = document.createElement("div");
container.classList.add("o_web_client");
container.style = "max-height: 300px";
target.appendChild(container);
const webClient = await createWebClient({ target: container, serverData });
await doAction(webClient, 1);
async function scroll(top) {
target.querySelector(".o_search_panel").scrollTop = top;
await testUtils.nextTick();
}
assert.containsOnce(target, '.o_content .o_legacy_kanban_view');
assert.strictEqual($(target).find('.o_search_panel').scrollTop(), 0);
// simulate a scroll in the search panel and switch into list
await scroll(50);
await switchView(target, 'list');
await legacyExtraNextTick();
assert.containsOnce(target, '.o_content .o_legacy_list_view');
assert.strictEqual($(target).find('.o_search_panel').scrollTop(), 50);
// simulate another scroll and switch back to kanban
await scroll(30);
await switchView(target, 'kanban');
await legacyExtraNextTick();
assert.containsOnce(target, '.o_content .o_legacy_kanban_view');
assert.strictEqual($(target).find('.o_search_panel').scrollTop(), 30);
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_legacy_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 toggleFilterMenu(kanban.$el[0]);
await toggleMenuItem(kanban.$el[0], '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 .o_search_panel_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 .o_search_panel_label')].map(el => el.innerText),
['All', 'ABC', 'DEF', 'GHI']
);
// select DEF in filter menu --> the external domain changes --> the values should be updated
await toggleFilterMenu(kanban.$el[0]);
await toggleMenuItem(kanban.$el[0], 'DEF');
assert.verifySteps([
'search_panel_select_range',
]);
assert.deepEqual(
[...kanban.el.querySelectorAll('.o_search_panel_category_value header .o_search_panel_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"/>
<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();
});
});
});