From 1995f21d424692e467f88261ee464e3e35ae8f3f Mon Sep 17 00:00:00 2001 From: Mathieu Duckerts-Antoine Date: Thu, 22 Jun 2023 11:32:48 +0200 Subject: [PATCH] [FIX] web: field service: protect async methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The async methods of field service were not declared in its key async. This would allow destroyed components to process the results of those methods (despite an initial call to useService). We fix that. closes odoo/odoo#135950 X-original-commit: 7955def19c7d4a8c49be472413423bbf3beec2e5 Signed-off-by: Géry Debongnie --- addons/web/static/src/core/field_service.js | 1 + .../static/tests/core/field_service_tests.js | 65 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/addons/web/static/src/core/field_service.js b/addons/web/static/src/core/field_service.js index 99555dd17a2..87bdd35be26 100644 --- a/addons/web/static/src/core/field_service.js +++ b/addons/web/static/src/core/field_service.js @@ -12,6 +12,7 @@ import { registry } from "@web/core/registry"; export const fieldService = { dependencies: ["orm"], + async: ["loadFields", "loadPath", "loadPropertyDefinitions"], start(env, { orm }) { const cache = new Cache( (resModel, options) => { diff --git a/addons/web/static/tests/core/field_service_tests.js b/addons/web/static/tests/core/field_service_tests.js index c18fe3249ce..a9deba1c053 100644 --- a/addons/web/static/tests/core/field_service_tests.js +++ b/addons/web/static/tests/core/field_service_tests.js @@ -3,6 +3,9 @@ import { fieldService } from "@web/core/field_service"; import { makeTestEnv } from "@web/../tests/helpers/mock_env"; import { registry } from "@web/core/registry"; +import { Component, useState, xml } from "@odoo/owl"; +import { useService } from "@web/core/utils/hooks"; +import { getFixture, makeDeferred, mount, nextTick } from "@web/../tests/helpers/utils"; const serviceRegistry = registry.category("services"); @@ -270,3 +273,65 @@ QUnit.test("does not store loadFields calls in cache when failed", async (assert assert.verifySteps(["fields_get", "fields_get"]); }); + +QUnit.test("async method loadFields is protected", async (assert) => { + assert.expect(7); + + let callFieldService; + class Child extends Component { + static template = xml` +
+ `; + setup() { + this.fieldService = useService("field"); + callFieldService = async () => { + assert.step("loadFields called"); + await this.fieldService.loadFields("tortoise"); + assert.step("loadFields result get"); + }; + } + } + + class Parent extends Component { + static components = { Child }; + static template = xml` + + + + `; + setup() { + this.state = useState({ displayChild: true }); + } + } + + const target = getFixture(); + const def = makeDeferred(); + const env = await makeTestEnv({ + serverData, + async mockRPC() { + await def; + }, + }); + const parent = await mount(Parent, target, { env }); + + assert.containsOnce(target, ".o_child_component"); + + callFieldService(); + assert.verifySteps(["loadFields called"]); + + parent.state.displayChild = false; + await nextTick(); + + def.resolve(); + await nextTick(); + + assert.verifySteps([]); + + try { + await callFieldService(); + } catch (e) { + assert.step(e.message); + } + + assert.verifySteps(["loadFields called", "Component is destroyed"]); +});