[FIX] *: tests: utility to mock RPCError

In this commit we add the makeServerError utility which allows you
to easily create a valid RPCError in the tests.

We have added this utility to prevent the use of invalid or incomplete
errors. We will give default values for all the parameters needed for
a valid RPCError.

For example, in some tests, we only check the presence of an error
dialog and not the expected one. So there was a set of tests that were
green because the failure was in the RPCError handling. So we had an
error dialog but it was not the right one. In general, the crash occurred
because the RPCError contained no data.

closes odoo/odoo#121932

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
FrancoisGe
2023-05-23 16:32:04 +02:00
parent 669d4afd60
commit ddf67f83be
14 changed files with 94 additions and 145 deletions
@@ -1,20 +1,23 @@
/** @odoo-modules */
import { browser } from "@web/core/browser/browser";
import { registry } from "@web/core/registry";
import { errorService } from "@web/core/errors/error_service";
import { dialogService } from "@web/core/dialog/dialog_service";
import { MainComponentsContainer } from "@web/core/main_components_container";
import { notificationService } from "@web/core/notifications/notification_service";
import { uiService } from "@web/core/ui/ui_service";
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
import { registerCleanup } from "@web/../tests/helpers/cleanup";
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
import { makeFakeRPCService, makeFakeLocalizationService } from "@web/../tests/helpers/mock_services";
import { RPCError } from "@web/core/network/rpc_service";
import { makeServerError } from "@web/../tests/helpers/mock_server";
import {
makeFakeLocalizationService,
makeFakeRPCService,
} from "@web/../tests/helpers/mock_services";
import { browser } from "@web/core/browser/browser";
import { dialogService } from "@web/core/dialog/dialog_service";
import { errorService } from "@web/core/errors/error_service";
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
import { MainComponentsContainer } from "@web/core/main_components_container";
import { notificationService } from "@web/core/notifications/notification_service";
import { registry } from "@web/core/registry";
import { uiService } from "@web/core/ui/ui_service";
import { getFixture, mount, nextTick, patchWithCleanup } from "@web/../tests/helpers/utils";
import { BaseAutomationErrorDialog } from "../src/js/base_automation_error_dialog";
import { patchWithCleanup,getFixture, mount, nextTick } from "@web/../tests/helpers/utils";
const { toRaw } = owl;
@@ -23,7 +26,6 @@ const serviceRegistry = registry.category("services");
let target;
QUnit.module("base_automation", {}, function () {
let unhandledRejectionCb;
QUnit.module("Error Dialog", {
async beforeEach() {
@@ -55,7 +57,6 @@ QUnit.module("base_automation", {}, function () {
QUnit.test("Error due to an automated action", async function (assert) {
assert.expect(4);
const error = new RPCError();
const errorContext = {
exception_class: "base_automation",
base_automation: {
@@ -63,14 +64,11 @@ QUnit.module("base_automation", {}, function () {
name: "Test base automation error dialog",
},
};
Object.assign(error, {
const error = makeServerError({
subType: "Odoo Client Error",
message: "Message",
data: {
debug: "Traceback",
context: errorContext,
},
exceptionName: errorContext.exception_class,
context: errorContext,
});
patchWithCleanup(BaseAutomationErrorDialog.prototype, {
@@ -99,21 +97,17 @@ QUnit.module("base_automation", {}, function () {
});
await unhandledRejectionCb(errorEvent);
await nextTick();
assert.containsOnce(target, '.modal .fa-clipboard');
assert.containsOnce(target, '.modal .o_disable_action_button');
assert.containsOnce(target, '.modal .o_edit_action_button');
assert.containsOnce(target, ".modal .fa-clipboard");
assert.containsOnce(target, ".modal .o_disable_action_button");
assert.containsOnce(target, ".modal .o_edit_action_button");
});
QUnit.test("Error not due to an automated action", async function (assert) {
assert.expect(3);
const error = new RPCError();
Object.assign(error, {
const error = makeServerError({
subType: "Odoo Client Error",
message: "Message",
data: {
debug: "Traceback",
},
});
const env = await makeTestEnv();
@@ -131,9 +125,8 @@ QUnit.module("base_automation", {}, function () {
});
await unhandledRejectionCb(errorEvent);
await nextTick();
assert.containsOnce(target, '.modal .fa-clipboard');
assert.containsNone(target, '.modal .o_disable_action_button');
assert.containsNone(target, '.modal .o_edit_action_button');
assert.containsOnce(target, ".modal .fa-clipboard");
assert.containsNone(target, ".modal .o_disable_action_button");
assert.containsNone(target, ".modal .o_edit_action_button");
});
});
@@ -7,7 +7,7 @@ import { nextTick } from "@web/../tests/helpers/utils";
import { createSpreadsheetWithChart, insertChartInSpreadsheet } from "../../utils/chart";
import { createModelWithDataSource, waitForDataSourcesLoaded } from "../../utils/model";
import * as spreadsheet from "@odoo/o-spreadsheet";
import { RPCError } from "@web/core/network/rpc_service";
import { makeServerError } from "@web/../tests/helpers/mock_server";
const { toZone } = spreadsheet.helpers;
@@ -411,9 +411,7 @@ QUnit.module("spreadsheet > odoo chart plugin", {}, () => {
args.method === "web_read_group" &&
!hasAccessRights
) {
const error = new RPCError();
error.data = { message: "ya done!" };
throw error;
throw makeServerError({ description: "ya done!" });
}
},
});
@@ -3,7 +3,7 @@
import { nextTick } from "@web/../tests/helpers/utils";
import { LoadableDataSource } from "@spreadsheet/data_sources/data_source";
import { Deferred } from "@web/core/utils/concurrency";
import { RPCError } from "@web/core/network/rpc_service";
import { makeServerError } from "@web/../tests/helpers/mock_server";
QUnit.module("spreadsheet data source", {}, () => {
QUnit.test(
@@ -60,9 +60,7 @@ QUnit.module("spreadsheet data source", {}, () => {
notify: () => {},
orm: {
call: () => {
const error = new RPCError();
error.data = { message: "Ya done!" };
throw error;
throw makeServerError({ description: "Ya done!" });
},
},
});
@@ -2,6 +2,7 @@
import { session } from "@web/session";
import { nextTick, patchWithCleanup } from "@web/../tests/helpers/utils";
import { makeServerError } from "@web/../tests/helpers/mock_server";
import CommandResult from "@spreadsheet/o_spreadsheet/cancelled_reason";
import { createModelWithDataSource, waitForDataSourcesLoaded } from "../utils/model";
@@ -17,7 +18,6 @@ import {
} from "../utils/getters";
import { createSpreadsheetWithList } from "../utils/list";
import { registry } from "@web/core/registry";
import { RPCError } from "@web/core/network/rpc_service";
import { getBasicServerData } from "../utils/data";
QUnit.module("spreadsheet > list plugin", {}, () => {
@@ -571,9 +571,7 @@ QUnit.module("spreadsheet > list plugin", {}, () => {
args.method === "search_read" &&
!hasAccessRights
) {
const error = new RPCError();
error.data = { message: "ya done!" };
throw error;
throw makeServerError({ description: "ya done!" });
}
},
});
@@ -18,7 +18,7 @@ import {
} from "@spreadsheet/../tests/utils/model";
import { makeDeferred, nextTick, patchWithCleanup } from "@web/../tests/helpers/utils";
import { session } from "@web/session";
import { RPCError } from "@web/core/network/rpc_service";
import { makeServerError } from "@web/../tests/helpers/mock_server";
QUnit.module("spreadsheet > pivot plugin", {}, () => {
QUnit.test("can select a Pivot from cell formula", async function (assert) {
@@ -763,9 +763,7 @@ QUnit.module("spreadsheet > pivot plugin", {}, () => {
args.method === "read_group" &&
!hasAccessRights
) {
const error = new RPCError();
error.data = { message: "ya done!" };
throw error;
throw makeServerError({ description: "ya done!" });
}
},
});
@@ -2,7 +2,8 @@
import { click, editInput, getFixture, nextTick, triggerHotkey } from "@web/../tests/helpers/utils";
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
import { RPCError } from "@web/core/network/rpc_service";
import { makeServerError } from "@web/../tests/helpers/mock_server";
QUnit.module("QuestionPageOneToManyField", (hooks) => {
let serverData;
@@ -188,14 +189,11 @@ QUnit.module("QuestionPageOneToManyField", (hooks) => {
mockRPC(route, args) {
if (args.method === "write" && args.model === "survey") {
assert.step("save parent form");
const surveyValidationError = new RPCError();
Object.assign(surveyValidationError, {
exceptionName: "odoo.exceptions.ValidationError",
data: {
message: "This isn't right!",
},
throw makeServerError({
description: "This isn't right!",
type: "ValidationError",
});
return Promise.reject(surveyValidationError);
}
},
});
@@ -278,7 +278,7 @@ export class Many2XAutocomplete extends Component {
const context = this.getCreationContext(request);
return this.openMany2X({ context });
}
throw e;
throw e.message ? e.message : e;
}
},
});
@@ -16,6 +16,7 @@ import { intersection } from "@web/core/utils/arrays";
import { deepCopy, pick } from "@web/core/utils/objects";
import { makeFakeRPCService, makeMockFetch } from "./mock_services";
import { patchWithCleanup } from "./utils";
import { makeErrorFromResponse } from "@web/core/network/rpc_service";
const serviceRegistry = registry.category("services");
@@ -104,6 +105,21 @@ function makeLogger(prefix, title) {
return { request, response };
}
export function makeServerError({ code, context, description, message, subType, type } = {}) {
return makeErrorFromResponse({
code: code || 200,
message: message || "Odoo Server Error",
data: {
name: `odoo.exceptions.${type || "UserError"}`,
debug: "traceback",
arguments: [],
context: context || {},
subType,
message: description,
},
});
}
/**
* @param {Element} tree
* @param {(node: Element) => any} cb
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { registerCleanup } from "@web/../tests/helpers/cleanup";
import { makeServerError } from "@web/../tests/helpers/mock_server";
import { makeMockXHR } from "@web/../tests/helpers/mock_services";
import {
click,
@@ -12,7 +13,6 @@ import {
} from "@web/../tests/helpers/utils";
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
import { browser } from "@web/core/browser/browser";
import { RPCError } from "@web/core/network/rpc_service";
const BINARY_FILE =
"R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7";
@@ -482,9 +482,7 @@ QUnit.module("Fields", (hooks) => {
serverData.models.partner.onchanges = {
document: function (obj) {
if (obj.document) {
const error = new RPCError();
error.exceptionName = "odoo.exceptions.ValidationError";
throw error;
throw makeServerError({ type: "ValidationError" });
}
},
};
@@ -1,8 +1,6 @@
/** @odoo-module **/
import { AutoComplete } from "@web/core/autocomplete/autocomplete";
import { browser } from "@web/core/browser/browser";
import { many2ManyTagsFieldColorEditable } from "@web/views/fields/many2many_tags/many2many_tags_field";
import { makeServerError } from "@web/../tests/helpers/mock_server";
import {
click,
clickDiscard,
@@ -19,8 +17,10 @@ import {
triggerEvent,
triggerHotkey,
} from "@web/../tests/helpers/utils";
import { RPCError } from "@web/core/network/rpc_service";
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
import { AutoComplete } from "@web/core/autocomplete/autocomplete";
import { browser } from "@web/core/browser/browser";
import { many2ManyTagsFieldColorEditable } from "@web/views/fields/many2many_tags/many2many_tags_field";
let serverData;
let target;
@@ -1483,9 +1483,7 @@ QUnit.module("Fields", (hooks) => {
arch: '<form><field name="timmy" widget="many2many_tags"/></form>',
mockRPC(route, args) {
if (args.method === "name_create") {
const error = new RPCError("Something went wrong");
error.exceptionName = "odoo.exceptions.ValidationError";
throw error;
throw makeServerError({ type: "ValidationError" });
}
if (args.method === "create") {
assert.deepEqual(args.args[0], {
@@ -2,6 +2,7 @@
import { registerCleanup } from "@web/../tests/helpers/cleanup";
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
import { makeServerError } from "@web/../tests/helpers/mock_server";
import {
addRow,
click,
@@ -26,15 +27,14 @@ import {
import {
editSearch,
getFacetTexts,
toggleSearchBarMenu,
toggleMenuItem,
toggleSearchBarMenu,
validateSearch,
} from "@web/../tests/search/helpers";
import { makeView, makeViewInDialog, setupViewRegistries } from "@web/../tests/views/helpers";
import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
import { browser } from "@web/core/browser/browser";
import { errorService } from "@web/core/errors/error_service";
import { RPCError } from "@web/core/network/rpc_service";
import { registry } from "@web/core/registry";
import { session } from "@web/session";
import { Field } from "@web/views/fields/field";
@@ -3024,9 +3024,7 @@ QUnit.module("Fields", (hooks) => {
arch: '<form><field name="product_id" /></form>',
mockRPC(route, { args, method }) {
if (method === "name_create") {
const error = new RPCError("Something went wrong");
error.exceptionName = "odoo.exceptions.ValidationError";
throw error;
throw makeServerError({ type: "ValidationError" });
}
if (method === "create") {
assert.deepEqual(args[0], { name: "xyz" });
@@ -3067,7 +3065,7 @@ QUnit.module("Fields", (hooks) => {
arch: '<form><field name="product_id" /></form>',
mockRPC(route, { args, method }) {
if (method === "name_create") {
return new RPCError("Something went wrong");
throw makeServerError();
}
},
});
@@ -3099,9 +3097,7 @@ QUnit.module("Fields", (hooks) => {
arch: '<form><field name="p" /></form>',
mockRPC(route, { args, method }) {
if (method === "name_create") {
const error = new RPCError("Something went wrong");
error.exceptionName = "odoo.exceptions.ValidationError";
throw error;
throw makeServerError({ type: "ValidationError" });
}
if (method === "create") {
assert.deepEqual(args[0], { name: "xyz" });
@@ -2,6 +2,7 @@
import { Component, EventBus, xml } from "@odoo/owl";
import { registerCleanup } from "@web/../tests/helpers/cleanup";
import { makeServerError } from "@web/../tests/helpers/mock_server";
import { makeFakeNotificationService } from "@web/../tests/helpers/mock_services";
import {
addRow,
@@ -32,7 +33,6 @@ import { browser } from "@web/core/browser/browser";
import { WarningDialog } from "@web/core/errors/error_dialogs";
import { errorService } from "@web/core/errors/error_service";
import { localization } from "@web/core/l10n/localization";
import { RPCError } from "@web/core/network/rpc_service";
import { registry } from "@web/core/registry";
import { scrollerService } from "@web/core/scroller_service";
import { tooltipService } from "@web/core/tooltip/tooltip_service";
@@ -11470,7 +11470,7 @@ QUnit.module("Views", (hooks) => {
mockRPC(route, { method }) {
if (method === "write") {
assert.step("write");
throw new RPCError("Something went wrong");
throw makeServerError();
}
},
});
@@ -12068,7 +12068,7 @@ QUnit.module("Views", (hooks) => {
mockRPC(route, { method }) {
if (method === "write") {
assert.step("write");
throw new RPCError("Something went wrong");
throw makeServerError();
}
},
});
@@ -12133,7 +12133,7 @@ QUnit.module("Views", (hooks) => {
mockRPC(route, { method }) {
if (method === "write") {
assert.step("write");
throw new RPCError("Something went wrong");
throw makeServerError();
}
},
});
@@ -12167,10 +12167,7 @@ QUnit.module("Views", (hooks) => {
mockRPC(route, { method }) {
if (method === "create") {
assert.step("create");
const error = new RPCError("Some business message");
error.data = { context: {} };
error.exceptionName = "odoo.exceptions.UserError";
throw error;
throw makeServerError({ message: "Some business message" });
}
},
});
@@ -13056,7 +13053,7 @@ QUnit.module("Views", (hooks) => {
async function (assert) {
class TestClientAction extends Component {
setup() {
throw new RPCError("Something went wrong");
throw new Error("Something went wrong");
}
}
TestClientAction.template = xml`<div></div>`;
@@ -13139,7 +13136,7 @@ QUnit.module("Views", (hooks) => {
async function (assert) {
class TestClientAction extends Component {
setup() {
throw new RPCError("Something went wrong");
throw new Error("Something went wrong");
}
}
TestClientAction.template = xml`<div></div>`;
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { makeServerError } from "@web/../tests/helpers/mock_server";
import { makeFakeDialogService } from "@web/../tests/helpers/mock_services";
import {
click,
@@ -19,10 +20,10 @@ import {
triggerHotkey,
} from "@web/../tests/helpers/utils";
import {
getVisibleButtons,
getFacetTexts,
getPagerLimit,
getPagerValue,
getVisibleButtons,
pagerNext,
toggleSearchBarMenu,
validateSearch,
@@ -31,22 +32,21 @@ import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
import { browser } from "@web/core/browser/browser";
import { dialogService } from "@web/core/dialog/dialog_service";
import { makeErrorFromResponse, RPCError } from "@web/core/network/rpc_service";
import { registry } from "@web/core/registry";
import { tooltipService } from "@web/core/tooltip/tooltip_service";
import { nbsp } from "@web/core/utils/strings";
import { getNextTabableElement } from "@web/core/utils/ui";
import { session } from "@web/session";
import { AnimatedNumber } from "@web/views/view_components/animated_number";
import { KanbanController } from "@web/views/kanban/kanban_controller";
import { kanbanView } from "@web/views/kanban/kanban_view";
import { DynamicRecordList } from "@web/views/relational_model";
import { ViewButton } from "@web/views/view_button/view_button";
import { AnimatedNumber } from "@web/views/view_components/animated_number";
import { Component, onWillRender, xml } from "@odoo/owl";
import { SampleServer } from "@web/views/sample_server";
import { pick } from "@web/core/utils/objects";
import { KanbanDynamicGroupList } from "@web/views/kanban/kanban_model";
import { SampleServer } from "@web/views/sample_server";
const serviceRegistry = registry.category("services");
const viewWidgetRegistry = registry.category("view_widgets");
@@ -3746,16 +3746,7 @@ QUnit.module("Views", (hooks) => {
groupBy: ["product_id"],
async mockRPC(route, args) {
if (args.method === "name_create") {
throw makeErrorFromResponse({
code: 200,
message: "Odoo Server Error",
data: {
name: "odoo.exceptions.UserError",
debug: "traceback",
arguments: ["This is a user error"],
context: {},
},
});
throw makeServerError({ message: "This is a user error" });
}
},
});
@@ -3801,16 +3792,7 @@ QUnit.module("Views", (hooks) => {
groupBy: ["product_id"],
async mockRPC(route, args) {
if (args.method === "name_create") {
throw makeErrorFromResponse({
code: 200,
message: "Odoo Server Error",
data: {
name: "odoo.exceptions.UserError",
debug: "traceback",
arguments: ["This is a user error"],
context: {},
},
});
throw makeServerError({ message: "This is a user error" });
}
},
});
@@ -3848,16 +3830,7 @@ QUnit.module("Views", (hooks) => {
</kanban>`,
async mockRPC(route, args) {
if (args.method === "name_create") {
throw makeErrorFromResponse({
code: 200,
message: "Odoo Server Error",
data: {
name: "odoo.exceptions.UserError",
debug: "traceback",
arguments: ["This is a user error"],
context: {},
},
});
throw makeServerError({ message: "This is a user error" });
}
if (args.method === "create") {
assert.deepEqual(args.args[0], { foo: "blip" });
@@ -3907,16 +3880,7 @@ QUnit.module("Views", (hooks) => {
</kanban>`,
async mockRPC(route, args) {
if (args.method === "name_create") {
throw makeErrorFromResponse({
code: 200,
message: "Odoo Server Error",
data: {
name: "odoo.exceptions.UserError",
debug: "traceback",
arguments: ["This is a user error"],
context: {},
},
});
throw makeServerError({ message: "This is a user error" });
}
if (args.method === "create") {
assert.deepEqual(args.args[0], { state: "abc" });
@@ -12719,7 +12683,7 @@ QUnit.module("Views", (hooks) => {
async mockRPC(route, { method }) {
assert.step(method || route);
if (method === "name_create") {
throw new RPCError();
throw makeServerError();
}
},
});
@@ -1,31 +1,31 @@
/** @odoo-module **/
import { makeServerError } from "@web/../tests/helpers/mock_server";
import { fakeCookieService, makeFakeUserService } from "@web/../tests/helpers/mock_services";
import * as cpHelpers from "@web/../tests/search/helpers";
import { browser } from "@web/core/browser/browser";
import { WarningDialog } from "@web/core/errors/error_dialogs";
import { registry } from "@web/core/registry";
import { session } from "@web/session";
import { editView } from "@web/views/debug_items";
import { clearUncommittedChanges } from "@web/webclient/actions/action_service";
import { listView } from "@web/views/list/list_view";
import { useSetupAction } from "@web/webclient/actions/action_hook";
import { clearUncommittedChanges } from "@web/webclient/actions/action_service";
import testUtils from "web.test_utils";
import { session } from "@web/session";
import { errorService } from "../../../src/core/errors/error_service";
import { registerCleanup } from "../../helpers/cleanup";
import {
click,
getFixture,
clickSave,
editInput,
getFixture,
getNodesTextContent,
legacyExtraNextTick,
makeDeferred,
nextTick,
patchWithCleanup,
clickSave,
} from "../../helpers/utils";
import { createWebClient, doAction, getActionManagerServerData, loadState } from "./../helpers";
import { errorService } from "../../../src/core/errors/error_service";
import { RPCError } from "@web/core/network/rpc_service";
import { registerCleanup } from "../../helpers/cleanup";
import { WarningDialog } from "@web/core/errors/error_dialogs";
import { makeFakeUserService, fakeCookieService } from "@web/../tests/helpers/mock_services";
import * as cpHelpers from "@web/../tests/search/helpers";
import { onMounted } from "@odoo/owl";
let serverData;
@@ -2181,10 +2181,7 @@ QUnit.module("ActionManager", (hooks) => {
const mockRPC = (route, args) => {
if (args.method === "onchange" && args.model === "partner") {
const error = new RPCError();
error.exceptionName = "odoo.exceptions.ValidationError";
error.code = 200;
return Promise.reject(error);
throw makeServerError({ type: "ValidationError" });
}
};