[FIX] web: popover service: prevent the crash when undefined target

Steps to reproduce:
- In debug mode
- Go to a cancelled invoice in Accounting
- Click to the "Reset to draft" button in a fast timing (before the
`PopoverContainer` is mounted).

When hovering an element, a tooltip appears. If the target element
disappears before the `onMounted` of the `PopoverContainer`, then the
popover causes a crash because the target element does not exists
anymore.

This fix makes the `tooltip_service` verify that the target still exists
before adding the popover.

closes odoo/odoo#113406

X-original-commit: 589acab37a4c1b68876386b752a91c3412dc4569
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Signed-off-by: Dardenne Florent (dafl) <dafl@odoo.com>
This commit is contained in:
fdardenne
2023-02-22 20:26:04 +01:00
parent 52413adae6
commit e160d7b33d
2 changed files with 35 additions and 8 deletions
@@ -104,14 +104,17 @@ export const tooltipService = {
}
openTooltipTimeout = browser.setTimeout(() => {
closeTooltip = popover.add(
target,
Tooltip,
{ tooltip, template, info },
{ position }
);
// Prevent title from showing on a parent at the same time
target.title = "";
// verify that the element is still in the DOM
if (target.isConnected) {
closeTooltip = popover.add(
target,
Tooltip,
{ tooltip, template, info },
{ position }
);
// Prevent title from showing on a parent at the same time
target.title = "";
}
}, delay);
}
@@ -424,4 +424,28 @@ QUnit.module("Tooltip service", (hooks) => {
await triggerEvent(target, "button[data-tooltip]", "touchstart");
assert.containsNone(target, ".o_popover_container .o_popover");
});
QUnit.test("tooltip does not crash with disappearing target", async (assert) => {
class MyComponent extends Component {}
MyComponent.template = xml`<button class="mybtn" data-tooltip="hello">Action</button>`;
let simulateTimeout;
const mockSetTimeout = async (fn) => {
simulateTimeout = fn;
};
await makeParent(MyComponent, { mockSetTimeout });
assert.containsNone(target, ".o_popover_container .o_popover");
target.querySelector(".mybtn").dispatchEvent(new Event("mouseenter"));
await nextTick();
assert.containsNone(target, ".o_popover_container .o_popover");
// the element disappeared from the DOM during the setTimeout
target.querySelector(".mybtn").remove();
simulateTimeout();
await nextTick();
// tooltip did not crash and is not shown
assert.containsNone(target, ".o_popover_container .o_popover");
});
});