887 lines
41 KiB
JavaScript
887 lines
41 KiB
JavaScript
|
import { describe, expect, test } from "@odoo/hoot";
|
||
|
import {
|
||
|
click,
|
||
|
fill,
|
||
|
getActiveElement,
|
||
|
press,
|
||
|
queryAllTexts,
|
||
|
queryFirst,
|
||
|
queryOne,
|
||
|
queryText,
|
||
|
select,
|
||
|
waitFor,
|
||
|
waitUntil,
|
||
|
} from "@odoo/hoot-dom";
|
||
|
import { animationFrame, tick } from "@odoo/hoot-mock";
|
||
|
import { markup } from "@odoo/owl";
|
||
|
import { contains, onRpc } from "@web/../tests/web_test_helpers";
|
||
|
import { setupEditor } from "../_helpers/editor";
|
||
|
import { cleanLinkArtifacts } from "../_helpers/format";
|
||
|
import { getContent, setContent, setSelection } from "../_helpers/selection";
|
||
|
import { insertLineBreak, insertText, splitBlock, undo } from "../_helpers/user_actions";
|
||
|
|
||
|
const base64Img =
|
||
|
"data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA\n AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO\n 9TXL0Y4OHwAAAABJRU5ErkJggg==";
|
||
|
|
||
|
describe("should open a popover", () => {
|
||
|
test("should open a popover when the selection is inside a link and close outside of a link", async () => {
|
||
|
const { el } = await setupEditor("<p>this is a <a>link</a></p>");
|
||
|
expect(".o-we-linkpopover").toHaveCount(0);
|
||
|
// selection inside a link
|
||
|
setContent(el, "<p>this is a <a>li[]nk</a></p>");
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
// selection outside a link
|
||
|
setContent(el, "<p>this []is a <a>link</a></p>");
|
||
|
await waitUntil(() => !queryFirst(".o-we-linkpopover"), { timeout: 1500 });
|
||
|
expect(".o-we-linkpopover").toHaveCount(0);
|
||
|
});
|
||
|
test("link popover should have input field for href when the link doesn't have href", async () => {
|
||
|
await setupEditor("<p>this is a <a>li[]nk</a></p>");
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
expect(".o_we_label_link").toHaveValue("link");
|
||
|
expect(".o_we_href_input_link").toHaveValue("");
|
||
|
});
|
||
|
test("link popover should have buttons for link operation when the link has href", async () => {
|
||
|
await setupEditor('<p>this is a <a href="test.com">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
expect(".o_we_copy_link").toHaveCount(1);
|
||
|
expect(".o_we_edit_link").toHaveCount(1);
|
||
|
expect(".o_we_remove_link").toHaveCount(1);
|
||
|
});
|
||
|
test("link popover should not repositioned when clicking in the input field", async () => {
|
||
|
await setupEditor("<p>this is a <a>li[]nk</a></p>");
|
||
|
await waitFor(".o_we_href_input_link");
|
||
|
const style = queryOne(".o-we-linkpopover").parentElement.style.cssText;
|
||
|
queryOne(".o_we_href_input_link").focus();
|
||
|
await animationFrame();
|
||
|
expect(queryOne(".o-we-linkpopover").parentElement).toHaveAttribute("style", style);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("popover should switch UI depending on editing state", () => {
|
||
|
test("after clicking on edit button, the popover should switch to editing mode", async () => {
|
||
|
await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
await waitFor(".o_we_href_input_link");
|
||
|
expect(".o_we_label_link").toHaveValue("link");
|
||
|
expect(".o_we_href_input_link").toHaveValue("http://test.com/");
|
||
|
});
|
||
|
test("after clicking on apply button, the selection should be restored", async () => {
|
||
|
const { el } = await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
await waitFor(".o_we_href_input_link");
|
||
|
await click(".o_we_href_input_link");
|
||
|
await click(".o_we_apply_link");
|
||
|
await waitFor(".o_we_edit_link");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>this is a <a href="http://test.com/">li[]nk</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("after editing the URL and clicking on apply button, the selection should be restored", async () => {
|
||
|
const { el } = await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
await waitFor(".o_we_href_input_link");
|
||
|
// This changes the selection to outside the editor.
|
||
|
await click(".o_we_href_input_link");
|
||
|
await tick();
|
||
|
await press("a");
|
||
|
await click(".o_we_apply_link");
|
||
|
await waitFor(".o_we_edit_link");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>this is a <a href="http://test.com/a">li[]nk</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("after clicking on apply button, the popover should be with the non editing mode, e.g. with three buttons", async () => {
|
||
|
await setupEditor("<p>this is a <a>li[]nk</a></p>");
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_href_input_link");
|
||
|
await click(".o_we_apply_link");
|
||
|
await waitFor(".o_we_edit_link");
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
expect(".o_we_copy_link").toHaveCount(1);
|
||
|
expect(".o_we_edit_link").toHaveCount(1);
|
||
|
expect(".o_we_remove_link").toHaveCount(1);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("popover should edit,copy,remove the link", () => {
|
||
|
test("after apply url on a link without href, the link element should be updated", async () => {
|
||
|
const { el } = await setupEditor("<p>this is a <a>li[]nk</a></p>");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").edit("http://test.com/");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>this is a <a href="http://test.com/">li[]nk</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("after clicking on remove button, the link element should be unwrapped", async () => {
|
||
|
const { el } = await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_remove_link");
|
||
|
await waitUntil(() => !queryFirst(".o-we-linkpopover"), { timeout: 1500 });
|
||
|
expect(getContent(el)).toBe("<p>this is a li[]nk</p>");
|
||
|
});
|
||
|
test("after edit the label, the text of the link should be updated", async () => {
|
||
|
const { el } = await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_label_link").fill("new");
|
||
|
await click(".o_we_apply_link");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>this is a <a href="http://test.com/">linknew[]</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("when the label is empty, it should be set as the URL", async () => {
|
||
|
const { el } = await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
await waitFor(".o_we_apply_link");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_label_link").clear();
|
||
|
await click(".o_we_apply_link");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>this is a <a href="http://test.com/">http://test.com/[]</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("after clicking on copy button, the url should be copied to clipboard", async () => {
|
||
|
await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_copy_link");
|
||
|
await waitFor(".o_notification_bar.bg-success");
|
||
|
const notifications = queryAllTexts(".o_notification_body");
|
||
|
expect(notifications).toInclude("Link copied to clipboard.");
|
||
|
await animationFrame();
|
||
|
expect(".o-we-linkpopover").toHaveCount(0);
|
||
|
await expect(navigator.clipboard.readText()).resolves.toBe("http://test.com/");
|
||
|
});
|
||
|
test("when edit a link's label and URL to '', the link should be removed", async () => {
|
||
|
const { el } = await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_label_link").clear();
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").clear();
|
||
|
expect(getContent(el)).toBe("<p>this is a []</p>");
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Incorrect URL should be corrected", () => {
|
||
|
test("when edit a link's URL to 'test.com', the link's URL should be corrected", async () => {
|
||
|
const { el } = await setupEditor('<p>this is a <a href="http://test.com/">li[]nk</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").edit("newtest.com");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>this is a <a href="http://newtest.com">li[]nk</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("when a link's URL is an email, the link's URL should start with mailto:", async () => {
|
||
|
const { el } = await setupEditor("<p>this is a <a>li[]nk</a></p>");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").edit("test@test.com");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>this is a <a href="mailto:test@test.com">li[]nk</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("when a link's URL is an phonenumber, the link's URL should start with tel://:", async () => {
|
||
|
const { el } = await setupEditor("<p>this is a <a>li[]nk</a></p>");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").edit("+1234567890");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>this is a <a href="tel://+1234567890">li[]nk</a></p>'
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Link creation", () => {
|
||
|
describe("Creation by space", () => {
|
||
|
test("typing valid URL + space should convert to link", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>[]</p>");
|
||
|
await insertText(editor, "http://google.co.in");
|
||
|
await insertText(editor, " ");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p><a href="http://google.co.in">http://google.co.in</a> []</p>'
|
||
|
);
|
||
|
});
|
||
|
test("typing invalid URL + space should not convert to link", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>[]</p>");
|
||
|
await insertText(editor, "www.odoo");
|
||
|
await insertText(editor, " ");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe("<p>www.odoo []</p>");
|
||
|
});
|
||
|
});
|
||
|
describe("Creation by powerbox", () => {
|
||
|
test("click on link command in powerbox should create a link element and open the linkpopover", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>ab[]</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
expect(".active .o-we-command-name").toHaveText("Link");
|
||
|
|
||
|
await click(".o-we-command-name:first");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe("<p>ab<a>[]</a></p>");
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
expect(getActiveElement()).toBe(queryOne(".o-we-linkpopover input.o_we_label_link"), {
|
||
|
message: "should focus label input by default, when we don't have a label",
|
||
|
});
|
||
|
});
|
||
|
|
||
|
test("when create a new link by powerbox and not input anything, the link should be removed", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>ab[]</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe("<p>ab<a></a></p>");
|
||
|
|
||
|
const pNode = queryOne("p");
|
||
|
setSelection({
|
||
|
anchorNode: pNode,
|
||
|
anchorOffset: 0,
|
||
|
focusNode: pNode,
|
||
|
focusOffset: 0,
|
||
|
});
|
||
|
await animationFrame();
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe("<p>[]ab</p>");
|
||
|
});
|
||
|
test("when create a new link by powerbox and only input the URL, the link should be created with corrected URL", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>ab[]</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("test.com");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>ab<a href="http://test.com">test.com[]</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("Should be able to insert link on empty p", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>[]</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("#");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe('<p><a href="#">#[]</a></p>');
|
||
|
});
|
||
|
test("should insert a link and preserve spacing", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>a [] b</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_label_link").fill("link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("#");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>a <a href="#">link[]</a> b</p>'
|
||
|
);
|
||
|
});
|
||
|
test("should insert a link then create a new <p>", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>ab[]</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_label_link").fill("link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("#");
|
||
|
// press("Enter");
|
||
|
splitBlock(editor);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">link</a></p><p placeholder='Type "/" for commands' class="o-we-hint">[]<br></p>`
|
||
|
);
|
||
|
});
|
||
|
test("should insert a link then create a new <p>, and another character", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>a[]b</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_label_link").fill("link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("#");
|
||
|
// press("Enter");
|
||
|
splitBlock(editor);
|
||
|
await insertText(editor, "D");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>a<a href="#">link</a></p><p>D[]b</p>'
|
||
|
);
|
||
|
});
|
||
|
test("should insert a link then insert a <br>", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>a[]b</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_label_link").fill("link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("#");
|
||
|
// press("Enter");
|
||
|
insertLineBreak(editor);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe('<p>a<a href="#">link</a><br>[]b</p>');
|
||
|
});
|
||
|
test("should insert a link then insert a <br> and another character", async () => {
|
||
|
const { editor, el } = await setupEditor("<p>a[]b</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_label_link").fill("link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("#");
|
||
|
// press("Enter");
|
||
|
insertLineBreak(editor);
|
||
|
await insertText(editor, "D");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe('<p>a<a href="#">link</a><br>D[]b</p>');
|
||
|
});
|
||
|
});
|
||
|
describe("Creation by toolbar", () => {
|
||
|
test("should convert all selected text to link", async () => {
|
||
|
const { el } = await setupEditor("<p>[Hello]</p>");
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link", { timeout: 1500 }).edit(
|
||
|
"#"
|
||
|
);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe('<p><a href="#">Hello[]</a></p>');
|
||
|
});
|
||
|
test("should set the link on two existing characters", async () => {
|
||
|
const { el } = await setupEditor("<p>H[el]lo</p>");
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link", { timeout: 1500 }).edit(
|
||
|
"#"
|
||
|
);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe('<p>H<a href="#">el[]</a>lo</p>');
|
||
|
});
|
||
|
test("when you open link popover with a label, url input should be focus by default ", async () => {
|
||
|
const { el } = await setupEditor("<p>[Hello]</p>");
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await waitFor(".o-we-linkpopover", { timeout: 1500 });
|
||
|
expect(getActiveElement()).toBe(
|
||
|
queryOne(".o-we-linkpopover input.o_we_href_input_link")
|
||
|
);
|
||
|
|
||
|
await fill("test.com");
|
||
|
await click(".o_we_apply_link");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p><a href="http://test.com">Hello[]</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("should be correctly unlink/link", async () => {
|
||
|
const { el } = await setupEditor('<p>aaaa[b<a href="http://test.com/">cd</a>e]f</p>');
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
|
||
|
await click(".o-we-toolbar .fa-unlink");
|
||
|
await tick();
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe("<p>aaaa[bcde]f</p>");
|
||
|
const pNode = queryOne("p");
|
||
|
setSelection({
|
||
|
anchorNode: pNode.childNodes[1],
|
||
|
anchorOffset: 1,
|
||
|
focusNode: pNode.childNodes[0],
|
||
|
focusOffset: 3,
|
||
|
});
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link", { timeout: 1500 }).edit(
|
||
|
"#"
|
||
|
);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe('<p>aaa<a href="#">ab[]</a>cdef</p>');
|
||
|
});
|
||
|
test("should remove link when click away without inputting url", async () => {
|
||
|
const { el } = await setupEditor("<p>H[el]lo</p>");
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await waitFor(".o-we-linkpopover", { timeout: 1500 });
|
||
|
const pNode = queryOne("p");
|
||
|
setSelection({
|
||
|
anchorNode: pNode,
|
||
|
anchorOffset: 0,
|
||
|
focusNode: pNode,
|
||
|
focusOffset: 0,
|
||
|
});
|
||
|
await tick();
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe("<p>[]Hello</p>");
|
||
|
});
|
||
|
test("when selection includes partially a link and click the link icon in toolbar, the link should be extended", async () => {
|
||
|
const { el } = await setupEditor('<p>a[b<a href="http://test.com/">c]d</a>ef</p>');
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await waitFor(".o-we-linkpopover", { timeout: 1500 });
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>a<a href="http://test.com/">bcd[]</a>ef</p>'
|
||
|
);
|
||
|
});
|
||
|
test("when selection includes a link and click the link icon in toolbar, the link should be extended", async () => {
|
||
|
const { el } = await setupEditor('<p>a[b<a href="http://test.com/">cd</a>e]f</p>');
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await waitFor(".o-we-linkpopover", { timeout: 1500 });
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>a<a href="http://test.com/">bcde[]</a>f</p>'
|
||
|
);
|
||
|
});
|
||
|
test("when selection includes another block and the link extending stays inside of the block", async () => {
|
||
|
const { el } = await setupEditor(
|
||
|
'<p>a[b<a href="http://test.com/">cd</a>ef</p><p>gh]</p>'
|
||
|
);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await waitFor(".o-we-linkpopover", { timeout: 1500 });
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>a<a href="http://test.com/">bcdef[]</a></p><p>gh</p>'
|
||
|
);
|
||
|
});
|
||
|
test("when create a link on selection which doesn't include a link, it should create a new one", async () => {
|
||
|
await setupEditor(
|
||
|
'<p><strong>abc<a href="http://test.com/">de</a>te[st</strong> m]e</p>'
|
||
|
);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await waitFor(".o-we-linkpopover", { timeout: 1500 });
|
||
|
expect(".o_we_label_link").toHaveValue("st m");
|
||
|
expect(".o_we_href_input_link").toHaveValue("");
|
||
|
});
|
||
|
test("create a link and undo it", async () => {
|
||
|
const { el, editor } = await setupEditor("<p>[Hello]</p>");
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link", { timeout: 1500 }).edit(
|
||
|
"#"
|
||
|
);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe('<p><a href="#">Hello[]</a></p>');
|
||
|
|
||
|
undo(editor);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe("<p>[Hello]</p>");
|
||
|
});
|
||
|
test("extend a link on selection and undo it", async () => {
|
||
|
const { el, editor } = await setupEditor(
|
||
|
`<p>[<a href="https://www.test.com">Hello</a> my friend]</p>`
|
||
|
);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await waitFor(".o-we-linkpopover", { timeout: 1500 });
|
||
|
expect(queryFirst(".o-we-linkpopover a").href).toBe("https://www.test.com/");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p><a href="https://www.test.com">Hello my friend[]</a></p>`
|
||
|
);
|
||
|
|
||
|
undo(editor);
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>[<a href="https://www.test.com">Hello</a> my friend]</p>`
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe.tags("desktop");
|
||
|
describe("Link formatting in the popover", () => {
|
||
|
test("click on link, the link popover should load the current format correctly", async () => {
|
||
|
await setupEditor(
|
||
|
'<p><a href="http://test.com/" class="btn btn-outline-primary rounded-circle btn-lg">link2[]</a></p>'
|
||
|
);
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
const linkPreviewEl = await waitFor("#link-preview");
|
||
|
expect(linkPreviewEl).toHaveClass([
|
||
|
"btn",
|
||
|
"btn-outline-primary",
|
||
|
"rounded-circle",
|
||
|
"btn-lg",
|
||
|
]);
|
||
|
expect(queryOne('select[name="link_type"]').selectedIndex).toBe(1);
|
||
|
expect(queryOne('select[name="link_style_size"]').selectedIndex).toBe(2);
|
||
|
expect(queryOne('select[name="link_style_shape"]').selectedIndex).toBe(3);
|
||
|
});
|
||
|
test("after changing the link format, the link preview should be updated", async () => {
|
||
|
await setupEditor(
|
||
|
'<p><a href="http://test.com/" class="btn btn-fill-secondary rounded-circle btn-sm">link2[]</a></p>'
|
||
|
);
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
|
||
|
const linkPreviewEl = await waitFor("#link-preview");
|
||
|
expect(linkPreviewEl).toHaveClass([
|
||
|
"btn",
|
||
|
"rounded-circle",
|
||
|
"btn-fill-secondary",
|
||
|
"btn-sm",
|
||
|
]);
|
||
|
expect(queryOne('select[name="link_type"]').selectedIndex).toBe(2);
|
||
|
expect(queryOne('select[name="link_style_size"]').selectedIndex).toBe(0);
|
||
|
expect(queryOne('select[name="link_style_shape"]').selectedIndex).toBe(1);
|
||
|
|
||
|
await click('select[name="link_type"');
|
||
|
await select("primary");
|
||
|
await click('select[name="link_style_size"');
|
||
|
await select("lg");
|
||
|
await click('select[name="link_style_shape"');
|
||
|
await select("fill,rounded-circle");
|
||
|
await animationFrame();
|
||
|
expect(linkPreviewEl).toHaveClass(["btn", "btn-fill-primary", "rounded-circle", "btn-lg"]);
|
||
|
});
|
||
|
test("after applying the link format, the link's format should be updated", async () => {
|
||
|
const { el } = await setupEditor('<p><a href="http://test.com/">link2[]</a></p>');
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
await click(".o_we_edit_link");
|
||
|
await waitFor("#link-preview");
|
||
|
expect(queryOne('select[name="link_type"]').selectedIndex).toBe(0);
|
||
|
|
||
|
await click('select[name="link_type"');
|
||
|
await select("secondary");
|
||
|
await animationFrame();
|
||
|
await click('select[name="link_style_shape"');
|
||
|
await select("outline,rounded-circle");
|
||
|
await animationFrame();
|
||
|
|
||
|
const linkPreviewEl = queryOne("#link-preview");
|
||
|
expect(linkPreviewEl).toHaveClass(["btn", "btn-outline-secondary", "rounded-circle"]);
|
||
|
|
||
|
await click(".o_we_apply_link");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p><a href="http://test.com/" class="btn btn-outline-secondary rounded-circle">link2[]</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("no preview of the link when the url is empty", async () => {
|
||
|
await setupEditor("<p><a>link2[]</a></p>");
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect("#link-preview").toHaveCount(0);
|
||
|
});
|
||
|
test("when no label input, the link preview should have the content of the url", async () => {
|
||
|
const { editor } = await setupEditor("<p>ab[]</p>");
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
|
||
|
queryOne(".o_we_href_input_link").focus();
|
||
|
for (const char of "newtest.com") {
|
||
|
await press(char);
|
||
|
}
|
||
|
await animationFrame();
|
||
|
const linkPreviewEl = queryOne("#link-preview");
|
||
|
expect(linkPreviewEl).toHaveText("newtest.com");
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("shortcut", () => {
|
||
|
test("create link shortcut should be at the first", async () => {
|
||
|
const { editor } = await setupEditor(`<p>[]</p>`);
|
||
|
editor.services.command.add("A test command", () => {}, {
|
||
|
hotkey: "alt+k",
|
||
|
category: "app",
|
||
|
});
|
||
|
|
||
|
await press(["ctrl", "k"]);
|
||
|
await animationFrame();
|
||
|
expect(queryText(".o_command_name:first")).toBe("Create link");
|
||
|
});
|
||
|
test("create a link with shortcut", async () => {
|
||
|
const { el } = await setupEditor(`<p>[]</p>`);
|
||
|
|
||
|
await press(["ctrl", "k"]);
|
||
|
await animationFrame();
|
||
|
await click(".o_command_name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").edit("test.com");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p><a href="http://test.com">test.com[]</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("should be able to create link with ctrl+k and ctrl+k", async () => {
|
||
|
const { el } = await setupEditor(`<p>[]</p>`);
|
||
|
|
||
|
await press(["ctrl", "k"]);
|
||
|
await animationFrame();
|
||
|
await press(["ctrl", "k"]);
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").edit("test.com");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p><a href="http://test.com">test.com[]</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("should be able to create link with ctrl+k , and should make link on two existing characters", async () => {
|
||
|
const { el } = await setupEditor(`<p>H[el]lo</p>`);
|
||
|
|
||
|
await press(["ctrl", "k"]);
|
||
|
await animationFrame();
|
||
|
await press(["ctrl", "k"]);
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").edit("test.com");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>H<a href="http://test.com">el[]</a>lo</p>'
|
||
|
);
|
||
|
});
|
||
|
test("Press enter to apply when create a link", async () => {
|
||
|
const { el } = await setupEditor(`<p><a>li[]nk</a></p>`);
|
||
|
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("test.com");
|
||
|
await press("Enter");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p><a href="http://test.com">li[]nk</a></p>'
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("link preview", () => {
|
||
|
test("test internal link preview", async () => {
|
||
|
onRpc("/html_editor/link_preview_internal", () => {
|
||
|
return {
|
||
|
description: markup("Test description"),
|
||
|
link_preview_name: "Task name | Project name",
|
||
|
};
|
||
|
});
|
||
|
onRpc("/odoo/project/1/tasks/8", () => new Response("", { status: 200 }));
|
||
|
const { editor, el } = await setupEditor(`<p>[]</p>`);
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill(
|
||
|
window.location.origin + "/odoo/project/1/tasks/8"
|
||
|
);
|
||
|
await animationFrame();
|
||
|
expect(".o_we_replace_title_btn").toHaveCount(1);
|
||
|
expect(".o_we_url_link").toHaveText("Task name | Project name");
|
||
|
expect(".o_we_description_link_preview").toHaveText("Test description");
|
||
|
|
||
|
await click(".o_we_replace_title_btn");
|
||
|
await animationFrame();
|
||
|
|
||
|
expect(".o_we_replace_title_btn").toHaveCount(0);
|
||
|
expect(cleanLinkArtifacts(el.textContent)).toBe("Task name | Project name");
|
||
|
});
|
||
|
test("test external link preview", async () => {
|
||
|
onRpc("/html_editor/link_preview_external", () => {
|
||
|
return {
|
||
|
og_description:
|
||
|
"From ERP to CRM, eCommerce and CMS. Download Odoo or use it in the cloud. Grow Your Business.",
|
||
|
og_image: "https://www.odoo.com/web/image/41207129-1abe7a15/homepage-seo.png",
|
||
|
og_title: "Open Source ERP and CRM | Odoo",
|
||
|
og_type: "website",
|
||
|
og_site_name: "Odoo",
|
||
|
source_url: "http://odoo.com/",
|
||
|
};
|
||
|
});
|
||
|
const { editor } = await setupEditor(`<p>[]</p>`);
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("http://odoo.com/");
|
||
|
await animationFrame();
|
||
|
expect(".o_we_replace_title_btn").toHaveCount(1);
|
||
|
expect(".o_extra_info_card").toHaveCount(1);
|
||
|
expect(".o_we_url_link").toHaveText("Open Source ERP and CRM | Odoo");
|
||
|
expect(".o_we_description_link_preview").toHaveText(
|
||
|
"From ERP to CRM, eCommerce and CMS. Download Odoo or use it in the cloud. Grow Your Business."
|
||
|
);
|
||
|
});
|
||
|
test("test internal metadata cached correctly", async () => {
|
||
|
onRpc("/html_editor/link_preview_internal", () => {
|
||
|
expect.step("/html_editor/link_preview_internal");
|
||
|
return {
|
||
|
description: markup("<p>Test description</p>"),
|
||
|
link_preview_name: "Task name | Project name",
|
||
|
};
|
||
|
});
|
||
|
onRpc("/odoo/cachetest/8", () => new Response("", { status: 200 }));
|
||
|
const { editor } = await setupEditor(`<p>abc[]</p>`);
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill(
|
||
|
window.location.origin + "/odoo/cachetest/8"
|
||
|
);
|
||
|
await animationFrame();
|
||
|
expect.verifySteps(["/html_editor/link_preview_internal"]);
|
||
|
expect(".o_we_url_link").toHaveText("Task name | Project name");
|
||
|
|
||
|
const pNode = queryOne("p");
|
||
|
setSelection({
|
||
|
anchorNode: pNode,
|
||
|
anchorOffset: 1,
|
||
|
focusNode: pNode,
|
||
|
focusOffset: 1,
|
||
|
});
|
||
|
await waitUntil(() => !queryFirst(".o-we-linkpopover"), { timeout: 1500 });
|
||
|
|
||
|
const linkNode = queryOne("a");
|
||
|
setSelection({
|
||
|
anchorNode: linkNode,
|
||
|
anchorOffset: 1,
|
||
|
focusNode: linkNode,
|
||
|
focusOffset: 1,
|
||
|
});
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect.verifySteps([]);
|
||
|
});
|
||
|
test("test external metadata cached correctly", async () => {
|
||
|
onRpc("/html_editor/link_preview_external", () => {
|
||
|
expect.step("/html_editor/link_preview_external");
|
||
|
return {
|
||
|
og_description:
|
||
|
"From ERP to CRM, eCommerce and CMS. Download Odoo or use it in the cloud. Grow Your Business.",
|
||
|
og_image: "https://www.odoo.com/web/image/41207129-1abe7a15/homepage-seo.png",
|
||
|
og_title: "Open Source ERP and CRM | Odoo",
|
||
|
og_type: "website",
|
||
|
og_site_name: "Odoo",
|
||
|
source_url: "http://odoo.com/",
|
||
|
};
|
||
|
});
|
||
|
const { editor } = await setupEditor(`<p>[]</p>`);
|
||
|
await insertText(editor, "/link");
|
||
|
await animationFrame();
|
||
|
await click(".o-we-command-name:first");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link").fill("http://odoo.com/");
|
||
|
await animationFrame();
|
||
|
expect.verifySteps(["/html_editor/link_preview_external"]);
|
||
|
expect(".o_we_url_link").toHaveText("Open Source ERP and CRM | Odoo");
|
||
|
|
||
|
const pNode = queryOne("p");
|
||
|
setSelection({
|
||
|
anchorNode: pNode,
|
||
|
anchorOffset: 1,
|
||
|
focusNode: pNode,
|
||
|
focusOffset: 1,
|
||
|
});
|
||
|
await waitUntil(() => !queryFirst(".o-we-linkpopover"), { timeout: 1500 });
|
||
|
|
||
|
const linkNode = queryOne("a");
|
||
|
setSelection({
|
||
|
anchorNode: linkNode,
|
||
|
anchorOffset: 1,
|
||
|
focusNode: linkNode,
|
||
|
focusOffset: 1,
|
||
|
});
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect.verifySteps([]);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("link in templates", () => {
|
||
|
test("Should not remove a link with t-attf-href", async () => {
|
||
|
const { el } = await setupEditor('<p>test<a t-attf-href="/test/1">li[]nk</a></p>');
|
||
|
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
const pNode = queryOne("p");
|
||
|
setSelection({
|
||
|
anchorNode: pNode,
|
||
|
anchorOffset: 0,
|
||
|
focusNode: pNode,
|
||
|
focusOffset: 0,
|
||
|
});
|
||
|
await waitUntil(() => !queryFirst(".o-we-linkpopover"), { timeout: 1500 });
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>[]test<a t-attf-href="/test/1">link</a></p>'
|
||
|
);
|
||
|
});
|
||
|
test("Should not remove a link with t-att-href", async () => {
|
||
|
const { el } = await setupEditor('<p>test<a t-att-href="/test/1">li[]nk</a></p>');
|
||
|
|
||
|
await waitFor(".o-we-linkpopover");
|
||
|
expect(".o-we-linkpopover").toHaveCount(1);
|
||
|
const pNode = queryOne("p");
|
||
|
setSelection({
|
||
|
anchorNode: pNode,
|
||
|
anchorOffset: 0,
|
||
|
focusNode: pNode,
|
||
|
focusOffset: 0,
|
||
|
});
|
||
|
await waitUntil(() => !queryFirst(".o-we-linkpopover"), { timeout: 1500 });
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
'<p>[]test<a t-att-href="/test/1">link</a></p>'
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("links with inline image", () => {
|
||
|
test("can add link to inline image + text", async () => {
|
||
|
const { el } = await setupEditor(`<p>ab[cd<img src="${base64Img}">ef]g</p>`);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link", { timeout: 1500 }).edit("#");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">cd<img src="${base64Img}">ef[]</a>g</p>`
|
||
|
);
|
||
|
});
|
||
|
test("can undo add link to inline image + text", async () => {
|
||
|
const { editor, el } = await setupEditor(`<p>ab[cd<img src="${base64Img}">ef]g</p>`);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-link");
|
||
|
await contains(".o-we-linkpopover input.o_we_href_input_link", { timeout: 1500 }).edit("#");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">cd<img src="${base64Img}">ef[]</a>g</p>`
|
||
|
);
|
||
|
undo(editor);
|
||
|
await animationFrame();
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(`<p>ab[cd<img src="${base64Img}">ef]g</p>`);
|
||
|
});
|
||
|
test("can remove link from an inline image", async () => {
|
||
|
const { el } = await setupEditor(`<p>ab<a href="#">cd<img src="${base64Img}">ef</a>g</p>`);
|
||
|
await click("img");
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
expect("button[name='unlink']").toHaveCount(1);
|
||
|
await click("button[name='unlink']");
|
||
|
await animationFrame();
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">cd[</a><img src="${base64Img}"><a href="#">]ef</a>g</p>`
|
||
|
);
|
||
|
expect(".o-we-linkpopover").toHaveCount(0);
|
||
|
});
|
||
|
test("can remove link from a selection of an inline image + text", async () => {
|
||
|
const { el } = await setupEditor(
|
||
|
`<p>ab<a href="#">c[d<img src="${base64Img}">e]f</a>g</p>`
|
||
|
);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-unlink");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">c</a>[d<img src="${base64Img}">e]<a href="#">f</a>g</p>`
|
||
|
);
|
||
|
});
|
||
|
test("can remove link from a selection (ltr) with multiple inline images", async () => {
|
||
|
const { el } = await setupEditor(
|
||
|
`<p>ab<a href="#">c[d<img src="${base64Img}">e<img src="${base64Img}">f]g</a>h</p>`
|
||
|
);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-unlink");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">c</a>[d<img src="${base64Img}">e<img src="${base64Img}">f]<a href="#">g</a>h</p>`
|
||
|
);
|
||
|
});
|
||
|
test("can remove link from a selection (rtl) with multiple inline images", async () => {
|
||
|
const { el } = await setupEditor(
|
||
|
`<p>ab<a href="#">c]d<img src="${base64Img}">e<img src="${base64Img}">f[g</a>h</p>`
|
||
|
);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-unlink");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">c</a>]d<img src="${base64Img}">e<img src="${base64Img}">f[<a href="#">g</a>h</p>`
|
||
|
);
|
||
|
});
|
||
|
test("can remove link from a selection (ltr) with multiple inline images acrossing different links", async () => {
|
||
|
const { el } = await setupEditor(
|
||
|
`<p>ab<a href="#">c[d<img src="${base64Img}">e</a>xx<a href="#">f<img src="${base64Img}">g]h</a>i</p>`
|
||
|
);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-unlink");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">c</a>[d<img src="${base64Img}">exxf<img src="${base64Img}">g]<a href="#">h</a>i</p>`
|
||
|
);
|
||
|
});
|
||
|
test("can remove link from a selection (rtl) with multiple inline images acrossing different links", async () => {
|
||
|
const { el } = await setupEditor(
|
||
|
`<p>ab<a href="#">c]d<img src="${base64Img}">e</a>xx<a href="#">f<img src="${base64Img}">g[h</a>i</p>`
|
||
|
);
|
||
|
await waitFor(".o-we-toolbar");
|
||
|
await click(".o-we-toolbar .fa-unlink");
|
||
|
expect(cleanLinkArtifacts(getContent(el))).toBe(
|
||
|
`<p>ab<a href="#">c</a>]d<img src="${base64Img}">exxf<img src="${base64Img}">g[<a href="#">h</a>i</p>`
|
||
|
);
|
||
|
});
|
||
|
});
|