[IMP] tools: update debug script, add tests

closes #525
closes #526
This commit is contained in:
Géry Debongnie
2019-12-02 17:13:12 +01:00
committed by aab-odoo
parent ed4ab51c17
commit 85f26a0286
8 changed files with 157 additions and 40 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ The component `Portal` has no particular state, rather it is meant to be a slave
and ultimately just a way for the parent to teleport a piece of its own DOM elsewhere. and ultimately just a way for the parent to teleport a piece of its own DOM elsewhere.
The `Portal`'s root node is always `<portal/>` and is placed where the teleported content The `Portal`'s root node is always `<portal/>` and is placed where the teleported content
*would have* been. It is this element that the [teleported events](#expected-behaviors) are re-directed on. _would have_ been. It is this element that the [teleported events](#expected-behaviors) are re-directed on.
### Example ### Example
+3 -4
View File
@@ -91,16 +91,15 @@ logging useful information is extremely valuable. There is a [javascript file](.
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: 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 = { function debugOwl(o,t){let e,n="[OWL_DEBUG]";function l(o){let t=JSON.stringify(o||{});return t.length>200&&(t=t.slice(0,200)+"..."),t}if(Object.defineProperty(o.Component,"current",{get:()=>e,set(s){e=s;const c=s.constructor.name;if(t.componentBlackList&&t.componentBlackList.test(c))return;if(t.componentWhiteList&&!t.componentWhiteList.test(c))return;let r;Object.defineProperty(e,"__owl__",{get:()=>r,set(e){!function(e,s,c){let r=`${s}<id=${c}>`,i=o=>(!t.methodBlackList||!t.methodBlackList.includes(o))&&!(t.methodWhiteList&&!t.methodWhiteList.includes(o));i("constructor")&&console.log(`${n} ${r} constructor, props=${l(e.props)}`);i("willStart")&&o.hooks.onWillStart(()=>{console.log(`${n} ${r} willStart`)});i("mounted")&&o.hooks.onMounted(()=>{console.log(`${n} ${r} mounted`)});i("willUpdateProps")&&o.hooks.onWillUpdateProps(o=>{console.log(`${n} ${r} willUpdateProps, nextprops=${l(o)}`)});i("willPatch")&&o.hooks.onWillPatch(()=>{console.log(`${n} ${r} willPatch`)});i("patched")&&o.hooks.onPatched(()=>{console.log(`${n} ${r} patched`)});i("willUnmount")&&o.hooks.onWillUnmount(()=>{console.log(`${n} ${r} willUnmount`)});const u=e.__render.bind(e);e.__render=function(...o){console.log(`${n} ${r} rendering template`),u(...o)};const d=e.render.bind(e);e.render=function(...o){return console.log(`${n} ${r} render`),d(...o)};const p=e.mount.bind(e);e.mount=function(...o){return console.log(`${n} ${r} mount`),p(...o)}}(s,c,(r=e).id)}})}}),t.logScheduler){let t=o.Component.scheduler.start,e=o.Component.scheduler.stop;o.Component.scheduler.start=function(){console.log(`${n} scheduler: start running tasks queue`),t.call(this)},o.Component.scheduler.stop=function(){console.log(`${n} scheduler: stop running tasks queue`),e.call(this)}}if(t.logStore){let t=o.Store.prototype.dispatch;o.Store.prototype.dispatch=function(o,...e){return console.log(`${n} store: action '${o}' dispatched. Payload: '${l(e)}'`),t.call(this,o,...e)}}}
debugOwl({
// componentBlackList: /App/, // regexp // componentBlackList: /App/, // regexp
// componentWhiteList: /SomeComponent/, // regexp // componentWhiteList: /SomeComponent/, // regexp
// methodBlackList: ["mounted"], // list of method names // methodBlackList: ["mounted"], // list of method names
// methodWhiteList: ["willStart"], // list of method names // methodWhiteList: ["willStart"], // list of method names
logScheduler: false, // display/mute scheduler logs logScheduler: false, // display/mute scheduler logs
logStore: true, // display/mute store 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, Note that it is certainly useful to run this code at some point in an application,
+14 -5
View File
@@ -25,6 +25,15 @@ export class Scheduler {
this.requestAnimationFrame = requestAnimationFrame; this.requestAnimationFrame = requestAnimationFrame;
} }
start() {
this.isRunning = true;
this.scheduleTasks();
}
stop() {
this.isRunning = false;
}
addFiber(fiber): Promise<void> { addFiber(fiber): Promise<void> {
// if the fiber was remapped into a larger rendering fiber, it may not be a // if the fiber was remapped into a larger rendering fiber, it may not be a
// root fiber. But we only want to register root fibers // root fiber. But we only want to register root fibers
@@ -43,7 +52,7 @@ export class Scheduler {
} }
}); });
if (!this.isRunning) { if (!this.isRunning) {
this.scheduleTasks(); this.start();
} }
}); });
} }
@@ -74,16 +83,16 @@ export class Scheduler {
return true; return true;
}); });
this.tasks = tasks.concat(this.tasks); this.tasks = tasks.concat(this.tasks);
if (this.tasks.length === 0) {
this.stop();
}
} }
scheduleTasks() { scheduleTasks() {
this.isRunning = true;
this.requestAnimationFrame(() => { this.requestAnimationFrame(() => {
this.flush(); this.flush();
if (this.tasks.length > 0) { if (this.isRunning) {
this.scheduleTasks(); this.scheduleTasks();
} else {
this.isRunning = false;
} }
}); });
} }
+1 -1
View File
@@ -740,7 +740,7 @@ describe("Portal: Events handling", () => {
<button>child</button>`; <button>child</button>`;
} }
class Parent extends Component<any, any> { class Parent extends Component<any, any> {
static components = { Portal, Child, }; static components = { Portal, Child };
static template = xml` static template = xml`
<div> <div>
<Portal target="'#outside'"> <Portal target="'#outside'">
+57
View File
@@ -0,0 +1,57 @@
/**
* We can only make one test per file, since the debug tool modify in place
* the owl object in a way that is difficult to undo.
*/
import { debugOwl } from "../../tools/debug";
import * as owl from "../../src/index";
import { Component, Env } from "../../src/component/component";
import { xml } from "../../src/tags";
import { useState } from "../../src/hooks";
import { makeTestFixture, makeTestEnv, nextTick } from "../helpers";
let fixture: HTMLElement = makeTestFixture();
let env: Env = makeTestEnv();
Component.env = env;
debugOwl(owl, {});
test("can log full lifecycle", async () => {
const steps: string[] = [];
const log = console.log;
console.log = arg => steps.push(arg);
class Child extends Component<any, any> {
static template = xml`<div>child</div>`;
}
class Parent extends Component<any, any> {
static template = xml`<div><Child t-if="state.flag"/></div>`;
static components = { Child };
state = useState({ flag: false });
}
const parent = new Parent(null, {});
await parent.mount(fixture);
parent.state.flag = true;
await nextTick();
expect(steps).toEqual([
"[OWL_DEBUG] Parent<id=1> constructor, props={}",
"[OWL_DEBUG] Parent<id=1> mount",
"[OWL_DEBUG] Parent<id=1> willStart",
"[OWL_DEBUG] Parent<id=1> rendering template",
"[OWL_DEBUG] Parent<id=1> mounted",
"[OWL_DEBUG] Parent<id=1> render",
"[OWL_DEBUG] Parent<id=1> rendering template",
"[OWL_DEBUG] Child<id=2> constructor, props={}",
"[OWL_DEBUG] Child<id=2> willStart",
"[OWL_DEBUG] Child<id=2> rendering template",
"[OWL_DEBUG] Parent<id=1> willPatch",
"[OWL_DEBUG] Child<id=2> mounted",
"[OWL_DEBUG] Parent<id=1> patched"
]);
console.log = log;
});
+50
View File
@@ -0,0 +1,50 @@
/**
* We can only make one test per file, since the debug tool modify in place
* the owl object in a way that is difficult to undo.
*/
import { debugOwl } from "../../tools/debug";
import * as owl from "../../src/index";
import { Component, Env } from "../../src/component/component";
import { xml } from "../../src/tags";
import { makeTestFixture, makeTestEnv } from "../helpers";
let fixture: HTMLElement = makeTestFixture();
let env: Env = makeTestEnv();
Component.env = env;
debugOwl(owl, { logScheduler: true });
test("can log scheduler start and stop", async () => {
const steps: string[] = [];
const log = console.log;
console.log = arg => steps.push(arg);
class Child extends Component<any, any> {
static template = xml`<div>child</div>`;
}
class Parent extends Component<any, any> {
static template = xml`<div><Child /></div>`;
static components = { Child };
}
const parent = new Parent(null, {});
await parent.mount(fixture);
expect(steps).toEqual([
"[OWL_DEBUG] Parent<id=1> constructor, props={}",
"[OWL_DEBUG] Parent<id=1> mount",
"[OWL_DEBUG] Parent<id=1> willStart",
"[OWL_DEBUG] scheduler: start running tasks queue",
"[OWL_DEBUG] Parent<id=1> rendering template",
"[OWL_DEBUG] Child<id=2> constructor, props={}",
"[OWL_DEBUG] Child<id=2> willStart",
"[OWL_DEBUG] Child<id=2> rendering template",
"[OWL_DEBUG] Child<id=2> mounted",
"[OWL_DEBUG] Parent<id=1> mounted",
"[OWL_DEBUG] scheduler: stop running tasks queue"
]);
console.log = log;
});
+31 -29
View File
@@ -5,15 +5,7 @@
* to log lot of helpful information on how Owl components behave. * to log lot of helpful information on how Owl components behave.
*/ */
let debugSetup = { function debugOwl(owl, options) {
// 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 prefix = "[OWL_DEBUG]";
let current; let current;
Object.defineProperty(owl.Component, "current", { Object.defineProperty(owl.Component, "current", {
@@ -23,10 +15,10 @@ let debugSetup = {
set(comp) { set(comp) {
current = comp; current = comp;
const name = comp.constructor.name; const name = comp.constructor.name;
if (debugSetup.componentBlackList && debugSetup.componentBlackList.test(name)) { if (options.componentBlackList && options.componentBlackList.test(name)) {
return; return;
} }
if (debugSetup.componentWhiteList && !debugSetup.componentWhiteList.test(name)) { if (options.componentWhiteList && !options.componentWhiteList.test(name)) {
return; return;
} }
let __owl__; let __owl__;
@@ -53,10 +45,10 @@ let debugSetup = {
function debugComponent(component, name, id) { function debugComponent(component, name, id) {
let fullName = `${name}<id=${id}>`; let fullName = `${name}<id=${id}>`;
let shouldDebug = method => { let shouldDebug = method => {
if (debugSetup.methodBlackList && debugSetup.methodBlackList.includes(method)) { if (options.methodBlackList && options.methodBlackList.includes(method)) {
return false; return false;
} }
if (debugSetup.methodWhiteList && !debugSetup.methodWhiteList.includes(method)) { if (options.methodWhiteList && !options.methodWhiteList.includes(method)) {
return false; return false;
} }
return true; return true;
@@ -111,23 +103,19 @@ let debugSetup = {
}; };
} }
if (debugSetup.logScheduler) { if (options.logScheduler) {
let isRunning; let start = owl.Component.scheduler.start;
Object.defineProperty(owl.Component.scheduler, "isRunning", { let stop = owl.Component.scheduler.stop;
get() { owl.Component.scheduler.start = function () {
return isRunning; console.log(`${prefix} scheduler: start running tasks queue`);
}, start.call(this);
set(val) { };
if (val) { owl.Component.scheduler.stop = function () {
console.log(`${prefix} scheduler: start running tasks queue`); console.log(`${prefix} scheduler: stop running tasks queue`);
} else { stop.call(this);
console.log(`${prefix} scheduler: stop running tasks queue`); };
}
isRunning = val;
}
});
} }
if (debugSetup.logStore) { if (options.logStore) {
let dispatch = owl.Store.prototype.dispatch; let dispatch = owl.Store.prototype.dispatch;
owl.Store.prototype.dispatch = function(action, ...payload) { owl.Store.prototype.dispatch = function(action, ...payload) {
console.log(`${prefix} store: action '${action}' dispatched. Payload: '${toStr(payload)}'`); console.log(`${prefix} store: action '${action}' dispatched. Payload: '${toStr(payload)}'`);
@@ -135,3 +123,17 @@ let debugSetup = {
}; };
} }
} }
// This debug function can then be used like this:
//
// debugOwl(owl, {
// 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
// });
module.exports.debugOwl = debugOwl;