mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
@@ -1,10 +1,9 @@
|
|||||||
# 🦉 Testing and Debugging Owl components 🦉
|
# 🦉 Testing Owl components 🦉
|
||||||
|
|
||||||
## Content
|
## Content
|
||||||
|
|
||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
- [Unit Tests](#unit-tests)
|
- [Unit Tests](#unit-tests)
|
||||||
- [Debugging](#debugging)
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -131,81 +130,3 @@ function afterUpdates() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Debugging
|
|
||||||
|
|
||||||
Non trivial applications become quickly more difficult to understand. It is then
|
|
||||||
useful to have a solid understanding of what is going on. To help with that,
|
|
||||||
the following code can simply be copy/pasted in an application. Once it is
|
|
||||||
executed, it will log a lot of information on each component main hooks.
|
|
||||||
|
|
||||||
```js
|
|
||||||
let current;
|
|
||||||
Object.defineProperty(owl.Component, "current", {
|
|
||||||
get() {
|
|
||||||
return current;
|
|
||||||
},
|
|
||||||
set(comp) {
|
|
||||||
current = comp;
|
|
||||||
const name = comp.constructor.name;
|
|
||||||
let __owl__;
|
|
||||||
Object.defineProperty(current, "__owl__", {
|
|
||||||
get() {
|
|
||||||
return __owl__;
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
__owl__ = val;
|
|
||||||
debugComponent(comp, name, __owl__.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function toStr(props) {
|
|
||||||
let str = JSON.stringify(props || {});
|
|
||||||
if (str.length > 200) {
|
|
||||||
str = str.slice(0, 200) + "...";
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function debugComponent(component, name, id) {
|
|
||||||
console.log(`[DEBUG] constructor ${name}<id=${id}>, props=${toStr(component.props)}`);
|
|
||||||
owl.hooks.onWillStart(() => {
|
|
||||||
console.log(`[DEBUG] willStart: '${name}<id=${id}>'`);
|
|
||||||
});
|
|
||||||
owl.hooks.onMounted(() => {
|
|
||||||
console.log(`[DEBUG] mounted: '${name}<id=${id}>'`);
|
|
||||||
});
|
|
||||||
owl.hooks.onWillUpdateProps(nextProps => {
|
|
||||||
console.log(`[DEBUG] willUpdateProps: '${name}<id=${id}> nextprops=${toStr(nextProps)}`);
|
|
||||||
});
|
|
||||||
owl.hooks.onWillPatch(() => {
|
|
||||||
console.log(`[DEBUG] willPatch: '${name}<id=${id}>'`);
|
|
||||||
});
|
|
||||||
owl.hooks.onPatched(() => {
|
|
||||||
console.log(`[DEBUG] patched: '${name}<id=${id}>'`);
|
|
||||||
});
|
|
||||||
owl.hooks.onWillUnmount(() => {
|
|
||||||
console.log(`[DEBUG] willUnmount: '${name}<id=${id}>'`);
|
|
||||||
});
|
|
||||||
const __render = component.__render.bind(component);
|
|
||||||
component.__render = function(...args) {
|
|
||||||
console.log(`[DEBUG] rendering template: '${name}<id=${id}>'`);
|
|
||||||
__render(...args);
|
|
||||||
};
|
|
||||||
const render = component.render.bind(component);
|
|
||||||
component.render = function(...args) {
|
|
||||||
console.log(`[DEBUG] render: '${name}<id=${id}>'`);
|
|
||||||
return render(...args);
|
|
||||||
};
|
|
||||||
const mount = component.mount.bind(component);
|
|
||||||
component.mount = function(...args) {
|
|
||||||
console.log(`[DEBUG] mount: '${name}<id=${id}>'`);
|
|
||||||
return mount(...args);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that it is certainly useful to run this code at some point in an application,
|
|
||||||
just to get a feel of what each user action implies, for the framework.
|
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
- [Quick Start: create an (almost) empty Owl application](learning/quick_start.md)
|
- [Quick Start: create an (almost) empty Owl application](learning/quick_start.md)
|
||||||
- [Tutorial: create a TodoList application](learning/tutorial_todoapp.md)
|
- [Tutorial: create a TodoList application](learning/tutorial_todoapp.md)
|
||||||
- [Testing and Debugging Owl components](learning/testing_components.md)
|
- [Testing Owl components](learning/testing_components.md)
|
||||||
|
|
||||||
## Miscellaneous
|
## Miscellaneous
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
- [Playground](#playground)
|
- [Playground](#playground)
|
||||||
- [Benchmarks](#benchmarks)
|
- [Benchmarks](#benchmarks)
|
||||||
- [Single File Component](#single-file-component)
|
- [Single File Component](#single-file-component)
|
||||||
|
- [Debugging Script](#debugging-script)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -78,3 +79,29 @@ Note that the above example has an inline xml comment, just after the `xml` call
|
|||||||
This is useful for some editor plugins, such as the VS Code addon
|
This is useful for some editor plugins, such as the VS Code addon
|
||||||
`Comment tagged template`, which, if installed, add syntax highlighting to the
|
`Comment tagged template`, which, if installed, add syntax highlighting to the
|
||||||
content of the template string.
|
content of the template string.
|
||||||
|
|
||||||
|
## Debugging Script
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
Non trivial applications become quickly more difficult to understand. It is then
|
||||||
|
useful to have a solid understanding of what is going on. To help with that,
|
||||||
|
logging useful information is extremely valuable. There is a [javascript file](../tools/debug.js) which can be evaluated in an application.
|
||||||
|
|
||||||
|
Once it is executed, it will log a lot of information on each component main hooks. The following code is a minified version to make it easier to copy/paste:
|
||||||
|
|
||||||
|
```
|
||||||
|
let debugSetup = {
|
||||||
|
// componentBlackList: /App/, // regexp
|
||||||
|
// componentWhiteList: /SomeComponent/, // regexp
|
||||||
|
// methodBlackList: ["mounted"], // list of method names
|
||||||
|
// methodWhiteList: ["willStart"], // list of method names
|
||||||
|
logScheduler: false, // display/mute scheduler logs
|
||||||
|
logStore: true, // display/mute store logs
|
||||||
|
};
|
||||||
|
{let o,t="[OWL_DEBUG]";function toStr(o){let t=JSON.stringify(o||{});return t.length>200&&(t=t.slice(0,200)+"..."),t}function debugComponent(o,e,n){let l=`${e}<id=${n}>`,r=o=>(!debugSetup.methodBlackList||!debugSetup.methodBlackList.includes(o))&&!(debugSetup.methodWhiteList&&!debugSetup.methodWhiteList.includes(o));r("constructor")&&console.log(`${t} ${l} constructor, props=${toStr(o.props)}`),r("willStart")&&owl.hooks.onWillStart(()=>{console.log(`${t} ${l} willStart`)}),r("mounted")&&owl.hooks.onMounted(()=>{console.log(`${t} ${l} mounted`)}),r("willUpdateProps")&&owl.hooks.onWillUpdateProps(o=>{console.log(`${t} ${l} willUpdateProps, nextprops=${toStr(o)}`)}),r("willPatch")&&owl.hooks.onWillPatch(()=>{console.log(`${t} ${l} willPatch`)}),r("patched")&&owl.hooks.onPatched(()=>{console.log(`${t} ${l} patched`)}),r("willUnmount")&&owl.hooks.onWillUnmount(()=>{console.log(`${t} ${l} willUnmount`)});const s=o.__render.bind(o);o.__render=function(...o){console.log(`${t} ${l} rendering template`),s(...o)};const u=o.render.bind(o);o.render=function(...o){return console.log(`${t} ${l} render`),u(...o)};const c=o.mount.bind(o);o.mount=function(...o){return console.log(`${t} ${l} mount`),c(...o)}}if(Object.defineProperty(owl.Component,"current",{get:()=>o,set(t){o=t;const e=t.constructor.name;if(debugSetup.componentBlackList&&debugSetup.componentBlackList.test(e))return;if(debugSetup.componentWhiteList&&!debugSetup.componentWhiteList.test(e))return;let n;Object.defineProperty(o,"__owl__",{get:()=>n,set(o){debugComponent(t,e,(n=o).id)}})}}),debugSetup.logScheduler){let o;Object.defineProperty(owl.Component.scheduler,"isRunning",{get:()=>o,set(e){e?console.log(`${t} scheduler: start running tasks queue`):console.log(`${t} scheduler: stop running tasks queue`),o=e}})}if(debugSetup.logStore){let o=owl.Store.prototype.dispatch;owl.Store.prototype.dispatch=function(e,...n){return console.log(`${t} store: action '${e}' dispatched. Payload: '${toStr(n)}'`),o.call(this,e,...n)}}}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that it is certainly useful to run this code at some point in an application,
|
||||||
|
just to get a feel of what each user action implies, for the framework.
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ function getFiles(path: string[] = []): FileData[] {
|
|||||||
return Array.prototype.concat(...files);
|
return Array.prototype.concat(...files);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LOCAL_FILES = ["LICENSE"];
|
const LOCAL_FILES = ["LICENSE", "tools/debug.js"];
|
||||||
export function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
|
export function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
|
||||||
if (link.link.startsWith("http")) {
|
if (link.link.startsWith("http")) {
|
||||||
// no check on external links
|
// no check on external links
|
||||||
|
|||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
/**
|
||||||
|
* Debug Script
|
||||||
|
*
|
||||||
|
* This code is intended to be evaluated in an environment where owl is available,
|
||||||
|
* to log lot of helpful information on how Owl components behave.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let debugSetup = {
|
||||||
|
// componentBlackList: /App/, // regexp
|
||||||
|
// componentWhiteList: /SomeComponent/, // regexp
|
||||||
|
// methodBlackList: ["mounted"], // list of method names
|
||||||
|
// methodWhiteList: ["willStart"], // list of method names
|
||||||
|
logScheduler: true, // display/mute scheduler logs
|
||||||
|
logStore: true // display/mute store logs
|
||||||
|
};
|
||||||
|
{
|
||||||
|
let prefix = "[OWL_DEBUG]";
|
||||||
|
let current;
|
||||||
|
Object.defineProperty(owl.Component, "current", {
|
||||||
|
get() {
|
||||||
|
return current;
|
||||||
|
},
|
||||||
|
set(comp) {
|
||||||
|
current = comp;
|
||||||
|
const name = comp.constructor.name;
|
||||||
|
if (debugSetup.componentBlackList && debugSetup.componentBlackList.test(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (debugSetup.componentWhiteList && !debugSetup.componentWhiteList.test(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let __owl__;
|
||||||
|
Object.defineProperty(current, "__owl__", {
|
||||||
|
get() {
|
||||||
|
return __owl__;
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
__owl__ = val;
|
||||||
|
debugComponent(comp, name, __owl__.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function toStr(obj) {
|
||||||
|
let str = JSON.stringify(obj || {});
|
||||||
|
if (str.length > 200) {
|
||||||
|
str = str.slice(0, 200) + "...";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugComponent(component, name, id) {
|
||||||
|
let fullName = `${name}<id=${id}>`;
|
||||||
|
let shouldDebug = method => {
|
||||||
|
if (debugSetup.methodBlackList && debugSetup.methodBlackList.includes(method)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (debugSetup.methodWhiteList && !debugSetup.methodWhiteList.includes(method)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (shouldDebug("constructor")) {
|
||||||
|
console.log(`${prefix} ${fullName} constructor, props=${toStr(component.props)}`);
|
||||||
|
}
|
||||||
|
if (shouldDebug("willStart")) {
|
||||||
|
owl.hooks.onWillStart(() => {
|
||||||
|
console.log(`${prefix} ${fullName} willStart`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (shouldDebug("mounted")) {
|
||||||
|
owl.hooks.onMounted(() => {
|
||||||
|
console.log(`${prefix} ${fullName} mounted`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (shouldDebug("willUpdateProps")) {
|
||||||
|
owl.hooks.onWillUpdateProps(nextProps => {
|
||||||
|
console.log(`${prefix} ${fullName} willUpdateProps, nextprops=${toStr(nextProps)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (shouldDebug("willPatch")) {
|
||||||
|
owl.hooks.onWillPatch(() => {
|
||||||
|
console.log(`${prefix} ${fullName} willPatch`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (shouldDebug("patched")) {
|
||||||
|
owl.hooks.onPatched(() => {
|
||||||
|
console.log(`${prefix} ${fullName} patched`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (shouldDebug("willUnmount")) {
|
||||||
|
owl.hooks.onWillUnmount(() => {
|
||||||
|
console.log(`${prefix} ${fullName} willUnmount`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const __render = component.__render.bind(component);
|
||||||
|
component.__render = function(...args) {
|
||||||
|
console.log(`${prefix} ${fullName} rendering template`);
|
||||||
|
__render(...args);
|
||||||
|
};
|
||||||
|
const render = component.render.bind(component);
|
||||||
|
component.render = function(...args) {
|
||||||
|
console.log(`${prefix} ${fullName} render`);
|
||||||
|
return render(...args);
|
||||||
|
};
|
||||||
|
const mount = component.mount.bind(component);
|
||||||
|
component.mount = function(...args) {
|
||||||
|
console.log(`${prefix} ${fullName} mount`);
|
||||||
|
return mount(...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debugSetup.logScheduler) {
|
||||||
|
let isRunning;
|
||||||
|
Object.defineProperty(owl.Component.scheduler, "isRunning", {
|
||||||
|
get() {
|
||||||
|
return isRunning;
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
if (val) {
|
||||||
|
console.log(`${prefix} scheduler: start running tasks queue`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} scheduler: stop running tasks queue`);
|
||||||
|
}
|
||||||
|
isRunning = val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (debugSetup.logStore) {
|
||||||
|
let dispatch = owl.Store.prototype.dispatch;
|
||||||
|
owl.Store.prototype.dispatch = function(action, ...payload) {
|
||||||
|
console.log(`${prefix} store: action '${action}' dispatched. Payload: '${toStr(payload)}'`);
|
||||||
|
return dispatch.call(this, action, ...payload);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user