/** @odoo-module */ import { after, describe, expect, getFixture, test } from "@odoo/hoot"; import { clear, click, dblclick, drag, edit, fill, hover, keyDown, keyUp, leave, middleClick, on, pointerDown, pointerUp, press, queryOne, resize, rightClick, scroll, select, setInputFiles, setInputRange, uncheck, } from "@odoo/hoot-dom"; import { advanceTime, animationFrame, mockFetch, mockTouch, mockUserAgent } from "@odoo/hoot-mock"; import { Component, xml } from "@odoo/owl"; import { EventList } from "@web/../lib/hoot-dom/helpers/events"; import { mountForTest, parseUrl, waitForIframes } from "../local_helpers"; /** * @param {Event} ev */ const formatEvent = (ev) => { const { currentTarget, type } = ev; const id = currentTarget.id ? `#${currentTarget.id}` : currentTarget.tagName.toLowerCase(); let formatted = ""; // Mouse if (ev.button >= 0) { formatted += `:${ev.button}`; } if (ev.buttons) { formatted += `(${ev.buttons})`; } // Keyboard if (ev.key) { formatted += `:${ev.key}`; } if (ev.altKey) { formatted += `.alt`; } if (ev.ctrlKey) { formatted += `.ctrl`; } if (ev.metaKey) { formatted += `.meta`; } if (ev.shiftKey) { formatted += `.shift`; } // Input if (ev.data) { formatted += `:${ev.data}`; } return `${type}${formatted}@${id}`; }; /** * @param {import("../../helpers/dom").Target} target * @param {(ev: Event) => string} [formatStep] */ const monitorEvents = (target, formatStep) => { const handleEvent = (element, type) => after( on(element, type, (ev) => { const formattedStep = formatStep(ev); if (formattedStep) { expect.step(formattedStep); } }) ); formatStep ||= formatEvent; for (const element of document.querySelectorAll(target)) { for (const prop in element) { const type = prop.match(/^on(\w+)/)?.[1]; if (!type || BLACK_LISTED_EVENT_TYPES.includes(type)) { continue; } handleEvent(element, type); } for (const type of ADDITIONAL_EVENT_TYPES) { handleEvent(element, type); } } }; const ADDITIONAL_EVENT_TYPES = ["focusin", "focusout"]; const BLACK_LISTED_EVENT_TYPES = ["selectionchange"]; describe(parseUrl(import.meta.url), () => { test("clear", async () => { await mountForTest(/* xml */ ``); expect("input").toHaveValue("Test"); expect.verifySteps([]); await click("input"); monitorEvents("input"); await clear({ delay: 0 }); expect("input").not.toHaveValue(); expect.verifySteps([ "keydown:a.ctrl@input", "select@input", "keyup:a.ctrl@input", "keydown:Backspace@input", "beforeinput@input", "input@input", "keyup:Backspace@input", ]); }); test("clear: email", async () => { await mountForTest(/* xml */ ``); expect("input").toHaveValue("john@doe.com"); await click("input"); await clear(); expect("input").toHaveValue(""); }); test("clear: number", async () => { await mountForTest(/* xml */ ``); expect("input").toHaveValue(421); await click("input"); await clear(); expect("input").not.toHaveValue(); }); test("clear: files", async () => { await mountForTest(/* xml */ ``); const file = new File([""], "file.txt"); expect("input").not.toHaveValue(); await click("input"); await fill(file); expect("input").toHaveValue([file]); await clear(); expect("input").not.toHaveValue(); }); test("click", async () => { mockTouch(false); await mountForTest(/* xml */ ``); monitorEvents("button"); const events = await click("button"); const clickEvent = events.get("click"); expect(clickEvent.pointerId).toBeGreaterThan(0); expect(clickEvent.pointerType).toBe("mouse"); expect.verifySteps([ // Hover "pointerover:0@button", "mouseover:0@button", "pointerenter:0@button", "mouseenter:0@button", "pointermove:0@button", "mousemove:0@button", // Click "pointerdown:0(1)@button", "mousedown:0(1)@button", "focus@button", "focusin@button", "pointerup:0@button", "mouseup:0@button", "click:0@button", ]); }); test("dblclick", async () => { await mountForTest(/* xml */ ``); monitorEvents("button"); await dblclick("button"); expect.verifySteps([ // Hover "pointerover:0@button", "mouseover:0@button", "pointerenter:0@button", "mouseenter:0@button", "pointermove:0@button", "mousemove:0@button", // Click 1 "pointerdown:0(1)@button", "mousedown:0(1)@button", "focus@button", "focusin@button", "pointerup:0@button", "mouseup:0@button", "click:0@button", // Click 2 "pointerdown:0(1)@button", "mousedown:0(1)@button", "pointerup:0@button", "mouseup:0@button", "click:0@button", // Double click event "dblclick:0@button", ]); }); test("triple click", async () => { await mountForTest(/* xml */ ``); const allEvents = new EventList( // trigger 3 clicks await click("button"), await click("button"), await click("button") ); const clickEvents = allEvents.getAll("click"); const mouseDownEvents = allEvents.getAll("mousedown"); const mouseUpEvents = allEvents.getAll("mouseup"); const pointerDownEvents = allEvents.getAll("pointerdown"); const pointerUpEvents = allEvents.getAll("pointerup"); expect(pointerDownEvents).toHaveLength(3); expect(pointerDownEvents[0].detail).toBe(0); expect(pointerDownEvents[1].detail).toBe(0); expect(pointerDownEvents[2].detail).toBe(0); expect(mouseDownEvents).toHaveLength(3); expect(mouseDownEvents[0].detail).toBe(1); expect(mouseDownEvents[1].detail).toBe(2); expect(mouseDownEvents[2].detail).toBe(3); expect(pointerUpEvents).toHaveLength(3); expect(pointerUpEvents[0].detail).toBe(0); expect(pointerUpEvents[1].detail).toBe(0); expect(pointerUpEvents[2].detail).toBe(0); expect(mouseUpEvents).toHaveLength(3); expect(mouseUpEvents[0].detail).toBe(1); expect(mouseUpEvents[1].detail).toBe(2); expect(mouseUpEvents[2].detail).toBe(3); expect(clickEvents).toHaveLength(3); expect(clickEvents[0].detail).toBe(1); expect(clickEvents[1].detail).toBe(2); expect(clickEvents[2].detail).toBe(3); expect(allEvents.getAll("dblclick")).toHaveLength(1); await advanceTime(1_000); const events = await click("button"); expect(events.get("click").detail).toBe(1); }); test("auxclick", async () => { mockTouch(false); await mountForTest(/* xml */ ``); await hover("button"); monitorEvents("button"); await middleClick("button"); expect.verifySteps([ "pointerdown:1(4)@button", "mousedown:1(4)@button", "focus@button", "focusin@button", "pointerup:1@button", "mouseup:1@button", "auxclick:1@button", ]); await rightClick("button"); expect.verifySteps([ "pointerdown:2(2)@button", "mousedown:2(2)@button", "contextmenu:2(2)@button", "pointerup:2@button", "mouseup:2@button", "auxclick:2@button", ]); }); test("click on disabled element", async () => { await mountForTest(/* xml */ ``); monitorEvents("button"); await click("button"); expect.verifySteps([ // Hover "pointerover:0@button", "mouseover:0@button", "pointerenter:0@button", "mouseenter:0@button", "pointermove:0@button", "mousemove:0@button", // Click (mouse events disabled) "pointerdown:0(1)@button", "pointerup:0@button", ]); }); test("click on common parent", async () => { await mountForTest(/* xml */ `
`); monitorEvents(".parent"); monitorEvents(".first"); monitorEvents(".second"); await pointerDown(".first"); await pointerUp(".second"); expect.verifySteps([ // Move to first "pointerover:0@button", "pointerover:0@main", "mouseover:0@button", "mouseover:0@main", "pointerenter:0@main", "pointerenter:0@button", "mouseenter:0@main", "mouseenter:0@button", "pointermove:0@button", "pointermove:0@main", "mousemove:0@button", "mousemove:0@main", // Pointer down on first "pointerdown:0(1)@button", "pointerdown:0(1)@main", "mousedown:0(1)@button", "mousedown:0(1)@main", "focus@button", "focusin@button", "focusin@main", // Move to second "pointermove:0(1)@button", "pointermove:0(1)@main", "mousemove:0(1)@button", "mousemove:0(1)@main", "pointerout:0(1)@button", "pointerout:0(1)@main", "mouseout:0(1)@button", "mouseout:0(1)@main", "pointerleave:0(1)@button", "mouseleave:0(1)@button", "pointerover:0(1)@input", "pointerover:0(1)@main", "mouseover:0(1)@input", "mouseover:0(1)@main", "pointerenter:0(1)@input", "mouseenter:0(1)@input", "pointermove:0(1)@input", "pointermove:0(1)@main", "mousemove:0(1)@input", "mousemove:0(1)@main", // Pointer up on second "pointerup:0@input", "pointerup:0@main", "mouseup:0@input", "mouseup:0@main", "click:0@main", ]); }); test("click can be dispatched with pointer events prevented", async () => { await mountForTest(/* xml */ ``); const prevent = (ev) => ev.preventDefault(); on("button", "pointerdown", prevent); on("button", "mousedown", prevent); on("button", "pointerup", prevent); on("button", "mouseup", prevent); await hover("button"); monitorEvents("button"); await click("button"); expect.verifySteps(["pointerdown:0(1)@button", "pointerup:0@button", "click:0@button"]); }); test("click: iframe", async () => { await mountForTest(/* xml */ `