From 6418a55fe40fdbaf93964fad349d40902f4bd48d Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 18 Mar 2021 09:25:46 +0000 Subject: [PATCH] [IMP] web: patch/unpatch: allow to re-apply a patch In tests, we might want to disable a specific patch for a specific test (e.g. to remove a monkey patch done when another addon is installed, to test the behavior of the current addon). Before this commit, it was possible to unpatch at the beginning of the test, but we couldn't re-patch when the test was over. Feature required by task~2392303 --- addons/web/static/src/js/core/utils.js | 6 +++ addons/web/static/tests/core/patch_tests.js | 46 +++++++++++++++++++++ doc/reference/javascript_reference.rst | 12 ++++-- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js index 15f3f973f24..d6165d0a841 100644 --- a/addons/web/static/src/js/core/utils.js +++ b/addons/web/static/src/js/core/utils.js @@ -897,6 +897,7 @@ var utils = { * * @param {Object} obj * @param {string} patchName + * @returns {Object} the removed patch */ unpatch: function (obj, patchName) { const objDesc = patchMap.get(obj); @@ -915,11 +916,16 @@ var utils = { } // Re-apply the patches except the one to remove. + let removedPatch; for (const patchDesc of objDesc.patches) { if (patchDesc.name !== patchName) { utils.patch(obj, patchDesc.name, patchDesc.patch); + } else { + removedPatch = patchDesc.patch; } } + + return removedPatch; }, /** * @param {any} node diff --git a/addons/web/static/tests/core/patch_tests.js b/addons/web/static/tests/core/patch_tests.js index d1b8a340fe5..4668bdf217c 100644 --- a/addons/web/static/tests/core/patch_tests.js +++ b/addons/web/static/tests/core/patch_tests.js @@ -290,6 +290,52 @@ QUnit.module('core', {}, function () { }); }); + QUnit.test('unpatch and re-patch', async function (assert) { + assert.expect(13); + + const BaseClass = makeBaseClass(assert); + + patch(BaseClass.prototype, 'patch', { + setup() { + this._super(); + assert.step('patch.setup'); + }, + fn() { + this._super(); + assert.step('patch.fn'); + }, + }); + + (new BaseClass()).fn(); + + assert.verifySteps([ + 'base.setup', + 'patch.setup', + 'base.fn', + 'patch.fn', + ]); + + const p = unpatch(BaseClass.prototype, 'patch'); + + (new BaseClass()).fn(); + + assert.verifySteps([ + 'base.setup', + 'base.fn', + ]); + + patch(BaseClass.prototype, 'patch', p); + + (new BaseClass()).fn(); + + assert.verifySteps([ + 'base.setup', + 'patch.setup', + 'base.fn', + 'patch.fn', + ]); + }); + QUnit.test('patch for specialization', async function (assert) { assert.expect(1); diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst index eaa8c927dcf..c0d69668ca1 100644 --- a/doc/reference/javascript_reference.rst +++ b/doc/reference/javascript_reference.rst @@ -2811,10 +2811,14 @@ Therefore, patching a component constructor is always possible: Removing a patch ----------------- -We can also remove the patch by calling the ``unpatch`` function. This is really -only useful for tests. +In tests, it is sometimes useful to remove a patch for a specific test, for instance, +to remove a patch applied when another addon is installed, which overrides the behavior +implemented in the addon where the test is defined. The function ``unpatch`` exists for +that purpose. It returns the removed patch, such that it can be re-applied at the end +of the test. .. code-block:: javascript - unpatch(object, "patch name"); - + const p = unpatch(object, "patch name"); + // test stuff here + patch(object, "patch name", p);