mirror of
https://github.com/bringout/oca-ocb-web.git
synced 2026-04-19 13:12:00 +02:00
web_editor was removed in Odoo 19.0 and replaced by html_editor
and html_builder. The old web_editor was incorrectly included in
the 19.0 vanilla import.
🤖 assisted by claude
701 lines
28 KiB
JavaScript
701 lines
28 KiB
JavaScript
import {
|
|
addBuilderAction,
|
|
addBuilderOption,
|
|
setupHTMLBuilder,
|
|
} from "@html_builder/../tests/helpers";
|
|
import { BuilderAction } from "@html_builder/core/builder_action";
|
|
import { BaseOptionComponent, useDomState } from "@html_builder/core/utils";
|
|
import { OptionsContainer } from "@html_builder/sidebar/option_container";
|
|
import { setContent, setSelection } from "@html_editor/../tests/_helpers/selection";
|
|
import { redo, undo } from "@html_editor/../tests/_helpers/user_actions";
|
|
import { describe, expect, test } from "@odoo/hoot";
|
|
import { animationFrame, queryAllTexts, queryFirst } from "@odoo/hoot-dom";
|
|
import { Component, onWillStart, xml } from "@odoo/owl";
|
|
import { contains, patchWithCleanup } from "@web/../tests/web_test_helpers";
|
|
|
|
describe.current.tags("desktop");
|
|
|
|
test("Open custom tab with template option", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderRow label="'Row 1'">
|
|
Test
|
|
</BuilderRow>`,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target" data-name="Yop">b</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(".options-container").toBeVisible();
|
|
expect(queryAllTexts(".options-container > div")).toEqual(["Yop", "Row 1\nTest"]);
|
|
});
|
|
|
|
test("Open custom tab with Component option", async () => {
|
|
class TestOption extends BaseOptionComponent {
|
|
static template = xml`
|
|
<BuilderRow label="'Row 1'">
|
|
Test
|
|
</BuilderRow>`;
|
|
static props = {};
|
|
}
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
Component: TestOption,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target" data-name="Yop">b</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(".options-container").toBeVisible();
|
|
expect(queryAllTexts(".options-container > div")).toEqual(["Yop", "Row 1\nTest"]);
|
|
});
|
|
|
|
test("OptionContainer should display custom title", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderRow label="'Row 1'">
|
|
Test
|
|
</BuilderRow>`,
|
|
title: "My custom title",
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target" data-name="Yop">b</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(".options-container").toBeVisible();
|
|
expect(queryAllTexts(".options-container > div")).toEqual(["My custom title", "Row 1\nTest"]);
|
|
});
|
|
|
|
test("Don't display option base on exclude", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
exclude: ".test-exclude",
|
|
template: xml`<BuilderRow label="'Row 1'">a</BuilderRow>`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
exclude: ".test-exclude-2",
|
|
template: xml`<BuilderRow label="'Row 2'">b</BuilderRow>`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`<BuilderRow label="'Row 3'">
|
|
<BuilderButton classAction="'test-exclude-2'">c</BuilderButton>
|
|
</BuilderRow>`,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target test-exclude">b</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(queryAllTexts(".options-container .hb-row")).toEqual(["Row 2\nb", "Row 3\nc"]);
|
|
|
|
await contains("[data-class-action='test-exclude-2']").click();
|
|
expect(queryAllTexts(".options-container .hb-row")).toEqual(["Row 3\nc"]);
|
|
});
|
|
|
|
test("Don't display option base on applyTo", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
applyTo: ".test-target",
|
|
template: xml`<BuilderRow label="'Row 1'">
|
|
<BuilderButton classAction="'test-target-2'">a</BuilderButton>
|
|
</BuilderRow>`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
applyTo: ".test-target-2",
|
|
template: xml`<BuilderRow label="'Row 2'">b</BuilderRow>`,
|
|
});
|
|
await setupHTMLBuilder(`
|
|
<div class="test-options-target">
|
|
<div class="test-target">b</div>
|
|
</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(queryAllTexts(".options-container .hb-row")).toEqual(["Row 1\na"]);
|
|
|
|
await contains("[data-class-action='test-target-2']").click();
|
|
await animationFrame();
|
|
expect(queryAllTexts(".options-container .hb-row")).toEqual(["Row 1\na", "Row 2\nb"]);
|
|
});
|
|
|
|
test("basic multi options containers", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderRow label="'Row 1'">A</BuilderRow>`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".a",
|
|
template: xml`
|
|
<BuilderRow label="'Row 2'">B</BuilderRow>`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".main",
|
|
template: xml`
|
|
<BuilderRow label="'Row 3'">C</BuilderRow>`,
|
|
});
|
|
await setupHTMLBuilder(`<div class="main"><p class="test-options-target a">b</p></div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(".options-container").toHaveCount(2);
|
|
expect(queryAllTexts(".options-container:first .we-bg-options-container > div > div")).toEqual([
|
|
"Row 3",
|
|
"C",
|
|
]);
|
|
expect(
|
|
queryAllTexts(".options-container:nth-child(2) .we-bg-options-container > div > div")
|
|
).toEqual(["Row 1", "A", "Row 2", "B"]);
|
|
});
|
|
|
|
test("option that matches several elements", async () => {
|
|
addBuilderOption({
|
|
selector: ".a",
|
|
template: xml`<BuilderRow label="'Row'">
|
|
<BuilderButton classAction="'my-custom-class'">Test</BuilderButton>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
await setupHTMLBuilder(`<div class="a"><div class="a test-target">b</div></div>`);
|
|
await contains(":iframe .test-target").click();
|
|
expect(".options-container:not(.d-none)").toHaveCount(2);
|
|
expect(queryAllTexts(".options-container:not(.d-none)")).toEqual([
|
|
"Block\nRow\nTest",
|
|
"Block\nRow\nTest",
|
|
]);
|
|
});
|
|
|
|
test("Snippets options respect sequencing", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderRow label="'Row 2'">
|
|
Test
|
|
</BuilderRow>`,
|
|
sequence: 2,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderRow label="'Row 1'">
|
|
Test
|
|
</BuilderRow>`,
|
|
sequence: 1,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderRow label="'Row 3'">
|
|
Test
|
|
</BuilderRow>`,
|
|
sequence: 3,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target" data-name="Yop">b</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(".options-container").toBeVisible();
|
|
expect(queryAllTexts(".options-container .we-bg-options-container > div > div")).toEqual([
|
|
"Row 1",
|
|
"Test",
|
|
"Row 2",
|
|
"Test",
|
|
"Row 3",
|
|
"Test",
|
|
]);
|
|
});
|
|
|
|
test("hide empty OptionContainer and display OptionContainer with content", async () => {
|
|
addBuilderOption({
|
|
selector: ".parent-target",
|
|
template: xml`<BuilderRow label="'Row 1'">
|
|
<BuilderButton applyTo="'.child-target'" classAction="'my-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".parent-target > div",
|
|
template: xml`<BuilderRow label="'Row 3'">
|
|
<BuilderButton applyTo="'.my-custom-class'" classAction="'test'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
await setupHTMLBuilder(
|
|
`<div class="parent-target"><div><div class="child-target">b</div></div></div>`
|
|
);
|
|
|
|
await contains(":iframe .parent-target > div").click();
|
|
expect(".options-container:not(.d-none)").toHaveCount(1);
|
|
|
|
await contains("[data-class-action='my-custom-class']").click();
|
|
expect(".options-container:not(.d-none)").toHaveCount(2);
|
|
});
|
|
|
|
test("hide empty OptionContainer and display OptionContainer with content (with BuilderButtonGroup)", async () => {
|
|
addBuilderOption({
|
|
selector: ".parent-target",
|
|
template: xml`<BuilderRow label="'Row 1'">
|
|
<BuilderButton applyTo="'.child-target'" classAction="'my-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
addBuilderOption({
|
|
selector: ".parent-target > div",
|
|
template: xml`
|
|
<BuilderRow label="'Row 2'">
|
|
<BuilderButtonGroup>
|
|
<BuilderButton applyTo="'.my-custom-class'" classAction="'test'">Test</BuilderButton>
|
|
</BuilderButtonGroup>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
await setupHTMLBuilder(
|
|
`<div class="parent-target"><div><div class="child-target">b</div></div></div>`
|
|
);
|
|
await contains(":iframe .parent-target > div").click();
|
|
expect(".options-container:not(.d-none)").toHaveCount(1);
|
|
|
|
await contains("[data-class-action='my-custom-class']").click();
|
|
expect(".options-container:not(.d-none)").toHaveCount(2);
|
|
expect(".options-container:not(.d-none):nth-child(2)").toHaveText("Block\nRow 2\nTest");
|
|
});
|
|
|
|
test("hide empty OptionContainer and display OptionContainer with content (with BuilderButtonGroup) - 2", async () => {
|
|
addBuilderOption({
|
|
selector: ".parent-target",
|
|
template: xml`<BuilderRow label="'Row 1'">
|
|
<BuilderButton applyTo="'.child-target'" classAction="'my-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
addBuilderOption({
|
|
selector: ".parent-target > div",
|
|
template: xml`
|
|
<BuilderRow label="'Row 2'">
|
|
<BuilderButtonGroup applyTo="'.my-custom-class'">
|
|
<BuilderButton classAction="'test'">Test</BuilderButton>
|
|
</BuilderButtonGroup>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
await setupHTMLBuilder(
|
|
`<div class="parent-target"><div><div class="child-target">b</div></div></div>`
|
|
);
|
|
await contains(":iframe .parent-target > div").click();
|
|
expect(".options-container:not(.d-none)").toHaveCount(1);
|
|
|
|
await contains("[data-class-action='my-custom-class']").click();
|
|
expect(".options-container:not(.d-none)").toHaveCount(2);
|
|
expect(".options-container:not(.d-none):nth-child(2)").toHaveText("Block\nRow 2\nTest");
|
|
});
|
|
|
|
test("fallback on the 'Blocks' tab if no option match the selected element", async () => {
|
|
await setupHTMLBuilder(`<div class="parent-target"><div class="child-target">b</div></div>`);
|
|
await contains(":iframe .parent-target > div").click();
|
|
expect(".o-snippets-tabs button:contains('Blocks')").toHaveClass("active");
|
|
});
|
|
|
|
test("display empty message if no option container is visible", async () => {
|
|
addBuilderOption({
|
|
selector: ".parent-target",
|
|
template: xml`<BuilderRow label="'Row 1'">
|
|
<BuilderButton applyTo="'.invalid'" classAction="'my-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
await setupHTMLBuilder(`<div class="parent-target"><div class="child-target">b</div></div>`);
|
|
await contains(":iframe .parent-target > div").click();
|
|
await animationFrame();
|
|
expect(".o_customize_tab").toHaveText("Select a block on your page to style it.");
|
|
});
|
|
test("hide/display option base on selector", async () => {
|
|
addBuilderOption({
|
|
selector: ".parent-target",
|
|
template: xml`<BuilderRow label="'Row 1'">
|
|
<BuilderButton classAction="'my-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".my-custom-class",
|
|
template: xml`<BuilderRow label="'Row 2'">
|
|
<BuilderButton classAction="'test'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
await setupHTMLBuilder(`<div class="parent-target"><div class="child-target">b</div></div>`);
|
|
await contains(":iframe .parent-target").click();
|
|
expect("[data-class-action='test']").not.toHaveCount();
|
|
|
|
await contains("[data-class-action='my-custom-class']").click();
|
|
expect("[data-class-action='test']").toBeVisible();
|
|
});
|
|
|
|
test("hide/display option container base on selector", async () => {
|
|
addBuilderOption({
|
|
selector: ".parent-target",
|
|
template: xml`<BuilderRow label="'Row 1'">
|
|
<BuilderButton applyTo="'.child-target'" classAction="'my-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".my-custom-class",
|
|
template: xml`<BuilderRow label="'Row 2'">
|
|
<BuilderButton classAction="'test'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
addBuilderOption({
|
|
selector: ".sub-child-target",
|
|
template: xml`<BuilderRow label="'Row 3'">
|
|
<BuilderButton classAction="'another-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
await setupHTMLBuilder(`
|
|
<div class="parent-target">
|
|
<div class="child-target">
|
|
<div class="sub-child-target">b</div>
|
|
</div>
|
|
</div>`);
|
|
await contains(":iframe .sub-child-target").click();
|
|
expect("[data-class-action='test']").not.toHaveCount();
|
|
const selectorRowLabel = ".options-container .hb-row:not(.d-none) .hb-row-label";
|
|
expect(queryAllTexts(selectorRowLabel)).toEqual(["Row 1", "Row 3"]);
|
|
|
|
await contains("[data-class-action='my-custom-class']").click();
|
|
expect("[data-class-action='test']").toBeVisible();
|
|
expect(queryAllTexts(selectorRowLabel)).toEqual(["Row 1", "Row 2", "Row 3"]);
|
|
});
|
|
|
|
test("don't rerender the OptionsContainer every time you click on the same element", async () => {
|
|
addBuilderOption({
|
|
selector: ".parent-target",
|
|
template: xml`<BuilderRow label="'Row 1'">
|
|
<BuilderButton applyTo="'.child-target'" classAction="'my-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
patchWithCleanup(OptionsContainer.prototype, {
|
|
setup() {
|
|
super.setup();
|
|
onWillStart(() => {
|
|
expect.step("onWillStart");
|
|
});
|
|
},
|
|
});
|
|
|
|
await setupHTMLBuilder(`
|
|
<div class="parent-target">
|
|
<div class="child-target">
|
|
<div class="sub-child-target">b</div>
|
|
</div>
|
|
</div>`);
|
|
await contains(":iframe .sub-child-target").click();
|
|
expect("[data-class-action='test']").not.toHaveCount();
|
|
expect.verifySteps(["onWillStart"]);
|
|
|
|
await contains(":iframe .sub-child-target").click();
|
|
expect.verifySteps([]);
|
|
});
|
|
|
|
test("no need to define 'isApplied' method for custom action if the widget already has a generic action", async () => {
|
|
addBuilderAction({
|
|
customAction: class extends BuilderAction {
|
|
static id = "customAction";
|
|
apply({ editingElement, value }) {
|
|
editingElement.textContent = value;
|
|
}
|
|
},
|
|
});
|
|
addBuilderOption({
|
|
selector: ".s_test",
|
|
template: xml`
|
|
<BuilderRow label.translate="Type">
|
|
<BuilderSelect>
|
|
<BuilderSelectItem classAction="'A-class'" action="'customAction'" actionParam="'A'">A</BuilderSelectItem>
|
|
</BuilderSelect>
|
|
</BuilderRow>
|
|
`,
|
|
});
|
|
|
|
await setupHTMLBuilder(`
|
|
<div class="s_test A-class">
|
|
a
|
|
</div>`);
|
|
await contains(":iframe .s_test").click();
|
|
expect(".options-container [data-class-action='A-class']").toHaveText("A");
|
|
});
|
|
|
|
test("useDomState callback shouldn't be called when the editingElement is removed", async () => {
|
|
let editor;
|
|
let count = 0;
|
|
class TestOption extends Component {
|
|
static template = xml`<div class="test_option">test</div>`;
|
|
static props = {};
|
|
|
|
setup() {
|
|
useDomState(() => {
|
|
expect.step(`useDomState ${count}`);
|
|
return {
|
|
count: (count = count + 1),
|
|
};
|
|
});
|
|
}
|
|
}
|
|
addBuilderOption({
|
|
selector: ".s_test",
|
|
editableOnly: false,
|
|
Component: TestOption,
|
|
});
|
|
addBuilderOption({
|
|
selector: "*",
|
|
template: xml`<BuilderButton action="'addTestSnippet'">Add</BuilderButton>`,
|
|
});
|
|
addBuilderAction({
|
|
addTestSnippet: class extends BuilderAction {
|
|
static id = "addTestSnippet";
|
|
apply({ editingElement }) {
|
|
const testEl = document.createElement("div");
|
|
testEl.classList.add("s_test", "alert-info");
|
|
testEl.textContent = "test";
|
|
editingElement.after(testEl);
|
|
editor.shared["builderOptions"].setNextTarget(testEl);
|
|
}
|
|
},
|
|
});
|
|
|
|
const { getEditor } = await setupHTMLBuilder(`<div class="s_dummy">Hello</div>`);
|
|
editor = getEditor();
|
|
await contains(":iframe .s_dummy").click();
|
|
await contains("[data-action-id='addTestSnippet']").click();
|
|
expect(".options-container .test_option").toHaveCount(1);
|
|
expect.verifySteps(["useDomState 0"]);
|
|
|
|
undo(editor);
|
|
await animationFrame();
|
|
expect(".options-container .test_option").toHaveCount(0);
|
|
expect.verifySteps([]);
|
|
|
|
redo(editor);
|
|
await animationFrame();
|
|
expect(".options-container .test_option").toHaveCount(1);
|
|
expect.verifySteps(["useDomState 1"]);
|
|
});
|
|
|
|
test("Update editing elements at dom change with multiple levels of applyTo", async () => {
|
|
addBuilderAction({
|
|
customAction: class extends BuilderAction {
|
|
static id = "customAction";
|
|
apply({ editingElement }) {
|
|
const createdEl = editingElement.cloneNode(true);
|
|
const parentEl = editingElement.parentElement;
|
|
parentEl.appendChild(createdEl);
|
|
}
|
|
},
|
|
});
|
|
addBuilderOption({
|
|
selector: ".parent-target",
|
|
template: xml`<BuilderRow label="'Row 1'" applyTo="'.child-target'">
|
|
<BuilderButton action="'customAction'" />
|
|
<BuilderButton applyTo="'.sub-child-target'" classAction="'my-custom-class'"/>
|
|
</BuilderRow>`,
|
|
});
|
|
|
|
await setupHTMLBuilder(`
|
|
<div class="parent-target">
|
|
<div class="child-target">
|
|
<div class="sub-child-target">b</div>
|
|
</div>
|
|
</div>`);
|
|
await contains(":iframe .parent-target").click();
|
|
await contains("[data-action-id='customAction']").click();
|
|
await contains("[data-class-action='my-custom-class']").click();
|
|
expect(":iframe .sub-child-target").toHaveClass("my-custom-class");
|
|
});
|
|
|
|
test("An option should only appear if its target is inside an editable area, unless specified otherwise", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-target",
|
|
template: xml`
|
|
<BuilderButton classAction="'dummy-class-a'">Option A</BuilderButton>
|
|
`,
|
|
});
|
|
addBuilderOption({
|
|
selector: ".test-target",
|
|
editableOnly: false,
|
|
template: xml`
|
|
<BuilderButton classAction="'dummy-class-b'">Option B</BuilderButton>
|
|
`,
|
|
});
|
|
const { getEditor } = await setupHTMLBuilder(`<div></div>`);
|
|
const editor = getEditor();
|
|
setContent(
|
|
editor.editable,
|
|
`<div class="content">
|
|
<div class="test-target test-not-editable">NOT IN EDITABLE</div>
|
|
</div>
|
|
<div class="content o_editable">
|
|
<div class="test-target test-editable">IN EDITABLE</div>
|
|
</div>`
|
|
);
|
|
editor.shared.history.addStep();
|
|
|
|
await contains(":iframe .test-not-editable").click();
|
|
expect(queryAllTexts(".options-container [data-class-action]")).toEqual(["Option B"]);
|
|
|
|
await contains(":iframe .test-editable").click();
|
|
expect(queryAllTexts(".options-container [data-class-action]")).toEqual([
|
|
"Option A",
|
|
"Option B",
|
|
]);
|
|
});
|
|
|
|
describe("isActiveItem", () => {
|
|
test("a button should not be visible if its dependency isn't (with undo)", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderButton attributeAction="'my-attribute1'" attributeActionValue="'x'" id="'id1'">b1</BuilderButton>
|
|
<BuilderButton attributeAction="'my-attribute1'" attributeActionValue="'y'" id="'id2'">b2</BuilderButton>
|
|
<BuilderButton attributeAction="'my-attribute2'" attributeActionValue="'1'" t-if="this.isActiveItem('id1')">b3</BuilderButton>
|
|
<BuilderButton attributeAction="'my-attribute2'" attributeActionValue="'2'" t-if="this.isActiveItem('id2')">b4</BuilderButton>
|
|
`,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target">b</div>`);
|
|
setSelection({
|
|
anchorNode: queryFirst(":iframe .test-options-target").childNodes[0],
|
|
anchorOffset: 0,
|
|
});
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(".options-container").toBeVisible();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).not.toHaveCount();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='2']"
|
|
).not.toHaveCount();
|
|
await contains(
|
|
"[data-attribute-action='my-attribute1'][data-attribute-action-value='x']"
|
|
).click();
|
|
expect(":iframe .test-options-target").toHaveAttribute("my-attribute1", "x");
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).toBeVisible();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='2']"
|
|
).not.toHaveCount();
|
|
await contains(
|
|
"[data-attribute-action='my-attribute1'][data-attribute-action-value='y']"
|
|
).click();
|
|
expect(":iframe .test-options-target").toHaveAttribute("my-attribute1", "y");
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).not.toHaveCount();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='2']"
|
|
).toBeVisible();
|
|
await contains(".fa-undo").click();
|
|
expect(":iframe .test-options-target").toHaveAttribute("my-attribute1", "x");
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).toBeVisible();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='2']"
|
|
).not.toHaveCount();
|
|
await contains(".fa-undo").click();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).not.toHaveCount();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='2']"
|
|
).not.toHaveCount();
|
|
});
|
|
test("a button should not be visible if its dependency isn't (in a BuilderSelect with priority)", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderSelect>
|
|
<BuilderSelectItem classAction="'a'" id="'x'">x</BuilderSelectItem>
|
|
<BuilderSelectItem classAction="'a b'" id="'y'">y</BuilderSelectItem>
|
|
</BuilderSelect>
|
|
<BuilderButton classAction="'b1'" t-if="this.isActiveItem('x')">b1</BuilderButton>
|
|
<BuilderButton classAction="'b2'" t-if="this.isActiveItem('y')">b2</BuilderButton>
|
|
`,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target a">a</div>`);
|
|
setSelection({
|
|
anchorNode: queryFirst(":iframe .test-options-target").childNodes[0],
|
|
anchorOffset: 0,
|
|
});
|
|
await contains(":iframe .test-options-target").click();
|
|
await animationFrame();
|
|
expect(".options-container").toBeVisible();
|
|
|
|
expect(".we-bg-options-container .dropdown").toHaveText("x");
|
|
expect("[data-class-action='b1']").toBeVisible();
|
|
expect("[data-class-action='b2']").not.toHaveCount();
|
|
|
|
await contains(".we-bg-options-container .dropdown").click();
|
|
await contains("[data-class-action='a b']").click();
|
|
expect(".we-bg-options-container .dropdown").toHaveText("y");
|
|
expect("[data-class-action='b1']").not.toHaveCount();
|
|
expect("[data-class-action='b2']").toBeVisible();
|
|
});
|
|
test("a button should not be visible if the dependency is active", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderButton attributeAction="'my-attribute1'" attributeActionValue="'x'" id="'id1'">b1</BuilderButton>
|
|
<BuilderButton attributeAction="'my-attribute2'" attributeActionValue="'1'" t-if="!this.isActiveItem('id1')">b3</BuilderButton>
|
|
`,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target">b</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(".options-container").toBeVisible();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).toBeVisible();
|
|
await contains(
|
|
"[data-attribute-action='my-attribute1'][data-attribute-action-value='x']"
|
|
).click();
|
|
expect(":iframe .test-options-target").toHaveAttribute("my-attribute1", "x");
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).not.toHaveCount();
|
|
});
|
|
test("a button should not be visible if the dependency is active (when a dependency is added after a dependent)", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderButton attributeAction="'my-attribute2'" attributeActionValue="'1'" t-if="this.isActiveItem('id')">b1</BuilderButton>
|
|
<BuilderButton attributeAction="'my-attribute2'" attributeActionValue="'2'" t-if="!this.isActiveItem('id')">b2</BuilderButton>
|
|
<BuilderRow label="'dependency'">
|
|
<BuilderButton attributeAction="'my-attribute1'" attributeActionValue="'x'" id="'id'">b3</BuilderButton>
|
|
</BuilderRow>
|
|
`,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target">b</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
expect(".options-container").toBeVisible();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).not.toHaveCount();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='2']"
|
|
).toBeVisible();
|
|
await contains(
|
|
"[data-attribute-action='my-attribute1'][data-attribute-action-value='x']"
|
|
).click();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='1']"
|
|
).toBeVisible();
|
|
expect(
|
|
"[data-attribute-action='my-attribute2'][data-attribute-action-value='2']"
|
|
).not.toHaveCount();
|
|
});
|
|
test("a button should not be visible if its dependency is removed from the DOM", async () => {
|
|
addBuilderOption({
|
|
selector: ".test-options-target",
|
|
template: xml`
|
|
<BuilderButton classAction="'my-class1'" id="'id1'">b1</BuilderButton>
|
|
<BuilderButton classAction="'my-class2'" id="'id2'" t-if="this.isActiveItem('id1')">b2</BuilderButton>
|
|
<BuilderButton classAction="'my-class3'" t-if="this.isActiveItem('id2')">b3</BuilderButton>
|
|
`,
|
|
});
|
|
await setupHTMLBuilder(`<div class="test-options-target my-class1 my-class2">b</div>`);
|
|
await contains(":iframe .test-options-target").click();
|
|
await contains("[data-class-action='my-class1']").click();
|
|
// Wait 2 animation frames: one for id2 to be removed and another for
|
|
// id3 to be removed.
|
|
await animationFrame();
|
|
expect("[data-class-action='my-class3']").not.toHaveCount();
|
|
});
|
|
});
|