Odoo18-Base/addons/web_hierarchy/static/tests/hierarchy_view.test.js
2025-01-06 10:57:38 +07:00

1387 lines
50 KiB
JavaScript

import { describe, expect, test } from "@odoo/hoot";
import { press, queryAllTexts, queryOne, scroll } from "@odoo/hoot-dom";
import { advanceFrame, animationFrame } from "@odoo/hoot-mock";
import {
contains,
defineModels,
fields,
makeServerError,
mockService,
models,
mountView,
onRpc,
patchWithCleanup,
removeFacet,
toggleMenuItem,
toggleSearchBarMenu,
} from "@web/../tests/web_test_helpers";
import { HierarchyModel } from "@web_hierarchy/hierarchy_model";
async function enableFilters(filterNames = []) {
await toggleSearchBarMenu();
for (const filter of filterNames) {
await toggleMenuItem(filter);
}
}
class Employee extends models.Model {
_name = "hr.employee";
name = fields.Char();
parent_id = fields.Many2one({ string: "Manager", relation: "hr.employee" });
child_ids = fields.One2many({
string: "Subordinates",
relation: "hr.employee",
relation_field: "parent_id",
});
_records = [
{ id: 1, name: "Albert", parent_id: false, child_ids: [2, 3] },
{ id: 2, name: "Georges", parent_id: 1, child_ids: [] },
{ id: 3, name: "Josephine", parent_id: 1, child_ids: [4] },
{ id: 4, name: "Louis", parent_id: 3, child_ids: [] },
];
_views = {
hierarchy: `
<hierarchy>
<templates>
<t t-name="hierarchy-box">
<div class="o_hierarchy_node_header">
<field name="name"/>
</div>
<div class="o_hierarchy_node_body">
<field name="parent_id"/>
</div>
</t>
</templates>
</hierarchy>
`,
"hierarchy,1": `
<hierarchy child_field="child_ids">
<field name="child_ids" invisible="1"/>
<templates>
<t t-name="hierarchy-box">
<div class="o_hierarchy_node_header">
<field name="name"/>
</div>
<div class="o_hierarchy_node_body">
<field name="parent_id"/>
</div>
</t>
</templates>
</hierarchy>
`,
form: `
<form>
<sheet>
<group>
<field name="name"/>
<field name="parent_id"/>
</group>
</sheet>
</form>
`,
};
}
defineModels([Employee]);
describe.current.tags("desktop");
test("load hierarchy view", async () => {
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_view").toHaveCount(1);
expect(".o_hierarchy_button_add").toHaveCount(1);
expect(".o_hierarchy_view .o_hierarchy_renderer").toHaveCount(1);
expect(".o_hierarchy_view .o_hierarchy_renderer > .o_hierarchy_container").toHaveCount(1);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_separator").toHaveCount(1);
expect(".o_hierarchy_line_part").toHaveCount(2);
expect(".o_hierarchy_line_left").toHaveCount(1);
expect(".o_hierarchy_line_right").toHaveCount(1);
expect(".o_hierarchy_node_container").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(3);
expect(".o_hierarchy_node_button").toHaveCount(2);
expect(".o_hierarchy_node_button.btn-primary").toHaveCount(1);
expect(".o_hierarchy_node_button.btn-primary.d-grid").toHaveCount(1);
expect(".o_hierarchy_node_button.btn-primary.rounded-0").toHaveCount(1);
expect(".o_hierarchy_node_button.btn-primary .o_hierarchy_icon").toHaveCount(1);
expect(".o_hierarchy_node_button.btn-primary").toHaveText("Unfold\n1");
expect(".o_hierarchy_row:eq(0) .o_hierarchy_node").toHaveCount(1);
expect(".o_hierarchy_row:eq(0) .o_hierarchy_node_content").toHaveText("Albert");
expect(".o_hierarchy_node_button.btn-secondary").toHaveCount(1);
expect(".o_hierarchy_node_button.btn-secondary").toHaveText("Fold");
});
test("display child nodes", async () => {
onRpc("search_read", () => {
expect.step("get child data");
});
onRpc("read_group", () => {
expect.step("fetch descendants");
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node_button").toHaveCount(2);
expect(".o_hierarchy_node_button.btn-secondary").toHaveCount(1);
expect(".o_hierarchy_node_button.btn-primary").toHaveCount(1);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_separator").toHaveCount(2);
expect(".o_hierarchy_line_part").toHaveCount(4);
expect(".o_hierarchy_line_left").toHaveCount(2);
expect(".o_hierarchy_line_right").toHaveCount(2);
expect(".o_hierarchy_node_container").toHaveCount(4);
expect(".o_hierarchy_node").toHaveCount(4);
expect(".o_hierarchy_node_button").toHaveCount(2);
expect(".o_hierarchy_node_button.btn-primary").toHaveCount(0);
expect(".o_hierarchy_node_button.btn-secondary").toHaveCount(2);
expect(".o_hierarchy_node_button.btn-secondary").toHaveText("Fold");
// check nodes in each row
expect(".o_hierarchy_row:eq(0) .o_hierarchy_node").toHaveCount(1);
expect(".o_hierarchy_row:eq(0) .o_hierarchy_node_content").toHaveText("Albert");
expect(".o_hierarchy_row:eq(1) .o_hierarchy_node").toHaveCount(2);
expect(queryAllTexts(".o_hierarchy_row:eq(1) .o_hierarchy_node_content")).toEqual([
// Name + Parent name
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_hierarchy_row:eq(2) .o_hierarchy_node").toHaveCount(1);
expect(".o_hierarchy_row:eq(2) .o_hierarchy_node_content").toHaveText("Louis\nJosephine");
expect.verifySteps(["get child data", "fetch descendants"]);
});
test("display child nodes with child_field set on the view", async () => {
onRpc("search_read", () => {
expect.step("get child data with descendants");
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
viewId: 1,
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node_button").toHaveCount(2);
expect(".o_hierarchy_node_button.btn-secondary").toHaveCount(1);
expect(".o_hierarchy_node_button.btn-primary").toHaveCount(1);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_separator").toHaveCount(2);
expect(".o_hierarchy_line_part").toHaveCount(4);
expect(".o_hierarchy_line_left").toHaveCount(2);
expect(".o_hierarchy_line_right").toHaveCount(2);
expect(".o_hierarchy_node_container").toHaveCount(4);
expect(".o_hierarchy_node").toHaveCount(4);
expect(".o_hierarchy_node_button").toHaveCount(2);
expect(".o_hierarchy_node_button.btn-primary").toHaveCount(0);
expect(".o_hierarchy_node_button.btn-secondary").toHaveCount(2);
expect.verifySteps(["get child data with descendants"]);
});
test("collapse child nodes", async () => {
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_separator").toHaveCount(1);
expect(".o_hierarchy_line_part").toHaveCount(2);
expect(".o_hierarchy_line_left").toHaveCount(1);
expect(".o_hierarchy_line_right").toHaveCount(1);
expect(".o_hierarchy_node_container").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(3);
await contains(".o_hierarchy_node_button.btn-secondary").click();
expect(".o_hierarchy_row").toHaveCount(1);
expect(".o_hierarchy_separator").toHaveCount(0);
expect(".o_hierarchy_line_part").toHaveCount(0);
expect(".o_hierarchy_line_left").toHaveCount(0);
expect(".o_hierarchy_line_right").toHaveCount(0);
expect(".o_hierarchy_node_container").toHaveCount(1);
expect(".o_hierarchy_node").toHaveCount(1);
expect(".o_hierarchy_node_button.btn-secondary").toHaveCount(0);
expect(".o_hierarchy_node_button").toHaveCount(1);
expect(".o_hierarchy_node_container:not(.o_hierarchy_node_button)").toHaveCount(1);
expect(queryAllTexts(".o_hierarchy_row .o_hierarchy_node_content")).toEqual(["Albert"]);
});
test("display the parent above the line when many records on the parent row", async () => {
Employee._records.push({
name: "Alfred",
parent_id: false,
child_ids: [],
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(1);
expect(".o_hierarchy_separator").toHaveCount(0);
expect(".o_hierarchy_node").toHaveCount(2);
expect(".o_hierarchy_node_button.btn-primary").toHaveCount(1);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_separator").toHaveCount(1);
expect(".o_hierarchy_line_left").toHaveCount(1);
expect(".o_hierarchy_line_right").toHaveCount(1);
expect(".o_hierarchy_parent_node_container").toHaveCount(1);
expect(".o_hierarchy_parent_node_container").toHaveText("Albert");
});
test("Add a custom domain leaf on default state of the view with a globalDomain and search default filters", async () => {
Employee._records = [
{ id: 1, name: "A", parent_id: false, child_ids: [] },
{ id: 2, name: "B", parent_id: false, child_ids: [3, 4] },
{ id: 3, name: "C", parent_id: false, child_ids: [] },
{ id: 4, name: "D", parent_id: 2, child_ids: [5, 6] },
{ id: 5, name: "E", parent_id: 4, child_ids: [] },
{ id: 6, name: "F", parent_id: 4, child_ids: [] },
];
await mountView({
type: "hierarchy",
resModel: "hr.employee",
arch: Employee._views["hierarchy,false"],
searchViewArch: `
<search>
<filter name="exclude_third" domain="[['id', '!=', 3]]"/>
<filter name="find_fifth" domain="[['id', '=', 5]]"/>
</search>
`,
context: { search_default_exclude_third: true },
domain: [["id", "not in", [1, 6]]],
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(2);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(["B", "D\nB"], {
message: `A (and F) should be hidden by the globalDomain, C is hidden because of the search_default and
E (and F) are hidden because the custom domain leaf [['parent_id', '=', false]] is applied
since the view is in its "default state", D is shown because the query has only one result (B) so
its direct children are also fetched`,
});
await removeFacet("exclude_third");
expect(".o_hierarchy_row").toHaveCount(1);
expect(".o_hierarchy_node").toHaveCount(2);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(["B", "C"], {
message: `C is now visible because the search_default was removed. E (and F) are still hidden because
the custom domain leaf [['parent_id', '=', false]] is also applied when the search query is
empty, D is hidden because the query now has more than one "root" record (B and C)`,
});
await enableFilters(["find_fifth"]);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(["D\nB", "E\nD", "F\nD"], {
message: `E is shown because it matches the query and the custom domain leaf [['parent_id', '=', false]]
is not applied since the view is not in its "default state", nor is the query empty. D and F are
shown since E was the only record matching the query, so its parent and siblings are fetched`,
});
await removeFacet("find_fifth");
await enableFilters(["exclude_third"]);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(["B", "D\nB"], {
message: `Manually going back to the "default state" of the view should give the same result as the
first load.`,
});
});
test("search record in hierarchy view", async () => {
await mountView({
type: "hierarchy",
resModel: "hr.employee",
arch: Employee._views["hierarchy,false"],
searchViewArch: `
<search>
<filter name="test_filter" domain="[['id', '=', 4]]"/>
</search>
`,
});
await enableFilters(["test_filter"]);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(2);
expect(".o_hierarchy_separator").toHaveCount(1);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Josephine\nAlbert",
"Louis\nJosephine",
]);
});
test("search record in hierarchy view with child field name defined in the arch", async () => {
await mountView({
type: "hierarchy",
resModel: "hr.employee",
viewId: 1,
arch: Employee._views["hierarchy,false"],
searchViewArch: `
<search>
<filter name="test_filter" domain="[['id', '=', 4]]"/>
</search>
`,
});
await enableFilters(["test_filter"]);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(2);
expect(".o_hierarchy_separator").toHaveCount(1);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Josephine\nAlbert",
"Louis\nJosephine",
]);
});
test("fetch parent record", async () => {
await mountView({
type: "hierarchy",
resModel: "hr.employee",
arch: Employee._views["hierarchy,false"],
searchViewArch: `
<search>
<filter name="test_filter" domain="[['id', '=', 4]]"/>
</search>
`,
});
await enableFilters(["test_filter"]);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(2);
expect(".o_hierarchy_separator").toHaveCount(1);
expect(".o_hierarchy_row:eq(0) .o_hierarchy_node_content").toHaveText("Josephine\nAlbert");
expect(".o_hierarchy_row:eq(1) .o_hierarchy_node_content").toHaveText("Louis\nJosephine");
expect(".o_hierarchy_node_container button .fa-chevron-up").toHaveCount(1, {
message:
"Button to fetch the parent node should be visible on the first node displayed in the view.",
});
await contains(".o_hierarchy_node_container button .fa-chevron-up").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(".o_hierarchy_separator").toHaveCount(2);
expect(".o_hierarchy_row:eq(0) .o_hierarchy_node_content").toHaveText("Albert");
expect(".o_hierarchy_row:eq(1) .o_hierarchy_node").toHaveCount(2);
expect(queryAllTexts(".o_hierarchy_row:eq(1) .o_hierarchy_node_content")).toEqual([
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_hierarchy_row:eq(2) .o_hierarchy_node_content").toHaveText("Louis\nJosephine");
});
test("fetch parent when there are many records without the same parent in the same row", async () => {
Employee._records.push({
id: 5,
name: "Lisa",
parent_id: 2,
child_ids: [],
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
arch: Employee._views["hierarchy,false"],
searchViewArch: `
<search>
<filter name="test_filter" domain="[['name', 'ilike', 'l']]"/>
</search>
`,
});
await enableFilters(["test_filter"]);
expect(".o_hierarchy_row").toHaveCount(1);
expect(".o_hierarchy_node_container").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Lisa\nGeorges",
"Louis\nJosephine",
"Albert",
]);
expect(".o_hierarchy_node_container button .fa-chevron-up").toHaveCount(2);
await contains(".o_hierarchy_node_container button .fa-chevron-up").click();
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(2);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Georges\nAlbert",
"Lisa\nGeorges",
]);
expect(".o_hierarchy_node_container button .fa-chevron-up").toHaveCount(1);
await contains(".o_hierarchy_node_container button .fa-chevron-up").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
});
test("fetch parent when parent record is in the same row", async () => {
Employee._records.push({
id: 5,
name: "Lisa",
parent_id: 2,
child_ids: [],
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
arch: Employee._views["hierarchy,false"],
searchViewArch: `
<search>
<filter name="test_filter" domain="[['id', 'in', [1, 2, 3, 4, 5]]]"/>
</search>
`,
});
await enableFilters(["test_filter"]);
expect(".o_hierarchy_row").toHaveCount(1);
expect(".o_hierarchy_node_container").toHaveCount(5);
expect(".o_hierarchy_node_container button .fa-chevron-up").toHaveCount(4);
await contains(".o_hierarchy_node_container button .fa-chevron-up").click();
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
});
test("fetch parent of node with children displayed", async () => {
Employee._records.push({
id: 5,
name: "Lisa",
parent_id: 2,
child_ids: [],
});
Employee._records.find((rec) => rec.id === 2).child_ids.push(5);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
arch: Employee._views["hierarchy,false"],
searchViewArch: `
<search>
<filter name="test_filter" domain="[['id', 'in', [1, 2, 3, 4, 5]]]"/>
</search>
`,
});
await enableFilters(["test_filter"]);
expect(".o_hierarchy_row").toHaveCount(1);
expect(".o_hierarchy_node_container").toHaveCount(5);
expect(".o_hierarchy_node_container button .fa-chevron-up").toHaveCount(4);
const georgesNode = queryOne(
".o_hierarchy_node_container:has(button[name=hierarchy_search_parent_node]):eq(0)"
);
expect(queryOne(".o_hierarchy_node_content", { root: georgesNode })).toHaveText(
"Georges\nAlbert"
);
await contains(
queryOne("button[name=hierarchy_search_subsidiaries]", { root: georgesNode })
).click();
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(5);
expect(".o_hierarchy_row:eq(0) .o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_row:eq(0) .o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
"Louis\nJosephine",
]);
expect(".o_hierarchy_row:eq(1) .o_hierarchy_node").toHaveCount(1);
expect(".o_hierarchy_row:eq(1) .o_hierarchy_node_content").toHaveText("Lisa\nGeorges");
await contains(".o_hierarchy_node_container button .fa-chevron-up").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
});
test("drag and drop is disabled by default", async () => {
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
await contains(".o_hierarchy_node_container:eq(1) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_row:first-child"
);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
});
test("drag and drop record on another row", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
await contains(".o_hierarchy_node_container:eq(1) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_row:first-child"
);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
["Albert", "Georges", "Josephine\nAlbert"],
{ message: "Georges should no longer have a manager" }
);
});
test("drag and drop record at an invalid position", async () => {
expect.assertions(8);
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
patchWithCleanup(HierarchyModel.prototype, {
async updateParentId(node, parentResId = false) {
return this.orm.call(this.resModel, "custom_update_parent_id", [node.resId], {
parent_id: parentResId,
});
},
async updateParentNode() {
await expect(super.updateParentNode(...arguments)).rejects.toThrow(
makeServerError({
type: "ValidationError",
description: "Prevented update parent",
})
);
},
});
onRpc("custom_update_parent_id", () => {
throw makeServerError({
type: "ValidationError",
description: "Prevented update parent",
});
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
await contains(".o_hierarchy_node_container:eq(1) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_row:first-child"
);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
["Albert", "Georges\nAlbert", "Josephine\nAlbert"],
{ message: "The view should not have been modified since the position is invalid" }
);
});
test("drag and drop record on sibling node", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText(
"Josephine\nAlbert"
);
await contains(".o_hierarchy_node_container:eq(1) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_node_container:eq(2)"
);
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
["Albert", "Josephine\nAlbert", "Georges\nJosephine", "Louis\nJosephine"],
{ message: "Georges should have Josephine as manager" }
);
});
test("drag and drop record on node of another tree", async () => {
Employee._views["hierarchy,1"] = Employee._views["hierarchy,1"].replace(
`<hierarchy child_field="child_ids">`,
`<hierarchy child_field="child_ids" draggable="1">`
);
Employee._records = [
{ id: 1, name: "A", parent_id: false, child_ids: [3, 4] },
{ id: 2, name: "B", parent_id: false, child_ids: [] },
{ id: 3, name: "C", parent_id: 1, child_ids: [5] },
{ id: 4, name: "D", parent_id: 1, child_ids: [] },
{ id: 5, name: "E", parent_id: 3, child_ids: [] },
];
await mountView({
type: "hierarchy",
resModel: "hr.employee",
viewId: 1,
});
expect(".o_hierarchy_row").toHaveCount(1);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(2);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_row .o_hierarchy_node_content")).toEqual([
"A",
"B",
"C\nA",
"D\nA",
"E\nC",
]);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText("B");
expect(".o_hierarchy_node_container:eq(3) .o_hierarchy_node_content").toHaveText("D\nA");
await contains(".o_hierarchy_node_container:eq(3) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_node_container:eq(1)"
);
expect(".o_hierarchy_row").toHaveCount(2);
expect(queryAllTexts(".o_hierarchy_row .o_hierarchy_node_content")).toEqual(["A", "B", "D\nB"]);
expect(".o_hierarchy_node_container:eq(0) .o_hierarchy_node_content").toHaveText("A");
await contains(
".o_hierarchy_node_container:eq(0) .o_hierarchy_node_button.btn-primary"
).click();
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText("C\nA");
await contains(
".o_hierarchy_node_container:eq(2) .o_hierarchy_node_button.btn-primary"
).click();
expect(queryAllTexts(".o_hierarchy_row .o_hierarchy_node_content")).toEqual(
["A", "B", "C\nA", "E\nC"],
{
message:
"Nodes that were folded as a result of the drop operation should all still be unfoldable",
}
);
});
test("drag and drop node unfolded on first row", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText(
"Josephine\nAlbert"
);
await contains(
".o_hierarchy_node_container:eq(2) button[name='hierarchy_search_subsidiaries']"
).click();
await contains(".o_hierarchy_node_container:eq(2) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_row:first-child"
);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
["Albert", "Josephine", "Louis\nJosephine"],
{ message: "Georges should have Josephine as manager" }
);
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText(
"Louis\nJosephine"
);
await contains(".o_hierarchy_node_container:eq(2) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_row:first-child"
);
expect(".o_hierarchy_row").toHaveCount(1);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(["Albert", "Josephine", "Louis"], {
message: "Louis should still be draggable after being dragged along with Josephine",
});
});
test("drag and drop node when other node is unfolded on first row", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText(
"Josephine\nAlbert"
);
await contains(
".o_hierarchy_node_container:eq(2) button[name='hierarchy_search_subsidiaries']"
).click();
await contains(".o_hierarchy_node_container:eq(1) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_row:eq(0)"
);
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
["Albert", "Georges", "Josephine\nAlbert", "Louis\nJosephine"],
{ message: "Georges should no longer have a manager" }
);
});
test("drag and drop node unfolded on another row", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
Employee._records = [
{ id: 1, name: "Albert", parent_id: false, child_ids: [2] },
{ id: 2, name: "Georges", parent_id: 1, child_ids: [3] },
{ id: 3, name: "Josephine", parent_id: 2, child_ids: [4] },
{ id: 4, name: "Louis", parent_id: 3, child_ids: [] },
{ id: 5, name: "Kelly", parent_id: 2, child_ids: [] },
];
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(2);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(["Albert", "Georges\nAlbert"]);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
await contains(
".o_hierarchy_node_container:eq(1) button[name='hierarchy_search_subsidiaries']"
).click();
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText(
"Josephine\nGeorges"
);
await contains(
".o_hierarchy_node_container:eq(2) button[name='hierarchy_search_subsidiaries']"
).click();
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText(
"Josephine\nGeorges"
);
expect(".o_hierarchy_node_container:eq(4) .o_hierarchy_node_content").toHaveText(
"Louis\nJosephine"
);
expect(".o_hierarchy_row").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
["Albert", "Georges\nAlbert", "Josephine\nGeorges", "Kelly\nGeorges", "Louis\nJosephine"],
{ message: "Kelly should be displayed" }
);
await contains(".o_hierarchy_node_container:eq(2) .o_hierarchy_node").dragAndDrop(
":nth-child(2 of .o_hierarchy_row)"
);
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
["Albert", "Georges\nAlbert", "Josephine\nAlbert", "Louis\nJosephine"],
{ message: "Josephine should have Albert as a manager and Kelly should be hidden" }
);
expect(".o_hierarchy_node_container:eq(3) .o_hierarchy_node_content").toHaveText(
"Louis\nJosephine"
);
await contains(".o_hierarchy_node_container:eq(3) .o_hierarchy_node").dragAndDrop(
":nth-child(2 of .o_hierarchy_row)"
);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
["Albert", "Georges\nAlbert", "Josephine\nAlbert", "Louis\nAlbert"],
{ message: "Louis should still be draggable after being dragged along with Josephine" }
);
});
test("drag and drop node as a child of a sibling of its parent", async () => {
Employee._views["hierarchy,1"] = Employee._views["hierarchy,1"].replace(
`<hierarchy child_field="child_ids">`,
`<hierarchy child_field="child_ids" draggable="1">`
);
Employee._records = [
{ id: 1, name: "A", parent_id: false, child_ids: [2, 3] },
{ id: 2, name: "B", parent_id: 1, child_ids: [4, 5] },
{ id: 3, name: "C", parent_id: 1, child_ids: [] },
{ id: 4, name: "D", parent_id: 2, child_ids: [] },
{ id: 5, name: "E", parent_id: 2, child_ids: [] },
];
await mountView({
type: "hierarchy",
resModel: "hr.employee",
viewId: 1,
});
expect(".o_hierarchy_row").toHaveCount(2);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_row .o_hierarchy_node_content")).toEqual([
"A",
"B\nA",
"C\nA",
"D\nB",
"E\nB",
]);
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText("C\nA");
expect(".o_hierarchy_node_container:eq(4) .o_hierarchy_node_content").toHaveText("E\nB");
await contains(".o_hierarchy_node_container:eq(4) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_node_container:eq(2)"
);
expect(".o_hierarchy_row").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_row .o_hierarchy_node_content")).toEqual(
["A", "B\nA", "C\nA", "E\nC"],
{ message: "B should be folded and C unfolded" }
);
});
test("drag and drop record and respect ordering", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy default_order='name' draggable='1'>"
);
Employee._records = [
{ id: 1, name: "F", parent_id: false, child_ids: [] },
{ id: 2, name: "E", parent_id: 6, child_ids: [] },
{ id: 3, name: "D", parent_id: 6, child_ids: [] },
{ id: 4, name: "C", parent_id: 6, child_ids: [] },
{ id: 5, name: "B", parent_id: 6, child_ids: [] },
{ id: 6, name: "A", parent_id: false, child_ids: [2, 3, 4, 5] },
];
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(1);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(6);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"A",
"F",
"B\nA",
"C\nA",
"D\nA",
"E\nA",
]);
await contains(".o_hierarchy_node_container:eq(4) .o_hierarchy_node").dragAndDrop(
".o_hierarchy_row:first-child"
);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(6);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"A",
"D",
"F",
"B\nA",
"C\nA",
"E\nA",
]);
await contains(".o_hierarchy_node_container:eq(1) .o_hierarchy_node").dragAndDrop(
":nth-child(2 of .o_hierarchy_row)"
);
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(6);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"A",
"F",
"B\nA",
"C\nA",
"D\nA",
"E\nA",
]);
});
test("drag node and move it on a row", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_node").toHaveCount(3);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
const { drop, moveTo } = await contains(
".o_hierarchy_node_container:eq(1) .o_hierarchy_node"
).drag();
await moveTo(".o_hierarchy_row:eq(0)");
expect(".o_hierarchy_node_container:eq(1)").toHaveClass("o_hierarchy_dragged");
expect(".o_hierarchy_row:eq(0)").toHaveClass("o_hierarchy_hover");
await drop();
expect(".o_hierarchy_node.o_hierarchy_dragged").toHaveCount(0);
expect(".o_hierarchy_row.o_hierarchy_hover").toHaveCount(0);
});
test("drag node and move it on another node", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_node").toHaveCount(3);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText(
"Josephine\nAlbert"
);
const { drop, moveTo } = await contains(
".o_hierarchy_node_container:eq(1) .o_hierarchy_node"
).drag();
await moveTo(".o_hierarchy_node_container:eq(2) .o_hierarchy_node");
expect(".o_hierarchy_node_container:eq(1)").toHaveClass("o_hierarchy_dragged");
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node").toHaveClass("shadow");
expect(".o_hierarchy_node_container:eq(2)").toHaveClass("o_hierarchy_hover");
await drop();
expect(".o_hierarchy_node.o_hierarchy_dragged").toHaveCount(0);
expect(".o_hierarchy_node.o_hierarchy_hover").toHaveCount(0);
expect(".o_hierarchy_node.shadow").toHaveCount(0);
});
test("drag node to scroll", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
Employee._records = [
{ id: 1, name: "A", parent_id: false, child_ids: [2] },
{ id: 2, name: "B", parent_id: 1, child_ids: [3] },
{ id: 3, name: "C", parent_id: 2, child_ids: [4] },
{ id: 4, name: "D", parent_id: 3, child_ids: [5] },
{ id: 5, name: "E", parent_id: 4, child_ids: [] },
{ id: 6, name: "F", parent_id: false, child_ids: [] },
];
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
// Fully open the view
expect(".o_hierarchy_row").toHaveCount(1);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(2);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(3);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(4);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(5);
expect(queryAllTexts(".o_hierarchy_row .o_hierarchy_node_content")).toEqual([
"A",
"F",
"B\nA",
"C\nB",
"D\nC",
"E\nD",
]);
await animationFrame();
const content = queryOne(".o_content");
await scroll(content, { top: 0 });
// Limit the height and enable scrolling
content.setAttribute("style", "min-height:600px;max-height:600px;overflow-y:auto;");
expect(content.scrollTop).toBe(0);
expect(content).toHaveRect({ height: 600 });
const dragActions = await contains(".o_hierarchy_node:contains(F)").drag();
await dragActions.moveTo(".o_hierarchy_row:eq(4)");
await animationFrame();
expect(content.scrollTop).toBeGreaterThan(0);
expect(".o_hierarchy_node_container.o_hierarchy_dragged").toHaveCount(1);
await advanceFrame(50);
// should be at the end of the content
expect(content.clientHeight + content.scrollTop).toBe(content.scrollHeight);
await dragActions.moveTo(".o_hierarchy_row:eq(0)");
await animationFrame();
expect(content.clientHeight + content.scrollTop).toBeLessThan(content.scrollHeight);
expect(".o_hierarchy_node_container.o_hierarchy_dragged").toHaveCount(1);
await advanceFrame(50);
// should be at the top of the content
expect(content.scrollTop).toBe(0);
// cancel drag: press "Escape"
await press("Escape");
await animationFrame();
expect(".o_hierarchy_node_container.o_hierarchy_dragged").toHaveCount(0);
});
test("check default icon is correctly used inside button to display child nodes", async () => {
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_node button[name=hierarchy_search_subsidiaries].btn-primary").toHaveCount(
1
);
expect(".o_hierarchy_node button[name=hierarchy_search_subsidiaries].btn-primary").toHaveText(
"Unfold\n1"
);
expect(
".o_hierarchy_node button[name=hierarchy_search_subsidiaries] i.fa-share-alt.o_hierarchy_icon"
).toHaveCount(1, {
message:
"The default icon of the hierarchy view should be displayed inside the button to unfold the node.",
});
});
test("use other icon used next to Unfold string displayed inside the button", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy icon='fa-users'>"
);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_node button[name=hierarchy_search_subsidiaries].btn-primary").toHaveCount(
1
);
expect(".o_hierarchy_node button[name=hierarchy_search_subsidiaries].btn-primary").toHaveText(
"Unfold\n1"
);
expect(".o_hierarchy_node button[name=hierarchy_search_subsidiaries] i.fa-users").toHaveCount(
1,
{
message:
"The icon defined in the attribute icon in hierarchy tag should be displayed inside the button to unfold the node instead of the default one.",
}
);
});
test("use `hierarchy_res_id` context to load the view at that specific node with its siblings and parent node", async () => {
Employee._records.push({
id: 5,
name: "Lisa",
parent_id: 3,
child_ids: [],
});
Employee._records.find((rec) => rec.id === 3).child_ids.push(5);
await mountView({
type: "hierarchy",
resModel: "hr.employee",
context: {
hierarchy_res_id: 5,
},
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Josephine\nAlbert",
"Lisa\nJosephine",
"Louis\nJosephine",
]);
expect(".o_hierarchy_node_container button[name=hierarchy_search_parent_node]").toHaveCount(1);
});
test("cannot set the record dragged as parent", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
onRpc("hr.employee", "write", () => {
expect.step("setManager");
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
await contains(".o_hierarchy_node:eq(0)").dragAndDrop(".o_hierarchy_row:eq(1)");
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_notification").toHaveCount(1);
expect(".o_notification_bar.bg-danger").toHaveCount(1);
expect.verifySteps([]);
});
test("cannot create cyclic", async () => {
Employee._views["hierarchy,false"] = Employee._views["hierarchy,false"].replace(
"<hierarchy>",
"<hierarchy draggable='1'>"
);
onRpc("hr.employee", "write", () => {
expect.step("setManager");
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
await contains(".o_hierarchy_node:eq(0)").dragAndDrop(".o_hierarchy_node:eq(1)");
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
]);
expect(".o_notification").toHaveCount(1);
expect(".o_notification_bar.bg-danger").toHaveCount(1);
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
"Louis\nJosephine",
]);
await contains(".o_hierarchy_node:eq(0)").dragAndDrop(".o_hierarchy_node:eq(3)");
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
"Louis\nJosephine",
]);
expect(".o_notification").toHaveCount(2);
expect(".o_notification_bar.bg-danger").toHaveCount(2);
await contains(".o_hierarchy_node:eq(0)").dragAndDrop(".o_hierarchy_row:eq(2)");
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert",
"Georges\nAlbert",
"Josephine\nAlbert",
"Louis\nJosephine",
]);
expect(".o_notification").toHaveCount(3);
expect(".o_notification_bar.bg-danger").toHaveCount(3);
expect.verifySteps([]);
});
test("can properly evaluate invisible elements in a hierarchy card", async () => {
await mountView({
type: "hierarchy",
resModel: "hr.employee",
arch: `
<hierarchy child_field="child_ids">
<field name="child_ids" invisible="1"/>
<templates>
<t t-name="hierarchy-box">
<div class="o_hierarchy_node_header">
<field name="name"/>
</div>
<div class="o_hierarchy_node_body">
<field name="parent_id"/>
</div>
<div invisible="not child_ids" class="o_children_text">
<p>withChildren</p>
</div>
</t>
</templates>
</hierarchy>
`,
});
expect(".o_hierarchy_row").toHaveCount(2);
expect(".o_hierarchy_node").toHaveCount(3);
expect(".o_hierarchy_node_container:eq(0) .o_hierarchy_node_content").toHaveText(
"Albert\n\nwithChildren"
);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert"
);
expect(".o_hierarchy_node_container:eq(2) .o_hierarchy_node_content").toHaveText(
"Josephine\nAlbert\n\nwithChildren"
);
expect(".o_hierarchy_node_container:eq(0) .o_children_text").toHaveCount(1);
expect(".o_hierarchy_node_container:eq(1) .o_children_text").toHaveCount(0);
expect(".o_hierarchy_node_container:eq(2) .o_children_text").toHaveCount(1);
});
test("Reload the view with the same unfolded records when clicking with a view button", async () => {
mockService("action", {
doActionButton({ resId, onClose }) {
const record = Employee._records[resId - 1];
record.name = "_" + record.name;
onClose();
},
});
await mountView({
type: "hierarchy",
resModel: "hr.employee",
arch: `
<hierarchy child_field="child_ids">
<templates>
<t t-name="hierarchy-box">
<div class="o_hierarchy_node_header">
<field name="name"/>
</div>
<div class="o_hierarchy_node_body">
<field name="parent_id"/>
</div>
<button type="object" name="prefix_underscore">prefix</button>
</t>
</templates>
</hierarchy>
`,
});
await contains(".o_hierarchy_node_button.btn-primary").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual([
"Albert\nprefix",
"Georges\nAlbert\nprefix",
"Josephine\nAlbert\nprefix",
"Louis\nJosephine\nprefix",
]);
expect(".o_hierarchy_node_container:eq(1) .o_hierarchy_node_content").toHaveText(
"Georges\nAlbert\nprefix"
);
await contains(".o_hierarchy_node_container:eq(1) button[name='prefix_underscore']").click();
expect(".o_hierarchy_row").toHaveCount(3);
expect(".o_hierarchy_node").toHaveCount(4);
expect(queryAllTexts(".o_hierarchy_node_content")).toEqual(
[
"Albert\nprefix",
"_Georges\nAlbert\nprefix",
"Josephine\nAlbert\nprefix",
"Louis\nJosephine\nprefix",
],
{ message: "The view should have reloaded the same data (with Louis)" }
);
});
test("The view displays the No Content help", async () => {
Employee._records = [];
await mountView({
type: "hierarchy",
resModel: "hr.employee",
});
expect("div.o_view_nocontent").toHaveCount(1);
});