[IMP] web: add include archived to domain selector

This commit introduces a new feature to the domain selector that allows
to toggle a new "include archived" option that alters the domain in a way
such that archived records are included or not. This domain part is naive
and may overlap with custom parts of the domain. The special domain part
which includes archived records (['&', <previous_domain>, '|', ('active'
, '=', True), ('active', '=', False)]) is invisible inside the domain
tree but remains visible in the domain code editor.

Also rename the "Match records with xxx of the following rules" statement
to "Match xxx of the following rules".

task-3482381

closes odoo/odoo#135861

Related: odoo/enterprise#47599
Signed-off-by: Mathieu Duckerts-Antoine (dam) <dam@odoo.com>
This commit is contained in:
Julien Carion (juca)
2023-10-02 14:12:47 +00:00
parent 2cede25222
commit 0792200c9d
6 changed files with 229 additions and 93 deletions
@@ -12,6 +12,7 @@ import {
} from "@web/core/domain_selector/utils";
import { Component, onWillStart, onWillUpdateProps } from "@odoo/owl";
import { Domain } from "@web/core/domain";
import { CheckBox } from "@web/core/checkbox/checkbox";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { getOperatorInfo, toOperator } from "@web/core/domain_selector/domain_selector_operators";
@@ -26,6 +27,8 @@ import {
import { ModelFieldSelector } from "@web/core/model_field_selector/model_field_selector";
import { useLoadFieldInfo } from "@web/core/model_field_selector/utils";
import { formatValue } from "@web/core/domain_tree";
import { deepEqual } from "@web/core/utils/objects";
import { useService } from "@web/core/utils/hooks";
function collectDifferences(tree, otherTree) {
// some differences shadow the other differences "below":
@@ -92,6 +95,7 @@ export class DomainSelector extends Component {
ModelFieldSelector,
Editor,
PathEditor,
CheckBox,
};
static props = {
domain: String,
@@ -114,9 +118,18 @@ export class DomainSelector extends Component {
setup() {
this.getDefaultLeafDomain = useGetDefaultLeafDomain();
this.loadDisplayNames = useLoadDisplayNames();
this.loadFieldInfo = useLoadFieldInfo();
this.fieldService = useService("field");
this.loadFieldInfo = useLoadFieldInfo(this.fieldService);
this.tree = null;
this.previousTree = null;
this.includeArchived = false;
this.archivedConnector = {
type: "condition",
value: [true, false],
negate: false,
path: "active",
operator: "in",
};
onWillStart(() => this.onPropsUpdated(this.props));
onWillUpdateProps((np) => this.onPropsUpdated(np));
}
@@ -157,6 +170,7 @@ export class DomainSelector extends Component {
const paths = new Set([
...extractPathsFromDomain(domain),
...extractPathsFromDomain(defaultLeafDomain),
"active",
]);
await this.loadFieldDefs(p.resModel, paths);
@@ -177,6 +191,32 @@ export class DomainSelector extends Component {
options
).children[0];
this.showArchivedCheckbox = Boolean(this.fieldDefs.active);
this.includeArchived = false;
if (this.showArchivedCheckbox) {
if (this.tree.value === "&") {
this.tree.children = this.tree.children.filter((child) => {
if (deepEqual(child, this.archivedConnector)) {
this.includeArchived = true;
return false;
}
return true;
});
if (this.tree.children.length === 1) {
if (this.tree.children[0].type !== "connector") {
this.tree.value = this.props.defaultConnector;
} else {
this.tree = this.tree.children[0];
}
}
} else if (
this.tree.children.length === 1 &&
deepEqual(this.tree.children[0], this.archivedConnector)
) {
this.includeArchived = true;
this.tree = buildDomainSelectorTree(new Domain(`[]`));
}
}
if (this.previousTree) {
// find "first" difference
restoreVirtualOperators(this.tree, this.previousTree);
@@ -184,9 +224,17 @@ export class DomainSelector extends Component {
}
}
toggleIncludeArchived() {
this.includeArchived = !this.includeArchived;
this.notifyChanges();
}
notifyChanges() {
this.previousTree = cloneTree(this.tree);
const domain = this.tree ? buildDomain(this.tree) : `[]`;
const archiveDomain = this.includeArchived ? `[("active", "in", [True, False])]` : `[]`;
const domain = this.tree
? Domain.and([buildDomain(this.tree), archiveDomain]).toString()
: archiveDomain;
this.props.update(domain);
}
@@ -4,23 +4,31 @@
<t t-name="web._DomainSelector">
<div class="o_domain_node o_domain_tree o_domain_selector w-100" aria-atomic="true" t-att-class="className" t-ref="root">
<t t-if="tree">
<t t-set="node" t-value="tree" />
<t t-if="node.children.length === 0">
<span>Match <strong>all records</strong></span>
<t t-if="!props.readonly">
<button class="btn btn-sm btn-primary o_domain_add_first_node_button ms-1" t-on-click="() => this.insertRootLeaf(node)">
<i class="fa fa-plus"/> Add condition
</button>
</t>
</t>
<t t-else="">
<span>Match records with </span>
<t t-call="web._DomainSelector.connector.dropdown" />
<span> of the following rules:</span>
<t t-call="web._DomainSelector.connector.children" />
<t t-if="!props.readonly">
<a href="#" class="pt-1 ms-1" role="button" t-on-click="() => this.insertRootLeaf(node)">New Rule</a>
</t>
<t t-set="node" t-value="tree"/>
<div class="d-flex justify-content-between">
<div>
<t t-if="node.children.length === 0">
<span>Match <strong>all records</strong></span>
</t>
<t t-else="">
<span>Match </span>
<t t-call="web._DomainSelector.connector.dropdown" />
<span> of the following rules:</span>
</t>
</div>
<CheckBox
t-if="showArchivedCheckbox"
value="includeArchived"
disabled="props.readonly"
className="'form-switch'"
onChange.bind="toggleIncludeArchived"
>
Include archived
</CheckBox>
</div>
<t t-if="node.children.length" t-call="web._DomainSelector.connector.children" />
<t t-if="!props.readonly">
<a href="#" class="pt-1 ms-1" role="button" t-on-click="() => this.insertRootLeaf(node)">New Rule</a>
</t>
</t>
<t t-else="">
@@ -19,6 +19,15 @@ export function shallowEqual(obj1, obj2, comparisonFn = (a, b) => a === b) {
);
}
/**
* Deeply compares two objects.
*
* @template {unknown} T
* @param {T} obj1
* @param {T} obj2
*/
export const deepEqual = (obj1, obj2) => shallowEqual(obj1, obj2, deepEqual);
/**
* Deep copies an object. As it relies on JSON this function as some limitations
* - no support for circular objects
@@ -172,17 +172,16 @@ QUnit.module("Components", (hooks) => {
isDebugMode: true,
});
// As we gave an empty domain, there should be a visible button to add
// the first domain part
// When we have an empty domain, the "New Rule" button should be available
assert.containsOnce(
target,
".o_domain_add_first_node_button",
"there should be a button to create first domain element"
"a[role=button]",
"there should be a button to create a domain element"
);
// Clicking on the button should add a visible field selector in the
// widget so that the user can change the field chain
await click(target, ".o_domain_add_first_node_button");
await click(target, "a[role=button]");
assert.containsOnce(target, ".o_model_field_selector");
// Focusing the field selector input should open a field selector popover
@@ -431,7 +430,7 @@ QUnit.module("Components", (hooks) => {
});
// Clicking on the button should add a visible field selector in the
// widget so that the user can change the field chain
await click(target, ".o_domain_add_first_node_button");
await click(target, "a[role=button]");
});
QUnit.test("edit a domain with the debug textarea", async (assert) => {
@@ -756,10 +755,10 @@ QUnit.module("Components", (hooks) => {
await click(target, ".o_reset_domain_button");
assert.strictEqual(
target.querySelector(".o_domain_selector").innerText.toLowerCase(),
"match all records add condition"
"match all records\nnew rule"
);
assert.containsNone(target, ".o_reset_domain_button");
assert.containsOnce(target, ".o_domain_add_first_node_button");
assert.containsOnce(target, "a[role=button]");
assert.verifySteps(["[]"]);
});
@@ -905,7 +904,7 @@ QUnit.module("Components", (hooks) => {
});
assert.strictEqual(
target.querySelector(".o_domain_selector").innerText,
`Match records with any of the following rules:\ncreate_date\nis between 2023-04-01 00:00:00 and 2023-04-30 23:59:59\n0\n= 1`
`Match any of the following rules:\ncreate_date\nis between 2023-04-01 00:00:00 and 2023-04-30 23:59:59\n0\n= 1`
);
});
@@ -934,99 +933,99 @@ QUnit.module("Components", (hooks) => {
const toTest = [
{
domain: `["!", ("foo", "=", "abc")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc`,
result: `Match all of the following rules:\nFoo\n!= abc`,
},
{
domain: `["!", "!", ("foo", "=", "abc")]`,
result: `Match records with all of the following rules:\nFoo\n= abc`,
result: `Match all of the following rules:\nFoo\n= abc`,
},
{
domain: `["!", "!", "!", ("foo", "=", "abc")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc`,
result: `Match all of the following rules:\nFoo\n!= abc`,
},
{
domain: `["!", "&", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n!= abc\nFoo\n!= def`,
result: `Match any of the following rules:\nFoo\n!= abc\nFoo\n!= def`,
},
{
domain: `["!", "|", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc\nFoo\n!= def`,
result: `Match all of the following rules:\nFoo\n!= abc\nFoo\n!= def`,
},
{
domain: `["&", "!", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc\nFoo\n= def`,
result: `Match all of the following rules:\nFoo\n!= abc\nFoo\n= def`,
},
{
domain: `["&", "!", "!", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match all of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["&", ("foo", "=", "abc"), "!", ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n= abc\nFoo\n!= def`,
result: `Match all of the following rules:\nFoo\n= abc\nFoo\n!= def`,
},
{
domain: `["&", ("foo", "=", "abc"), "!", "!", ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match all of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["|", "!", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n!= abc\nFoo\n= def`,
result: `Match any of the following rules:\nFoo\n!= abc\nFoo\n= def`,
},
{
domain: `["|", "!", "!", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match any of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["|", ("foo", "=", "abc"), "!", ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n= abc\nFoo\n!= def`,
result: `Match any of the following rules:\nFoo\n= abc\nFoo\n!= def`,
},
{
domain: `["|", ("foo", "=", "abc"), "!", "!", ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match any of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["&", "!", "&", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with all of the following rules:\nany\nof:\nFoo\n!= abc\nFoo\n!= def\nFoo\n= ghi`,
result: `Match all of the following rules:\nany\nof:\nFoo\n!= abc\nFoo\n!= def\nFoo\n= ghi`,
},
{
domain: `["&", "!", "|", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc\nFoo\n!= def\nFoo\n= ghi`,
result: `Match all of the following rules:\nFoo\n!= abc\nFoo\n!= def\nFoo\n= ghi`,
},
{
domain: `["|", "!", "&", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with any of the following rules:\nFoo\n!= abc\nFoo\n!= def\nFoo\n= ghi`,
result: `Match any of the following rules:\nFoo\n!= abc\nFoo\n!= def\nFoo\n= ghi`,
},
{
domain: `["|", "!", "|", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with any of the following rules:\nall\nof:\nFoo\n!= abc\nFoo\n!= def\nFoo\n= ghi`,
result: `Match any of the following rules:\nall\nof:\nFoo\n!= abc\nFoo\n!= def\nFoo\n= ghi`,
},
{
domain: `["!", "&", "&", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with any of the following rules:\nFoo\n!= abc\nFoo\n!= def\nFoo\n!= ghi`,
result: `Match any of the following rules:\nFoo\n!= abc\nFoo\n!= def\nFoo\n!= ghi`,
},
{
domain: `["!", "|", "|", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc\nFoo\n!= def\nFoo\n!= ghi`,
result: `Match all of the following rules:\nFoo\n!= abc\nFoo\n!= def\nFoo\n!= ghi`,
},
{
domain: `["!", "&", "|", ("foo", "=", "abc"), "!", ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with any of the following rules:\nall\nof:\nFoo\n!= abc\nFoo\n= def\nFoo\n!= ghi`,
result: `Match any of the following rules:\nall\nof:\nFoo\n!= abc\nFoo\n= def\nFoo\n!= ghi`,
},
{
domain: `["!", "|", "&", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with all of the following rules:\nany\nof:\nFoo\n!= abc\nFoo\n!= def\nFoo\n!= ghi`,
result: `Match all of the following rules:\nany\nof:\nFoo\n!= abc\nFoo\n!= def\nFoo\n!= ghi`,
},
{
domain: `["!", "&", ("foo", "=", "abc"), "|", ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with any of the following rules:\nFoo\n!= abc\nall\nof:\nFoo\n!= def\nFoo\n!= ghi`,
result: `Match any of the following rules:\nFoo\n!= abc\nall\nof:\nFoo\n!= def\nFoo\n!= ghi`,
},
{
domain: `["!", "|", ("foo", "=", "abc"), "&", ("foo", "=", "def"), ("foo", "!=", "ghi")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc\nany\nof:\nFoo\n!= def\nFoo\n= ghi`,
result: `Match all of the following rules:\nFoo\n!= abc\nany\nof:\nFoo\n!= def\nFoo\n= ghi`,
},
{
domain: `["!", "|", ("foo", "=", "abc"), "&", ("foo", "!=", "def"), "!", ("foo", "=", "ghi")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc\nany\nof:\nFoo\n= def\nFoo\n= ghi`,
result: `Match all of the following rules:\nFoo\n!= abc\nany\nof:\nFoo\n= def\nFoo\n= ghi`,
},
];
@@ -1051,99 +1050,99 @@ QUnit.module("Components", (hooks) => {
const toTest = [
{
domain: `["!", ("foo", "=", "abc")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc`,
result: `Match all of the following rules:\nFoo\n!= abc`,
},
{
domain: `["!", "!", ("foo", "=", "abc")]`,
result: `Match records with all of the following rules:\nFoo\n= abc`,
result: `Match all of the following rules:\nFoo\n= abc`,
},
{
domain: `["!", "!", "!", ("foo", "=", "abc")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc`,
result: `Match all of the following rules:\nFoo\n!= abc`,
},
{
domain: `["!", "&", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with not all of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match not all of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["!", "|", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with none of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match none of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["&", "!", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n!= abc\nFoo\n= def`,
result: `Match all of the following rules:\nFoo\n!= abc\nFoo\n= def`,
},
{
domain: `["&", "!", "!", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match all of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["&", ("foo", "=", "abc"), "!", ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n= abc\nFoo\n!= def`,
result: `Match all of the following rules:\nFoo\n= abc\nFoo\n!= def`,
},
{
domain: `["&", ("foo", "=", "abc"), "!", "!", ("foo", "=", "def")]`,
result: `Match records with all of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match all of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["|", "!", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n!= abc\nFoo\n= def`,
result: `Match any of the following rules:\nFoo\n!= abc\nFoo\n= def`,
},
{
domain: `["|", "!", "!", ("foo", "=", "abc"), ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match any of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["|", ("foo", "=", "abc"), "!", ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n= abc\nFoo\n!= def`,
result: `Match any of the following rules:\nFoo\n= abc\nFoo\n!= def`,
},
{
domain: `["|", ("foo", "=", "abc"), "!", "!", ("foo", "=", "def")]`,
result: `Match records with any of the following rules:\nFoo\n= abc\nFoo\n= def`,
result: `Match any of the following rules:\nFoo\n= abc\nFoo\n= def`,
},
{
domain: `["&", "!", "&", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with all of the following rules:\nnot all\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
result: `Match all of the following rules:\nnot all\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["&", "!", "|", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with all of the following rules:\nnone\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
result: `Match all of the following rules:\nnone\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["|", "!", "&", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with any of the following rules:\nnot all\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
result: `Match any of the following rules:\nnot all\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["|", "!", "|", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with any of the following rules:\nnone\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
result: `Match any of the following rules:\nnone\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["!", "&", "&", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with not all of the following rules:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
result: `Match not all of the following rules:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["!", "|", "|", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with none of the following rules:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
result: `Match none of the following rules:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["!", "&", "|", ("foo", "=", "abc"), "!", ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with not all of the following rules:\nany\nof:\nFoo\n= abc\nFoo\n!= def\nFoo\n= ghi`,
result: `Match not all of the following rules:\nany\nof:\nFoo\n= abc\nFoo\n!= def\nFoo\n= ghi`,
},
{
domain: `["!", "|", "&", ("foo", "=", "abc"), ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with none of the following rules:\nall\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
result: `Match none of the following rules:\nall\nof:\nFoo\n= abc\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["!", "&", ("foo", "=", "abc"), "|", ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with not all of the following rules:\nFoo\n= abc\nany\nof:\nFoo\n= def\nFoo\n= ghi`,
result: `Match not all of the following rules:\nFoo\n= abc\nany\nof:\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["!", "|", ("foo", "=", "abc"), "&", ("foo", "=", "def"), ("foo", "=", "ghi")]`,
result: `Match records with none of the following rules:\nFoo\n= abc\nall\nof:\nFoo\n= def\nFoo\n= ghi`,
result: `Match none of the following rules:\nFoo\n= abc\nall\nof:\nFoo\n= def\nFoo\n= ghi`,
},
{
domain: `["!", "|", ("foo", "=", "abc"), "&", ("foo", "=", "def"), "!", ("foo", "=", "ghi")]`,
result: `Match records with none of the following rules:\nFoo\n= abc\nall\nof:\nFoo\n= def\nFoo\n!= ghi`,
result: `Match none of the following rules:\nFoo\n= abc\nall\nof:\nFoo\n= def\nFoo\n!= ghi`,
},
];
@@ -1268,17 +1267,14 @@ QUnit.module("Components", (hooks) => {
QUnit.test("button 'New Rule' (edit mode)", async (assert) => {
await makeDomainSelector();
assert.containsNone(target, ".o_domain_leaf");
assert.containsOnce(target, ".o_domain_add_first_node_button");
assert.containsNone(target, "a[role=button]");
assert.containsOnce(target, "a[role=button]");
await click(target, ".o_domain_add_first_node_button");
await click(target, "a[role=button]");
assert.containsOnce(target, ".o_domain_leaf");
assert.containsNone(target, ".o_domain_add_first_node_button");
assert.containsOnce(target, "a[role=button]");
await click(target, "a[role=button]");
assert.containsN(target, ".o_domain_leaf", 2);
assert.containsNone(target, ".o_domain_add_first_node_button");
assert.containsOnce(target, "a[role=button]");
});
@@ -2080,4 +2076,81 @@ QUnit.module("Components", (hooks) => {
assert.containsNone(target, ".o_ds_value_cell");
assert.verifySteps([`[("product_ids", "!=", False)]`]);
});
QUnit.test("Include archived button basic use", async (assert) => {
serverData.models.partner.fields.active = {
string: "Active",
type: "boolean",
searchable: true,
};
await makeDomainSelector({
isDebugMode: true,
domain: `["&", ("foo", "=", "test"), ("bar", "=", True)]`,
update(domain) {
assert.step(domain);
},
});
assert.containsN(target, ".o_domain_leaf", 2);
assert.containsOnce(target, '.form-switch label:contains("Include archived")');
await click(target, ".form-switch");
assert.containsN(target, ".o_domain_leaf", 2);
assert.verifySteps([
'["&", "&", ("foo", "=", "test"), ("bar", "=", True), ("active", "in", [True, False])]',
]);
await click(target, ".dropdown-toggle");
await click(target, ".dropdown-menu span:nth-child(2)");
assert.containsN(target, ".o_domain_leaf", 2);
assert.verifySteps([
'["&", "|", ("foo", "=", "test"), ("bar", "=", True), ("active", "in", [True, False])]',
]);
await click(target, ".form-switch");
assert.containsN(target, ".o_domain_leaf", 2);
assert.verifySteps(['["|", ("foo", "=", "test"), ("bar", "=", True)]']);
});
QUnit.test("Include archived on empty tree", async (assert) => {
serverData.models.partner.fields.active = {
string: "Active",
type: "boolean",
searchable: true,
};
await makeDomainSelector({
isDebugMode: true,
domain: `[("foo", "=", "test")]`,
update(domain) {
assert.step(domain);
},
});
assert.containsOnce(target, ".o_domain_leaf");
assert.containsOnce(target, '.form-switch label:contains("Include archived")');
await click(target, ".form-switch");
assert.containsOnce(target, ".o_domain_leaf");
assert.verifySteps(['["&", ("foo", "=", "test"), ("active", "in", [True, False])]']);
await click(target, ".o_domain_delete_node_button");
assert.containsNone(target, ".o_domain_leaf");
assert.verifySteps(['[("active", "in", [True, False])]']);
await click(target, ".form-switch");
assert.verifySteps(["[]"]);
await click(target, ".form-switch");
assert.containsNone(target, ".o_domain_leaf");
assert.verifySteps(['[("active", "in", [True, False])]']);
await click(target, "a[role=button]");
assert.containsOnce(target, ".o_domain_leaf");
assert.verifySteps(['["&", ("id", "=", 1), ("active", "in", [True, False])]']);
});
QUnit.test(
"Include archived not shown when model doesn't have the active field",
async (assert) => {
await makeDomainSelector({
isDebugMode: true,
domain: `[("foo", "=", "test")]`,
update(domain) {
assert.step(domain);
},
});
assert.containsOnce(target, ".o_domain_leaf");
assert.containsNone(target, '.form-switch label:contains("Include archived")');
}
);
});
@@ -1476,7 +1476,7 @@ QUnit.module("Search", (hooks) => {
assert.containsNone(target, ".o_domain_leaf");
assert.ok(target.querySelector(".modal footer button").disabled);
await click(target, ".modal .o_domain_add_first_node_button");
await click(target, ".modal .o_domain_tree a[role=button]");
assert.containsOnce(target, ".o_domain_leaf");
assert.strictEqual(target.querySelector(".o_model_field_selector_value").innerText, "ID");
assert.strictEqual(target.querySelector(".o_domain_leaf_operator_select").value, "=");
@@ -181,13 +181,12 @@ QUnit.module("Fields", (hooks) => {
</form>`,
});
// As the domain is empty, there should be a button to add the first
// domain part
assert.containsOnce(target, ".o_domain_add_first_node_button");
// As the domain is empty, there should be a button to add a new rule
assert.containsOnce(target, ".o_domain_tree a[role=button]");
// Clicking on the button should add the [["id", "=", "1"]] domain, so
// there should be a field selector in the DOM
await click(target, ".o_domain_add_first_node_button");
await click(target, ".o_domain_tree a[role=button]");
assert.containsOnce(target, ".o_model_field_selector", "there should be a field selector");
// Focusing the field selector input should open the field selector
@@ -242,7 +241,7 @@ QUnit.module("Fields", (hooks) => {
</form>`,
});
await click(target, ".o_domain_add_first_node_button");
await click(target, ".o_domain_tree a[role=button]");
await click(target, ".o_model_field_selector");
await click(
document.body.querySelector(
@@ -930,7 +929,7 @@ QUnit.module("Fields", (hooks) => {
assert.containsNone(target, ".modal");
await click(target, ".o_field_domain_dialog_button");
assert.containsOnce(target, ".modal");
await click(target, ".modal .o_domain_add_first_node_button");
await click(target, ".modal .o_domain_tree a[role=button]");
await click(target, ".modal-footer .btn-primary");
assert.containsOnce(target, ".o_domain_leaf");
assert.strictEqual(target.querySelector(".o_domain_leaf").textContent, "ID = 1");
@@ -957,7 +956,7 @@ QUnit.module("Fields", (hooks) => {
await click(target, ".o_field_domain_dialog_button");
assert.containsOnce(target, ".modal");
await click(target, ".modal .o_domain_add_first_node_button");
await click(target, ".modal .o_domain_tree a[role=button]");
await editInput(target, ".o_domain_debug_input", "[(0, '=', expr)]");
await click(target, ".modal-footer .btn-primary");
assert.containsOnce(target, ".modal", "the domain is invalid: the dialog is not closed");
@@ -1027,13 +1026,12 @@ QUnit.module("Fields", (hooks) => {
// Unfold the domain
await click(target, ".o_field_domain > div > div");
// As the domain is empty, there should be a button to add the first
// domain part
assert.containsOnce(target, ".o_domain_add_first_node_button");
// As the domain is empty, there should be a button to add a new rule
assert.containsOnce(target, ".o_domain_tree a[role=button]");
// Clicking on the button should add the [["id", "=", "1"]] domain, so
// there should be a field selector in the DOM
await click(target, ".o_domain_add_first_node_button");
await click(target, ".o_domain_tree a[role=button]");
assert.containsOnce(target, ".o_model_field_selector");
// Focusing the field selector input should open the field selector