mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 08:52:08 +02:00
vanilla 19.0
This commit is contained in:
parent
991d2234ca
commit
d1963a3c3a
3066 changed files with 1651266 additions and 922560 deletions
100
odoo-bringout-oca-ocb-web/web/static/tests/public/helpers.js
Normal file
100
odoo-bringout-oca-ocb-web/web/static/tests/public/helpers.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { getFixture, after } from "@odoo/hoot";
|
||||
import { clearRegistry, makeMockEnv, patchWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
let activeInteractions = null;
|
||||
const elementRegistry = registry.category("public.interactions");
|
||||
const content = elementRegistry.content;
|
||||
|
||||
export function setupInteractionWhiteList(interactions) {
|
||||
if (arguments.length > 1) {
|
||||
throw new Error("Multiple white-listed interactions should be listed in an array.");
|
||||
}
|
||||
if (typeof interactions === "string") {
|
||||
interactions = [interactions];
|
||||
}
|
||||
activeInteractions = interactions;
|
||||
}
|
||||
|
||||
setupInteractionWhiteList.getWhiteList = () => activeInteractions;
|
||||
|
||||
export async function startInteraction(I, html, options) {
|
||||
clearRegistry(elementRegistry);
|
||||
for (const Interaction of Array.isArray(I) ? I : [I]) {
|
||||
elementRegistry.add(Interaction.name, Interaction);
|
||||
}
|
||||
return startInteractions(html, options);
|
||||
}
|
||||
|
||||
export async function startInteractions(
|
||||
html,
|
||||
options = { waitForStart: true, editMode: false, translateMode: false }
|
||||
) {
|
||||
if (odoo.loader.modules.has("@mail/../tests/mail_test_helpers")) {
|
||||
const { defineMailModels } = odoo.loader.modules.get("@mail/../tests/mail_test_helpers");
|
||||
defineMailModels();
|
||||
}
|
||||
const fixture = getFixture();
|
||||
if (!html.includes("wrapwrap")) {
|
||||
html = `<div id="wrapwrap">${html}</div>`;
|
||||
}
|
||||
fixture.innerHTML = html;
|
||||
if (options.translateMode) {
|
||||
fixture.closest("html").dataset.edit_translations = "1";
|
||||
}
|
||||
if (activeInteractions) {
|
||||
clearRegistry(elementRegistry);
|
||||
if (!options.editMode) {
|
||||
for (const name of activeInteractions) {
|
||||
if (name in content) {
|
||||
elementRegistry.add(name, content[name][1]);
|
||||
} else {
|
||||
throw new Error(`White-listed Interaction does not exist: ${name}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const env = await makeMockEnv();
|
||||
const core = env.services["public.interactions"];
|
||||
if (options.waitForStart) {
|
||||
await core.isReady;
|
||||
}
|
||||
after(() => {
|
||||
delete fixture.closest("html").dataset.edit_translations;
|
||||
core.stopInteractions();
|
||||
});
|
||||
|
||||
return {
|
||||
core,
|
||||
};
|
||||
}
|
||||
|
||||
export function mockSendRequests() {
|
||||
const requests = [];
|
||||
patchWithCleanup(HTMLFormElement.prototype, {
|
||||
submit: function () {
|
||||
requests.push({
|
||||
url: this.getAttribute("action"),
|
||||
method: this.getAttribute("method"),
|
||||
});
|
||||
},
|
||||
});
|
||||
return requests;
|
||||
}
|
||||
|
||||
export function isElementInViewport(el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const width = window.innerWidth || document.documentElement.clientWidth;
|
||||
const height = window.innerHeight || document.documentElement.clientHeight;
|
||||
return (
|
||||
Math.round(rect.top) >= 0 &&
|
||||
Math.round(rect.left) >= 0 &&
|
||||
Math.round(rect.right) <= width &&
|
||||
Math.round(rect.bottom) <= height
|
||||
);
|
||||
}
|
||||
|
||||
export function isElementVerticallyInViewportOf(el, scrollEl) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
return rect.top <= scrollEl.clientHeight && rect.bottom >= 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="web.testRenderAt">
|
||||
<div class="rendered">Test</div>
|
||||
</t>
|
||||
<t t-name="web.TestSubInteraction1">
|
||||
<div class="sub1" t-att-data-which="first">Sub 1</div>
|
||||
<div class="sub2" t-att-data-which="second">Sub 2</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,381 @@
|
|||
import { describe, expect, test } from "@odoo/hoot";
|
||||
import { queryOne } from "@odoo/hoot-dom";
|
||||
import { animationFrame } from "@odoo/hoot-mock";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import { makeMockEnv } from "@web/../tests/web_test_helpers";
|
||||
import { Interaction } from "@web/public/interaction";
|
||||
import { startInteraction } from "./helpers";
|
||||
|
||||
describe.current.tags("interaction_dev");
|
||||
|
||||
test("properly fallback to body when we have no match for wrapwrap", async () => {
|
||||
const env = await makeMockEnv();
|
||||
expect(env.services["public.interactions"].el).toBe(document.querySelector("body"));
|
||||
});
|
||||
|
||||
test("wait for translation before starting interactions", async () => {
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
setup() {
|
||||
expect("localization" in this.services).toBe(true);
|
||||
}
|
||||
}
|
||||
await startInteraction(Test, `<div class="test"></div>`);
|
||||
});
|
||||
|
||||
test("starting interactions twice should only actually do it once", async () => {
|
||||
let n = 0;
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
setup() {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(Test, `<div class="test"></div>`);
|
||||
|
||||
expect(n).toBe(1);
|
||||
core.startInteractions();
|
||||
await animationFrame();
|
||||
expect(n).toBe(1);
|
||||
});
|
||||
|
||||
test("start interactions even if there is a crash", async () => {
|
||||
class Boom extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
setup() {
|
||||
expect.step("start boom");
|
||||
throw new Error("boom");
|
||||
}
|
||||
destroy() {
|
||||
expect.step("destroy boom");
|
||||
}
|
||||
}
|
||||
class NotBoom extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
setup() {
|
||||
expect.step("start notboom");
|
||||
}
|
||||
destroy() {
|
||||
expect.step("destroy notboom");
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction([Boom, NotBoom], `<div class="test"></div>`, {
|
||||
waitForStart: false,
|
||||
});
|
||||
await expect(core.isReady).rejects.toThrow("boom");
|
||||
expect.verifySteps(["start boom", "start notboom"]);
|
||||
core.stopInteractions();
|
||||
expect.verifySteps(["destroy notboom"]);
|
||||
});
|
||||
|
||||
test("start interactions even if there is a crash when evaluating selector", async () => {
|
||||
class Boom extends Interaction {
|
||||
static selector = "div:invalid(coucou)";
|
||||
|
||||
setup() {
|
||||
expect.step("start boom");
|
||||
}
|
||||
destroy() {
|
||||
expect.step("destroy boom");
|
||||
}
|
||||
}
|
||||
class NotBoom extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
setup() {
|
||||
expect.step("start notboom");
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction([Boom, NotBoom], `<div class="test"></div>`, {
|
||||
waitForStart: false,
|
||||
});
|
||||
|
||||
await expect(core.isReady).rejects.toThrow(
|
||||
"Could not start interaction Boom (invalid selector: 'div:invalid(coucou)')"
|
||||
);
|
||||
expect.verifySteps(["start notboom"]);
|
||||
});
|
||||
|
||||
test("start interactions even if there is a crash when evaluating selectorHas", async () => {
|
||||
class Boom extends Interaction {
|
||||
static selector = ".test";
|
||||
static selectorHas = "div:invalid(coucou)";
|
||||
|
||||
setup() {
|
||||
expect.step("start boom");
|
||||
}
|
||||
destroy() {
|
||||
expect.step("destroy boom");
|
||||
}
|
||||
}
|
||||
class NotBoom extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
setup() {
|
||||
expect.step("start notboom");
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(
|
||||
[Boom, NotBoom],
|
||||
`<div class="test"><div></div></div>`,
|
||||
{
|
||||
waitForStart: false,
|
||||
}
|
||||
);
|
||||
|
||||
await expect(core.isReady).rejects.toThrow(
|
||||
"Could not start interaction Boom (invalid selector: '.test' or selectorHas: 'div:invalid(coucou)')"
|
||||
);
|
||||
expect.verifySteps(["start notboom"]);
|
||||
});
|
||||
|
||||
test("start interactions with selectorHas", async () => {
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
static selectorHas = ".inner";
|
||||
|
||||
start() {
|
||||
expect.step("start");
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(
|
||||
Test,
|
||||
`
|
||||
<div class="test"><div class="inner"></div></div>
|
||||
<div class="test"><div class="not-inner"></div></div>
|
||||
`
|
||||
);
|
||||
expect(core.interactions).toHaveLength(1);
|
||||
expect.verifySteps(["start"]);
|
||||
expect(core.interactions[0].interaction.el).toBe(queryOne(".test:has(.inner)"));
|
||||
});
|
||||
|
||||
test("start interactions even if there is a crash when evaluating selectorNotHas", async () => {
|
||||
class Boom extends Interaction {
|
||||
static selector = ".test";
|
||||
static selectorNotHas = "div:invalid(coucou)";
|
||||
|
||||
setup() {
|
||||
expect.step("start boom");
|
||||
}
|
||||
destroy() {
|
||||
expect.step("destroy boom");
|
||||
}
|
||||
}
|
||||
class NotBoom extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
setup() {
|
||||
expect.step("start notboom");
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(
|
||||
[Boom, NotBoom],
|
||||
`<div class="test"><div></div></div>`,
|
||||
{
|
||||
waitForStart: false,
|
||||
}
|
||||
);
|
||||
|
||||
await expect(core.isReady).rejects.toThrow(
|
||||
"Could not start interaction Boom (invalid selector: '.test' or selectorNotHas: 'div:invalid(coucou)')"
|
||||
);
|
||||
expect.verifySteps(["start notboom"]);
|
||||
});
|
||||
|
||||
test("start interactions with selectorNotHas", async () => {
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
static selectorNotHas = ".inner";
|
||||
|
||||
start() {
|
||||
expect.step("start");
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(
|
||||
Test,
|
||||
`
|
||||
<div class="test"><div class="inner"></div></div>
|
||||
<div class="test"><div class="not-inner"></div></div>
|
||||
`
|
||||
);
|
||||
expect(core.interactions).toHaveLength(1);
|
||||
expect.verifySteps(["start"]);
|
||||
expect(core.interactions[0].interaction.el).toBe(queryOne(".test:not(:has(.inner))"));
|
||||
});
|
||||
|
||||
test("recover from error as much as possible when applying dynamiccontent", async () => {
|
||||
let a = "a";
|
||||
let b = "b";
|
||||
let c = "c";
|
||||
let interaction = null;
|
||||
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
dynamicContent = {
|
||||
_root: {
|
||||
"t-att-a": () => a,
|
||||
"t-att-b": () => {
|
||||
if (b === "boom") {
|
||||
throw new Error("boom");
|
||||
}
|
||||
return b;
|
||||
},
|
||||
"t-att-c": () => c,
|
||||
},
|
||||
};
|
||||
setup() {
|
||||
interaction = this;
|
||||
}
|
||||
}
|
||||
|
||||
await startInteraction(Test, `<div class="test"></div>`);
|
||||
expect(".test").toHaveOuterHTML(`<div class="test" a="a" b="b" c="c"></div>`);
|
||||
|
||||
a = "aa";
|
||||
b = "boom";
|
||||
c = "cc";
|
||||
expect(() => interaction.updateContent()).toThrow(
|
||||
"An error occured while updating dynamic attribute 'b' (in interaction 'Test')"
|
||||
);
|
||||
expect(".test").toHaveOuterHTML(`<div class="test" a="aa" b="b" c="cc"></div>`);
|
||||
});
|
||||
|
||||
test("interactions are stopped in reverse order", async () => {
|
||||
let n = 1;
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
setup() {
|
||||
this.n = n++;
|
||||
expect.step(`setup ${this.n}`);
|
||||
}
|
||||
destroy() {
|
||||
expect.step(`destroy ${this.n}`);
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(
|
||||
Test,
|
||||
`<div class="test"></div><div class="test"></div>`
|
||||
);
|
||||
expect.verifySteps(["setup 1", "setup 2"]);
|
||||
core.stopInteractions();
|
||||
expect.verifySteps(["destroy 2", "destroy 1"]);
|
||||
});
|
||||
|
||||
test("can mount a component", async () => {
|
||||
class Test extends Component {
|
||||
static selector = ".test";
|
||||
static template = xml`owl component`;
|
||||
static props = {};
|
||||
}
|
||||
const { core } = await startInteraction(Test, `<div class="test"></div>`);
|
||||
expect(".test").toHaveInnerHTML(
|
||||
`<owl-root contenteditable="false" data-oe-protected="true" style="display: contents;">owl component</owl-root>`
|
||||
);
|
||||
core.stopInteractions();
|
||||
expect(".test").toHaveOuterHTML(`<div class="test"></div>`);
|
||||
});
|
||||
|
||||
test("can start interaction in specific el", async () => {
|
||||
let n = 0;
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
dynamicContent = {
|
||||
_root: { "t-att-a": () => "b" },
|
||||
};
|
||||
|
||||
setup() {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(Test, `<p></p>`);
|
||||
|
||||
expect(n).toBe(0);
|
||||
const p = queryOne("p");
|
||||
p.innerHTML = `<div class="test">hello</div>`;
|
||||
core.startInteractions(queryOne(".test"));
|
||||
await animationFrame();
|
||||
expect(n).toBe(1);
|
||||
expect(p).toHaveInnerHTML(`<div class="test" a="b">hello</div>`);
|
||||
});
|
||||
|
||||
test("can start and stop interaction in specific el", async () => {
|
||||
let n = 0;
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
|
||||
start() {
|
||||
n++;
|
||||
this.el.dataset.start = "true";
|
||||
}
|
||||
destroy() {
|
||||
n--;
|
||||
delete this.el.dataset.start;
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(
|
||||
Test,
|
||||
`
|
||||
<p>
|
||||
<span class="a test"></span>
|
||||
<span class="b"></span>
|
||||
</p>`
|
||||
);
|
||||
|
||||
expect(n).toBe(1);
|
||||
const p = queryOne("p");
|
||||
expect(p).toHaveInnerHTML(
|
||||
`<span class="a test" data-start="true"></span> <span class="b"></span>`
|
||||
);
|
||||
const b = queryOne("p .b");
|
||||
b.classList.add("test");
|
||||
await core.startInteractions(b);
|
||||
expect(n).toBe(2);
|
||||
expect(p).toHaveInnerHTML(
|
||||
`<span class="a test" data-start="true"></span> <span class="b test" data-start="true"></span>`
|
||||
);
|
||||
|
||||
core.stopInteractions(b);
|
||||
expect(n).toBe(1);
|
||||
expect(p).toHaveInnerHTML(
|
||||
`<span class="a test" data-start="true"></span> <span class="b test"></span>`
|
||||
);
|
||||
});
|
||||
|
||||
test("does not start interaction in el if not attached", async () => {
|
||||
let n = 0;
|
||||
class Test extends Interaction {
|
||||
static selector = ".test";
|
||||
start() {
|
||||
n++;
|
||||
}
|
||||
destroy() {
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
const { core } = await startInteraction(Test, `<p><span class="test"></span></p>`);
|
||||
expect(n).toBe(1);
|
||||
const span = queryOne("span.test");
|
||||
core.stopInteractions(span);
|
||||
expect(n).toBe(0);
|
||||
span.remove();
|
||||
await core.startInteractions(span);
|
||||
expect(n).toBe(0);
|
||||
});
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { setupInteractionWhiteList, startInteractions } from "@web/../tests/public/helpers";
|
||||
|
||||
import { describe, expect, test } from "@odoo/hoot";
|
||||
import { queryOne } from "@odoo/hoot-dom";
|
||||
|
||||
setupInteractionWhiteList("public.login");
|
||||
|
||||
describe.current.tags("interaction_dev");
|
||||
|
||||
test("add and remove loading effect", async () => {
|
||||
const { core } = await startInteractions(`
|
||||
<div class="oe_login_form">
|
||||
<button type="submit">log in</button>
|
||||
</div>`);
|
||||
expect(core.interactions).toHaveLength(1);
|
||||
// Not using manuallyDispatchProgrammaticEvent to keep a minimalist test. We
|
||||
// don't need to send a proper "submit" event with FormData, method, action,
|
||||
// etc. for this test.
|
||||
const ev = new Event("submit");
|
||||
queryOne(".oe_login_form").dispatchEvent(ev);
|
||||
expect("button").toHaveClass(["o_btn_loading", "disabled"]);
|
||||
ev.preventDefault();
|
||||
expect("button").not.toHaveClass(["o_btn_loading", "disabled"]);
|
||||
});
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import { expect, test } from "@odoo/hoot";
|
||||
import { queryAllTexts } from "@odoo/hoot-dom";
|
||||
import { animationFrame } from "@odoo/hoot-mock";
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import { setupInteractionWhiteList, startInteractions } from "./helpers";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
setupInteractionWhiteList("public_components");
|
||||
|
||||
const publicComponentRegistry = registry.category("public_components");
|
||||
|
||||
test(`render Public Component`, async () => {
|
||||
class MyPublicComp extends Component {
|
||||
static template = xml`<div class="my_public_comp" t-esc="value"/>`;
|
||||
static props = ["*"];
|
||||
setup() {
|
||||
const { info } = this.props;
|
||||
this.value = typeof info === "object" ? JSON.stringify(info) : info;
|
||||
expect.step(`MyPublicComp: ${this.value} - ${typeof info}`);
|
||||
}
|
||||
}
|
||||
publicComponentRegistry.add("my_public_comp", MyPublicComp);
|
||||
|
||||
const html = `
|
||||
<div>
|
||||
<owl-component name="my_public_comp" props='{"info": "blibli"}'></owl-component>
|
||||
<owl-component name="my_public_comp" props='{"info": 3}'></owl-component>
|
||||
<owl-component name="my_public_comp" props='{"info": {"test": "plop"}}'></owl-component>
|
||||
</div>
|
||||
`;
|
||||
|
||||
await startInteractions(html);
|
||||
// interaction is now ready, but components not mounted yet
|
||||
expect(`.my_public_comp`).toHaveCount(0);
|
||||
expect.verifySteps([
|
||||
"MyPublicComp: blibli - string",
|
||||
"MyPublicComp: 3 - number",
|
||||
'MyPublicComp: {"test":"plop"} - object',
|
||||
]);
|
||||
|
||||
await animationFrame();
|
||||
// components are now mounted
|
||||
expect(`.my_public_comp`).toHaveCount(3);
|
||||
expect(queryAllTexts`.my_public_comp`).toEqual(["blibli", "3", `{"test":"plop"}`]);
|
||||
});
|
||||
|
||||
test(`content of owl-component tag is cleared`, async () => {
|
||||
class MyPublicComp extends Component {
|
||||
static template = xml`<div>component</div>`;
|
||||
static props = ["*"];
|
||||
}
|
||||
publicComponentRegistry.add("my_public_comp", MyPublicComp);
|
||||
|
||||
const html = `
|
||||
<div>
|
||||
<owl-component name="my_public_comp">some content</owl-component>
|
||||
</div>
|
||||
`;
|
||||
|
||||
await startInteractions(html);
|
||||
await animationFrame();
|
||||
expect(`.my_public_comp`).toHaveCount(0);
|
||||
expect("owl-component").toHaveOuterHTML(`
|
||||
<owl-component name="my_public_comp">
|
||||
<owl-root contenteditable="false" data-oe-protected="true" style="display: contents;">
|
||||
<div> component </div>
|
||||
</owl-root>
|
||||
</owl-component>`);
|
||||
});
|
||||
171
odoo-bringout-oca-ocb-web/web/static/tests/public/utils.test.js
Normal file
171
odoo-bringout-oca-ocb-web/web/static/tests/public/utils.test.js
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
import { describe, expect, test } from "@odoo/hoot";
|
||||
|
||||
import { PairSet, patchDynamicContent } from "@web/public/utils";
|
||||
|
||||
describe.current.tags("headless");
|
||||
|
||||
describe("PairSet", () => {
|
||||
test("can add and delete pairs", () => {
|
||||
const pairSet = new PairSet();
|
||||
|
||||
const a = {};
|
||||
const b = {};
|
||||
expect(pairSet.has(a, b)).toBe(false);
|
||||
pairSet.add(a, b);
|
||||
expect(pairSet.has(a, b)).toBe(true);
|
||||
pairSet.delete(a, b);
|
||||
expect(pairSet.has(a, b)).toBe(false);
|
||||
});
|
||||
|
||||
test("can add and delete pairs with the same first element", () => {
|
||||
const pairSet = new PairSet();
|
||||
|
||||
const a = {};
|
||||
const b = {};
|
||||
const c = {};
|
||||
expect(pairSet.has(a, b)).toBe(false);
|
||||
expect(pairSet.has(a, c)).toBe(false);
|
||||
pairSet.add(a, b);
|
||||
expect(pairSet.has(a, b)).toBe(true);
|
||||
expect(pairSet.has(a, c)).toBe(false);
|
||||
pairSet.add(a, c);
|
||||
expect(pairSet.has(a, b)).toBe(true);
|
||||
expect(pairSet.has(a, c)).toBe(true);
|
||||
pairSet.delete(a, c);
|
||||
expect(pairSet.has(a, b)).toBe(true);
|
||||
expect(pairSet.has(a, c)).toBe(false);
|
||||
pairSet.delete(a, b);
|
||||
expect(pairSet.has(a, b)).toBe(false);
|
||||
expect(pairSet.has(a, c)).toBe(false);
|
||||
});
|
||||
|
||||
test("do not duplicated pairs", () => {
|
||||
const pairSet = new PairSet();
|
||||
|
||||
const a = {};
|
||||
const b = {};
|
||||
expect(pairSet.map.size).toBe(0);
|
||||
pairSet.add(a, b);
|
||||
expect(pairSet.map.size).toBe(1);
|
||||
pairSet.add(a, b);
|
||||
expect(pairSet.map.size).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("patch dynamic content", () => {
|
||||
test("patch applies new values", () => {
|
||||
const parent = {
|
||||
somewhere: {
|
||||
"t-att-doNotTouch": 123,
|
||||
},
|
||||
};
|
||||
const patch = {
|
||||
somewhere: {
|
||||
"t-att-class": () => ({
|
||||
abc: true,
|
||||
}),
|
||||
"t-att-xyz": "123",
|
||||
},
|
||||
elsewhere: {
|
||||
"t-att-class": () => ({
|
||||
xyz: true,
|
||||
}),
|
||||
"t-att-abc": "123",
|
||||
},
|
||||
};
|
||||
patchDynamicContent(parent, patch);
|
||||
expect(Object.keys(parent)).toEqual(["somewhere", "elsewhere"]);
|
||||
expect(Object.keys(parent.somewhere)).toEqual([
|
||||
"t-att-doNotTouch",
|
||||
"t-att-class",
|
||||
"t-att-xyz",
|
||||
]);
|
||||
expect(Object.keys(parent.elsewhere)).toEqual(["t-att-class", "t-att-abc"]);
|
||||
});
|
||||
|
||||
test("patch removes undefined values", () => {
|
||||
const parent = {
|
||||
somewhere: {
|
||||
"t-att-doNotTouch": 123,
|
||||
"t-att-removeMe": "abc",
|
||||
},
|
||||
};
|
||||
const patch = {
|
||||
somewhere: {
|
||||
"t-att-removeMe": undefined,
|
||||
},
|
||||
};
|
||||
patchDynamicContent(parent, patch);
|
||||
expect(parent).toEqual({
|
||||
somewhere: {
|
||||
"t-att-doNotTouch": 123,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("patch combines function outputs", () => {
|
||||
const parent = {
|
||||
somewhere: {
|
||||
"t-att-style": () => ({
|
||||
doNotTouch: true,
|
||||
changeMe: 10,
|
||||
doubleMe: 100,
|
||||
}),
|
||||
},
|
||||
};
|
||||
const patch = {
|
||||
somewhere: {
|
||||
"t-att-style": (el, old) => ({
|
||||
changeMe: 50,
|
||||
doubleMe: old.doubleMe * 2,
|
||||
addMe: 1000,
|
||||
}),
|
||||
},
|
||||
};
|
||||
patchDynamicContent(parent, patch);
|
||||
expect(parent.somewhere["t-att-style"]()).toEqual({
|
||||
doNotTouch: true,
|
||||
changeMe: 50,
|
||||
doubleMe: 200,
|
||||
addMe: 1000,
|
||||
});
|
||||
});
|
||||
|
||||
test("patch t-on-... provides access to super", () => {
|
||||
const parent = {
|
||||
somewhere: {
|
||||
"t-on-click": () => {
|
||||
expect.step("base");
|
||||
},
|
||||
},
|
||||
};
|
||||
const patch = {
|
||||
somewhere: {
|
||||
"t-on-click": (el, oldFn) => {
|
||||
oldFn();
|
||||
expect.step("patch");
|
||||
},
|
||||
},
|
||||
};
|
||||
patchDynamicContent(parent, patch);
|
||||
parent.somewhere["t-on-click"]();
|
||||
expect.verifySteps(["base", "patch"]);
|
||||
});
|
||||
|
||||
test("patch t-on-... does not require knowledge about there being a super", () => {
|
||||
const parent = {
|
||||
// No t-on-click here.
|
||||
};
|
||||
const patch = {
|
||||
somewhere: {
|
||||
"t-on-click": (el, oldFn) => {
|
||||
oldFn();
|
||||
expect.step("patch");
|
||||
},
|
||||
},
|
||||
};
|
||||
patchDynamicContent(parent, patch);
|
||||
parent.somewhere["t-on-click"]();
|
||||
expect.verifySteps(["patch"]);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue