mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
test utils refactoring
This commit is contained in:
@@ -232,6 +232,9 @@ export class QWeb {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(xmlstr, "text/xml");
|
||||
const templates = doc.getElementsByTagName("templates")[0];
|
||||
if (!templates) {
|
||||
return;
|
||||
}
|
||||
for (let elem of <any>templates.children) {
|
||||
const name = elem.getAttribute("t-name");
|
||||
this._processTemplate(elem);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// Registry code
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* The registry is basically a simple hashmap. It is only a little safer and
|
||||
* more structured than a simple object.
|
||||
*/
|
||||
export class Registry<T> {
|
||||
private map: { [key: string]: T } = {};
|
||||
|
||||
/**
|
||||
* Add an element to the registry. Note that the add method returns the
|
||||
* registry, to it can be chained.
|
||||
*/
|
||||
add(key: string, item: T): Registry<T> {
|
||||
if (key in this.map) {
|
||||
throw new Error(`Key ${key} already exists!`);
|
||||
@@ -12,7 +17,12 @@ export class Registry<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
get(key: string): T {
|
||||
/**
|
||||
* Returns the element corresponding to the key
|
||||
*
|
||||
* Nothing is done to check that the key actually exists.
|
||||
*/
|
||||
get(key: string): T | undefined {
|
||||
return this.map[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,22 +83,24 @@ export function actionManagerMixin<T extends ReturnType<typeof rpcMixin>>(
|
||||
default:
|
||||
throw new Error("unhandled action");
|
||||
}
|
||||
const action: Action = {
|
||||
id: this.generateID(),
|
||||
executor,
|
||||
activate() {
|
||||
if (self.currentAction && self.currentAction.widget) {
|
||||
self.currentAction.widget.destroy();
|
||||
if (executor) {
|
||||
const action: Action = {
|
||||
id: this.generateID(),
|
||||
executor,
|
||||
activate() {
|
||||
if (self.currentAction && self.currentAction.widget) {
|
||||
self.currentAction.widget.destroy();
|
||||
}
|
||||
self.currentAction = action;
|
||||
self.update({
|
||||
inHome: false
|
||||
});
|
||||
document.title = descr.name + " - Odoo";
|
||||
}
|
||||
self.currentAction = action;
|
||||
self.update({
|
||||
inHome: false
|
||||
});
|
||||
document.title = descr.name + " - Odoo";
|
||||
}
|
||||
};
|
||||
self.lastAction = action;
|
||||
this.trigger("update_action", action);
|
||||
};
|
||||
self.lastAction = action;
|
||||
this.trigger("update_action", action);
|
||||
}
|
||||
}
|
||||
|
||||
doActWindowAction(descr: ActWindowActionDescription) {
|
||||
@@ -111,11 +113,21 @@ export function actionManagerMixin<T extends ReturnType<typeof rpcMixin>>(
|
||||
};
|
||||
}
|
||||
|
||||
doClientAction(descr: ClientActionDescription) {
|
||||
doClientAction(
|
||||
descr: ClientActionDescription
|
||||
): Action["executor"] | undefined {
|
||||
let key = descr.tag;
|
||||
let Widget = this.actionRegistry.get(key);
|
||||
if (!Widget) {
|
||||
this.addNotification({
|
||||
title: "Invalid Client Action",
|
||||
type: "warning",
|
||||
message: `Cannot find widget '${key}' in the action registry`
|
||||
});
|
||||
return;
|
||||
}
|
||||
return async function executor(this: Action, parent: Widget<any, any>) {
|
||||
const widget = new Widget(parent, {});
|
||||
const widget = new Widget!(parent, {});
|
||||
const div = document.createElement("div");
|
||||
await widget.mount(div);
|
||||
this.widget = widget;
|
||||
|
||||
@@ -1,104 +1,11 @@
|
||||
import { readFile } from "fs";
|
||||
import { WEnv } from "../../src/ts/core/component";
|
||||
import { Callback } from "../../src/ts/core/event_bus";
|
||||
import { QWeb } from "../../src/ts/core/qweb_vdom";
|
||||
import { idGenerator } from "../../src/ts/core/utils";
|
||||
import { getMenuInfo } from "../../src/ts/loaders";
|
||||
import { menuInfo, actions } from "./test_data";
|
||||
import { actionRegistry } from "../../src/ts/registries";
|
||||
import { IRouter, Query, RouterEvent } from "../../src/ts/services/router";
|
||||
import { MenuInfo, Services, Store } from "../../src/ts/store/store";
|
||||
|
||||
export function makeTestFixture() {
|
||||
let fixture = document.createElement("div");
|
||||
document.body.appendChild(fixture);
|
||||
return fixture;
|
||||
}
|
||||
|
||||
export function makeTestWEnv(): WEnv {
|
||||
return {
|
||||
qweb: new QWeb(),
|
||||
getID: idGenerator()
|
||||
};
|
||||
}
|
||||
|
||||
export function makeTestStore(services: Partial<Services> = {}): Store {
|
||||
const fullservices: Services = Object.assign(
|
||||
{
|
||||
rpc: mockFetch,
|
||||
router: new MockRouter()
|
||||
},
|
||||
services
|
||||
);
|
||||
const menuInfo = makeDemoMenuInfo();
|
||||
const store = new Store(fullservices, menuInfo, actionRegistry);
|
||||
return store;
|
||||
}
|
||||
|
||||
export function mockFetch(route: string, params: any): Promise<any> {
|
||||
if (route === "web/action/load") {
|
||||
const action = actions.find(a => a.id === params.action_id);
|
||||
return Promise.resolve(action);
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
export class MockRouter implements IRouter {
|
||||
currentQuery: Query;
|
||||
|
||||
constructor(query: Query = {}) {
|
||||
this.currentQuery = query;
|
||||
}
|
||||
|
||||
navigate(query: Query) {
|
||||
this.currentQuery = query;
|
||||
}
|
||||
on(event: RouterEvent, owner: any, callback: Callback) {}
|
||||
getQuery(): Query {
|
||||
return this.currentQuery;
|
||||
}
|
||||
|
||||
formatURL(path: string, query: Query): string {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function normalize(str: string): string {
|
||||
return str.replace(/\s+/g, "");
|
||||
}
|
||||
|
||||
export async function loadTemplates(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
readFile("web/static/src/xml/templates.xml", "utf-8", (err, result) => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function makeDemoMenuInfo(): MenuInfo {
|
||||
return getMenuInfo(menuInfo);
|
||||
}
|
||||
|
||||
export function nextMicroTick(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function nextTick(): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve));
|
||||
}
|
||||
|
||||
interface Deferred extends Promise<any> {
|
||||
resolve(val?: any): void;
|
||||
reject(): void;
|
||||
}
|
||||
|
||||
export function makeDeferred(): Deferred {
|
||||
let resolve, reject;
|
||||
let def = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve;
|
||||
reject = _reject;
|
||||
});
|
||||
(<Deferred>def).resolve = resolve;
|
||||
(<Deferred>def).reject = reject;
|
||||
return <Deferred>def;
|
||||
}
|
||||
export { MockRouter } from "./mock_router";
|
||||
export { MockServer } from "./mock_server";
|
||||
export { TestData, makeTestData, makeMenuInfo } from "./test_data";
|
||||
export { TestEnv, TestInfo, makeTestWEnv, makeTestEnv } from "./test_env";
|
||||
export {
|
||||
nextMicroTick,
|
||||
nextTick,
|
||||
makeTestFixture,
|
||||
normalize,
|
||||
makeDeferred
|
||||
} from "./test_utils";
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Callback } from "../../src/ts/core/event_bus";
|
||||
import { IRouter, Query, RouterEvent } from "../../src/ts/services/router";
|
||||
|
||||
export class MockRouter implements IRouter {
|
||||
currentQuery: Query;
|
||||
|
||||
constructor(query: Query = {}) {
|
||||
this.currentQuery = query;
|
||||
}
|
||||
|
||||
navigate(query: Query) {
|
||||
this.currentQuery = query;
|
||||
}
|
||||
on(event: RouterEvent, owner: any, callback: Callback) {}
|
||||
getQuery(): Query {
|
||||
return this.currentQuery;
|
||||
}
|
||||
|
||||
formatURL(path: string, query: Query): string {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TestData } from "./test_data";
|
||||
|
||||
export class MockServer {
|
||||
data: TestData;
|
||||
|
||||
constructor(data: TestData) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
rpc(route: string, params: any): Promise<any> {
|
||||
if (route === "web/action/load") {
|
||||
const action = this.data.actions.find(a => a.id === params.action_id);
|
||||
return Promise.resolve(action);
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
@@ -1,179 +1,220 @@
|
||||
import { BaseMenuItem } from "../../src/ts/loaders";
|
||||
import { readFile } from "fs";
|
||||
import { BaseMenuItem, getMenuInfo } from "../../src/ts/loaders";
|
||||
import { actionRegistry } from "../../src/ts/registries";
|
||||
import { ActionDescription } from "../../src/ts/store/action_manager_mixin";
|
||||
import { MenuInfo } from "../../src/ts/store/store";
|
||||
import { Registry } from "../../src/ts/core/registry";
|
||||
|
||||
export const menuInfo: BaseMenuItem[] = [
|
||||
{
|
||||
id: 96,
|
||||
name: "Discuss",
|
||||
parent_id: false,
|
||||
action: "ir.actions.client,131",
|
||||
icon: "fa fa-comment",
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 205,
|
||||
name: "Notes",
|
||||
parent_id: false,
|
||||
action: "ir.actions.act_window,250",
|
||||
icon: "fa fa-pen",
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 409,
|
||||
name: "CRM",
|
||||
parent_id: false,
|
||||
action: "ir.actions.act_window,597",
|
||||
icon: "fa fa-handshake",
|
||||
children: [
|
||||
{
|
||||
id: 418,
|
||||
name: "Sales",
|
||||
parent_id: 409,
|
||||
action: false,
|
||||
icon: false,
|
||||
children: [
|
||||
{
|
||||
id: 423,
|
||||
name: "My Pipeline",
|
||||
parent_id: 418,
|
||||
action: "ir.actions.act_window,597",
|
||||
icon: false,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 812,
|
||||
name: "My Quotations",
|
||||
parent_id: 418,
|
||||
action: "ir.actions.act_window,1051",
|
||||
icon: false,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 419,
|
||||
name: "Team Pipelines",
|
||||
parent_id: 418,
|
||||
action: "ir.actions.act_window,275",
|
||||
icon: false,
|
||||
children: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 421,
|
||||
name: "Leads",
|
||||
parent_id: 409,
|
||||
action: false,
|
||||
icon: false,
|
||||
children: [
|
||||
{
|
||||
id: 422,
|
||||
name: "Leads",
|
||||
parent_id: 421,
|
||||
action: "ir.actions.act_window,595",
|
||||
icon: false,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 752,
|
||||
name: "Scoring Rules",
|
||||
parent_id: 421,
|
||||
icon: false,
|
||||
action: "ir.actions.act_window,1083",
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
export interface TestData {
|
||||
menuInfo: MenuInfo;
|
||||
actions: ActionDescription[];
|
||||
actionRegistry: typeof actionRegistry;
|
||||
templates: string;
|
||||
}
|
||||
|
||||
export const actions: ActionDescription[] = [
|
||||
{
|
||||
id: 131,
|
||||
type: "ir.actions.client",
|
||||
target: "current",
|
||||
name: "Discuss",
|
||||
tag: "mail.discuss"
|
||||
},
|
||||
{
|
||||
id: 250,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Notes",
|
||||
target: "current",
|
||||
domain: false,
|
||||
context: "{}",
|
||||
views: [[false, "kanban"], [false, "list"], [false, "form"]],
|
||||
res_id: 0,
|
||||
res_model: "note.note"
|
||||
},
|
||||
{
|
||||
id: 597,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Pipeline",
|
||||
target: "current",
|
||||
domain: false,
|
||||
context: { default_team_id: 1 },
|
||||
views: [[2103, "kanban"], [2106, "list"], [2105, "form"]],
|
||||
res_id: 0,
|
||||
res_model: "crm.lead"
|
||||
},
|
||||
{
|
||||
id: 1051,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Quotations",
|
||||
target: "current",
|
||||
domain: false,
|
||||
context: "{'search_default_my_quotation': 1}",
|
||||
views: [
|
||||
[3387, "list"],
|
||||
[3385, "kanban"],
|
||||
[3389, "form"],
|
||||
[3382, "calendar"],
|
||||
[3384, "pivot"],
|
||||
[3383, "graph"]
|
||||
],
|
||||
res_id: 0,
|
||||
res_model: "sale.order"
|
||||
},
|
||||
{
|
||||
id: 275,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Team Pipelines",
|
||||
target: "current",
|
||||
context: "{}",
|
||||
domain: "[('use_opportunities', '=', True)]",
|
||||
views: [[false, "kanban"], [false, "form"]],
|
||||
res_id: 0,
|
||||
res_model: "crm.team"
|
||||
},
|
||||
{
|
||||
id: 595,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Leads",
|
||||
target: "current",
|
||||
context:
|
||||
"{ 'default_type':'lead', 'search_default_type': 'lead', 'search_default_to_process':1, }",
|
||||
domain: "['|', ('type','=','lead'), ('type','=',False)]",
|
||||
views: [
|
||||
[2098, "list"],
|
||||
[2099, "kanban"],
|
||||
[2100, "calendar"],
|
||||
[2108, "pivot"],
|
||||
[2107, "graph"],
|
||||
[false, "form"]
|
||||
],
|
||||
res_id: 0,
|
||||
res_model: "crm.lead"
|
||||
},
|
||||
{
|
||||
id: 1083,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Scores",
|
||||
target: "current",
|
||||
context: "{}",
|
||||
domain: false,
|
||||
views: [[false, "list"], [false, "kanban"], [false, "form"]],
|
||||
res_id: 0,
|
||||
res_model: "website.crm.score"
|
||||
let templates: string;
|
||||
|
||||
export async function makeTestData(): Promise<TestData> {
|
||||
if (!templates) {
|
||||
templates = await loadTemplates();
|
||||
}
|
||||
];
|
||||
const registry: typeof actionRegistry = new Registry();
|
||||
(<any>registry).map = Object.assign({}, (<any>actionRegistry).map);
|
||||
|
||||
return {
|
||||
menuInfo: makeMenuInfo(),
|
||||
actions: makeActionData(),
|
||||
actionRegistry: registry,
|
||||
templates
|
||||
};
|
||||
}
|
||||
|
||||
export function makeMenuInfo(): MenuInfo {
|
||||
const items: BaseMenuItem[] = [
|
||||
{
|
||||
id: 96,
|
||||
name: "Discuss",
|
||||
parent_id: false,
|
||||
action: "ir.actions.client,131",
|
||||
icon: "fa fa-comment",
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 205,
|
||||
name: "Notes",
|
||||
parent_id: false,
|
||||
action: "ir.actions.act_window,250",
|
||||
icon: "fa fa-pen",
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 409,
|
||||
name: "CRM",
|
||||
parent_id: false,
|
||||
action: "ir.actions.act_window,597",
|
||||
icon: "fa fa-handshake",
|
||||
children: [
|
||||
{
|
||||
id: 418,
|
||||
name: "Sales",
|
||||
parent_id: 409,
|
||||
action: false,
|
||||
icon: false,
|
||||
children: [
|
||||
{
|
||||
id: 423,
|
||||
name: "My Pipeline",
|
||||
parent_id: 418,
|
||||
action: "ir.actions.act_window,597",
|
||||
icon: false,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 812,
|
||||
name: "My Quotations",
|
||||
parent_id: 418,
|
||||
action: "ir.actions.act_window,1051",
|
||||
icon: false,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 419,
|
||||
name: "Team Pipelines",
|
||||
parent_id: 418,
|
||||
action: "ir.actions.act_window,275",
|
||||
icon: false,
|
||||
children: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 421,
|
||||
name: "Leads",
|
||||
parent_id: 409,
|
||||
action: false,
|
||||
icon: false,
|
||||
children: [
|
||||
{
|
||||
id: 422,
|
||||
name: "Leads",
|
||||
parent_id: 421,
|
||||
action: "ir.actions.act_window,595",
|
||||
icon: false,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 752,
|
||||
name: "Scoring Rules",
|
||||
parent_id: 421,
|
||||
icon: false,
|
||||
action: "ir.actions.act_window,1083",
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
return getMenuInfo(items);
|
||||
}
|
||||
|
||||
function makeActionData(): ActionDescription[] {
|
||||
return [
|
||||
{
|
||||
id: 131,
|
||||
type: "ir.actions.client",
|
||||
target: "current",
|
||||
name: "Discuss",
|
||||
tag: "mail.discuss"
|
||||
},
|
||||
{
|
||||
id: 250,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Notes",
|
||||
target: "current",
|
||||
domain: false,
|
||||
context: "{}",
|
||||
views: [[false, "kanban"], [false, "list"], [false, "form"]],
|
||||
res_id: 0,
|
||||
res_model: "note.note"
|
||||
},
|
||||
{
|
||||
id: 597,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Pipeline",
|
||||
target: "current",
|
||||
domain: false,
|
||||
context: { default_team_id: 1 },
|
||||
views: [[2103, "kanban"], [2106, "list"], [2105, "form"]],
|
||||
res_id: 0,
|
||||
res_model: "crm.lead"
|
||||
},
|
||||
{
|
||||
id: 1051,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Quotations",
|
||||
target: "current",
|
||||
domain: false,
|
||||
context: "{'search_default_my_quotation': 1}",
|
||||
views: [
|
||||
[3387, "list"],
|
||||
[3385, "kanban"],
|
||||
[3389, "form"],
|
||||
[3382, "calendar"],
|
||||
[3384, "pivot"],
|
||||
[3383, "graph"]
|
||||
],
|
||||
res_id: 0,
|
||||
res_model: "sale.order"
|
||||
},
|
||||
{
|
||||
id: 275,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Team Pipelines",
|
||||
target: "current",
|
||||
context: "{}",
|
||||
domain: "[('use_opportunities', '=', True)]",
|
||||
views: [[false, "kanban"], [false, "form"]],
|
||||
res_id: 0,
|
||||
res_model: "crm.team"
|
||||
},
|
||||
{
|
||||
id: 595,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Leads",
|
||||
target: "current",
|
||||
context:
|
||||
"{ 'default_type':'lead', 'search_default_type': 'lead', 'search_default_to_process':1, }",
|
||||
domain: "['|', ('type','=','lead'), ('type','=',False)]",
|
||||
views: [
|
||||
[2098, "list"],
|
||||
[2099, "kanban"],
|
||||
[2100, "calendar"],
|
||||
[2108, "pivot"],
|
||||
[2107, "graph"],
|
||||
[false, "form"]
|
||||
],
|
||||
res_id: 0,
|
||||
res_model: "crm.lead"
|
||||
},
|
||||
{
|
||||
id: 1083,
|
||||
type: "ir.actions.act_window",
|
||||
name: "Scores",
|
||||
target: "current",
|
||||
context: "{}",
|
||||
domain: false,
|
||||
views: [[false, "list"], [false, "kanban"], [false, "form"]],
|
||||
res_id: 0,
|
||||
res_model: "website.crm.score"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
function loadTemplates(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
readFile("web/static/src/xml/templates.xml", "utf-8", (err, result) => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { WEnv } from "../../src/ts/core/component";
|
||||
import { QWeb } from "../../src/ts/core/qweb_vdom";
|
||||
import { Registry } from "../../src/ts/core/registry";
|
||||
import { idGenerator } from "../../src/ts/core/utils";
|
||||
import { Env, makeEnv } from "../../src/ts/env";
|
||||
import { IRouter } from "../../src/ts/services/router";
|
||||
import { MenuInfo, Store } from "../../src/ts/store/store";
|
||||
import { MockRouter } from "./mock_router";
|
||||
import { MockServer } from "./mock_server";
|
||||
import { TestData } from "./test_data";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export interface TestEnv extends Env {
|
||||
store: Store;
|
||||
}
|
||||
|
||||
export interface TestInfo extends Partial<TestData> {
|
||||
mockRPC?(this: MockServer, route: string, params: any): Promise<any>;
|
||||
router?: IRouter;
|
||||
}
|
||||
|
||||
export function makeTestWEnv(): WEnv {
|
||||
return {
|
||||
qweb: new QWeb(),
|
||||
getID: idGenerator()
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Code
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export function makeTestEnv(info: TestInfo = {}): TestEnv {
|
||||
const templates = info.templates || "";
|
||||
const menuInfo: MenuInfo = info.menuInfo || {
|
||||
menus: {},
|
||||
actionMap: {},
|
||||
roots: []
|
||||
};
|
||||
const actionRegistry = info.actionRegistry || new Registry();
|
||||
const actions = info.actions || [];
|
||||
|
||||
const data: TestData = {
|
||||
menuInfo,
|
||||
actions,
|
||||
actionRegistry,
|
||||
templates
|
||||
};
|
||||
|
||||
const mockServer = new MockServer(data);
|
||||
|
||||
function rpc(route: string, params: any): Promise<any> {
|
||||
if (info.mockRPC) {
|
||||
return info.mockRPC.call(mockServer, route, params);
|
||||
}
|
||||
return mockServer.rpc(route, params);
|
||||
}
|
||||
const services = {
|
||||
rpc,
|
||||
router: info.router || new MockRouter()
|
||||
};
|
||||
|
||||
const store = new Store(services, menuInfo, actionRegistry);
|
||||
const env = makeEnv(store, templates);
|
||||
const testEnv = Object.assign({ store }, env);
|
||||
return testEnv;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
export function nextMicroTick(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function nextTick(): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve));
|
||||
}
|
||||
|
||||
export function makeTestFixture() {
|
||||
let fixture = document.createElement("div");
|
||||
document.body.appendChild(fixture);
|
||||
return fixture;
|
||||
}
|
||||
|
||||
export function normalize(str: string): string {
|
||||
return str.replace(/\s+/g, "");
|
||||
}
|
||||
|
||||
interface Deferred extends Promise<any> {
|
||||
resolve(val?: any): void;
|
||||
reject(): void;
|
||||
}
|
||||
|
||||
export function makeDeferred(): Deferred {
|
||||
let resolve, reject;
|
||||
let def = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve;
|
||||
reject = _reject;
|
||||
});
|
||||
(<Deferred>def).resolve = resolve;
|
||||
(<Deferred>def).reject = reject;
|
||||
return <Deferred>def;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { makeTestStore, mockFetch } from "../helpers";
|
||||
import { makeTestData, makeTestEnv } from "../helpers";
|
||||
import { Registry } from "../../src/ts/core/registry";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tests
|
||||
@@ -6,19 +7,34 @@ import { makeTestStore, mockFetch } from "../helpers";
|
||||
|
||||
test("does not reload action if already done", async () => {
|
||||
const routes: string[] = [];
|
||||
const store = makeTestStore({
|
||||
rpc: async (route, params) => {
|
||||
const data = await makeTestData();
|
||||
const testEnv = makeTestEnv({
|
||||
...data,
|
||||
mockRPC(route, params) {
|
||||
routes.push(route);
|
||||
return mockFetch(route, params);
|
||||
return this.rpc(route, params);
|
||||
}
|
||||
});
|
||||
|
||||
expect(routes).toEqual([]);
|
||||
|
||||
store.doAction(131);
|
||||
testEnv.store.doAction(131);
|
||||
|
||||
expect(routes).toEqual(["web/action/load"]);
|
||||
|
||||
store.doAction(131);
|
||||
testEnv.store.doAction(131);
|
||||
|
||||
expect(routes).toEqual(["web/action/load"]);
|
||||
});
|
||||
|
||||
test("display a warning if client action is not in registry", async () => {
|
||||
const data = await makeTestData();
|
||||
data.actionRegistry = new Registry();
|
||||
const testEnv = makeTestEnv(data);
|
||||
|
||||
await testEnv.store.doAction(131);
|
||||
|
||||
const notifs = testEnv.store.state.notifications;
|
||||
expect(notifs.length).toBe(1);
|
||||
expect(notifs[0].type).toBe("warning");
|
||||
});
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import { makeTestStore, nextMicroTick } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function mockFetch(route: string, params: any): Promise<any> {
|
||||
return Promise.resolve(`${route}`);
|
||||
}
|
||||
import { makeTestData, makeTestEnv, nextMicroTick } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tests
|
||||
@@ -14,13 +6,20 @@ function mockFetch(route: string, params: any): Promise<any> {
|
||||
|
||||
describe("rpc", () => {
|
||||
test("properly translate query in route", async () => {
|
||||
const store = makeTestStore({ rpc: mockFetch });
|
||||
const result = await store.rpc({ model: "test", method: "hey" });
|
||||
expect(result).toBe("/web/dataset/call_kw/test/hey");
|
||||
expect.assertions(1);
|
||||
|
||||
const store = makeTestEnv({
|
||||
mockRPC(route, params) {
|
||||
expect(route).toBe("/web/dataset/call_kw/test/hey");
|
||||
return this.rpc(route, params);
|
||||
}
|
||||
}).store;
|
||||
|
||||
await store.rpc({ model: "test", method: "hey" });
|
||||
});
|
||||
|
||||
test("trigger proper events", async () => {
|
||||
const store = makeTestStore({ rpc: mockFetch });
|
||||
const store = makeTestEnv().store;
|
||||
const events: string[] = [];
|
||||
store.on("rpc_status", null, s => {
|
||||
events.push(s);
|
||||
@@ -35,7 +34,7 @@ describe("rpc", () => {
|
||||
|
||||
describe("notifications", () => {
|
||||
test("can subscribe and add notification", () => {
|
||||
const store = makeTestStore();
|
||||
const store = makeTestEnv().store;
|
||||
expect(store.state.notifications.length).toBe(0);
|
||||
const id = store.addNotification({
|
||||
title: "test",
|
||||
@@ -46,7 +45,7 @@ describe("notifications", () => {
|
||||
});
|
||||
|
||||
test("can close a notification", () => {
|
||||
const store = makeTestStore();
|
||||
const store = makeTestEnv().store;
|
||||
|
||||
const id = store.addNotification({
|
||||
title: "test",
|
||||
@@ -60,7 +59,7 @@ describe("notifications", () => {
|
||||
|
||||
test("notifications closes themselves after a while", () => {
|
||||
jest.useFakeTimers();
|
||||
const store = makeTestStore();
|
||||
const store = makeTestEnv().store;
|
||||
|
||||
store.addNotification({ title: "test", message: "message" });
|
||||
|
||||
@@ -72,7 +71,7 @@ describe("notifications", () => {
|
||||
|
||||
test("sticky notifications do not close themselves after a while", () => {
|
||||
jest.useFakeTimers();
|
||||
const store = makeTestStore();
|
||||
const store = makeTestEnv().store;
|
||||
|
||||
store.addNotification({
|
||||
title: "test",
|
||||
@@ -89,7 +88,8 @@ describe("notifications", () => {
|
||||
|
||||
describe("state transitions", () => {
|
||||
test("toggle menu", async () => {
|
||||
const store = makeTestStore();
|
||||
const data = await makeTestData();
|
||||
const store = makeTestEnv(data).store;
|
||||
expect(store.state.inHome).toBe(true);
|
||||
store.toggleHomeMenu();
|
||||
|
||||
@@ -121,7 +121,8 @@ describe("state transitions", () => {
|
||||
|
||||
test("document title", async () => {
|
||||
document.title = "Odoo";
|
||||
const store = makeTestStore();
|
||||
const data = await makeTestData();
|
||||
const store = makeTestEnv(data).store;
|
||||
expect(store.state.inHome).toBe(true);
|
||||
expect(document.title).toBe("Odoo");
|
||||
const promise = store.activateMenuItem(96);
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
import { Env, makeEnv } from "../../src/ts/env";
|
||||
import { Store } from "../../src/ts/store/store";
|
||||
import { HomeMenu, Props } from "../../src/ts/ui/home_menu";
|
||||
import * as helpers from "../helpers";
|
||||
import { HomeMenu } from "../../src/ts/ui/home_menu";
|
||||
import { makeTestData, makeTestEnv, makeTestFixture } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
let fixture: HTMLElement;
|
||||
let store: Store;
|
||||
let env: Env;
|
||||
let props: Props;
|
||||
let templates: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
templates = await helpers.loadTemplates();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = helpers.makeTestFixture();
|
||||
store = helpers.makeTestStore();
|
||||
env = makeEnv(store, templates);
|
||||
props = { menuInfo: helpers.makeDemoMenuInfo() };
|
||||
fixture = makeTestFixture();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -33,7 +20,10 @@ afterEach(() => {
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
test("can be rendered", async () => {
|
||||
const homeMenu = new HomeMenu(env, props);
|
||||
const data = await makeTestData();
|
||||
const testEnv = makeTestEnv(data);
|
||||
const props = { menuInfo: data.menuInfo };
|
||||
const homeMenu = new HomeMenu(testEnv, props);
|
||||
await homeMenu.mount(fixture);
|
||||
expect(fixture.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Env, makeEnv } from "../../src/ts/env";
|
||||
import { MenuInfo, Store } from "../../src/ts/store/store";
|
||||
import { MenuInfo } from "../../src/ts/store/store";
|
||||
import { Navbar, Props } from "../../src/ts/ui/navbar";
|
||||
import * as helpers from "../helpers";
|
||||
|
||||
@@ -8,22 +7,16 @@ import * as helpers from "../helpers";
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
let fixture: HTMLElement;
|
||||
let store: Store;
|
||||
let env: Env;
|
||||
let env: helpers.TestEnv;
|
||||
let props: Props;
|
||||
let menuInfo: MenuInfo;
|
||||
let templates: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
templates = await helpers.loadTemplates();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
fixture = helpers.makeTestFixture();
|
||||
store = helpers.makeTestStore();
|
||||
env = makeEnv(store, templates);
|
||||
const data = await helpers.makeTestData();
|
||||
env = helpers.makeTestEnv(data);
|
||||
props = { inHome: false, app: null };
|
||||
menuInfo = helpers.makeDemoMenuInfo();
|
||||
menuInfo = helpers.makeMenuInfo();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -65,10 +58,10 @@ test("mobile mode: navbar is different", async () => {
|
||||
|
||||
test("clicking on left icon toggle home menu ", async () => {
|
||||
props.app = menuInfo.menus[96]!;
|
||||
store.state.inHome = false;
|
||||
env.store.state.inHome = false;
|
||||
const navbar = new Navbar(env, props);
|
||||
await navbar.mount(fixture);
|
||||
expect(store.state.inHome).toBe(false);
|
||||
expect(env.store.state.inHome).toBe(false);
|
||||
(<any>fixture).getElementsByClassName("o_title")[0].click();
|
||||
expect(store.state.inHome).toBe(true);
|
||||
expect(env.store.state.inHome).toBe(true);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Env, makeEnv } from "../../src/ts/env";
|
||||
import { Notification as INotification, Store } from "../../src/ts/store/store";
|
||||
import { Notification as INotification } from "../../src/ts/store/store";
|
||||
import { Notification } from "../../src/ts/ui/notification";
|
||||
import * as helpers from "../helpers";
|
||||
|
||||
@@ -8,19 +7,12 @@ import * as helpers from "../helpers";
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
let fixture: HTMLElement;
|
||||
let store: Store;
|
||||
let env: Env;
|
||||
let templates: string;
|
||||
let env: helpers.TestEnv;
|
||||
|
||||
beforeAll(async () => {
|
||||
templates = await helpers.loadTemplates();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
fixture = helpers.makeTestFixture();
|
||||
fixture = helpers.makeTestFixture();
|
||||
store = helpers.makeTestStore();
|
||||
env = makeEnv(store, templates);
|
||||
const data = await helpers.makeTestData();
|
||||
env = helpers.makeTestEnv(data);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -67,9 +59,9 @@ test("can be closed by clicking on it (if sticky)", async () => {
|
||||
sticky: true
|
||||
});
|
||||
|
||||
const navbar = new Notification(env, store.state.notifications[0]);
|
||||
const navbar = new Notification(env, env.store.state.notifications[0]);
|
||||
await navbar.mount(fixture);
|
||||
expect(store.state.notifications.length).toBe(1);
|
||||
expect(env.store.state.notifications.length).toBe(1);
|
||||
(<any>fixture.getElementsByClassName("o_close")[0]).click();
|
||||
expect(store.state.notifications.length).toBe(0);
|
||||
expect(env.store.state.notifications.length).toBe(0);
|
||||
});
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
import { Env, makeEnv } from "../../src/ts/env";
|
||||
import { Store } from "../../src/ts/store/store";
|
||||
import { Root } from "../../src/ts/ui/root";
|
||||
import * as helpers from "../helpers";
|
||||
import { makeTestData, makeTestEnv } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
let fixture: HTMLElement;
|
||||
let store: Store;
|
||||
let env: Env;
|
||||
let templates: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
templates = await helpers.loadTemplates();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = helpers.makeTestFixture();
|
||||
store = helpers.makeTestStore();
|
||||
env = makeEnv(store, templates);
|
||||
// props = { menuInfo: helpers.makeDemoMenuInfo() };
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -32,21 +21,23 @@ afterEach(() => {
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
test("can be rendered (in home menu)", async () => {
|
||||
const root = new Root(env, store);
|
||||
const data = await makeTestData();
|
||||
const testEnv = makeTestEnv(data);
|
||||
const root = new Root(testEnv, testEnv.store);
|
||||
await root.mount(fixture);
|
||||
expect(fixture.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("if url has action_id, will render action and navigate to proper menu_id", async () => {
|
||||
const data = await makeTestData();
|
||||
const router = new helpers.MockRouter({ action_id: "131" });
|
||||
store = helpers.makeTestStore({ router });
|
||||
env = makeEnv(store, templates);
|
||||
const testEnv = makeTestEnv({ ...data, router });
|
||||
await helpers.nextTick();
|
||||
|
||||
const root = new Root(env, store);
|
||||
const root = new Root(testEnv, testEnv.store);
|
||||
await root.mount(fixture);
|
||||
await helpers.nextTick();
|
||||
expect(env.services.router.getQuery()).toEqual({
|
||||
expect(router.getQuery()).toEqual({
|
||||
action_id: "131",
|
||||
menu_id: "96"
|
||||
});
|
||||
@@ -54,16 +45,18 @@ test("if url has action_id, will render action and navigate to proper menu_id",
|
||||
});
|
||||
|
||||
test("start with no action => clicks on client action => discuss is rendered", async () => {
|
||||
const root = new Root(env, store);
|
||||
const data = await makeTestData();
|
||||
const testEnv = makeTestEnv(data);
|
||||
const root = new Root(testEnv, testEnv.store);
|
||||
await root.mount(fixture);
|
||||
|
||||
expect(env.services.router.getQuery()).toEqual({ home: true });
|
||||
expect(testEnv.services.router.getQuery()).toEqual({ home: true });
|
||||
|
||||
// discuss menu item
|
||||
await (<any>document.querySelector('[data-menu="96"]')).click();
|
||||
await helpers.nextTick();
|
||||
expect(fixture.innerHTML).toMatchSnapshot();
|
||||
expect(env.services.router.getQuery()).toEqual({
|
||||
expect(testEnv.services.router.getQuery()).toEqual({
|
||||
action_id: "131",
|
||||
menu_id: "96"
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user