19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:12 +01:00
parent 79f83631d5
commit 73afc09215
6267 changed files with 1534193 additions and 1130106 deletions

View file

@ -0,0 +1,134 @@
import { describe, expect, test } from "@odoo/hoot";
import { animationFrame, click, queryOne } from "@odoo/hoot-dom";
import { defineStyle } from "@web/../tests/web_test_helpers";
import { setupInteractionWhiteList, startInteractions } from "@web/../tests/public/helpers";
setupInteractionWhiteList(["website_sale.carousel_product"]);
describe.current.tags("interaction_dev");
test("scroll miniatures", async () => {
defineStyle(/* css */`li { min-width: 64px !important; }`);
const { core } = await startInteractions(`
<div class="o_wsale_product_images position-relative" style="width: 600px" data-image-amount="16">
<div id="o-carousel-product" data-bs-ride="true" class="o_carousel_not_single carousel slide position-sticky mb-3 overflow-hidden" data-name="Product Carousel">
<div class="o_carousel_product_outer carousel-outer position-relative d-flex align-items-center w-100 overflow-hidden">
<span class="o_ribbon o_ribbon_right z-1" style=""></span>
<div class="carousel-inner h-100">
<div class="carousel-item h-100 text-center active">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_08" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
<div class="carousel-item h-100 text-center">
<div class="d-flex align-items-center justify-content-center h-100 oe_unmovable"><img src="/web/image/website.library_image_01" class="img img-fluid oe_unmovable product_detail_img w-100 mh-100" loading="lazy" style=""></div>
</div>
</div>
<a class="carousel-control-prev" href="#o-carousel-product" role="button" data-bs-slide="prev">
<i class="oi oi-chevron-left oe_unmovable border bg-white text-900" role="img" aria-label="Previous" title="Previous"></i>
</a>
<a class="carousel-control-next" href="#o-carousel-product" role="button" data-bs-slide="next">
<i class="oi oi-chevron-right oe_unmovable border bg-white text-900" role="img" aria-label="Next" title="Next"></i>
</a>
</div>
<div class="o_carousel_product_indicators pt-2 overflow-hidden">
<ol class="carousel-indicators position-static pt-2 pt-lg-0 mx-auto my-0" style="justify-content: start;">
<li data-bs-target="#o-carousel-product" class="align-top position-relative active" data-bs-slide-to="0" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="1" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="2" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="3" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="4" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="5" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="6" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="7" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="8" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="9" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="10" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="11" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="12" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="13" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="14" aria-current="true">
<div><img src="/web/image/website.library_image_08" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
<li data-bs-target="#o-carousel-product" class="align-top position-relative" data-bs-slide-to="15" aria-current="true">
<div><img src="/web/image/website.library_image_01" class="img o_image_64_cover" loading="lazy" style=""></div>
</li>
</ol>
</div>
</div>
</div>
`);
expect(core.interactions).toHaveLength(1);
const olEl = queryOne(".carousel-indicators");
expect(olEl.style.transform).toBe("");
await click(`[data-bs-slide-to="15"]`);
await animationFrame();
expect(olEl.style.transform).toMatch(/translate3d(.*px, 0px, 0px)/);
});

View file

@ -0,0 +1,39 @@
import { describe, expect, test } from "@odoo/hoot";
import { click } from "@odoo/hoot-dom";
import { onRpc } from "@web/../tests/web_test_helpers";
import { setupInteractionWhiteList, startInteractions } from "@web/../tests/public/helpers";
setupInteractionWhiteList("website.form");
describe.current.tags("interaction_dev");
test("only checkout form submits via main button", async () => {
onRpc("/website/form/shop.sale.order", async (a) => {
expect.step("checkoutForm");
});
onRpc("/website/form/mail.mail", async (a) => {
expect.step("customForm");
});
await startInteractions(`
<div id="wrapwrap">
<section class="s_website_form">
<form action="/website/form/" data-model_name="mail.mail" data-force_action="shop.sale.order" data-success-mode="nothing">
<input type="hidden" name="email_to" value="test@test.com"/>
<input type="hidden" name="name" value="checkout"/>
<span id="s_website_form_result"></span>
</form>
</section>
<section class="s_website_form">
<form action="/website/form/" data-model_name="mail.mail" data-success-mode="nothing">
<input type="hidden" name="email_to" value="test@test.com"/>
<input type="hidden" name="name" value="custom"/>
<span id="s_website_form_result"></span>
</form>
</section>
<button name="website_sale_main_button">Checkout</button>
</div>
`);
await click('[name="website_sale_main_button"]');
expect.verifySteps(["checkoutForm"]);
});

View file

@ -0,0 +1,44 @@
import { describe, expect, test } from "@odoo/hoot";
import { animationFrame, click, tick } from "@odoo/hoot-dom";
import { defineStyle } from "@web/../tests/web_test_helpers";
import { setupInteractionWhiteList, startInteractions } from "@web/../tests/public/helpers";
setupInteractionWhiteList("website.popup");
describe.current.tags("interaction_dev");
test("click on primary button which is add to cart button doesn't close popup", async () => {
defineStyle(/* css */`* { transition: none !important; }`);
const { core } = await startInteractions(`
<div class="s_popup o_snippet_invisible" data-vcss="001" data-snippet="s_popup"
data-name="Popup" id="sPopup" data-invisible="1">
<div class="modal fade s_popup_middle modal_shown"
style="background-color: var(--black-50) !important; display: none;"
data-show-after="0"
data-display="afterDelay"
data-consents-duration="7"
data-bs-focus="false"
data-bs-backdrop="false"
tabindex="-1"
aria-label="Popup"
aria-modal="true"
role="dialog">
<div class="modal-dialog d-flex">
<div class="modal-content oe_structure">
<div class="s_popup_close js_close_popup o_we_no_overlay o_not_editable" aria-label="Close">×</div>
<section>
<a href="#" class="btn btn-primary js_add_cart">Primary button</a>
</section>
</div>
</div>
</div>
</div >
`);
expect(core.interactions).toHaveLength(1);
const modal = "#sPopup .modal";
await tick();
await animationFrame();
expect(modal).toBeVisible();
await tick();
await click(".btn-primary");
expect(modal).toBeVisible();
});

View file

@ -0,0 +1,123 @@
import { beforeEach, describe, expect, test } from "@odoo/hoot";
import { animationFrame, click, queryAll, queryOne } from "@odoo/hoot-dom";
import { advanceTime } from "@odoo/hoot-mock";
import { setupInteractionWhiteList, startInteractions } from "@web/../tests/public/helpers";
import { onRpc } from "@web/../tests/web_test_helpers";
import { registry } from "@web/core/registry";
import { Interaction } from "@web/public/interaction";
class TestItem extends Interaction {
static selector = ".s_test_item";
dynamicContent = {
_root: {
"t-att-data-started": (el) => `*${el.dataset.testParam}*`,
},
};
}
setupInteractionWhiteList([
"website_sale.dynamic_snippet_products",
"website_sale.test_dynamic_carousel_products_item",
]);
beforeEach(() => {
registry
.category("public.interactions")
.add("website_sale.test_dynamic_carousel_products_item", TestItem);
});
describe.current.tags("interaction_dev");
test.tags("desktop");
test("dynamic snippet products loads items and displays them through template", async () => {
document.documentElement.dataset.mainObject = "product.public.category(2,)";
onRpc("/website/snippet/filters", async (args) => {
const json = JSON.parse(new TextDecoder().decode(await args.arrayBuffer()));
expect(json.params.filter_id).toBe(3);
expect(json.params.template_key).toBe(
"website_sale.dynamic_filter_template_product_product_products_item"
);
expect(json.params.limit).toBe(16);
expect(json.params.search_domain).toEqual([["public_categ_ids", "child_of", 2]]);
return [
`
<div class="s_test_item" data-test-param="test">
Some test record
</div>
`,
`
<div class="s_test_item" data-test-param="test2">
Another test record
</div>
`,
`
<div class="s_test_item" data-test-param="test3">
Yet another test record
</div>
`,
`
<div class="s_test_item" data-test-param="test4">
Last test record of first page
</div>
`,
`
<div class="s_test_item" data-test-param="test5">
Test record in second page
</div>
`,
];
});
const { core } = await startInteractions(`
<div id="wrapwrap">
<section data-snippet="s_dynamic_snippet_products" class="s_dynamic_snippet_products s_dynamic pt32 pb32 o_colored_level s_product_product_borderless_1"
data-name="Products"
data-filter-id="3"
data-product-category-id="current"
data-show-variants="true"
data-custom-template-data="{}"
data-number-of-records="16"
data-template-key="website_sale.dynamic_filter_template_product_product_products_item"
data-carousel-interval="5000"
>
<div class="container">
<div class="row s_nb_column_fixed">
<section class="s_dynamic_snippet_content oe_unremovable oe_unmovable o_not_editable col o_colored_level">
<div class="css_non_editable_mode_hidden">
<div class="missing_option_warning alert alert-info fade show d-none d-print-none rounded-0">
Your Dynamic Snippet will be displayed here... This message is displayed because you did not provide both a filter and a template to use.
<br/>
</div>
</div>
<div class="dynamic_snippet_template"></div>
</section>
</div>
</div>
</section>
</div>
`);
expect(core.interactions).toHaveLength(6);
// Neutralize carousel automatic sliding.
queryOne(".dynamic_snippet_template .carousel").dataset.bsRide = "false";
const itemEls = queryAll(".s_test_item");
expect(itemEls[0]).toHaveAttribute("data-test-param", "test");
expect(itemEls[1]).toHaveAttribute("data-test-param", "test2");
expect(itemEls[2]).toHaveAttribute("data-test-param", "test3");
expect(itemEls[3]).toHaveAttribute("data-test-param", "test4");
expect(itemEls[4]).toHaveAttribute("data-test-param", "test5");
expect(itemEls[3].closest(".carousel-item")).toHaveClass("active");
expect(itemEls[4].closest(".carousel-item")).not.toHaveClass("active");
await animationFrame();
await click(".carousel-control-next .oi");
await animationFrame();
await advanceTime(1000); // Slide duration.
expect(itemEls[3].closest(".carousel-item")).not.toHaveClass("active");
expect(itemEls[4].closest(".carousel-item")).toHaveClass("active");
// Make sure element interactions are started.
expect(itemEls[0]).toHaveAttribute("data-started", "*test*");
expect(itemEls[1]).toHaveAttribute("data-started", "*test2*");
expect(itemEls[2]).toHaveAttribute("data-started", "*test3*");
expect(itemEls[3]).toHaveAttribute("data-started", "*test4*");
expect(itemEls[4]).toHaveAttribute("data-started", "*test5*");
core.stopInteractions();
// Make sure element interactions are stopped.
expect(core.interactions).toHaveLength(0);
});