mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2d86dceb4 | |||
| 401b2a5b48 | |||
| 52f027fe46 |
+1
-3
@@ -23,6 +23,4 @@ package-lock.json
|
||||
node_modules
|
||||
|
||||
# Extras temp file
|
||||
/tools/owl.js
|
||||
|
||||
release-notes.md
|
||||
/tools/owl.js
|
||||
@@ -110,8 +110,8 @@ npm install @odoo/owl
|
||||
|
||||
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
||||
|
||||
- [owl-1.0.4.js](https://github.com/odoo/owl/releases/download/v1.0.4/owl.js)
|
||||
- [owl-1.0.4.min.js](https://github.com/odoo/owl/releases/download/v1.0.4/owl.min.js)
|
||||
- [owl-1.0.0-beta5.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta5/owl.js)
|
||||
- [owl-1.0.0-beta5.min.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta5/owl.min.js)
|
||||
|
||||
Some npm scripts are available:
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ to be called in the constructor.
|
||||
}
|
||||
```
|
||||
|
||||
- **`style`** (string, optional): it should be the return value of the [`css` tag](tags.md#css-tag),
|
||||
- **`style`** (string, optional): it should be the return value of the [`css tag](tags.md#css-tag),
|
||||
which is used to inject stylesheet whenever the component is visible on the
|
||||
screen.
|
||||
|
||||
@@ -425,7 +425,7 @@ likely via a change in its state/props or environment).
|
||||
|
||||
This method is not called on the initial render. It is useful to interact
|
||||
with the DOM (for example, through an external library) whenever the
|
||||
component was patched. Note that this hook will not be called if the component is
|
||||
component was patched. Note that this hook will not be called if the compoent is
|
||||
not in the DOM.
|
||||
|
||||
Updating the component state in this hook is possible, but not encouraged.
|
||||
|
||||
@@ -23,7 +23,7 @@ a rendering that is no longer relevant, restart it, reuse it in some cases.
|
||||
But even though using concurrency is quite simple (and is the default behaviour),
|
||||
asynchrony is difficult, because it introduces an additional dimension that
|
||||
vastly increase the complexity of an application. This section will explain
|
||||
how Owl manages this complexity, how concurrent rendering works in a general way.
|
||||
how Owl manages this complexity, how concuurent rendering works in a general way.
|
||||
|
||||
## Rendering Components
|
||||
|
||||
@@ -120,7 +120,7 @@ Here is what Owl will do:
|
||||
1. hook `willUpdateProps` is called on `D` (async)
|
||||
2. template `D` is rerendered
|
||||
- component `F` is created:
|
||||
1. hook `willStart` is called on `F` (async)
|
||||
1. hook `willStart` is called on `E` (async)
|
||||
2. template `F` is rendered
|
||||
|
||||
3. `willPatch` hooks are called recursively on components `C`, `D` (not on `F`,
|
||||
|
||||
@@ -40,8 +40,8 @@ all templates.
|
||||
Whenever a root component `App` is mounted, Owl will setup a valid environment by
|
||||
following the next steps:
|
||||
|
||||
- take the `env` object defined on `App.env` (if no `env` was explicitly setup,
|
||||
this will return the empty `env` object defined on `Component`)
|
||||
- take the `env` object defined on `App.env` (if no `env` was explicitely setup,
|
||||
this will be return the empty `env` object defined on `Component`)
|
||||
- if `env.qweb` is not set, then Owl will create a `QWeb` instance.
|
||||
|
||||
The correct way to customize an environment is to simply set it up on the root
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
- [`useContext`](#usecontext)
|
||||
- [`useRef`](#useref)
|
||||
- [`useSubEnv`](#usesubenv)
|
||||
- [`useExternalListener`](#useexternallistener)
|
||||
- [`useStore`](#usestore)
|
||||
- [`useDispatch`](#usedispatch)
|
||||
- [`useGetters`](#usegetters)
|
||||
@@ -355,17 +354,6 @@ will be added to the parent environment. Note that it will extend, not replace
|
||||
the parent environment. And of course, the parent environment will not be
|
||||
affected.
|
||||
|
||||
### `useExternalListener`
|
||||
|
||||
The `useExternalListener` hook helps solve a very common problem: adding and removing
|
||||
a listener on some target whenever a component is mounted/unmounted. For example,
|
||||
a dropdown menu (or its parent) may need to listen to a `click` event on `window`
|
||||
to be closed:
|
||||
|
||||
```js
|
||||
useExternalListener(window, "click", this.closeMenu);
|
||||
```
|
||||
|
||||
### `useStore`
|
||||
|
||||
The `useStore` hook is the entry point for a component to connect to the store.
|
||||
|
||||
+1
-13
@@ -208,11 +208,7 @@ Note that getters are not cached.
|
||||
### Connecting a Component
|
||||
|
||||
At some point, we need a way to interact with the store from a component. This
|
||||
means that the component needs a reference to the store. By default, it looks
|
||||
for it in the `env.store` key. However, this can be configured with the `useStore`
|
||||
hook.
|
||||
|
||||
Every component-store interactions are done with the help of the three store hooks:
|
||||
can be done with the help of the three store hooks:
|
||||
|
||||
- [`useStore`](#usestore) to subscribe a component to some part of the store state,
|
||||
- [`useDispatch`](#usedispatch) to get a reference to a dispatch function,
|
||||
@@ -233,14 +229,6 @@ const state = {
|
||||
const store = new owl.Store({ state, actions });
|
||||
```
|
||||
|
||||
To make it accessible to the complete application, we will put it in the
|
||||
environment:
|
||||
|
||||
```js
|
||||
// in this example, the root component is App
|
||||
App.env.store = store;
|
||||
```
|
||||
|
||||
A counter component can then select this value and dispatch an action like this:
|
||||
|
||||
```js
|
||||
|
||||
@@ -101,52 +101,6 @@ will be formatted as:
|
||||
}
|
||||
```
|
||||
|
||||
This extension brings another useful feature: the `&` selector which refers to
|
||||
the parent selector. For example, we want our component to be red when hovered.
|
||||
We would like to write something like:
|
||||
|
||||
```scss
|
||||
.my-component {
|
||||
display: block;
|
||||
:hover {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
but it will be formatted as:
|
||||
|
||||
```css
|
||||
.my-component {
|
||||
display: block;
|
||||
}
|
||||
.my-component :hover {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
The `&` selector can be used to solve this problem:
|
||||
|
||||
```scss
|
||||
.my-component {
|
||||
display: block;
|
||||
&:hover {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
will be formatted as:
|
||||
|
||||
```css
|
||||
.my-component {
|
||||
display: block;
|
||||
}
|
||||
.my-component:hover {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
Now, there is no additional processing done by the `css` tag. However, since it
|
||||
is done in javascript at runtime, we actually have more power. For example:
|
||||
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ 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:
|
||||
|
||||
```
|
||||
function debugOwl(t,e){let n,o="[OWL_DEBUG]";function r(t){let e;try{e=JSON.stringify(t||{})}catch(t){e="<JSON error>"}return e.length>200&&(e=e.slice(0,200)+"..."),e}if(Object.defineProperty(t.Component,"current",{get:()=>n,set(s){n=s;const i=s.constructor.name;if(e.componentBlackList&&e.componentBlackList.test(i))return;if(e.componentWhiteList&&!e.componentWhiteList.test(i))return;let l;Object.defineProperty(n,"__owl__",{get:()=>l,set(n){!function(n,s,i){let l=`${s}<id=${i}>`,c=t=>console.log(`${o} ${l} ${t}`),u=t=>(!e.methodBlackList||!e.methodBlackList.includes(t))&&!(e.methodWhiteList&&!e.methodWhiteList.includes(t));u("constructor")&&c(`constructor, props=${r(n.props)}`);u("willStart")&&t.hooks.onWillStart(()=>{c("willStart")});u("mounted")&&t.hooks.onMounted(()=>{c("mounted")});u("willUpdateProps")&&t.hooks.onWillUpdateProps(t=>{c(`willUpdateProps, nextprops=${r(t)}`)});u("willPatch")&&t.hooks.onWillPatch(()=>{c("willPatch")});u("patched")&&t.hooks.onPatched(()=>{c("patched")});u("willUnmount")&&t.hooks.onWillUnmount(()=>{c("willUnmount")});const d=n.__render.bind(n);n.__render=function(...t){c("rendering template"),d(...t)};const h=n.render.bind(n);n.render=function(...t){const e=n.__owl__;let o="render";return e.isMounted||e.currentFiber||(o+=" (warning: component is not mounted, this render has no effect)"),c(o),h(...t)};const p=n.mount.bind(n);n.mount=function(...t){return c("mount"),p(...t)}}(s,i,(l=n).id)}})}}),e.logScheduler){let e=t.Component.scheduler.start,n=t.Component.scheduler.stop;t.Component.scheduler.start=function(){this.isRunning||console.log(`${o} scheduler: start running tasks queue`),e.call(this)},t.Component.scheduler.stop=function(){this.isRunning&&console.log(`${o} scheduler: stop running tasks queue`),n.call(this)}}if(e.logStore){let e=t.Store.prototype.dispatch;t.Store.prototype.dispatch=function(t,...n){return console.log(`${o} store: action '${t}' dispatched. Payload: '${r(n)}'`),e.call(this,t,...n)}}}
|
||||
function debugOwl(t,n){let e,o="[OWL_DEBUG]";function s(t){let n=JSON.stringify(t||{});return n.length>200&&(n=n.slice(0,200)+"..."),n}if(Object.defineProperty(t.Component,"current",{get:()=>e,set(i){e=i;const r=i.constructor.name;if(n.componentBlackList&&n.componentBlackList.test(r))return;if(n.componentWhiteList&&!n.componentWhiteList.test(r))return;let l;Object.defineProperty(e,"__owl__",{get:()=>l,set(e){!function(e,i,r){let l=`${i}<id=${r}>`,c=t=>console.log(`${o} ${l} ${t}`),u=t=>(!n.methodBlackList||!n.methodBlackList.includes(t))&&!(n.methodWhiteList&&!n.methodWhiteList.includes(t));u("constructor")&&c(`constructor, props=${s(e.props)}`);u("willStart")&&t.hooks.onWillStart(()=>{c("willStart")});u("mounted")&&t.hooks.onMounted(()=>{c("mounted")});u("willUpdateProps")&&t.hooks.onWillUpdateProps(t=>{c(`willUpdateProps, nextprops=${s(t)}`)});u("willPatch")&&t.hooks.onWillPatch(()=>{c("willPatch")});u("patched")&&t.hooks.onPatched(()=>{c("patched")});u("willUnmount")&&t.hooks.onWillUnmount(()=>{c("willUnmount")});const d=e.__render.bind(e);e.__render=function(...t){c("rendering template"),d(...t)};const h=e.render.bind(e);e.render=function(...t){const n=e.__owl__;let o="render";return n.isMounted||n.currentFiber||(o+=" (warning: component is not mounted, this render has no effect)"),c(o),h(...t)};const p=e.mount.bind(e);e.mount=function(...t){return c("mount"),p(...t)}}(i,r,(l=e).id)}})}}),n.logScheduler){let n=t.Component.scheduler.start,e=t.Component.scheduler.stop;t.Component.scheduler.start=function(){this.isRunning||console.log(`${o} scheduler: start running tasks queue`),n.call(this)},t.Component.scheduler.stop=function(){this.isRunning&&console.log(`${o} scheduler: stop running tasks queue`),e.call(this)}}if(n.logStore){let n=t.Store.prototype.dispatch;t.Store.prototype.dispatch=function(t,...e){return console.log(`${o} store: action '${t}' dispatched. Payload: '${s(e)}'`),n.call(this,t,...e)}}}
|
||||
debugOwl(owl, {
|
||||
// componentBlackList: /App/, // regexp
|
||||
// componentWhiteList: /SomeComponent/, // regexp
|
||||
|
||||
+6
-12
@@ -1,13 +1,11 @@
|
||||
{
|
||||
"name": "@odoo/owl",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.0-beta5",
|
||||
"description": "Odoo Web Library (OWL)",
|
||||
"main": "dist/owl.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist/types/",
|
||||
"dist/owl.js"
|
||||
],
|
||||
"main": "dist/owl/index.js",
|
||||
"types": "dist/owl/index.d.ts",
|
||||
"prepublish": "npm run build",
|
||||
"files": ["dist/owl/"],
|
||||
"engines": {
|
||||
"node": ">=10.15.3"
|
||||
},
|
||||
@@ -23,9 +21,7 @@
|
||||
"tools": "npm run build && npm run tools:serve",
|
||||
"pretools:watch": "npm run build",
|
||||
"tools:watch": "npm-run-all --parallel tools:serve \"build:* -- --watch\"",
|
||||
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write",
|
||||
"publish": "npm run buildcommonjs && npm publish",
|
||||
"release": "node tools/release.js"
|
||||
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -40,10 +36,8 @@
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^23.3.12",
|
||||
"chalk": "^3.0.0",
|
||||
"cpx": "^1.5.0",
|
||||
"git-rev-sync": "^1.12.0",
|
||||
"github-api": "^3.3.0",
|
||||
"jest": "^23.6.0",
|
||||
"jest-environment-jsdom": "^24.7.1",
|
||||
"live-server": "^1.2.1",
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
# 🦉 OWL Roadmap 🦉
|
||||
|
||||
- Current version: 1.0.4
|
||||
- Status: stable
|
||||
- Current version: 1.0.0-beta5
|
||||
- Status: mostly stable
|
||||
|
||||
This roadmap is only an attempt at predicting Owl's future. Everything may
|
||||
change!
|
||||
|
||||
@@ -302,14 +302,7 @@ export class Component<T extends Env, Props extends {}> {
|
||||
const position = options.position || "last-child";
|
||||
const __owl__ = this.__owl__;
|
||||
if (__owl__.isMounted) {
|
||||
if (position !== "self" && this.el!.parentNode !== target) {
|
||||
// in this situation, we are trying to mount a component on a different
|
||||
// target. In this case, we need to unmount first, otherwise it will
|
||||
// not work.
|
||||
this.unmount();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) {
|
||||
let message = `Component '${this.constructor.name}' cannot be mounted: the target is not a valid DOM node.`;
|
||||
|
||||
@@ -15,16 +15,13 @@ export function processSheet(str: string): string {
|
||||
function generateSelector(stackIndex: number, parentSelector?: string) {
|
||||
const parts: string[] = [];
|
||||
for (const selector of selectorStack[stackIndex]) {
|
||||
let part = (parentSelector && parentSelector + " " + selector) || selector;
|
||||
if (part.includes("&")) {
|
||||
part = selector.replace(/&/g, parentSelector || "");
|
||||
}
|
||||
let part = parentSelector && parentSelector + ' ' + selector || selector;
|
||||
if (stackIndex < selectorStack.length - 1) {
|
||||
part = generateSelector(stackIndex + 1, part);
|
||||
}
|
||||
parts.push(part);
|
||||
}
|
||||
return parts.join(", ");
|
||||
return parts.join(', ');
|
||||
}
|
||||
function generateRules() {
|
||||
if (rules.length) {
|
||||
|
||||
@@ -131,32 +131,3 @@ export function useSubEnv(nextEnv) {
|
||||
const component = Component.current!;
|
||||
component.env = Object.assign(Object.create(component.env), nextEnv);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// useExternalListener
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* When a component needs to listen to DOM Events on element(s) that are not
|
||||
* part of his hierarchy, we can use the `useExternalListener` hook.
|
||||
* It will correctly add and remove the event listener, whenever the
|
||||
* component is mounted and unmounted.
|
||||
*
|
||||
* Example:
|
||||
* a menu needs to listen to the click on window to be closed automatically
|
||||
*
|
||||
* Usage:
|
||||
* in the constructor of the OWL component that needs to be notified,
|
||||
* `useExternalListener(window, 'click', this._doSomething);`
|
||||
* */
|
||||
export function useExternalListener(
|
||||
target: HTMLElement | typeof window,
|
||||
eventName: string,
|
||||
handler,
|
||||
eventParams?
|
||||
) {
|
||||
const boundHandler = handler.bind(Component.current);
|
||||
|
||||
onMounted(() => target.addEventListener(eventName, boundHandler, eventParams));
|
||||
onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams));
|
||||
}
|
||||
|
||||
@@ -128,12 +128,7 @@ QWeb.addDirective({
|
||||
qwebvar.expr = `scope.${variable}`;
|
||||
if (value) {
|
||||
const formattedValue = ctx.formatExpression(value);
|
||||
let scopeExpr = `scope`;
|
||||
if (ctx.protectedScopeNumber) {
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
scopeExpr = `utils.getScope(scope, '${variable}')`;
|
||||
}
|
||||
ctx.addLine(`${scopeExpr}.${variable} = ${formattedValue};`);
|
||||
ctx.addLine(`${qwebvar.expr} = ${formattedValue};`);
|
||||
qwebvar.value = formattedValue;
|
||||
}
|
||||
|
||||
@@ -270,9 +265,9 @@ QWeb.addDirective({
|
||||
// ------------------------------------------------
|
||||
const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)";
|
||||
const parentComponent = `utils.getComponent(context)`;
|
||||
const key = ctx.generateTemplateKey();
|
||||
const keyCode = ctx.loopNumber || ctx.hasKey0 ? `, key: ${ctx.generateTemplateKey()}` : "";
|
||||
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
|
||||
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`;
|
||||
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}${keyCode}})`;
|
||||
if (ctx.parentNode) {
|
||||
ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
|
||||
} else {
|
||||
@@ -318,7 +313,7 @@ QWeb.addDirective({
|
||||
ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`);
|
||||
ctx.closeIf();
|
||||
ctx.addLine(`let _length${keysID} = _${keysID}.length;`);
|
||||
let varsID = ctx.startProtectScope(true);
|
||||
let varsID = ctx.startProtectScope();
|
||||
const loopVar = `i${ctx.loopNumber}`;
|
||||
ctx.addLine(`for (let ${loopVar} = 0; ${loopVar} < _length${keysID}; ${loopVar}++) {`);
|
||||
ctx.indent();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compileExpr, compileExprToArray, QWebVar } from "./expression_parser";
|
||||
import { compileExpr, QWebVar } from "./expression_parser";
|
||||
|
||||
export const INTERP_REGEXP = /\{\{.*?\}\}/g;
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -17,7 +17,6 @@ export class CompilationContext {
|
||||
rootContext: CompilationContext;
|
||||
shouldDefineParent: boolean = false;
|
||||
shouldDefineScope: boolean = false;
|
||||
protectedScopeNumber: number = 0;
|
||||
shouldDefineQWeb: boolean = false;
|
||||
shouldDefineUtils: boolean = false;
|
||||
shouldDefineRefs: boolean = false;
|
||||
@@ -156,24 +155,6 @@ export class CompilationContext {
|
||||
this.rootContext.shouldDefineScope = true;
|
||||
return compileExpr(expr, this.variables);
|
||||
}
|
||||
captureExpression(expr: string): string {
|
||||
this.rootContext.shouldDefineScope = true;
|
||||
const argId = this.generateID();
|
||||
const tokens = compileExprToArray(expr, this.variables);
|
||||
const done = new Set();
|
||||
return tokens
|
||||
.map(tok => {
|
||||
if (tok.varName) {
|
||||
if (!done.has(tok.varName)) {
|
||||
done.add(tok.varName);
|
||||
this.addLine(`const ${tok.varName}_${argId} = ${tok.value};`);
|
||||
}
|
||||
tok.value = `${tok.varName}_${argId}`;
|
||||
}
|
||||
return tok.value;
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform string interpolation on the given string. Note that if the whole
|
||||
@@ -192,19 +173,18 @@ export class CompilationContext {
|
||||
let r = s.replace(/\{\{.*?\}\}/g, s => "${" + this.formatExpression(s.slice(2, -2)) + "}");
|
||||
return "`" + r + "`";
|
||||
}
|
||||
startProtectScope(codeBlock?: boolean): number {
|
||||
startProtectScope(): number {
|
||||
const protectID = this.generateID();
|
||||
this.rootContext.protectedScopeNumber++;
|
||||
this.rootContext.shouldDefineScope = true;
|
||||
const scopeExpr = codeBlock
|
||||
? `Object.create(scope);`
|
||||
: `Object.assign(Object.create(context), scope);`;
|
||||
this.addLine(`let _origScope${protectID} = scope;`);
|
||||
this.addLine(`scope = ${scopeExpr}`);
|
||||
this.addLine(`let _origScope${protectID} = scope;`);
|
||||
// dict of origscopes
|
||||
// export protectID in compiled string form
|
||||
// fetch in subsequent code the relevant scope
|
||||
this.addLine(`scope = Object.assign(Object.create(context), scope);`);
|
||||
return protectID;
|
||||
}
|
||||
stopProtectScope(protectID: number) {
|
||||
this.rootContext.protectedScopeNumber--;
|
||||
this.addLine(`scope = _origScope${protectID};`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,6 @@ interface Token {
|
||||
value: string;
|
||||
originalValue?: string;
|
||||
size?: number;
|
||||
varName?: string;
|
||||
}
|
||||
|
||||
const STATIC_TOKEN_MAP: { [key: string]: TKind } = {
|
||||
@@ -82,7 +81,7 @@ const STATIC_TOKEN_MAP: { [key: string]: TKind } = {
|
||||
|
||||
// note that the space after typeof is relevant. It makes sure that the formatted
|
||||
// expression has a space after typeof
|
||||
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ".split(",");
|
||||
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;".split(",");
|
||||
|
||||
type Tokenizer = (expr: string) => Token | false;
|
||||
|
||||
@@ -235,7 +234,7 @@ export function tokenize(expr: string): Token[] {
|
||||
* the arrow operator, then we add the current (or some previous tokens) token to
|
||||
* the list of variables so it does not get replaced by a lookup in the context
|
||||
*/
|
||||
export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar }): Token[] {
|
||||
export function compileExpr(expr: string, scope: { [key: string]: QWebVar }): string {
|
||||
scope = Object.create(scope);
|
||||
const tokens = tokenize(expr);
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
@@ -270,7 +269,6 @@ export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar
|
||||
}
|
||||
|
||||
if (isVar) {
|
||||
token.varName = token.value;
|
||||
if (token.value in scope && "id" in scope[token.value]) {
|
||||
token.value = scope[token.value].expr!;
|
||||
} else {
|
||||
@@ -279,11 +277,5 @@ export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar
|
||||
}
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
export function compileExpr(expr: string, scope: { [key: string]: QWebVar }): string {
|
||||
return compileExprToArray(expr, scope)
|
||||
.map(t => t.value)
|
||||
.join("");
|
||||
return tokens.map(t => t.value).join("");
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export function makeHandlerCode(
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
const comp = `utils.getComponent(context)`;
|
||||
if (args) {
|
||||
const argId = ctx.generateID();
|
||||
let argId = ctx.generateID();
|
||||
ctx.addLine(`let args${argId} = [${ctx.formatExpression(args)}];`);
|
||||
code = `${comp}['${name}'](...args${argId}, e);`;
|
||||
putInCache = false;
|
||||
@@ -67,9 +67,8 @@ export function makeHandlerCode(
|
||||
}
|
||||
} else {
|
||||
// if we get here, then it is an expression
|
||||
// we need to capture every variable in it
|
||||
putInCache = false;
|
||||
code = ctx.captureExpression(value);
|
||||
code = ctx.formatExpression(value);
|
||||
}
|
||||
const modCode = mods.map(mod => modcodes[mod]).join("");
|
||||
let handler = `function (e) {if (!context.__owl__.isMounted){return}${modCode}${code}}`;
|
||||
|
||||
+2
-16
@@ -86,10 +86,6 @@ interface Utils {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
function isComponent(obj) {
|
||||
return obj && obj.hasOwnProperty("__owl__");
|
||||
}
|
||||
|
||||
const UTILS: Utils = {
|
||||
zero: Symbol("zero"),
|
||||
toObj(expr) {
|
||||
@@ -126,21 +122,10 @@ const UTILS: Utils = {
|
||||
.join("");
|
||||
},
|
||||
getComponent(obj) {
|
||||
while (obj && !isComponent(obj)) {
|
||||
while (obj && !obj.hasOwnProperty("__owl__")) {
|
||||
obj = obj.__proto__;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
getScope(obj, property: string) {
|
||||
const obj0 = obj;
|
||||
while (obj && !obj.hasOwnProperty(property)) {
|
||||
const newObj = obj.__proto__;
|
||||
if (!newObj || isComponent(newObj)) {
|
||||
return obj0;
|
||||
}
|
||||
obj = newObj;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -298,6 +283,7 @@ export class QWeb extends EventBus {
|
||||
fn: function(this: QWeb, context, extra) {
|
||||
const compiledFunction = this._compile(name, elem);
|
||||
template.fn = compiledFunction;
|
||||
console.warn('CompiledFn', compiledFunction.toString());
|
||||
return compiledFunction.call(this, context, extra);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`class and style attributes with t-component dynamic t-att-style is properly added and updated on widget root el 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"ParentWidget\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'child'
|
||||
const _4 = scope['state'].style;
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined).then(()=>{if (w2.__owl__.isDestroyed) {return};w2.el.style=_4;});;
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`child\`;
|
||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['child'];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _4;}};});
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v2) 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"ParentWidget\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
context.__owl__.refs = context.__owl__.refs || {};
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Child'
|
||||
const ref4 = \`child\`;
|
||||
let _5 = {'a':true};
|
||||
Object.assign(_5, {b:scope['state'].b})
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined);
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`Child\`;
|
||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {insert(vn) {context.__owl__.refs[ref4] = w2;},remove() {},destroy(vn) {w2.destroy();delete context.__owl__.refs[ref4];}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.classObj=_5;
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v2) 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Child\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _7 = {'c':true};
|
||||
Object.assign(_7, utils.toObj({d:scope['state'].d}))
|
||||
let c8 = [], p8 = {key:8,class:_7};
|
||||
let vn8 = h('span', p8, c8);
|
||||
return vn8;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v3) 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"ParentWidget\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
context.__owl__.refs = context.__owl__.refs || {};
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Child'
|
||||
const ref4 = \`child\`;
|
||||
let _5 = {'a':true};
|
||||
Object.assign(_5, utils.toObj(scope['state'].b?'b':''))
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined);
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`Child\`;
|
||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {insert(vn) {context.__owl__.refs[ref4] = w2;},remove() {},destroy(vn) {w2.destroy();delete context.__owl__.refs[ref4];}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.classObj=_5;
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v3) 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Child\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _7 = {'c':true};
|
||||
Object.assign(_7, utils.toObj(scope['state'].d?'d':''))
|
||||
let c8 = [], p8 = {key:8,class:_7};
|
||||
let vn8 = h('span', p8, c8);
|
||||
return vn8;
|
||||
}"
|
||||
`;
|
||||
@@ -58,7 +58,7 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.section_first = i1 === 0
|
||||
scope.section_last = i1 === _length3 - 1
|
||||
@@ -75,7 +75,7 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
|
||||
}
|
||||
let _length7 = _7.length;
|
||||
let _origScope9 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i2 = 0; i2 < _length7; i2++) {
|
||||
scope.blip_first = i2 === 0
|
||||
scope.blip_last = i2 === _length7 - 1
|
||||
@@ -257,6 +257,161 @@ exports[`basic widget properties t-key on a component with t-if, and a sibling c
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component dynamic t-att-style is properly added and updated on widget root el 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"ParentWidget\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'child'
|
||||
const _4 = scope['state'].style;
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined).then(()=>{if (w2.__owl__.isDestroyed) {return};w2.el.style=_4;});;
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`child\`;
|
||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['child'];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _4;}};});
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v2) 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"ParentWidget\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
context.__owl__.refs = context.__owl__.refs || {};
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Child'
|
||||
const ref4 = \`child\`;
|
||||
let _5 = {'a':true};
|
||||
Object.assign(_5, {b:scope['state'].b})
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined);
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`Child\`;
|
||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {insert(vn) {context.__owl__.refs[ref4] = w2;},remove() {},destroy(vn) {w2.destroy();delete context.__owl__.refs[ref4];}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.classObj=_5;
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v2) 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Child\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _7 = {'c':true};
|
||||
Object.assign(_7, utils.toObj({d:scope['state'].d}))
|
||||
let c8 = [], p8 = {key:8,class:_7};
|
||||
let vn8 = h('span', p8, c8);
|
||||
return vn8;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v3) 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"ParentWidget\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
context.__owl__.refs = context.__owl__.refs || {};
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Child'
|
||||
const ref4 = \`child\`;
|
||||
let _5 = {'a':true};
|
||||
Object.assign(_5, utils.toObj(scope['state'].b?'b':''))
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined);
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`Child\`;
|
||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {insert(vn) {context.__owl__.refs[ref4] = w2;},remove() {},destroy(vn) {w2.destroy();delete context.__owl__.refs[ref4];}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.classObj=_5;
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v3) 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Child\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _7 = {'c':true};
|
||||
Object.assign(_7, utils.toObj(scope['state'].d?'d':''))
|
||||
let c8 = [], p8 = {key:8,class:_7};
|
||||
let vn8 = h('span', p8, c8);
|
||||
return vn8;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
@@ -277,7 +432,7 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.number_first = i1 === 0
|
||||
scope.number_last = i1 === _length3 - 1
|
||||
@@ -432,256 +587,6 @@ exports[`dynamic t-props basic use 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component slot setted value (with t-set) not accessible with t-esc 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__2\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.iter = 'source';
|
||||
let c2 = [], p2 = {key:2};
|
||||
let vn2 = h('p', p2, c2);
|
||||
c1.push(vn2);
|
||||
if (scope.iter != null) {
|
||||
c2.push({text: scope.iter});
|
||||
}
|
||||
// Component 'ChildWidget'
|
||||
let w3 = '__4__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__4__']] : false;
|
||||
let props3 = {};
|
||||
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
|
||||
w3.destroy();
|
||||
w3 = false;
|
||||
}
|
||||
if (w3) {
|
||||
w3.__updateProps(props3, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey3 = \`ChildWidget\`;
|
||||
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['ChildWidget'];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap['__4__'] = w3.__owl__.id;
|
||||
w3.__owl__.slotId = 1;
|
||||
let fiber = w3.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w3.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
let c5 = [], p5 = {key:5};
|
||||
let vn5 = h('p', p5, c5);
|
||||
c1.push(vn5);
|
||||
if (scope.iter != null) {
|
||||
c5.push({text: scope.iter});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-on expression captured in t-foreach 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.iter = 0;
|
||||
let _2 = scope['arr'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.val_first = i1 === 0
|
||||
scope.val_last = i1 === _length3 - 1
|
||||
scope.val_index = i1
|
||||
scope.val = _3[i1]
|
||||
scope.val_value = _4[i1]
|
||||
let key1 = scope['val'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('div', p6, c6);
|
||||
c1.push(vn6);
|
||||
let c7 = [], p7 = {key:\`\${key1}_7\`,on:{}};
|
||||
let vn7 = h('button', p7, c7);
|
||||
c6.push(vn7);
|
||||
const otherState_8 = scope['otherState'];
|
||||
const iter_8 = scope.iter;
|
||||
p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}otherState_8.vals.push(iter_8+'_'+iter_8)};
|
||||
c7.push({text: \`expr\`});
|
||||
utils.getScope(scope, 'iter').iter = scope.iter+1;
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-on expression in t-foreach 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['state'].values;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.val_first = i1 === 0
|
||||
scope.val_last = i1 === _length3 - 1
|
||||
scope.val_index = i1
|
||||
scope.val = _3[i1]
|
||||
scope.val_value = _4[i1]
|
||||
let key1 = scope['val'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('div', p6, c6);
|
||||
c1.push(vn6);
|
||||
let _7 = scope['val_index'];
|
||||
if (_7 != null) {
|
||||
c6.push({text: _7});
|
||||
}
|
||||
c6.push({text: \`: \`});
|
||||
let _8 = scope['val']+'';
|
||||
if (_8 != null) {
|
||||
c6.push({text: _8});
|
||||
}
|
||||
let c9 = [], p9 = {key:\`\${key1}_9\`,on:{}};
|
||||
let vn9 = h('button', p9, c9);
|
||||
c6.push(vn9);
|
||||
const otherState_10 = scope['otherState'];
|
||||
const val_10 = scope['val'];
|
||||
p9.on['click'] = function (e) {if (!context.__owl__.isMounted){return}otherState_10.vals.push(val_10)};
|
||||
c9.push({text: \`Expr\`});
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-on expression in t-foreach with t-set 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.bossa = 'nova';
|
||||
let _2 = scope['state'].values;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.val_first = i1 === 0
|
||||
scope.val_last = i1 === _length3 - 1
|
||||
scope.val_index = i1
|
||||
scope.val = _3[i1]
|
||||
scope.val_value = _4[i1]
|
||||
let key1 = scope['val'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('div', p6, c6);
|
||||
c1.push(vn6);
|
||||
utils.getScope(scope, 'bossa').bossa = scope.bossa+'_'+scope['val_index'];
|
||||
let _7 = scope['val_index'];
|
||||
if (_7 != null) {
|
||||
c6.push({text: _7});
|
||||
}
|
||||
c6.push({text: \`: \`});
|
||||
let _8 = scope['val']+'';
|
||||
if (_8 != null) {
|
||||
c6.push({text: _8});
|
||||
}
|
||||
let c9 = [], p9 = {key:\`\${key1}_9\`,on:{}};
|
||||
let vn9 = h('button', p9, c9);
|
||||
c6.push(vn9);
|
||||
const otherState_10 = scope['otherState'];
|
||||
const val_10 = scope['val'];
|
||||
const bossa_10 = scope.bossa;
|
||||
p9.on['click'] = function (e) {if (!context.__owl__.isMounted){return}otherState_10.vals.push(val_10+'_'+bossa_10)};
|
||||
c9.push({text: \`Expr\`});
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-on method call in t-foreach 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['state'].values;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.val_first = i1 === 0
|
||||
scope.val_last = i1 === _length3 - 1
|
||||
scope.val_index = i1
|
||||
scope.val = _3[i1]
|
||||
scope.val_value = _4[i1]
|
||||
let key1 = scope['val'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('div', p6, c6);
|
||||
c1.push(vn6);
|
||||
let _7 = scope['val_index'];
|
||||
if (_7 != null) {
|
||||
c6.push({text: _7});
|
||||
}
|
||||
c6.push({text: \`: \`});
|
||||
let _8 = scope['val']+'';
|
||||
if (_8 != null) {
|
||||
c6.push({text: _8});
|
||||
}
|
||||
let c9 = [], p9 = {key:\`\${key1}_9\`,on:{}};
|
||||
let vn9 = h('button', p9, c9);
|
||||
c6.push(vn9);
|
||||
let args10 = [scope['val']];
|
||||
p9.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['addVal'](...args10, e);};
|
||||
c9.push({text: \`meth call\`});
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-on with getter as handler 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
@@ -896,7 +801,6 @@ exports[`other directives with t-component t-on with inline statement 1`] = `
|
||||
c1.push({text: _2});
|
||||
}
|
||||
// Component 'Child'
|
||||
const state_5 = scope['state'];
|
||||
let w3 = '__4__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__4__']] : false;
|
||||
let props3 = {};
|
||||
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
|
||||
@@ -913,7 +817,7 @@ exports[`other directives with t-component t-on with inline statement 1`] = `
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap['__4__'] = w3.__owl__.id;
|
||||
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (!context.__owl__.isMounted){return}state_5.counter++});}};});
|
||||
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (!context.__owl__.isMounted){return}scope['state'].counter++});}};});
|
||||
let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
@@ -1113,195 +1017,6 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-set can't alter component 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
let vn2 = h('p', p2, c2);
|
||||
c1.push(vn2);
|
||||
let _3 = scope['iter'];
|
||||
if (_3 != null) {
|
||||
c2.push({text: _3});
|
||||
}
|
||||
scope.iter = 5;
|
||||
let c4 = [], p4 = {key:4};
|
||||
let vn4 = h('p', p4, c4);
|
||||
c1.push(vn4);
|
||||
if (scope.iter != null) {
|
||||
c4.push({text: scope.iter});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-set can't alter from within callee 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.iter = 'source';
|
||||
let c2 = [], p2 = {key:2};
|
||||
let vn2 = h('p', p2, c2);
|
||||
c1.push(vn2);
|
||||
if (scope.iter != null) {
|
||||
c2.push({text: scope.iter});
|
||||
}
|
||||
this.subTemplates['ChildWidget'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
let c5 = [], p5 = {key:5};
|
||||
let vn5 = h('p', p5, c5);
|
||||
c1.push(vn5);
|
||||
if (scope.iter != null) {
|
||||
c5.push({text: scope.iter});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-set can't alter in t-call body 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.iter = 'source';
|
||||
let c2 = [], p2 = {key:2};
|
||||
let vn2 = h('p', p2, c2);
|
||||
c1.push(vn2);
|
||||
if (scope.iter != null) {
|
||||
c2.push({text: scope.iter});
|
||||
}
|
||||
{
|
||||
let _origScope4 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'iter').iter = 'inCall';
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['ChildWidget'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
|
||||
scope = _origScope4;
|
||||
}
|
||||
let c6 = [], p6 = {key:6};
|
||||
let vn6 = h('p', p6, c6);
|
||||
c1.push(vn6);
|
||||
if (scope.iter != null) {
|
||||
c6.push({text: scope.iter});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-set not altered by child widget 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__2\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.iter = 'source';
|
||||
let c2 = [], p2 = {key:2};
|
||||
let vn2 = h('p', p2, c2);
|
||||
c1.push(vn2);
|
||||
if (scope.iter != null) {
|
||||
c2.push({text: scope.iter});
|
||||
}
|
||||
// Component 'ChildWidget'
|
||||
let w3 = '__4__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__4__']] : false;
|
||||
let props3 = {};
|
||||
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
|
||||
w3.destroy();
|
||||
w3 = false;
|
||||
}
|
||||
if (w3) {
|
||||
w3.__updateProps(props3, extra.fiber, undefined);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey3 = \`ChildWidget\`;
|
||||
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['ChildWidget'];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap['__4__'] = w3.__owl__.id;
|
||||
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w3.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
let c5 = [], p5 = {key:5};
|
||||
let vn5 = h('p', p5, c5);
|
||||
c1.push(vn5);
|
||||
if (scope.iter != null) {
|
||||
c5.push({text: scope.iter});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`other directives with t-component t-set outside modified in t-foreach 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.iter = 0;
|
||||
let _2 = scope['state'].values;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.val_first = i1 === 0
|
||||
scope.val_last = i1 === _length3 - 1
|
||||
scope.val_index = i1
|
||||
scope.val = _3[i1]
|
||||
scope.val_value = _4[i1]
|
||||
let key1 = scope['val'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('p', p6, c6);
|
||||
c1.push(vn6);
|
||||
c6.push({text: \`InLoop: \`});
|
||||
if (scope.iter != null) {
|
||||
c6.push({text: scope.iter});
|
||||
}
|
||||
utils.getScope(scope, 'iter').iter = scope.iter+1;
|
||||
}
|
||||
scope = _origScope5;
|
||||
let c7 = [], p7 = {key:7};
|
||||
let vn7 = h('p', p7, c7);
|
||||
c1.push(vn7);
|
||||
c7.push({text: \`EndLoop: \`});
|
||||
if (scope.iter != null) {
|
||||
c7.push({text: scope.iter});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`random stuff/miscellaneous can inject values in tagged templates 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
@@ -1311,7 +1026,7 @@ exports[`random stuff/miscellaneous can inject values in tagged templates 1`] =
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['__template__1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
this.subTemplates['__template__1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1378,7 +1093,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.item_first = i1 === 0
|
||||
scope.item_last = i1 === _length3 - 1
|
||||
@@ -1627,7 +1342,7 @@ exports[`t-model directive in a t-foreach 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.thing_first = i1 === 0
|
||||
scope.thing_last = i1 === _length3 - 1
|
||||
|
||||
@@ -263,7 +263,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
}
|
||||
let _length4 = _4.length;
|
||||
let _origScope6 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length4; i1++) {
|
||||
scope.user_first = i1 === 0
|
||||
scope.user_last = i1 === _length4 - 1
|
||||
@@ -360,7 +360,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
}
|
||||
let _length4 = _4.length;
|
||||
let _origScope6 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length4; i1++) {
|
||||
scope.user_first = i1 === 0
|
||||
scope.user_last = i1 === _length4 - 1
|
||||
@@ -371,7 +371,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
let c7 = [], p7 = {key:\`\${key1}_7\`};
|
||||
let vn7 = h('li', p7, c7);
|
||||
c2.push(vn7);
|
||||
utils.getScope(scope, 'userdescr').userdescr = 'User '+scope['user'].name;
|
||||
scope.userdescr = 'User '+scope['user'].name;
|
||||
// Component 'Link'
|
||||
let k9 = \`__9__\${key1}__\`;
|
||||
let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,271 +0,0 @@
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { QWeb } from "../../src/qweb/qweb";
|
||||
import { xml } from "../../src/tags";
|
||||
import { useState, useRef } from "../../src/hooks";
|
||||
import { makeTestFixture, makeTestEnv, nextTick } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// We create before each test:
|
||||
// - fixture: a div, appended to the DOM, intended to be the target of dom
|
||||
// manipulations. Note that it is removed after each test.
|
||||
// - env: a WEnv, necessary to create new components
|
||||
|
||||
let fixture: HTMLElement;
|
||||
let env: Env;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
env = makeTestEnv();
|
||||
Component.env = env;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.remove();
|
||||
});
|
||||
|
||||
describe("class and style attributes with t-component", () => {
|
||||
test("class is properly added on widget root el", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<div class="c"/>`;
|
||||
}
|
||||
class ParentWidget extends Component<any, any> {
|
||||
static template = xml`<div><Child class="a b"/></div>`;
|
||||
static components = { Child };
|
||||
}
|
||||
const widget = new ParentWidget();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div><div class="c a b"></div></div>`);
|
||||
});
|
||||
|
||||
test("empty class attribute is not added on widget root el", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span/>`;
|
||||
}
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`<div><Child class=""/></div>`;
|
||||
static components = { Child };
|
||||
}
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div><span></span></div>`);
|
||||
});
|
||||
|
||||
test("t-att-class is properly added/removed on widget root el", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<div class="c"/>`;
|
||||
}
|
||||
class ParentWidget extends Component<any, any> {
|
||||
static template = xml`<div><Child t-att-class="{a:state.a, b:state.b}"/></div>`;
|
||||
static components = { Child };
|
||||
state = useState({ a: true, b: false });
|
||||
}
|
||||
const widget = new ParentWidget();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div><div class="c a"></div></div>`);
|
||||
expect(QWeb.TEMPLATES[ParentWidget.template].fn.toString());
|
||||
|
||||
widget.state.a = false;
|
||||
widget.state.b = true;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(`<div><div class="c b"></div></div>`);
|
||||
});
|
||||
|
||||
test("class with extra whitespaces", async () => {
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`<div>
|
||||
<Child class="a b c d"/>
|
||||
</div>`
|
||||
);
|
||||
class Child extends Component<any, any> {}
|
||||
class ParentWidget extends Component<any, any> {
|
||||
static components = { Child };
|
||||
}
|
||||
env.qweb.addTemplate("Child", `<div/>`);
|
||||
const widget = new ParentWidget();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div><div class="a b c d"></div></div>`);
|
||||
});
|
||||
|
||||
test("t-att-class is properly added/removed on widget root el (v2)", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="ParentWidget">
|
||||
<Child class="a" t-att-class="{ b: state.b }" t-ref="child"/>
|
||||
</div>
|
||||
<span t-name="Child" class="c" t-att-class="{ d: state.d }"/>
|
||||
</templates>`);
|
||||
|
||||
class Child extends Component<any, any> {
|
||||
state = useState({ d: true });
|
||||
}
|
||||
class ParentWidget extends Component<any, any> {
|
||||
static components = { Child };
|
||||
state = useState({ b: true });
|
||||
child = useRef("child");
|
||||
}
|
||||
const widget = new ParentWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
const span = fixture.querySelector("span")!;
|
||||
expect(span.className).toBe("c d a b");
|
||||
|
||||
widget.state.b = false;
|
||||
await nextTick();
|
||||
expect(span.className).toBe("c d a");
|
||||
|
||||
(widget.child.comp as Child).state.d = false;
|
||||
await nextTick();
|
||||
expect(span.className).toBe("c a");
|
||||
|
||||
widget.state.b = true;
|
||||
await nextTick();
|
||||
expect(span.className).toBe("c a b");
|
||||
|
||||
(widget.child.comp as Child).state.d = true;
|
||||
await nextTick();
|
||||
expect(span.className).toBe("c a b d");
|
||||
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-att-class is properly added/removed on widget root el (v3)", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="ParentWidget">
|
||||
<Child class="a" t-att-class="state.b ? 'b' : ''" t-ref="child"/>
|
||||
</div>
|
||||
<span t-name="Child" class="c" t-att-class="state.d ? 'd' : ''"/>
|
||||
</templates>`);
|
||||
|
||||
class Child extends Component<any, any> {
|
||||
state = useState({ d: true });
|
||||
}
|
||||
class ParentWidget extends Component<any, any> {
|
||||
static components = { Child };
|
||||
state = useState({ b: true });
|
||||
child = useRef("child");
|
||||
}
|
||||
const widget = new ParentWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
const span = fixture.querySelector("span")!;
|
||||
expect(span.className).toBe("c d a b");
|
||||
|
||||
widget.state.b = false;
|
||||
await nextTick();
|
||||
expect(span.className).toBe("c d a");
|
||||
|
||||
(widget.child.comp as Child).state.d = false;
|
||||
await nextTick();
|
||||
expect(span.className).toBe("c a");
|
||||
|
||||
widget.state.b = true;
|
||||
await nextTick();
|
||||
expect(span.className).toBe("c a b");
|
||||
|
||||
(widget.child.comp as Child).state.d = true;
|
||||
await nextTick();
|
||||
expect(span.className).toBe("c a b d");
|
||||
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("class on components do not interfere with user defined classes", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="App" t-att-class="{ c: state.c }" />
|
||||
</templates>`);
|
||||
|
||||
class App extends Component<any, any> {
|
||||
state = useState({ c: true });
|
||||
mounted() {
|
||||
this.el!.classList.add("user");
|
||||
}
|
||||
}
|
||||
|
||||
const widget = new App();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe('<div class="c user"></div>');
|
||||
|
||||
widget.state.c = false;
|
||||
await nextTick();
|
||||
|
||||
expect(fixture.innerHTML).toBe('<div class="user"></div>');
|
||||
});
|
||||
|
||||
test("style is properly added on widget root el", async () => {
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`
|
||||
<div>
|
||||
<t t-component="child" style="font-weight: bold;"/>
|
||||
</div>`
|
||||
);
|
||||
|
||||
class SomeComponent extends Component<any, any> {
|
||||
static template = xml`<div/>`;
|
||||
}
|
||||
|
||||
class ParentWidget extends Component<any, any> {
|
||||
static components = { child: SomeComponent };
|
||||
}
|
||||
const widget = new ParentWidget();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div><div style="font-weight: bold;"></div></div>`);
|
||||
});
|
||||
|
||||
test("dynamic t-att-style is properly added and updated on widget root el", async () => {
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`
|
||||
<div>
|
||||
<t t-component="child" t-att-style="state.style"/>
|
||||
</div>`
|
||||
);
|
||||
|
||||
class SomeComponent extends Component<any, any> {
|
||||
static template = xml`<div/>`;
|
||||
}
|
||||
|
||||
class ParentWidget extends Component<any, any> {
|
||||
static components = { child: SomeComponent };
|
||||
state = useState({ style: "font-size: 20px" });
|
||||
}
|
||||
const widget = new ParentWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
|
||||
|
||||
expect(fixture.innerHTML).toBe(`<div><div style="font-size: 20px;"></div></div>`);
|
||||
|
||||
widget.state.style = "font-size: 30px";
|
||||
await nextTick();
|
||||
|
||||
expect(fixture.innerHTML).toBe(`<div><div style="font-size: 30px;"></div></div>`);
|
||||
});
|
||||
|
||||
test("error in subcomponent with class", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<div t-esc="this.will.crash"/>`;
|
||||
}
|
||||
class ParentWidget extends Component<any, any> {
|
||||
static template = xml`<div><Child class="a"/></div>`;
|
||||
static components = { Child };
|
||||
}
|
||||
const widget = new ParentWidget();
|
||||
let error;
|
||||
try {
|
||||
await widget.mount(fixture);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
});
|
||||
});
|
||||
+2241
-344
File diff suppressed because it is too large
Load Diff
@@ -1,523 +0,0 @@
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { useState } from "../../src/hooks";
|
||||
import { xml } from "../../src/tags";
|
||||
import { makeTestEnv, makeTestFixture, nextTick } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// We create before each test:
|
||||
// - fixture: a div, appended to the DOM, intended to be the target of dom
|
||||
// manipulations. Note that it is removed after each test.
|
||||
// - env: a WEnv, necessary to create new components
|
||||
|
||||
let fixture: HTMLElement;
|
||||
let env: Env;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
env = makeTestEnv();
|
||||
Component.env = env;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.remove();
|
||||
});
|
||||
|
||||
describe("component error handling (catchError)", () => {
|
||||
/**
|
||||
* This test suite requires often to wait for 3 ticks. Here is why:
|
||||
* - First tick is to let the app render and crash.
|
||||
* - When we crash, we call the catchError handler in a setTimeout (because we
|
||||
* need to wait for the previous rendering to be completely stopped). So, we
|
||||
* need to wait for the second tick.
|
||||
* - Then, when the handler changes the state, we need to wait for the interface
|
||||
* to be rerendered.
|
||||
* */
|
||||
|
||||
test("can catch an error in a component render function", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
const handler = jest.fn();
|
||||
env.qweb.on("error", null, handler);
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
static template = xml`<div>hey<t t-esc="props.flag and state.this.will.crash"/></div>`;
|
||||
}
|
||||
class ErrorBoundary extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.error">Error handled</t>
|
||||
<t t-else=""><t t-slot="default" /></t>
|
||||
</div>`;
|
||||
state = useState({ error: false });
|
||||
|
||||
catchError() {
|
||||
this.state.error = true;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<ErrorBoundary><ErrorComponent flag="state.flag"/></ErrorBoundary>
|
||||
</div>`;
|
||||
state = useState({ flag: false });
|
||||
static components = { ErrorBoundary, ErrorComponent };
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div><div>heyfalse</div></div></div>");
|
||||
app.state.flag = true;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
expect(handler).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("no component catching error lead to full app destruction", async () => {
|
||||
expect.assertions(6);
|
||||
const handler = jest.fn();
|
||||
env.qweb.on("error", null, handler);
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
static template = xml`<div>hey<t t-esc="props.flag and state.this.will.crash"/></div>`;
|
||||
}
|
||||
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><ErrorComponent flag="state.flag"/></div>`;
|
||||
static components = { ErrorComponent };
|
||||
state = useState({ flag: false });
|
||||
async render() {
|
||||
try {
|
||||
await super.render();
|
||||
} catch (e) {
|
||||
expect(e.message).toBe("Cannot read property 'this' of undefined");
|
||||
}
|
||||
}
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div>heyfalse</div></div>");
|
||||
app.state.flag = true;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
expect(app.__owl__.isDestroyed).toBe(true);
|
||||
expect(handler).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("can catch an error in the initial call of a component render function (parent mounted)", async () => {
|
||||
const handler = jest.fn();
|
||||
env.qweb.on("error", null, handler);
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
static template = xml`<div>hey<t t-esc="state.this.will.crash"/></div>`;
|
||||
}
|
||||
class ErrorBoundary extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.error">Error handled</t>
|
||||
<t t-else=""><t t-slot="default" /></t>
|
||||
</div>`;
|
||||
state = useState({ error: false });
|
||||
|
||||
catchError() {
|
||||
this.state.error = true;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<ErrorBoundary><ErrorComponent /></ErrorBoundary>
|
||||
</div>`;
|
||||
static components = { ErrorBoundary, ErrorComponent };
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
expect(handler).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("can catch an error in the initial call of a component render function (parent updated)", async () => {
|
||||
const handler = jest.fn();
|
||||
env.qweb.on("error", null, handler);
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
static template = xml`<div>hey<t t-esc="state.this.will.crash"/></div>`;
|
||||
}
|
||||
class ErrorBoundary extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.error">Error handled</t>
|
||||
<t t-else=""><t t-slot="default" /></t>
|
||||
</div>`;
|
||||
state = useState({ error: false });
|
||||
|
||||
catchError() {
|
||||
this.state.error = true;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<ErrorBoundary t-if="state.flag"><ErrorComponent /></ErrorBoundary>
|
||||
</div>`;
|
||||
state = useState({ flag: false });
|
||||
static components = { ErrorBoundary, ErrorComponent };
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
app.state.flag = true;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
expect(handler).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("can catch an error in the constructor call of a component render function", async () => {
|
||||
const handler = jest.fn();
|
||||
env.qweb.on("error", null, handler);
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="ErrorBoundary">
|
||||
<t t-if="state.error">Error handled</t>
|
||||
<t t-else=""><t t-slot="default" /></t>
|
||||
</div>
|
||||
<div t-name="ErrorComponent">Some text</div>
|
||||
<div t-name="App">
|
||||
<ErrorBoundary><ErrorComponent /></ErrorBoundary>
|
||||
</div>
|
||||
</templates>`);
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
constructor(parent) {
|
||||
super(parent);
|
||||
throw new Error("NOOOOO");
|
||||
}
|
||||
}
|
||||
class ErrorBoundary extends Component<any, any> {
|
||||
state = useState({ error: false });
|
||||
|
||||
catchError() {
|
||||
this.state.error = true;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static components = { ErrorBoundary, ErrorComponent };
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
expect(handler).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("can catch an error in the willStart call", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
static template = xml`<div t-name="ErrorComponent">Some text</div>`;
|
||||
async willStart() {
|
||||
// we wait a little bit to be in a different stack frame
|
||||
await nextTick();
|
||||
throw new Error("NOOOOO");
|
||||
}
|
||||
}
|
||||
class ErrorBoundary extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.error">Error handled</t>
|
||||
<t t-else=""><t t-slot="default" /></t>
|
||||
</div>`;
|
||||
state = useState({ error: false });
|
||||
|
||||
catchError() {
|
||||
this.state.error = true;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><ErrorBoundary><ErrorComponent /></ErrorBoundary></div>`;
|
||||
static components = { ErrorBoundary, ErrorComponent };
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
});
|
||||
|
||||
test.skip("can catch an error in the mounted call", async () => {
|
||||
// we do not catch error in mounted anymore
|
||||
console.error = jest.fn();
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="ErrorBoundary">
|
||||
<t t-if="state.error">Error handled</t>
|
||||
<t t-else=""><t t-slot="default" /></t>
|
||||
</div>
|
||||
<div t-name="ErrorComponent">Some text</div>
|
||||
<div t-name="App">
|
||||
<ErrorBoundary><ErrorComponent /></ErrorBoundary>
|
||||
</div>
|
||||
</templates>`);
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
mounted() {
|
||||
throw new Error("NOOOOO");
|
||||
}
|
||||
}
|
||||
class ErrorBoundary extends Component<any, any> {
|
||||
state = useState({ error: false });
|
||||
|
||||
catchError() {
|
||||
this.state.error = true;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static components = { ErrorBoundary, ErrorComponent };
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
});
|
||||
|
||||
test.skip("can catch an error in the willPatch call", async () => {
|
||||
// we do not catch error in willPatch anymore
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="props.message"/></div>`;
|
||||
willPatch() {
|
||||
throw new Error("NOOOOO");
|
||||
}
|
||||
}
|
||||
class ErrorBoundary extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.error">Error handled</t>
|
||||
<t t-else=""><t t-slot="default" /></t>
|
||||
</div>`;
|
||||
state = useState({ error: false });
|
||||
|
||||
catchError() {
|
||||
this.state.error = true;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<span><t t-esc="state.message"/></span>
|
||||
<ErrorBoundary><ErrorComponent message="state.message" /></ErrorBoundary>
|
||||
</div>`;
|
||||
state = useState({ message: "abc" });
|
||||
static components = { ErrorBoundary, ErrorComponent };
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>abc</span><div><div>abc</div></div></div>");
|
||||
app.state.message = "def";
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>def</span><div>Error handled</div></div>");
|
||||
expect(console.error).toHaveBeenCalledTimes(1);
|
||||
console.error = consoleError;
|
||||
});
|
||||
|
||||
test("a rendering error will reject the mount promise", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn(() => {});
|
||||
// we do not catch error in willPatch anymore
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="this.will.crash"/></div>`;
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
let error;
|
||||
try {
|
||||
await app.mount(fixture);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
});
|
||||
|
||||
test("an error in mounted call will reject the mount promise", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn(() => {});
|
||||
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div>abc</div>`;
|
||||
mounted() {
|
||||
throw new Error("boom");
|
||||
}
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
let error;
|
||||
try {
|
||||
await app.mount(fixture);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("boom");
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
});
|
||||
|
||||
test("an error in willPatch call will reject the render promise", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn(() => {});
|
||||
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="val"/></div>`;
|
||||
val = 3;
|
||||
willPatch() {
|
||||
throw new Error("boom");
|
||||
}
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
app.val = 4;
|
||||
let error;
|
||||
try {
|
||||
await app.render();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("boom");
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
});
|
||||
|
||||
test("an error in patched call will reject the render promise", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn(() => {});
|
||||
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="val"/></div>`;
|
||||
val = 3;
|
||||
patched() {
|
||||
throw new Error("boom");
|
||||
}
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
app.val = 4;
|
||||
let error;
|
||||
try {
|
||||
await app.render();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("boom");
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
});
|
||||
|
||||
test("a rendering error in a sub component will reject the mount promise", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn(() => {});
|
||||
// we do not catch error in willPatch anymore
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="this.will.crash"/></div>`;
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><Child/></div>`;
|
||||
static components = { Child };
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
let error;
|
||||
try {
|
||||
await app.mount(fixture);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
});
|
||||
|
||||
test("a rendering error will reject the render promise", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn(() => {});
|
||||
// we do not catch error in willPatch anymore
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><t t-if="flag" t-esc="this.will.crash"/></div>`;
|
||||
flag = false;
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div></div>");
|
||||
app.flag = true;
|
||||
let error;
|
||||
try {
|
||||
await app.render();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
});
|
||||
|
||||
test("a rendering error will reject the render promise (with sub components)", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span></span>`;
|
||||
}
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`<div><Child/><t t-esc="x.y"/></div>`;
|
||||
static components = { Child };
|
||||
}
|
||||
|
||||
let error;
|
||||
try {
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'y' of undefined");
|
||||
});
|
||||
});
|
||||
@@ -137,33 +137,8 @@ describe("styles and component", () => {
|
||||
color: red;
|
||||
}
|
||||
}`);
|
||||
|
||||
expect(sheet)
|
||||
.toBe(`.parent-a .child-a, .parent-a .child-b, .parent-b .child-a, .parent-b .child-b {
|
||||
color: red;
|
||||
}`);
|
||||
});
|
||||
|
||||
test("handle & selector", async () => {
|
||||
let sheet = processSheet(`.btn {
|
||||
&.danger {
|
||||
color: red;
|
||||
}
|
||||
}`);
|
||||
|
||||
expect(sheet).toBe(`.btn.danger {
|
||||
color: red;
|
||||
}`);
|
||||
|
||||
sheet = processSheet(`.some-class {
|
||||
&.btn {
|
||||
.other-class ~ & {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}`);
|
||||
|
||||
expect(sheet).toBe(`.other-class ~ .some-class.btn {
|
||||
|
||||
expect(sheet).toBe(`.parent-a .child-a, .parent-a .child-b, .parent-b .child-a, .parent-b .child-b {
|
||||
color: red;
|
||||
}`);
|
||||
});
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { useState } from "../../src/hooks";
|
||||
import { xml } from "../../src/tags";
|
||||
import { makeDeferred, makeTestEnv, makeTestFixture, nextTick } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// We create before each test:
|
||||
// - fixture: a div, appended to the DOM, intended to be the target of dom
|
||||
// manipulations. Note that it is removed after each test.
|
||||
// - env: a WEnv, necessary to create new components
|
||||
|
||||
let fixture: HTMLElement;
|
||||
let env: Env;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
env = makeTestEnv();
|
||||
Component.env = env;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.remove();
|
||||
});
|
||||
|
||||
describe("mount targets", () => {
|
||||
test("can attach a component to an existing node (if same tagname)", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div>app</div>`;
|
||||
}
|
||||
const div = document.createElement("div");
|
||||
fixture.appendChild(div);
|
||||
|
||||
const app = new App();
|
||||
await app.mount(div, { position: "self" });
|
||||
expect(fixture.innerHTML).toBe("<div>app</div>");
|
||||
});
|
||||
|
||||
test("cannot attach a component to an existing node (if not same tagname)", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<span>app</span>`;
|
||||
}
|
||||
const div = document.createElement("div");
|
||||
fixture.appendChild(div);
|
||||
|
||||
const app = new App();
|
||||
let error;
|
||||
try {
|
||||
await app.mount(div, { position: "self" });
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot attach 'App' to target node (not same tag name)");
|
||||
});
|
||||
|
||||
test("can mount a component (with position='first-child')", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div>app</div>`;
|
||||
}
|
||||
const span = document.createElement("span");
|
||||
fixture.appendChild(span);
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture, { position: "first-child" });
|
||||
expect(fixture.innerHTML).toBe("<div>app</div><span></span>");
|
||||
});
|
||||
|
||||
test("can mount a component (with position='last-child')", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div>app</div>`;
|
||||
}
|
||||
const span = document.createElement("span");
|
||||
fixture.appendChild(span);
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture, { position: "last-child" });
|
||||
expect(fixture.innerHTML).toBe("<span></span><div>app</div>");
|
||||
});
|
||||
|
||||
test("default mount option is 'last-child'", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div>app</div>`;
|
||||
}
|
||||
const span = document.createElement("span");
|
||||
fixture.appendChild(span);
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<span></span><div>app</div>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("unmounting and remounting", () => {
|
||||
test("widget can be unmounted and remounted", async () => {
|
||||
const steps: string[] = [];
|
||||
class MyWidget extends Component<any, any> {
|
||||
static template = xml`<div>Hey</div>`;
|
||||
async willStart() {
|
||||
steps.push("willstart");
|
||||
}
|
||||
mounted() {
|
||||
steps.push("mounted");
|
||||
}
|
||||
willUnmount() {
|
||||
steps.push("willunmount");
|
||||
}
|
||||
patched() {
|
||||
throw new Error("patched should not be called");
|
||||
}
|
||||
}
|
||||
|
||||
const w = new MyWidget();
|
||||
await w.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>Hey</div>");
|
||||
expect(steps).toEqual(["willstart", "mounted"]);
|
||||
|
||||
w.unmount();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
expect(steps).toEqual(["willstart", "mounted", "willunmount"]);
|
||||
|
||||
await w.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>Hey</div>");
|
||||
expect(steps).toEqual(["willstart", "mounted", "willunmount", "mounted"]);
|
||||
});
|
||||
|
||||
test("widget can be mounted twice without ill effect", async () => {
|
||||
const steps: string[] = [];
|
||||
class MyWidget extends Component<any, any> {
|
||||
static template = xml`<div>Hey</div>`;
|
||||
async willStart() {
|
||||
steps.push("willstart");
|
||||
}
|
||||
mounted() {
|
||||
steps.push("mounted");
|
||||
}
|
||||
willUnmount() {
|
||||
steps.push("willunmount");
|
||||
}
|
||||
}
|
||||
|
||||
const w = new MyWidget();
|
||||
await w.mount(fixture);
|
||||
await w.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>Hey</div>");
|
||||
expect(steps).toEqual(["willstart", "mounted"]);
|
||||
});
|
||||
|
||||
test("state changes in willUnmount do not trigger rerender", async () => {
|
||||
const steps: string[] = [];
|
||||
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`
|
||||
<span><t t-esc="props.val"/><t t-esc="state.n"/></span>
|
||||
`;
|
||||
state = useState({ n: 2 });
|
||||
__render(f) {
|
||||
steps.push("render");
|
||||
return super.__render(f);
|
||||
}
|
||||
willPatch() {
|
||||
steps.push("willPatch");
|
||||
}
|
||||
patched() {
|
||||
steps.push("patched");
|
||||
}
|
||||
|
||||
willUnmount() {
|
||||
steps.push("willUnmount");
|
||||
this.state.n = 3;
|
||||
}
|
||||
}
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<Child t-if="state.flag" val="state.val"/>
|
||||
</div>
|
||||
`;
|
||||
static components = { Child };
|
||||
state = useState({ val: 1, flag: true });
|
||||
}
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
expect(steps).toEqual(["render"]);
|
||||
expect(fixture.innerHTML).toBe("<div><span>12</span></div>");
|
||||
widget.state.flag = false;
|
||||
await nextTick();
|
||||
// we make sure here that no call to __render is done
|
||||
expect(steps).toEqual(["render", "willUnmount"]);
|
||||
});
|
||||
|
||||
test("state changes in willUnmount will be applied on remount", async () => {
|
||||
class TestWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div><t t-esc="state.val"/></div>
|
||||
`;
|
||||
state = useState({ val: 1 });
|
||||
willUnmount() {
|
||||
this.state.val = 3;
|
||||
}
|
||||
}
|
||||
|
||||
const widget = new TestWidget();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>1</div>");
|
||||
widget.unmount();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
await nextTick(); // wait for changes to be detected before remounting
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||
// we want to make sure that there are no remaining tasks left at this point.
|
||||
expect(Component.scheduler.tasks.length).toBe(0);
|
||||
});
|
||||
|
||||
test("sub component is still active after being unmounted and remounted", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`
|
||||
<p t-on-click="state.value++">
|
||||
<t t-esc="state.value"/>
|
||||
</p>`;
|
||||
|
||||
state = useState({ value: 1 });
|
||||
}
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static components = { Child };
|
||||
static template = xml`<div><Child/></div>`;
|
||||
}
|
||||
|
||||
const w = new Parent();
|
||||
await w.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><p>1</p></div>");
|
||||
|
||||
fixture.querySelector("p")!.click();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><p>2</p></div>");
|
||||
w.unmount();
|
||||
await nextTick();
|
||||
await w.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><p>2</p></div>");
|
||||
fixture.querySelector("p")!.click();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><p>3</p></div>");
|
||||
});
|
||||
|
||||
test("change state just before mounting component", async () => {
|
||||
const steps: number[] = [];
|
||||
class TestWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div><t t-esc="state.val"/></div>
|
||||
`;
|
||||
state = useState({ val: 1 });
|
||||
__render(f) {
|
||||
steps.push(this.state.val);
|
||||
return super.__render(f);
|
||||
}
|
||||
}
|
||||
TestWidget.prototype.__render = jest.fn(TestWidget.prototype.__render);
|
||||
|
||||
const widget = new TestWidget();
|
||||
widget.state.val = 2;
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>2</div>");
|
||||
expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(1);
|
||||
|
||||
// unmount and re-mount, as in this case, willStart won't be called, so it's
|
||||
// slightly different
|
||||
widget.unmount();
|
||||
widget.state.val = 3;
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||
expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(2);
|
||||
expect(steps).toEqual([2, 3]);
|
||||
});
|
||||
|
||||
test("change state while mounting component", async () => {
|
||||
const steps: number[] = [];
|
||||
class TestWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div><t t-esc="state.val"/></div>
|
||||
`;
|
||||
state = useState({ val: 1 });
|
||||
__render(f) {
|
||||
steps.push(this.state.val);
|
||||
return super.__render(f);
|
||||
}
|
||||
}
|
||||
TestWidget.prototype.__render = jest.fn(TestWidget.prototype.__render);
|
||||
TestWidget.prototype.__patch = jest.fn(TestWidget.prototype.__patch);
|
||||
|
||||
const widget = new TestWidget();
|
||||
let prom = widget.mount(fixture);
|
||||
widget.state.val = 2;
|
||||
await prom;
|
||||
expect(fixture.innerHTML).toBe("<div>2</div>");
|
||||
expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(1);
|
||||
|
||||
// unmount and re-mount, as in this case, willStart won't be called, so it's
|
||||
// slightly different
|
||||
widget.unmount();
|
||||
prom = widget.mount(fixture);
|
||||
widget.state.val = 3;
|
||||
await prom;
|
||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||
expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(3);
|
||||
expect(TestWidget.prototype.__patch).toHaveBeenCalledTimes(2);
|
||||
expect(steps).toEqual([2, 2, 3]);
|
||||
});
|
||||
|
||||
test("change state while component is unmounted", async () => {
|
||||
let child;
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span t-esc="state.val"/>`;
|
||||
state = useState({
|
||||
val: "C1"
|
||||
});
|
||||
constructor(parent, props) {
|
||||
super(parent, props);
|
||||
child = this;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static components = { Child };
|
||||
static template = xml`<div><t t-esc="state.val"/><Child/></div>`;
|
||||
state = useState({ val: "P1" });
|
||||
}
|
||||
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>P1<span>C1</span></div>");
|
||||
|
||||
parent.unmount();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
parent.state.val = "P2";
|
||||
child.state.val = "C2";
|
||||
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>P2<span>C2</span></div>");
|
||||
});
|
||||
|
||||
test("unmount component during a re-rendering", async () => {
|
||||
const def = makeDeferred();
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
||||
willUpdateProps() {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
Child.prototype.__render = jest.fn(Child.prototype.__render);
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`<div><Child val="state.val"/></div>`;
|
||||
static components = { Child };
|
||||
state = useState({ val: 1 });
|
||||
}
|
||||
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
expect(Child.prototype.__render).toBeCalledTimes(1);
|
||||
|
||||
parent.state.val = 2;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
|
||||
parent.unmount();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
expect(Child.prototype.__render).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("widget can be mounted on different target", async () => {
|
||||
const steps: string[] = [];
|
||||
class MyWidget extends Component<any, any> {
|
||||
static template = xml`<div>Hey</div>`;
|
||||
async willStart() {
|
||||
steps.push("willstart");
|
||||
}
|
||||
mounted() {
|
||||
steps.push("mounted");
|
||||
}
|
||||
willUnmount() {
|
||||
steps.push("willunmount");
|
||||
}
|
||||
patched() {
|
||||
throw new Error("patched should not be called");
|
||||
}
|
||||
}
|
||||
const div = document.createElement("div");
|
||||
const span = document.createElement("span");
|
||||
fixture.appendChild(div);
|
||||
fixture.appendChild(span);
|
||||
const w = new MyWidget();
|
||||
await w.mount(div);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div>Hey</div></div><span></span>");
|
||||
|
||||
await w.mount(span);
|
||||
expect(fixture.innerHTML).toBe("<div></div><span><div>Hey</div></span>");
|
||||
});
|
||||
});
|
||||
+1
-37
@@ -9,8 +9,7 @@ import {
|
||||
onWillPatch,
|
||||
onWillStart,
|
||||
onWillUpdateProps,
|
||||
useSubEnv,
|
||||
useExternalListener
|
||||
useSubEnv
|
||||
} from "../src/hooks";
|
||||
import { xml } from "../src/tags";
|
||||
|
||||
@@ -593,39 +592,4 @@ describe("hooks", () => {
|
||||
expect(fixture.innerHTML).toBe("<div><span>2</span></div>");
|
||||
expect(steps).toEqual(["onWillStart", "onWillUpdateProps"]);
|
||||
});
|
||||
|
||||
test("useExternalListener", async () => {
|
||||
let n = 0;
|
||||
|
||||
class MyComponent extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.value"/></span>`;
|
||||
constructor(parent, props) {
|
||||
super(parent, props);
|
||||
useExternalListener(window as any, "click", this.increment);
|
||||
}
|
||||
increment() {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><MyComponent t-if="state.flag"/></div>`;
|
||||
static components = { MyComponent };
|
||||
state = useState({ flag: false });
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
|
||||
expect(n).toBe(0);
|
||||
window.dispatchEvent(new Event("click"));
|
||||
expect(n).toBe(0);
|
||||
app.state.flag = true;
|
||||
await nextTick();
|
||||
window.dispatchEvent(new Event("click"));
|
||||
expect(n).toBe(1);
|
||||
app.state.flag = false;
|
||||
await nextTick();
|
||||
window.dispatchEvent(new Event("click"));
|
||||
expect(n).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -421,7 +421,7 @@ exports[`foreach does not pollute the rendering context 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.item_first = i1 === 0
|
||||
scope.item_last = i1 === _length3 - 1
|
||||
@@ -456,7 +456,7 @@ exports[`foreach iterate on items (on a element node) 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.item_first = i1 === 0
|
||||
scope.item_last = i1 === _length3 - 1
|
||||
@@ -494,7 +494,7 @@ exports[`foreach iterate on items 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.item_first = i1 === 0
|
||||
scope.item_last = i1 === _length3 - 1
|
||||
@@ -541,7 +541,7 @@ exports[`foreach iterate, dict param 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.item_first = i1 === 0
|
||||
scope.item_last = i1 === _length3 - 1
|
||||
@@ -588,7 +588,7 @@ exports[`foreach iterate, position 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.elem_first = i1 === 0
|
||||
scope.elem_last = i1 === _length3 - 1
|
||||
@@ -632,7 +632,7 @@ exports[`foreach t-foreach in t-forach 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.number_first = i1 === 0
|
||||
scope.number_last = i1 === _length3 - 1
|
||||
@@ -649,7 +649,7 @@ exports[`foreach t-foreach in t-forach 1`] = `
|
||||
}
|
||||
let _length7 = _7.length;
|
||||
let _origScope9 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i2 = 0; i2 < _length7; i2++) {
|
||||
scope.letter_first = i2 === 0
|
||||
scope.letter_last = i2 === _length7 - 1
|
||||
@@ -692,7 +692,7 @@ exports[`foreach warn if no key in some case 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.item_first = i1 === 0
|
||||
scope.item_last = i1 === _length3 - 1
|
||||
@@ -734,7 +734,7 @@ exports[`loading templates can load a few templates from a xml string 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('ul', p1, c1);
|
||||
this.subTemplates['items'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
this.subTemplates['items'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -757,7 +757,7 @@ exports[`misc global 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.value_first = i1 === 0
|
||||
scope.value_last = i1 === _length3 - 1
|
||||
@@ -782,7 +782,7 @@ exports[`misc global 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'foo').foo = 'aaa';
|
||||
scope.foo = 'aaa';
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
let k14 = \`__14__\${key1}__\`;
|
||||
@@ -791,7 +791,7 @@ exports[`misc global 1`] = `
|
||||
}
|
||||
let k15 = \`__15__\${key1}__\`;
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k15}));
|
||||
utils.getScope(scope, 'foo').foo = 'bbb';
|
||||
scope.foo = 'bbb';
|
||||
let k16 = \`__16__\${key1}__\`;
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16}));
|
||||
scope[utils.zero] = c__0;
|
||||
@@ -802,7 +802,7 @@ exports[`misc global 1`] = `
|
||||
}
|
||||
}
|
||||
scope = _origScope5;
|
||||
this.subTemplates['_callee-asc-toto'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__20__'}));
|
||||
this.subTemplates['_callee-asc-toto'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1041,7 +1041,7 @@ exports[`t-call (template calling basic caller 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1069,7 +1069,7 @@ exports[`t-call (template calling basic caller, no parent node 1`] = `
|
||||
let result;
|
||||
let h = this.h;
|
||||
result = []
|
||||
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__3__'}));
|
||||
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
|
||||
result = result[0]
|
||||
return result;
|
||||
}"
|
||||
@@ -1114,7 +1114,7 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] =
|
||||
c5.push({text: \`yay\`});
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'}));
|
||||
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope3;
|
||||
}
|
||||
return vn1;
|
||||
@@ -1162,7 +1162,7 @@ exports[`t-call (template calling cascading t-call t-raw='0' 1`] = `
|
||||
c14.push({text: \`yay\`});
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__15__'}));
|
||||
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope12;
|
||||
}
|
||||
return vn1;
|
||||
@@ -1179,7 +1179,7 @@ exports[`t-call (template calling inherit context 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.foo = 1;
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__2__'}));
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1198,7 +1198,7 @@ exports[`t-call (template calling recursive template, part 1 1`] = `
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`hey\`});
|
||||
if (false) {
|
||||
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'}));
|
||||
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
@@ -1241,10 +1241,10 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'node').node = scope['root'];
|
||||
scope.node = scope['root'];
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope11;
|
||||
}
|
||||
return vn1;
|
||||
@@ -1279,7 +1279,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
|
||||
}
|
||||
let _length6 = _6.length;
|
||||
let _origScope8 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length6; i1++) {
|
||||
scope.subtree_first = i1 === 0
|
||||
scope.subtree_last = i1 === _length6 - 1
|
||||
@@ -1292,7 +1292,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'node').node = scope['subtree'];
|
||||
scope.node = scope['subtree'];
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
let k10 = \`__10__\${key0}__\${key1}__\`;
|
||||
@@ -1318,10 +1318,10 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'node').node = scope['root'];
|
||||
scope.node = scope['root'];
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope11;
|
||||
}
|
||||
return vn1;
|
||||
@@ -1356,7 +1356,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
|
||||
}
|
||||
let _length6 = _6.length;
|
||||
let _origScope8 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length6; i1++) {
|
||||
scope.subtree_first = i1 === 0
|
||||
scope.subtree_last = i1 === _length6 - 1
|
||||
@@ -1369,7 +1369,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'node').node = scope['subtree'];
|
||||
scope.node = scope['subtree'];
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
let k10 = \`__10__\${key0}__\${key1}__\`;
|
||||
@@ -1395,10 +1395,10 @@ exports[`t-call (template calling scoped parameters 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'foo').foo = 42;
|
||||
scope.foo = 42;
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope2;
|
||||
}
|
||||
if (scope.foo != null) {
|
||||
@@ -1418,7 +1418,7 @@ exports[`t-call (template calling t-call with t-if 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
if (scope['flag']) {
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
@@ -1456,7 +1456,7 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.v_first = i1 === 0
|
||||
scope.v_last = i1 === _length3 - 1
|
||||
@@ -1464,13 +1464,13 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
|
||||
scope.v = _3[i1]
|
||||
scope.v_value = _4[i1]
|
||||
let key1 = i1;
|
||||
utils.getScope(scope, 'val').val = scope['v'].val;
|
||||
scope.val = scope['v'].val;
|
||||
{
|
||||
let _origScope8 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'val3').val3 = scope.val*3;
|
||||
scope.val3 = scope.val*3;
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
let k9 = \`__9__\${key1}__\`;
|
||||
@@ -1493,7 +1493,7 @@ exports[`t-call (template calling t-call with t-set inside and outside. 2 1`] =
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('p', p1, c1);
|
||||
scope.w = 'fromwrapper';
|
||||
this.subTemplates['main'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__11__'}));
|
||||
this.subTemplates['main'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1509,19 +1509,19 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.v1 = 'elif';
|
||||
if (scope.v1==='if') {
|
||||
this.subTemplates['callee1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
this.subTemplates['callee1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
}
|
||||
else if (scope.v1==='elif') {
|
||||
{
|
||||
let _origScope6 = scope;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'v').v = 'success';
|
||||
scope.v = 'success';
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['callee2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__7__'}));
|
||||
scope = _origScope6;
|
||||
this.subTemplates['callee2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope5;
|
||||
}
|
||||
}
|
||||
return vn1;
|
||||
@@ -1537,7 +1537,7 @@ exports[`t-call (template calling t-call, global templates 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['john'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
this.subTemplates['john'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1559,7 +1559,7 @@ exports[`t-call (template calling with unused body 1`] = `
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
result = []
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
|
||||
result = result[0]
|
||||
scope = _origScope3;
|
||||
}
|
||||
@@ -1580,11 +1580,11 @@ exports[`t-call (template calling with unused setbody 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'qux').qux = 3;
|
||||
scope.qux = 3;
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
result = []
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
|
||||
result = result[0]
|
||||
scope = _origScope3;
|
||||
}
|
||||
@@ -1609,7 +1609,7 @@ exports[`t-call (template calling with used body 1`] = `
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
result = []
|
||||
this.subTemplates['_callee-printsbody'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
|
||||
this.subTemplates['_callee-printsbody'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
|
||||
result = result[0]
|
||||
scope = _origScope3;
|
||||
}
|
||||
@@ -1631,10 +1631,10 @@ exports[`t-call (template calling with used set body 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
utils.getScope(scope, 'foo').foo = 'ok';
|
||||
scope.foo = 'ok';
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope3;
|
||||
}
|
||||
return vn1;
|
||||
@@ -1834,7 +1834,7 @@ exports[`t-esc t-esc=0 is escaped 1`] = `
|
||||
c4.push({text: \`escaped\`});
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['test'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
|
||||
this.subTemplates['test'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope3;
|
||||
}
|
||||
return vn1;
|
||||
@@ -2156,7 +2156,7 @@ exports[`t-key t-key directive in a list 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.beer_first = i1 === 0
|
||||
scope.beer_last = i1 === _length3 - 1
|
||||
@@ -2260,7 +2260,7 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.action_first = i1 === 0
|
||||
scope.action_last = i1 === _length3 - 1
|
||||
@@ -2400,8 +2400,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}state_2.incrementCounter(2)};
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}scope['state'].incrementCounter(2)};
|
||||
c1.push({text: \`Click\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2415,8 +2414,7 @@ exports[`t-on t-on with inline statement 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}state_2.counter++};
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}scope['state'].counter++};
|
||||
c1.push({text: \`Click\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2430,8 +2428,7 @@ exports[`t-on t-on with inline statement, part 2 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}state_2.flag=!state_2.flag};
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}scope['state'].flag=!scope['state'].flag};
|
||||
c1.push({text: \`Toggle\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2445,9 +2442,7 @@ exports[`t-on t-on with inline statement, part 3 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
const state_2 = scope['state'];
|
||||
const someFunction_2 = scope['someFunction'];
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}state_2.n=someFunction_2(3)};
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}scope['state'].n=scope['someFunction'](3)};
|
||||
c1.push({text: \`Toggle\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2522,7 +2517,7 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.project_first = i1 === 0
|
||||
scope.project_last = i1 === _length3 - 1
|
||||
@@ -2610,7 +2605,7 @@ exports[`t-on t-on with t-call 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -2624,7 +2619,7 @@ exports[`t-on t-on, with arguments and t-call 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -2768,7 +2763,7 @@ exports[`t-ref refs in a loop 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.item_first = i1 === 0
|
||||
scope.item_last = i1 === _length3 - 1
|
||||
@@ -2995,7 +2990,6 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
@@ -3010,7 +3004,7 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.elem_first = i1 === 0
|
||||
scope.elem_last = i1 === _length3 - 1
|
||||
@@ -3028,7 +3022,7 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
if (scope.v != null) {
|
||||
c7.push({text: scope.v});
|
||||
}
|
||||
utils.getScope(scope, 'v').v = scope['elem'];
|
||||
scope.v = scope['elem'];
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
|
||||
@@ -56,12 +56,6 @@ describe("tokenizer", () => {
|
||||
{ type: "OPERATOR", value: "..." },
|
||||
{ type: "VALUE", value: "1" }
|
||||
]);
|
||||
|
||||
expect(tokenize("a in b")).toEqual([
|
||||
{ type: "SYMBOL", value: "a" },
|
||||
{ type: "OPERATOR", value: "in " },
|
||||
{ type: "SYMBOL", value: "b" }
|
||||
]);
|
||||
});
|
||||
|
||||
test("strings", () => {
|
||||
@@ -140,7 +134,6 @@ describe("expression evaluation", () => {
|
||||
expect(compileExpr("color === 'black'", {})).toBe("scope['color']==='black'");
|
||||
expect(compileExpr("'li_'+item", {})).toBe("'li_'+scope['item']");
|
||||
expect(compileExpr("state.val > 1", {})).toBe("scope['state'].val>1");
|
||||
expect(compileExpr("a in b", {})).toBe("scope['a']in scope['b']");
|
||||
});
|
||||
|
||||
test("boolean operations", () => {
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* 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("log a sub component with non stringifiable props", async () => {
|
||||
const steps: string[] = [];
|
||||
const log = console.log;
|
||||
console.log = arg => steps.push(arg);
|
||||
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.obj.val"/></span>`;
|
||||
}
|
||||
|
||||
const circularObject: any = { val: 1 };
|
||||
circularObject.circularObject = circularObject;
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`<div><Child obj="obj"/></div>`;
|
||||
static components = { Child };
|
||||
obj = circularObject;
|
||||
}
|
||||
|
||||
const parent = new Parent(null, {});
|
||||
await parent.mount(fixture);
|
||||
parent.unmount();
|
||||
|
||||
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=<JSON error>",
|
||||
"[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",
|
||||
"[OWL_DEBUG] Parent<id=1> willUnmount",
|
||||
"[OWL_DEBUG] Child<id=2> willUnmount"
|
||||
]);
|
||||
console.log = log;
|
||||
});
|
||||
@@ -1,154 +0,0 @@
|
||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
||||
|
||||
const { useState, useRef } = owl.hooks;
|
||||
//------------------------------------------------------------------------------
|
||||
// Likes Counter Widget
|
||||
//------------------------------------------------------------------------------
|
||||
class Counter extends owl.Component {
|
||||
state = useState({ counter: 0 });
|
||||
|
||||
increment() {
|
||||
this.state.counter++;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Message Widget
|
||||
//------------------------------------------------------------------------------
|
||||
class Message extends owl.Component {
|
||||
shouldUpdate(nextProps) {
|
||||
return nextProps.message !== this.props.message;
|
||||
}
|
||||
removeMessage() {
|
||||
this.trigger("remove-message", {
|
||||
id: this.props.message.id
|
||||
});
|
||||
}
|
||||
}
|
||||
Message.components = { Counter };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Root Widget
|
||||
//------------------------------------------------------------------------------
|
||||
class App extends owl.Component {
|
||||
state = useState({ messages: [], multipleFlag: false, clearAfterFlag: false });
|
||||
logRef = useRef("log");
|
||||
|
||||
mounted() {
|
||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
||||
}
|
||||
|
||||
benchmark(message, fn, callback) {
|
||||
if (this.state.multipleFlag) {
|
||||
const N = 20;
|
||||
let n = N;
|
||||
let total = 0;
|
||||
let cb = info => {
|
||||
let finalize = () => {
|
||||
n--;
|
||||
total += info.delta;
|
||||
if (n === 0) {
|
||||
const avg = total / N;
|
||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
} else {
|
||||
this._benchmark(message, fn, cb);
|
||||
}
|
||||
};
|
||||
|
||||
if (this.state.clearAfterFlag) {
|
||||
this._benchmark(
|
||||
"clear",
|
||||
() => {
|
||||
this.state.messages = [];
|
||||
},
|
||||
finalize,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
finalize();
|
||||
}
|
||||
};
|
||||
this._benchmark(message, fn, cb);
|
||||
} else {
|
||||
this._benchmark(message, fn, callback);
|
||||
}
|
||||
}
|
||||
|
||||
_benchmark(message, fn, cb, log = true) {
|
||||
setTimeout(() => {
|
||||
startMeasure(message);
|
||||
fn();
|
||||
stopMeasure(info => {
|
||||
if (log) {
|
||||
this.log(info.msg);
|
||||
}
|
||||
if (cb) {
|
||||
cb(info);
|
||||
}
|
||||
});
|
||||
}, 10);
|
||||
}
|
||||
|
||||
addMessages(n) {
|
||||
this.benchmark("add " + n, () => {
|
||||
const newMessages = buildData(n);
|
||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._benchmark("clear", () => {
|
||||
this.state.messages = [];
|
||||
});
|
||||
}
|
||||
|
||||
updateSomeMessages() {
|
||||
this.benchmark("update every 10th", () => {
|
||||
const messages = this.state.messages;
|
||||
for (let i = 0; i < messages.length; i += 10) {
|
||||
const msg = Object.assign({}, messages[i]);
|
||||
msg.author += "!!!";
|
||||
messages[i] = msg;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeMessage(event) {
|
||||
this.benchmark("remove message", () => {
|
||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
||||
this.state.messages.splice(index, 1);
|
||||
});
|
||||
}
|
||||
|
||||
log(str, isBold) {
|
||||
const div = document.createElement("div");
|
||||
if (isBold) {
|
||||
div.classList.add("bold");
|
||||
}
|
||||
div.textContent = `> ${str}`;
|
||||
this.logRef.el.appendChild(div);
|
||||
this.logRef.el.scrollTop = this.logRef.el.scrollHeight;
|
||||
}
|
||||
|
||||
clearLog() {
|
||||
this.logRef.el.innerHTML = "";
|
||||
}
|
||||
}
|
||||
App.components = { Message };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Application initialization
|
||||
//------------------------------------------------------------------------------
|
||||
async function start() {
|
||||
const templates = await owl.utils.loadFile("templates.xml");
|
||||
App.env = {
|
||||
qweb: new owl.QWeb({ templates })
|
||||
};
|
||||
const app = new App();
|
||||
app.mount(document.body);
|
||||
}
|
||||
|
||||
start();
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>OWL v1.0.0 Benchmark</title>
|
||||
<link href="../shared/main.css" rel="stylesheet"/>
|
||||
<script src='owl.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<script src='app.js' type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
||||
<templates>
|
||||
<div t-name="App" class="main">
|
||||
<div class="left-thing">
|
||||
<div class="title">Actions</div>
|
||||
<div class="panel">
|
||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
||||
<button t-on-click="clear">Clear</button>
|
||||
</div>
|
||||
<div class="flags">
|
||||
<div>
|
||||
<input type="checkbox" id="multipleflag" t-model="state.multipleFlag"/>
|
||||
<label for="multipleflag">Do it 20x</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="clearFlag" t-model="state.clearAfterFlag" />
|
||||
<label for="clearFlag">Clear after</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
||||
<hr/>
|
||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
||||
<div class="log">
|
||||
<div class="log-content" t-ref="log"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-thing">
|
||||
<div class="content" t-on-remove-message="removeMessage">
|
||||
<t t-foreach="state.messages" t-as="message">
|
||||
<Message t-key="message.id" message="message"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div t-name="Message" class="message">
|
||||
<span class="author"><t t-esc="props.message.author"/></span>
|
||||
<span class="msg"><t t-esc="props.message.msg"/></span>
|
||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
||||
<Counter/>
|
||||
</div>
|
||||
|
||||
<div t-name="Counter">
|
||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
||||
</div>
|
||||
|
||||
</templates>
|
||||
+1
-6
@@ -35,12 +35,7 @@
|
||||
});
|
||||
|
||||
function toStr(obj) {
|
||||
let str;
|
||||
try {
|
||||
str = JSON.stringify(obj || {});
|
||||
} catch (e) {
|
||||
str = "<JSON error>";
|
||||
}
|
||||
let str = JSON.stringify(obj || {});
|
||||
if (str.length > 200) {
|
||||
str = str.slice(0, 200) + "...";
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
<li><a href="benchmarks/owl-0.21.0">OWL 0.21.0</a></li>
|
||||
<li><a href="benchmarks/owl-0.24.0">OWL 0.24.0</a></li>
|
||||
<li><a href="benchmarks/owl-1.0.0-beta1">OWL 1.0.0-beta1</a></li>
|
||||
<li><a href="benchmarks/owl-1.0.0">OWL 1.0.0</a></li>
|
||||
<li><a href="benchmarks/owl-master">OWL Master</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
|
||||
@@ -455,7 +455,7 @@ const actions = {
|
||||
todo.completed = !todo.completed;
|
||||
},
|
||||
clearCompleted({ state, dispatch }) {
|
||||
for (let todo of state.todos.slice()) {
|
||||
for (let todo of state.todos) {
|
||||
if (todo.completed) {
|
||||
dispatch("removeTodo", todo.id);
|
||||
}
|
||||
@@ -1089,20 +1089,20 @@ const RESPONSIVE_XML = `<templates>
|
||||
<button>Button!</button>
|
||||
</div>
|
||||
|
||||
<t t-name="maincontent">
|
||||
<FormView />
|
||||
<Chatter />
|
||||
</t>
|
||||
<div t-name="App" class="app" t-att-class="{mobile: env.isMobile, desktop: !env.isMobile}">
|
||||
<t t-set="maincontent">
|
||||
<FormView />
|
||||
<Chatter />
|
||||
</t>
|
||||
<Navbar/>
|
||||
<ControlPanel/>
|
||||
<div class="content-wrapper" t-if="!env.isMobile">
|
||||
<div class="content">
|
||||
<t t-call="maincontent"/>
|
||||
<t t-raw="maincontent"/>
|
||||
</div>
|
||||
</div>
|
||||
<t t-else="">
|
||||
<t t-call="maincontent"/>
|
||||
<t t-raw="maincontent"/>
|
||||
</t>
|
||||
</div>
|
||||
</templates>
|
||||
@@ -1811,49 +1811,6 @@ const WMS_CSS = `body {
|
||||
font-size: 20px;
|
||||
}`;
|
||||
|
||||
const SFC = `// This example illustrates how Owl enables single file components,
|
||||
// which include code, template and style.
|
||||
//
|
||||
// This is very useful in some situations, such as testing or quick prototyping.
|
||||
// Note that this example has no external xml or css file, everything is
|
||||
// contained in a single js file.
|
||||
|
||||
const { Component, useState, tags } = owl;
|
||||
const { xml, css } = tags;
|
||||
|
||||
// Counter component
|
||||
const COUNTER_TEMPLATE = xml\`
|
||||
<button t-on-click="state.value++">
|
||||
Click! [<t t-esc="state.value"/>]
|
||||
</button>\`;
|
||||
|
||||
const COUNTER_STYLE = css\`
|
||||
button {
|
||||
color: blue;
|
||||
}\`;
|
||||
|
||||
class Counter extends Component {
|
||||
state = useState({ value: 0})
|
||||
}
|
||||
Counter.template = COUNTER_TEMPLATE;
|
||||
Counter.style = COUNTER_STYLE;
|
||||
|
||||
// App
|
||||
const APP_TEMPLATE = xml\`
|
||||
<div>
|
||||
<Counter/>
|
||||
<Counter/>
|
||||
</div>\`;
|
||||
|
||||
class App extends Component {}
|
||||
App.template = APP_TEMPLATE;
|
||||
App.components = { Counter };
|
||||
|
||||
// Application setup
|
||||
const app = new App();
|
||||
app.mount(document.body);
|
||||
`;
|
||||
|
||||
export const SAMPLES = [
|
||||
{
|
||||
description: "Components",
|
||||
@@ -1866,10 +1823,6 @@ export const SAMPLES = [
|
||||
code: FORM,
|
||||
xml: FORM_XML
|
||||
},
|
||||
{
|
||||
description: "Single File Components",
|
||||
code: SFC
|
||||
},
|
||||
{
|
||||
description: "Animations",
|
||||
code: ANIMATION,
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
const package = require("../package.json");
|
||||
const readline = require("readline");
|
||||
const fs = require("fs");
|
||||
const exec = require("child_process").exec;
|
||||
const chalk = require("chalk");
|
||||
const GitHub = require("github-api");
|
||||
|
||||
const REL_NOTES_FILE = `release-notes.md`;
|
||||
const STEPS = 10;
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
startRelease().then(() => {
|
||||
rl.close();
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Relase Script
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
async function startRelease() {
|
||||
log(`*** Owl release script ***`);
|
||||
log(`Current Version: ${package.version}`);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 1/${STEPS}: collecting info...`);
|
||||
const current = package.version;
|
||||
const next = await ask("Next version: ");
|
||||
let file = await ask(`Release notes (${REL_NOTES_FILE}): `);
|
||||
file = file || REL_NOTES_FILE;
|
||||
let content;
|
||||
try {
|
||||
content = await readFile("./" + file);
|
||||
} catch (e) {
|
||||
logSubContent(e.message);
|
||||
log("Cannot find release notes... Aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
// Todo: add playground update feature
|
||||
// let shouldUpdateStr = await ask("Update Playground? (y/n)");
|
||||
// const shouldUpdatePlayground = shouldUpdateStr === "y";
|
||||
const token = await ask("Github token: ");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 2/${STEPS}: running tests...`);
|
||||
const testsResult = await execCommand("npm run test");
|
||||
if (testsResult !== 0) {
|
||||
log("Test suite does not pass. Aborting.");
|
||||
return;
|
||||
}
|
||||
ask("Ready for next step...");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 3/${STEPS}: updating package.json, readme.md and roadmap.md...`);
|
||||
await replaceInFile("./package.json", current, next);
|
||||
await replaceInFile("./README.md", current, next);
|
||||
await replaceInFile("./roadmap.md", current, next);
|
||||
ask("Ready for next step...");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 4/${STEPS}: creating git commit...`);
|
||||
const gitResult = await execCommand(`git commit -am "[REL] v${next}\n\n${content}"`);
|
||||
if (gitResult !== 0) {
|
||||
log("Git commit failed. Aborting.");
|
||||
return;
|
||||
}
|
||||
ask("Ready for next step...");
|
||||
|
||||
// -------------------j--------------------------------------------------------
|
||||
log(`Step 5/${STEPS}: building owl (iife version)...`);
|
||||
const buildResult = await execCommand("npm run build");
|
||||
if (buildResult !== 0) {
|
||||
log("Build failed. Aborting.");
|
||||
return;
|
||||
}
|
||||
ask("Ready for next step...");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 6/${STEPS}: minifying owl...`);
|
||||
const minifyResult = await execCommand("npm run minify");
|
||||
if (minifyResult !== 0) {
|
||||
log("Minify failed. Aborting.");
|
||||
return;
|
||||
}
|
||||
ask("Ready for next step...");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 7/${STEPS}: pushing on github...`);
|
||||
const pushResult = await execCommand("git push");
|
||||
if (pushResult !== 0) {
|
||||
log("git push failed. Aborting.");
|
||||
return;
|
||||
}
|
||||
ask("Ready for next step...");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 8/${STEPS}: publishing release notes on github...`);
|
||||
const options = {
|
||||
tag_name: `v${next}`,
|
||||
name: `v${next}`,
|
||||
body: content,
|
||||
draft: true // todo: remove this someday
|
||||
};
|
||||
const result = await createRelease(token, options);
|
||||
console.log(result);
|
||||
ask("Ready for next step...");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 9/${STEPS}: adding assets to release...`);
|
||||
await ask("Please add owl.js and owl.min.js to draft release, then confirm");
|
||||
// todo: do this with curl
|
||||
// curl \
|
||||
// -H "Authorization: token $GITHUB_TOKEN" \
|
||||
// -H "Content-Type: $(file -b --mime-type $FILE)" \
|
||||
// --data-binary @$FILE \
|
||||
// "https://uploads.github.com/repos/hubot/singularity/releases/123/assets?name=$(basename $FILE)"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 10/${STEPS}: publishing module on npm...`);
|
||||
await execCommand("npm run publish");
|
||||
|
||||
log("Owl Release process completed! Thank you for your patience");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function log(text) {
|
||||
console.log(chalk.yellow(formatLog(text)));
|
||||
}
|
||||
|
||||
function formatLog(text) {
|
||||
return `[REL] ${text}`;
|
||||
}
|
||||
|
||||
function logSubContent(text) {
|
||||
for (let line of text.split("\n")) {
|
||||
if (line.trim()) {
|
||||
console.log(" " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ask(question) {
|
||||
return new Promise(resolve => {
|
||||
rl.question(question, result => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function logStream(stream) {
|
||||
stream.on("data", data => {
|
||||
logSubContent(data);
|
||||
});
|
||||
}
|
||||
|
||||
function execCommand(command) {
|
||||
return new Promise(resolve => {
|
||||
const childProcess = exec(command, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
resolve(err.code);
|
||||
}
|
||||
});
|
||||
childProcess.on("exit", code => {
|
||||
resolve(code);
|
||||
});
|
||||
logStream(childProcess.stdout);
|
||||
logStream(childProcess.stderr);
|
||||
});
|
||||
}
|
||||
|
||||
function readFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(file, "utf8", function(err, content) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(content);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function replaceInFile(file, from, to) {
|
||||
const content = await readFile(file);
|
||||
return new Promise((resolve, reject) => {
|
||||
const updatedContent = content.replace(new RegExp(from, "g"), to);
|
||||
fs.writeFile(file, updatedContent, "utf8", err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createRelease(token, options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var gh = new GitHub({ token });
|
||||
gh.getRepo("odoo", "owl").createRelease(options, (err, result, req) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
+1
-2
@@ -13,8 +13,7 @@
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"strictNullChecks": true,
|
||||
"declarationDir": "dist/types"
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": ["src/**/*.ts","src/*.ts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user