diff --git a/addons/web/static/src/core/utils/patch.js b/addons/web/static/src/core/utils/patch.js index 0c4431960c5..e2ce1fa8867 100644 --- a/addons/web/static/src/core/utils/patch.js +++ b/addons/web/static/src/core/utils/patch.js @@ -77,10 +77,15 @@ export function patch(obj, patchName, patchValue, options = {}) { } else { newDesc[key] = { [patchedFnName](...args) { - const prevSuper = this._super; - this._super = _superFn.bind(this); + let prevSuper; + if (this) { + prevSuper = this._super; + this._super = _superFn.bind(this); + } const result = patchFn.call(this, ...args); - this._super = prevSuper; + if (this) { + this._super = prevSuper; + } return result; }, }[patchedFnName]; diff --git a/addons/web/static/tests/core/utils/patch_tests.js b/addons/web/static/tests/core/utils/patch_tests.js index 345d78c9fd6..f8c29f3107e 100644 --- a/addons/web/static/tests/core/utils/patch_tests.js +++ b/addons/web/static/tests/core/utils/patch_tests.js @@ -1084,6 +1084,30 @@ QUnit.module("utils", () => { assert.verifySteps(["obj"]); }); + QUnit.test("can call a non bound patched method", async function (assert) { + // use case: patching a function on window (e.g. setTimeout) + assert.expect(3); + + const obj = { + fn() { + assert.step("original"); + }, + }; + + const originalFn = obj.fn; + patch(obj, "patch", { + fn() { + assert.step("patched"); + originalFn(); + }, + }); + + const fn = obj.fn; // purposely not bound + fn(); + + assert.verifySteps(["patched", "original"]); + }); + QUnit.module("patch 'pure' option"); QUnit.test("function objects are preserved with 'pure' patch", async function (assert) {