mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b11a440625 |
@@ -117,7 +117,7 @@ const { loadFile, mount } = owl;
|
|||||||
Dev mode activates some additional checks and developer amenities:
|
Dev mode activates some additional checks and developer amenities:
|
||||||
|
|
||||||
- [Props validation](./props.md#props-validation) is performed
|
- [Props validation](./props.md#props-validation) is performed
|
||||||
- [t-for and t-foreach](./templates.md#loops) loops check for key unicity
|
- [t-foreach](./templates.md#loops) loops check for key unicity
|
||||||
- Lifecycle hooks are wrapped to report their errors in a more developer-friendly way
|
- Lifecycle hooks are wrapped to report their errors in a more developer-friendly way
|
||||||
- onWillStart and onWillUpdateProps will emit a warning in the console when they
|
- onWillStart and onWillUpdateProps will emit a warning in the console when they
|
||||||
take longer than 3 seconds in an effort to ease debugging the presence of deadlocks
|
take longer than 3 seconds in an effort to ease debugging the presence of deadlocks
|
||||||
|
|||||||
@@ -451,6 +451,5 @@ console.log(status(component));
|
|||||||
// logs either:
|
// logs either:
|
||||||
// - 'new', if the component is new and has not been mounted yet
|
// - 'new', if the component is new and has not been mounted yet
|
||||||
// - 'mounted', if the component is currently mounted
|
// - 'mounted', if the component is currently mounted
|
||||||
// - 'cancelled', if the component has not been mounted yet but will be destroyed soon
|
|
||||||
// - 'destroyed' if the component is currently destroyed
|
// - 'destroyed' if the component is currently destroyed
|
||||||
```
|
```
|
||||||
|
|||||||
+25
-37
@@ -61,7 +61,7 @@ For reference, here is a list of all standard QWeb directives:
|
|||||||
| `t-out` | [Outputting value, possibly without escaping](#outputting-data) |
|
| `t-out` | [Outputting value, possibly without escaping](#outputting-data) |
|
||||||
| `t-set`, `t-value` | [Setting variables](#setting-variables) |
|
| `t-set`, `t-value` | [Setting variables](#setting-variables) |
|
||||||
| `t-if`, `t-elif`, `t-else`, | [conditionally rendering](#conditionals) |
|
| `t-if`, `t-elif`, `t-else`, | [conditionally rendering](#conditionals) |
|
||||||
| `t-for/t-of`, `t-foreach/t-as` | [Loops](#loops) |
|
| `t-foreach`, `t-as` | [Loops](#loops) |
|
||||||
| `t-att`, `t-attf-*`, `t-att-*` | [Dynamic attributes](#dynamic-attributes) |
|
| `t-att`, `t-attf-*`, `t-att-*` | [Dynamic attributes](#dynamic-attributes) |
|
||||||
| `t-call` | [Rendering sub templates](#sub-templates) |
|
| `t-call` | [Rendering sub templates](#sub-templates) |
|
||||||
| `t-debug`, `t-log` | [Debugging](#debugging) |
|
| `t-debug`, `t-log` | [Debugging](#debugging) |
|
||||||
@@ -372,39 +372,19 @@ Like conditions, `t-foreach` applies to the element bearing the directive’s at
|
|||||||
|
|
||||||
is equivalent to the previous example.
|
is equivalent to the previous example.
|
||||||
|
|
||||||
Owl also has another pair of directives that can be used for looping that allow
|
|
||||||
destructuring the contents of the loop item: `t-for` and `t-of`, which behaves
|
|
||||||
much like `for..of` in javascript:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<t t-for="[left, right]" t-of="[['a', 1], ['b', 2], ['c', 3]]" t-key="left">
|
|
||||||
<p><t t-esc="left"/>: <t t-esc="right"/></p>
|
|
||||||
</t>
|
|
||||||
```
|
|
||||||
|
|
||||||
will be rendered as:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<p>a: 1</p>
|
|
||||||
<p>b: 2</p>
|
|
||||||
<p>c: 3</p>
|
|
||||||
```
|
|
||||||
|
|
||||||
An important difference should be made with the usual `QWeb` behaviour: Owl
|
An important difference should be made with the usual `QWeb` behaviour: Owl
|
||||||
requires the presence of a `t-key` directive, to be able to properly reconcile
|
requires the presence of a `t-key` directive, to be able to properly reconcile
|
||||||
renderings.
|
renderings.
|
||||||
|
|
||||||
`t-foreach` can iterate on any iterable, and also has special support for objects
|
`t-foreach` can iterate on an array (the current item will be the current value)
|
||||||
and maps, it will expose the key of the current iteration as the contents of the
|
or an object (the current item will be the current key).
|
||||||
`t-as`, and the corresponding value with the same name and the suffix `_value`.
|
|
||||||
|
|
||||||
In addition to the name passed via t-as, `t-foreach` (but not `t-for`) provides
|
In addition to the name passed via t-as, `t-foreach` provides a few other
|
||||||
a few other useful variables (note: `$as` will be replaced with the name passed
|
variables for various data points (note: `$as` will be replaced with the name
|
||||||
to `t-as`):
|
passed to `t-as`):
|
||||||
|
|
||||||
- `$as_value`: the current iteration value, identical to `$as` for arrays and
|
- `$as_value`: the current iteration value, identical to `$as` for lists and
|
||||||
other iterables, but for objects and maps, it provides the value (where `$as`
|
integers, but for objects, it provides the value (where `$as` provides the key)
|
||||||
provides the key)
|
|
||||||
- `$as_index`: the current iteration index (the first item of the iteration has index 0)
|
- `$as_index`: the current iteration index (the first item of the iteration has index 0)
|
||||||
- `$as_first`: whether the current item is the first of the iteration
|
- `$as_first`: whether the current item is the first of the iteration
|
||||||
(equivalent to `$as_index == 0`)
|
(equivalent to `$as_index == 0`)
|
||||||
@@ -412,9 +392,10 @@ to `t-as`):
|
|||||||
(equivalent to `$as_index + 1 == $as_size`), requires the iteratee’s size be
|
(equivalent to `$as_index + 1 == $as_size`), requires the iteratee’s size be
|
||||||
available
|
available
|
||||||
|
|
||||||
These variables and all new variables created inside`t-foreach` and `t-for` are
|
These extra variables provided and all new variables created into the `t-foreach`
|
||||||
only available inside of the loop. If a variable existed outside the context of
|
are only available in the scope of the `t-foreach`. If the variable exists outside
|
||||||
the loop, the assignment will affect the outer variable.
|
the context of the `t-foreach`, the value is copied at the end of the foreach
|
||||||
|
into the global context.
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<t t-set="existing_variable" t-value="false"/>
|
<t t-set="existing_variable" t-value="false"/>
|
||||||
@@ -426,7 +407,7 @@ the loop, the assignment will affect the outer variable.
|
|||||||
<!-- existing_variable and new_variable now true -->
|
<!-- existing_variable and new_variable now true -->
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- existing_variable still true -->
|
<!-- existing_variable always true -->
|
||||||
<!-- new_variable undefined -->
|
<!-- new_variable undefined -->
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -489,11 +470,18 @@ are all equivalent:
|
|||||||
</t>
|
</t>
|
||||||
```
|
```
|
||||||
|
|
||||||
The `t-key` directive is mandatory, and as mentioned should represent the object's
|
If there is no `t-key` directive, Owl will use the index as a default key.
|
||||||
identity. You may be tempted to use the loop index as a key, but keep in mind that
|
|
||||||
this is only correct if items in the loop cannot be reordered. If this is not the
|
Note: the `t-foreach` directive only accepts arrays (lists) or objects. It does
|
||||||
case, using the index as the key can lead to bugs that are difficult to find, so
|
not work with other iterables, such as `Set`. However, it is only a matter of
|
||||||
use the index as the key only if you are sure items cannot be reordered.
|
using the `...` javascript operator. For example:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<t t-foreach="[...items]" t-as="item">...</t>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `...` operator will convert the `Set` (or any other iterables) into a list,
|
||||||
|
which will work with Owl QWeb.
|
||||||
|
|
||||||
### Sub Templates
|
### Sub Templates
|
||||||
|
|
||||||
|
|||||||
+67
-99
@@ -122,16 +122,14 @@ function handleError(params) {
|
|||||||
}
|
}
|
||||||
const node = "node" in params ? params.node : params.fiber.node;
|
const node = "node" in params ? params.node : params.fiber.node;
|
||||||
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
||||||
if (fiber) {
|
// resets the fibers on components if possible. This is important so that
|
||||||
// resets the fibers on components if possible. This is important so that
|
// new renderings can be properly included in the initial one, if any.
|
||||||
// new renderings can be properly included in the initial one, if any.
|
let current = fiber;
|
||||||
let current = fiber;
|
do {
|
||||||
do {
|
current.node.fiber = current;
|
||||||
current.node.fiber = current;
|
current = current.parent;
|
||||||
current = current.parent;
|
} while (current);
|
||||||
} while (current);
|
fibersInError.set(fiber.root, error);
|
||||||
fibersInError.set(fiber.root, error);
|
|
||||||
}
|
|
||||||
const handled = _handleError(node, error);
|
const handled = _handleError(node, error);
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
console.warn(`[Owl] Unhandled error. Destroying the root component`);
|
console.warn(`[Owl] Unhandled error. Destroying the root component`);
|
||||||
@@ -313,13 +311,20 @@ function updateClass(val, oldVal) {
|
|||||||
* @returns a batched version of the original callback
|
* @returns a batched version of the original callback
|
||||||
*/
|
*/
|
||||||
function batched(callback) {
|
function batched(callback) {
|
||||||
let scheduled = false;
|
let called = false;
|
||||||
return async (...args) => {
|
return async () => {
|
||||||
if (!scheduled) {
|
// This await blocks all calls to the callback here, then releases them sequentially
|
||||||
scheduled = true;
|
// in the next microtick. This line decides the granularity of the batch.
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
scheduled = false;
|
if (!called) {
|
||||||
callback(...args);
|
called = true;
|
||||||
|
// wait for all calls in this microtick to fall through before resetting "called"
|
||||||
|
// so that only the first call to the batched function calls the original callback.
|
||||||
|
// Schedule this before calling the callback so that calls to the batched function
|
||||||
|
// within the callback will proceed only after resetting called to false, and have
|
||||||
|
// a chance to execute the callback again
|
||||||
|
Promise.resolve().then(() => (called = false));
|
||||||
|
callback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1652,7 +1657,8 @@ function cancelFibers(fibers) {
|
|||||||
let node = fiber.node;
|
let node = fiber.node;
|
||||||
fiber.render = throwOnRender;
|
fiber.render = throwOnRender;
|
||||||
if (node.status === 0 /* NEW */) {
|
if (node.status === 0 /* NEW */) {
|
||||||
node.cancel();
|
node.destroy();
|
||||||
|
delete node.parent.children[node.parentKey];
|
||||||
}
|
}
|
||||||
node.fiber = null;
|
node.fiber = null;
|
||||||
if (fiber.bdom) {
|
if (fiber.bdom) {
|
||||||
@@ -2379,9 +2385,6 @@ class ComponentNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async render(deep) {
|
async render(deep) {
|
||||||
if (this.status >= 2 /* CANCELLED */) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let current = this.fiber;
|
let current = this.fiber;
|
||||||
if (current && (current.root.locked || current.bdom === true)) {
|
if (current && (current.root.locked || current.bdom === true)) {
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
@@ -2407,7 +2410,7 @@ class ComponentNode {
|
|||||||
this.fiber = fiber;
|
this.fiber = fiber;
|
||||||
this.app.scheduler.addFiber(fiber);
|
this.app.scheduler.addFiber(fiber);
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
if (this.status >= 2 /* CANCELLED */) {
|
if (this.status === 2 /* DESTROYED */) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We only want to actually render the component if the following two
|
// We only want to actually render the component if the following two
|
||||||
@@ -2425,18 +2428,6 @@ class ComponentNode {
|
|||||||
fiber.render();
|
fiber.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cancel() {
|
|
||||||
this._cancel();
|
|
||||||
delete this.parent.children[this.parentKey];
|
|
||||||
this.app.scheduler.scheduleDestroy(this);
|
|
||||||
}
|
|
||||||
_cancel() {
|
|
||||||
this.status = 2 /* CANCELLED */;
|
|
||||||
const children = this.children;
|
|
||||||
for (let childKey in children) {
|
|
||||||
children[childKey]._cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
destroy() {
|
destroy() {
|
||||||
let shouldRemove = this.status === 1 /* MOUNTED */;
|
let shouldRemove = this.status === 1 /* MOUNTED */;
|
||||||
this._destroy();
|
this._destroy();
|
||||||
@@ -2464,7 +2455,7 @@ class ComponentNode {
|
|||||||
this.app.handleError({ error: e, node: this });
|
this.app.handleError({ error: e, node: this });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.status = 3 /* DESTROYED */;
|
this.status = 2 /* DESTROYED */;
|
||||||
}
|
}
|
||||||
async updateAndRender(props, parentFiber) {
|
async updateAndRender(props, parentFiber) {
|
||||||
this.nextProps = props;
|
this.nextProps = props;
|
||||||
@@ -2997,22 +2988,12 @@ function prepareList(collection) {
|
|||||||
keys = collection;
|
keys = collection;
|
||||||
values = collection;
|
values = collection;
|
||||||
}
|
}
|
||||||
else if (collection instanceof Map) {
|
else if (collection) {
|
||||||
keys = [...collection.keys()];
|
values = Object.keys(collection);
|
||||||
values = [...collection.values()];
|
keys = Object.values(collection);
|
||||||
}
|
|
||||||
else if (collection && typeof collection === "object") {
|
|
||||||
if (Symbol.iterator in collection) {
|
|
||||||
keys = [...collection];
|
|
||||||
values = keys;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
values = Object.keys(collection);
|
|
||||||
keys = Object.values(collection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new OwlError(`Invalid loop expression: "${collection}" is not iterable`);
|
throw new OwlError("Invalid loop expression");
|
||||||
}
|
}
|
||||||
const n = values.length;
|
const n = values.length;
|
||||||
return [keys, values, n, new Array(n)];
|
return [keys, values, n, new Array(n)];
|
||||||
@@ -4839,10 +4820,10 @@ function parseNode(node, ctx) {
|
|||||||
parseTCall(node, ctx) ||
|
parseTCall(node, ctx) ||
|
||||||
parseTCallBlock(node) ||
|
parseTCallBlock(node) ||
|
||||||
parseTEscNode(node, ctx) ||
|
parseTEscNode(node, ctx) ||
|
||||||
parseTOutNode(node, ctx) ||
|
|
||||||
parseTKey(node, ctx) ||
|
parseTKey(node, ctx) ||
|
||||||
parseTTranslation(node, ctx) ||
|
parseTTranslation(node, ctx) ||
|
||||||
parseTSlot(node, ctx) ||
|
parseTSlot(node, ctx) ||
|
||||||
|
parseTOutNode(node, ctx) ||
|
||||||
parseComponent(node, ctx) ||
|
parseComponent(node, ctx) ||
|
||||||
parseDOMNode(node, ctx) ||
|
parseDOMNode(node, ctx) ||
|
||||||
parseTSetNode(node, ctx) ||
|
parseTSetNode(node, ctx) ||
|
||||||
@@ -4955,8 +4936,10 @@ function parseDOMNode(node, ctx) {
|
|||||||
const typeAttr = node.getAttribute("type");
|
const typeAttr = node.getAttribute("type");
|
||||||
const isInput = tagName === "input";
|
const isInput = tagName === "input";
|
||||||
const isSelect = tagName === "select";
|
const isSelect = tagName === "select";
|
||||||
|
const isTextarea = tagName === "textarea";
|
||||||
const isCheckboxInput = isInput && typeAttr === "checkbox";
|
const isCheckboxInput = isInput && typeAttr === "checkbox";
|
||||||
const isRadioInput = isInput && typeAttr === "radio";
|
const isRadioInput = isInput && typeAttr === "radio";
|
||||||
|
const isOtherInput = isInput && !isCheckboxInput && !isRadioInput;
|
||||||
const hasLazyMod = attr.includes(".lazy");
|
const hasLazyMod = attr.includes(".lazy");
|
||||||
const hasNumberMod = attr.includes(".number");
|
const hasNumberMod = attr.includes(".number");
|
||||||
const hasTrimMod = attr.includes(".trim");
|
const hasTrimMod = attr.includes(".trim");
|
||||||
@@ -4968,8 +4951,8 @@ function parseDOMNode(node, ctx) {
|
|||||||
specialInitTargetAttr: isRadioInput ? "checked" : null,
|
specialInitTargetAttr: isRadioInput ? "checked" : null,
|
||||||
eventType,
|
eventType,
|
||||||
hasDynamicChildren: false,
|
hasDynamicChildren: false,
|
||||||
shouldTrim: hasTrimMod,
|
shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
|
||||||
shouldNumberize: hasNumberMod,
|
shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
|
||||||
};
|
};
|
||||||
if (isSelect) {
|
if (isSelect) {
|
||||||
// don't pollute the original ctx
|
// don't pollute the original ctx
|
||||||
@@ -5032,6 +5015,9 @@ function parseTEscNode(node, ctx) {
|
|||||||
content: [tesc],
|
content: [tesc],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (ast.type === 11 /* TComponent */) {
|
||||||
|
throw new OwlError("t-esc is not supported on Component nodes");
|
||||||
|
}
|
||||||
return tesc;
|
return tesc;
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -5473,21 +5459,19 @@ function normalizeTIf(el) {
|
|||||||
*
|
*
|
||||||
* @param el the element containing the tree that should be normalized
|
* @param el the element containing the tree that should be normalized
|
||||||
*/
|
*/
|
||||||
function normalizeTEscTOut(el) {
|
function normalizeTEsc(el) {
|
||||||
for (const d of ["t-esc", "t-out"]) {
|
const elements = [...el.querySelectorAll("[t-esc]")].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
|
||||||
const elements = [...el.querySelectorAll(`[${d}]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
|
for (const el of elements) {
|
||||||
for (const el of elements) {
|
if (el.childNodes.length) {
|
||||||
if (el.childNodes.length) {
|
throw new OwlError("Cannot have t-esc on a component that already has content");
|
||||||
throw new OwlError(`Cannot have ${d} on a component that already has content`);
|
|
||||||
}
|
|
||||||
const value = el.getAttribute(d);
|
|
||||||
el.removeAttribute(d);
|
|
||||||
const t = el.ownerDocument.createElement("t");
|
|
||||||
if (value != null) {
|
|
||||||
t.setAttribute(d, value);
|
|
||||||
}
|
|
||||||
el.appendChild(t);
|
|
||||||
}
|
}
|
||||||
|
const value = el.getAttribute("t-esc");
|
||||||
|
el.removeAttribute("t-esc");
|
||||||
|
const t = el.ownerDocument.createElement("t");
|
||||||
|
if (value != null) {
|
||||||
|
t.setAttribute("t-esc", value);
|
||||||
|
}
|
||||||
|
el.appendChild(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -5498,7 +5482,7 @@ function normalizeTEscTOut(el) {
|
|||||||
*/
|
*/
|
||||||
function normalizeXML(el) {
|
function normalizeXML(el) {
|
||||||
normalizeTIf(el);
|
normalizeTIf(el);
|
||||||
normalizeTEscTOut(el);
|
normalizeTEsc(el);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Parses an XML string into an XML document, throwing errors on parser errors
|
* Parses an XML string into an XML document, throwing errors on parser errors
|
||||||
@@ -5551,7 +5535,7 @@ function compile(template, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do not modify manually. This file is generated by the release script.
|
// do not modify manually. This file is generated by the release script.
|
||||||
const version = "2.2.3";
|
const version = "2.1.3";
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Scheduler
|
// Scheduler
|
||||||
@@ -5561,18 +5545,11 @@ class Scheduler {
|
|||||||
this.tasks = new Set();
|
this.tasks = new Set();
|
||||||
this.frame = 0;
|
this.frame = 0;
|
||||||
this.delayedRenders = [];
|
this.delayedRenders = [];
|
||||||
this.cancelledNodes = new Set();
|
|
||||||
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
|
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
|
||||||
}
|
}
|
||||||
addFiber(fiber) {
|
addFiber(fiber) {
|
||||||
this.tasks.add(fiber.root);
|
this.tasks.add(fiber.root);
|
||||||
}
|
}
|
||||||
scheduleDestroy(node) {
|
|
||||||
this.cancelledNodes.add(node);
|
|
||||||
if (this.frame === 0) {
|
|
||||||
this.frame = this.requestAnimationFrame(() => this.processTasks());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Process all current tasks. This only applies to the fibers that are ready.
|
* Process all current tasks. This only applies to the fibers that are ready.
|
||||||
* Other tasks are left unchanged.
|
* Other tasks are left unchanged.
|
||||||
@@ -5582,28 +5559,21 @@ class Scheduler {
|
|||||||
let renders = this.delayedRenders;
|
let renders = this.delayedRenders;
|
||||||
this.delayedRenders = [];
|
this.delayedRenders = [];
|
||||||
for (let f of renders) {
|
for (let f of renders) {
|
||||||
if (f.root && f.node.status !== 3 /* DESTROYED */ && f.node.fiber === f) {
|
if (f.root && f.node.status !== 2 /* DESTROYED */ && f.node.fiber === f) {
|
||||||
f.render();
|
f.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.frame === 0) {
|
if (this.frame === 0) {
|
||||||
this.frame = this.requestAnimationFrame(() => this.processTasks());
|
this.frame = this.requestAnimationFrame(() => {
|
||||||
}
|
this.frame = 0;
|
||||||
}
|
this.tasks.forEach((fiber) => this.processFiber(fiber));
|
||||||
processTasks() {
|
for (let task of this.tasks) {
|
||||||
this.frame = 0;
|
if (task.node.status === 2 /* DESTROYED */) {
|
||||||
for (let node of this.cancelledNodes) {
|
this.tasks.delete(task);
|
||||||
node._destroy();
|
}
|
||||||
}
|
}
|
||||||
this.cancelledNodes.clear();
|
});
|
||||||
for (let task of this.tasks) {
|
|
||||||
this.processFiber(task);
|
|
||||||
}
|
|
||||||
for (let task of this.tasks) {
|
|
||||||
if (task.node.status === 3 /* DESTROYED */) {
|
|
||||||
this.tasks.delete(task);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processFiber(fiber) {
|
processFiber(fiber) {
|
||||||
@@ -5616,7 +5586,7 @@ class Scheduler {
|
|||||||
this.tasks.delete(fiber);
|
this.tasks.delete(fiber);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fiber.node.status === 3 /* DESTROYED */) {
|
if (fiber.node.status === 2 /* DESTROYED */) {
|
||||||
this.tasks.delete(fiber);
|
this.tasks.delete(fiber);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -5708,8 +5678,8 @@ class App extends TemplateSet {
|
|||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.root) {
|
if (this.root) {
|
||||||
|
this.scheduler.flush();
|
||||||
this.root.destroy();
|
this.root.destroy();
|
||||||
this.scheduler.processTasks();
|
|
||||||
}
|
}
|
||||||
window.__OWL_DEVTOOLS__.apps.delete(this);
|
window.__OWL_DEVTOOLS__.apps.delete(this);
|
||||||
}
|
}
|
||||||
@@ -5840,11 +5810,9 @@ function status(component) {
|
|||||||
switch (component.__owl__.status) {
|
switch (component.__owl__.status) {
|
||||||
case 0 /* NEW */:
|
case 0 /* NEW */:
|
||||||
return "new";
|
return "new";
|
||||||
case 2 /* CANCELLED */:
|
|
||||||
return "cancelled";
|
|
||||||
case 1 /* MOUNTED */:
|
case 1 /* MOUNTED */:
|
||||||
return "mounted";
|
return "mounted";
|
||||||
case 3 /* DESTROYED */:
|
case 2 /* DESTROYED */:
|
||||||
return "destroyed";
|
return "destroyed";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5984,6 +5952,6 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
|
|||||||
export { App, Component, EventBus, OwlError, __info__, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
|
export { App, Component, EventBus, OwlError, __info__, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
|
||||||
|
|
||||||
|
|
||||||
__info__.date = '2023-07-20T06:05:29.796Z';
|
__info__.date = '2023-06-28T09:17:13.630Z';
|
||||||
__info__.hash = 'b1a3b32';
|
__info__.hash = '432ff44';
|
||||||
__info__.url = 'https://github.com/odoo/owl';
|
__info__.url = 'https://github.com/odoo/owl';
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odoo/owl",
|
"name": "@odoo/owl",
|
||||||
"version": "2.2.3",
|
"version": "2.1.4",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odoo/owl",
|
"name": "@odoo/owl",
|
||||||
"version": "2.2.3",
|
"version": "2.1.4",
|
||||||
"description": "Odoo Web Library (OWL)",
|
"description": "Odoo Web Library (OWL)",
|
||||||
"main": "dist/owl.cjs.js",
|
"main": "dist/owl.cjs.js",
|
||||||
"module": "dist/owl.es.js",
|
"module": "dist/owl.es.js",
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
ASTTCallBlock,
|
ASTTCallBlock,
|
||||||
ASTTEsc,
|
ASTTEsc,
|
||||||
ASTText,
|
ASTText,
|
||||||
ASTTFor,
|
|
||||||
ASTTForEach,
|
ASTTForEach,
|
||||||
ASTTif,
|
ASTTif,
|
||||||
ASTTKey,
|
ASTTKey,
|
||||||
@@ -472,8 +471,6 @@ export class CodeGenerator {
|
|||||||
return this.compileTIf(ast, ctx);
|
return this.compileTIf(ast, ctx);
|
||||||
case ASTType.TForEach:
|
case ASTType.TForEach:
|
||||||
return this.compileTForeach(ast, ctx);
|
return this.compileTForeach(ast, ctx);
|
||||||
case ASTType.TFor:
|
|
||||||
return this.compileTFor(ast, ctx);
|
|
||||||
case ASTType.TKey:
|
case ASTType.TKey:
|
||||||
return this.compileTKey(ast, ctx);
|
return this.compileTKey(ast, ctx);
|
||||||
case ASTType.Multi:
|
case ASTType.Multi:
|
||||||
@@ -910,7 +907,7 @@ export class CodeGenerator {
|
|||||||
if (!ast.hasNoValue) {
|
if (!ast.hasNoValue) {
|
||||||
this.addLine(`ctx[\`${ast.elem}_value\`] = ${keys}[${loopVar}];`);
|
this.addLine(`ctx[\`${ast.elem}_value\`] = ${keys}[${loopVar}];`);
|
||||||
}
|
}
|
||||||
this.define(`key${this.target.loopLevel}`, compileExpr(ast.key));
|
this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);
|
||||||
if (this.dev) {
|
if (this.dev) {
|
||||||
// Throw error on duplicate keys in dev mode
|
// Throw error on duplicate keys in dev mode
|
||||||
this.helpers.add("OwlError");
|
this.helpers.add("OwlError");
|
||||||
@@ -957,46 +954,6 @@ export class CodeGenerator {
|
|||||||
return block.varName;
|
return block.varName;
|
||||||
}
|
}
|
||||||
|
|
||||||
compileTFor(ast: ASTTFor, ctx: Context): string {
|
|
||||||
let { block } = ctx;
|
|
||||||
if (block) {
|
|
||||||
this.insertAnchor(block);
|
|
||||||
}
|
|
||||||
block = this.createBlock(block, "list", ctx);
|
|
||||||
this.target.loopLevel++;
|
|
||||||
this.addLine(`ctx = Object.create(ctx);`);
|
|
||||||
// Throw errors on duplicate keys in dev mode
|
|
||||||
if (this.dev) {
|
|
||||||
this.define(`keys${block.id}`, `new Set()`);
|
|
||||||
}
|
|
||||||
this.define(`c_block${block.id}`, "[]");
|
|
||||||
const index = `i${this.target.loopLevel}`;
|
|
||||||
this.addLine(`let ${index} = ${0};`);
|
|
||||||
const binding = compileExpr(ast.binding);
|
|
||||||
this.addLine(`for (${binding} of ${compileExpr(ast.iterable)}) {`);
|
|
||||||
this.target.indentLevel++;
|
|
||||||
this.define(`key${this.target.loopLevel}`, compileExpr(ast.key));
|
|
||||||
if (this.dev) {
|
|
||||||
// Throw error on duplicate keys in dev mode
|
|
||||||
this.helpers.add("OwlError");
|
|
||||||
this.addLine(
|
|
||||||
`if (keys${block.id}.has(String(key${this.target.loopLevel}))) { throw new OwlError(\`Got duplicate key in t-for: \${key${this.target.loopLevel}}\`)}`
|
|
||||||
);
|
|
||||||
this.addLine(`keys${block.id}.add(String(key${this.target.loopLevel}));`);
|
|
||||||
}
|
|
||||||
const subCtx = createContext(ctx, { block, index });
|
|
||||||
this.compileAST(ast.body, subCtx);
|
|
||||||
this.addLine(`${index}++;`);
|
|
||||||
this.target.indentLevel--;
|
|
||||||
this.target.loopLevel--;
|
|
||||||
this.addLine(`}`);
|
|
||||||
if (!ctx.isLast) {
|
|
||||||
this.addLine(`ctx = ctx.__proto__;`);
|
|
||||||
}
|
|
||||||
this.insertBlock("l", block, ctx);
|
|
||||||
return block.varName;
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTKey(ast: ASTTKey, ctx: Context): string | null {
|
compileTKey(ast: ASTTKey, ctx: Context): string | null {
|
||||||
const tKeyExpr = generateId("tKey_");
|
const tKeyExpr = generateId("tKey_");
|
||||||
this.define(tKeyExpr, compileExpr(ast.expr));
|
this.define(tKeyExpr, compileExpr(ast.expr));
|
||||||
|
|||||||
+1
-12
@@ -2,7 +2,6 @@ import type { TemplateSet } from "../runtime/template_set";
|
|||||||
import type { BDom } from "../runtime/blockdom";
|
import type { BDom } from "../runtime/blockdom";
|
||||||
import { CodeGenerator, Config } from "./code_generator";
|
import { CodeGenerator, Config } from "./code_generator";
|
||||||
import { parse } from "./parser";
|
import { parse } from "./parser";
|
||||||
import { OwlError } from "../runtime";
|
|
||||||
|
|
||||||
export type Template = (context: any, vnode: any, key?: string) => BDom;
|
export type Template = (context: any, vnode: any, key?: string) => BDom;
|
||||||
|
|
||||||
@@ -28,15 +27,5 @@ export function compile(
|
|||||||
const codeGenerator = new CodeGenerator(ast, { ...options, hasSafeContext });
|
const codeGenerator = new CodeGenerator(ast, { ...options, hasSafeContext });
|
||||||
const code = codeGenerator.generateCode();
|
const code = codeGenerator.generateCode();
|
||||||
// template function
|
// template function
|
||||||
try {
|
return new Function("app, bdom, helpers", code) as TemplateFunction;
|
||||||
return new Function("app, bdom, helpers", code) as TemplateFunction;
|
|
||||||
} catch (originalError: any) {
|
|
||||||
const { name } = options;
|
|
||||||
const nameStr = name ? `template "${name}"` : "anonymous template";
|
|
||||||
const err = new OwlError(
|
|
||||||
`Failed to compile ${nameStr}: ${originalError.message}\n\ngenerated code:\nfunction(app, bdom, helpers) {\n${code}\n}`
|
|
||||||
);
|
|
||||||
err.cause = originalError;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-62
@@ -26,7 +26,6 @@ export const enum ASTType {
|
|||||||
TCallBlock,
|
TCallBlock,
|
||||||
TTranslation,
|
TTranslation,
|
||||||
TPortal,
|
TPortal,
|
||||||
TFor,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTText {
|
export interface ASTText {
|
||||||
@@ -105,15 +104,7 @@ export interface ASTTForEach {
|
|||||||
hasNoLast: boolean;
|
hasNoLast: boolean;
|
||||||
hasNoIndex: boolean;
|
hasNoIndex: boolean;
|
||||||
hasNoValue: boolean;
|
hasNoValue: boolean;
|
||||||
key: string;
|
key: string | null;
|
||||||
}
|
|
||||||
|
|
||||||
export interface ASTTFor {
|
|
||||||
type: ASTType.TFor;
|
|
||||||
iterable: string;
|
|
||||||
binding: string;
|
|
||||||
body: AST;
|
|
||||||
key: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTKey {
|
export interface ASTTKey {
|
||||||
@@ -192,7 +183,6 @@ export type AST =
|
|||||||
| ASTTCall
|
| ASTTCall
|
||||||
| ASTTOut
|
| ASTTOut
|
||||||
| ASTTForEach
|
| ASTTForEach
|
||||||
| ASTTFor
|
|
||||||
| ASTTKey
|
| ASTTKey
|
||||||
| ASTComponent
|
| ASTComponent
|
||||||
| ASTSlot
|
| ASTSlot
|
||||||
@@ -240,16 +230,15 @@ function parseNode(node: Node, ctx: ParsingContext): AST | null {
|
|||||||
return (
|
return (
|
||||||
parseTDebugLog(node, ctx) ||
|
parseTDebugLog(node, ctx) ||
|
||||||
parseTForEach(node, ctx) ||
|
parseTForEach(node, ctx) ||
|
||||||
parseTFor(node, ctx) ||
|
|
||||||
parseTIf(node, ctx) ||
|
parseTIf(node, ctx) ||
|
||||||
parseTPortal(node, ctx) ||
|
parseTPortal(node, ctx) ||
|
||||||
parseTCall(node, ctx) ||
|
parseTCall(node, ctx) ||
|
||||||
parseTCallBlock(node, ctx) ||
|
parseTCallBlock(node, ctx) ||
|
||||||
parseTEscNode(node, ctx) ||
|
parseTEscNode(node, ctx) ||
|
||||||
parseTOutNode(node, ctx) ||
|
|
||||||
parseTKey(node, ctx) ||
|
parseTKey(node, ctx) ||
|
||||||
parseTTranslation(node, ctx) ||
|
parseTTranslation(node, ctx) ||
|
||||||
parseTSlot(node, ctx) ||
|
parseTSlot(node, ctx) ||
|
||||||
|
parseTOutNode(node, ctx) ||
|
||||||
parseComponent(node, ctx) ||
|
parseComponent(node, ctx) ||
|
||||||
parseDOMNode(node, ctx) ||
|
parseDOMNode(node, ctx) ||
|
||||||
parseTSetNode(node, ctx) ||
|
parseTSetNode(node, ctx) ||
|
||||||
@@ -376,8 +365,10 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const typeAttr = node.getAttribute("type");
|
const typeAttr = node.getAttribute("type");
|
||||||
const isInput = tagName === "input";
|
const isInput = tagName === "input";
|
||||||
const isSelect = tagName === "select";
|
const isSelect = tagName === "select";
|
||||||
|
const isTextarea = tagName === "textarea";
|
||||||
const isCheckboxInput = isInput && typeAttr === "checkbox";
|
const isCheckboxInput = isInput && typeAttr === "checkbox";
|
||||||
const isRadioInput = isInput && typeAttr === "radio";
|
const isRadioInput = isInput && typeAttr === "radio";
|
||||||
|
const isOtherInput = isInput && !isCheckboxInput && !isRadioInput;
|
||||||
const hasLazyMod = attr.includes(".lazy");
|
const hasLazyMod = attr.includes(".lazy");
|
||||||
const hasNumberMod = attr.includes(".number");
|
const hasNumberMod = attr.includes(".number");
|
||||||
const hasTrimMod = attr.includes(".trim");
|
const hasTrimMod = attr.includes(".trim");
|
||||||
@@ -390,8 +381,8 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
specialInitTargetAttr: isRadioInput ? "checked" : null,
|
specialInitTargetAttr: isRadioInput ? "checked" : null,
|
||||||
eventType,
|
eventType,
|
||||||
hasDynamicChildren: false,
|
hasDynamicChildren: false,
|
||||||
shouldTrim: hasTrimMod,
|
shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
|
||||||
shouldNumberize: hasNumberMod,
|
shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
|
||||||
};
|
};
|
||||||
if (isSelect) {
|
if (isSelect) {
|
||||||
// don't pollute the original ctx
|
// don't pollute the original ctx
|
||||||
@@ -455,6 +446,9 @@ function parseTEscNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
content: [tesc],
|
content: [tesc],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (ast.type === ASTType.TComponent) {
|
||||||
|
throw new OwlError("t-esc is not supported on Component nodes");
|
||||||
|
}
|
||||||
return tesc;
|
return tesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,36 +536,6 @@ function parseTForEach(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTFor(node: Element, ctx: ParsingContext): AST | null {
|
|
||||||
if (!node.hasAttribute("t-for")) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const binding = node.getAttribute("t-for")!;
|
|
||||||
node.removeAttribute("t-for");
|
|
||||||
const iterable = node.getAttribute("t-of") || "";
|
|
||||||
node.removeAttribute("t-of");
|
|
||||||
const key = node.getAttribute("t-key");
|
|
||||||
if (!key) {
|
|
||||||
throw new OwlError(
|
|
||||||
`"Directive t-for should always be used with a t-key!" (expression: t-for="${binding}" t-of="${iterable}")`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
node.removeAttribute("t-key");
|
|
||||||
const body = parseNode(node, ctx);
|
|
||||||
|
|
||||||
if (!body) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: ASTType.TFor,
|
|
||||||
iterable,
|
|
||||||
binding,
|
|
||||||
body,
|
|
||||||
key,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseTKey(node: Element, ctx: ParsingContext): AST | null {
|
function parseTKey(node: Element, ctx: ParsingContext): AST | null {
|
||||||
if (!node.hasAttribute("t-key")) {
|
if (!node.hasAttribute("t-key")) {
|
||||||
return null;
|
return null;
|
||||||
@@ -979,23 +943,21 @@ function normalizeTIf(el: Element) {
|
|||||||
*
|
*
|
||||||
* @param el the element containing the tree that should be normalized
|
* @param el the element containing the tree that should be normalized
|
||||||
*/
|
*/
|
||||||
function normalizeTEscTOut(el: Element) {
|
function normalizeTEsc(el: Element) {
|
||||||
for (const d of ["t-esc", "t-out"]) {
|
const elements = [...el.querySelectorAll("[t-esc]")].filter(
|
||||||
const elements = [...el.querySelectorAll(`[${d}]`)].filter(
|
(el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component")
|
||||||
(el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component")
|
);
|
||||||
);
|
for (const el of elements) {
|
||||||
for (const el of elements) {
|
if (el.childNodes.length) {
|
||||||
if (el.childNodes.length) {
|
throw new OwlError("Cannot have t-esc on a component that already has content");
|
||||||
throw new OwlError(`Cannot have ${d} on a component that already has content`);
|
|
||||||
}
|
|
||||||
const value = el.getAttribute(d);
|
|
||||||
el.removeAttribute(d);
|
|
||||||
const t = el.ownerDocument.createElement("t");
|
|
||||||
if (value != null) {
|
|
||||||
t.setAttribute(d, value);
|
|
||||||
}
|
|
||||||
el.appendChild(t);
|
|
||||||
}
|
}
|
||||||
|
const value = el.getAttribute("t-esc");
|
||||||
|
el.removeAttribute("t-esc");
|
||||||
|
const t = el.ownerDocument.createElement("t");
|
||||||
|
if (value != null) {
|
||||||
|
t.setAttribute("t-esc", value);
|
||||||
|
}
|
||||||
|
el.appendChild(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1007,7 +969,7 @@ function normalizeTEscTOut(el: Element) {
|
|||||||
*/
|
*/
|
||||||
function normalizeXML(el: Element) {
|
function normalizeXML(el: Element) {
|
||||||
normalizeTIf(el);
|
normalizeTIf(el);
|
||||||
normalizeTEscTOut(el);
|
normalizeTEsc(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -136,8 +136,8 @@ export class App<
|
|||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.root) {
|
if (this.root) {
|
||||||
|
this.scheduler.flush();
|
||||||
this.root.destroy();
|
this.root.destroy();
|
||||||
this.scheduler.processTasks();
|
|
||||||
}
|
}
|
||||||
window.__OWL_DEVTOOLS__.apps.delete(this);
|
window.__OWL_DEVTOOLS__.apps.delete(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,9 +145,6 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
|
|||||||
}
|
}
|
||||||
|
|
||||||
async render(deep: boolean) {
|
async render(deep: boolean) {
|
||||||
if (this.status >= STATUS.CANCELLED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let current = this.fiber;
|
let current = this.fiber;
|
||||||
if (current && (current.root!.locked || (current as any).bdom === true)) {
|
if (current && (current.root!.locked || (current as any).bdom === true)) {
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
@@ -174,7 +171,7 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
|
|||||||
|
|
||||||
this.app.scheduler.addFiber(fiber);
|
this.app.scheduler.addFiber(fiber);
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
if (this.status >= STATUS.CANCELLED) {
|
if (this.status === STATUS.DESTROYED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We only want to actually render the component if the following two
|
// We only want to actually render the component if the following two
|
||||||
@@ -193,20 +190,6 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this._cancel();
|
|
||||||
delete this.parent!.children[this.parentKey!];
|
|
||||||
this.app.scheduler.scheduleDestroy(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_cancel() {
|
|
||||||
this.status = STATUS.CANCELLED;
|
|
||||||
const children = this.children;
|
|
||||||
for (let childKey in children) {
|
|
||||||
children[childKey]._cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
let shouldRemove = this.status === STATUS.MOUNTED;
|
let shouldRemove = this.status === STATUS.MOUNTED;
|
||||||
this._destroy();
|
this._destroy();
|
||||||
|
|||||||
@@ -51,19 +51,17 @@ export function handleError(params: ErrorParams) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const node = "node" in params ? params.node : params.fiber.node;
|
const node = "node" in params ? params.node : params.fiber.node;
|
||||||
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
const fiber = "fiber" in params ? params.fiber : node.fiber!;
|
||||||
|
|
||||||
if (fiber) {
|
// resets the fibers on components if possible. This is important so that
|
||||||
// resets the fibers on components if possible. This is important so that
|
// new renderings can be properly included in the initial one, if any.
|
||||||
// new renderings can be properly included in the initial one, if any.
|
let current: Fiber | null = fiber;
|
||||||
let current: Fiber | null = fiber;
|
do {
|
||||||
do {
|
current.node.fiber = current;
|
||||||
current.node.fiber = current;
|
current = current.parent;
|
||||||
current = current.parent;
|
} while (current);
|
||||||
} while (current);
|
|
||||||
|
|
||||||
fibersInError.set(fiber.root!, error);
|
fibersInError.set(fiber.root!, error);
|
||||||
}
|
|
||||||
|
|
||||||
const handled = _handleError(node, error);
|
const handled = _handleError(node, error);
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ function cancelFibers(fibers: Fiber[]): number {
|
|||||||
let node = fiber.node;
|
let node = fiber.node;
|
||||||
fiber.render = throwOnRender;
|
fiber.render = throwOnRender;
|
||||||
if (node.status === STATUS.NEW) {
|
if (node.status === STATUS.NEW) {
|
||||||
node.cancel();
|
node.destroy();
|
||||||
|
delete node.parent!.children[node.parentKey!];
|
||||||
}
|
}
|
||||||
node.fiber = null;
|
node.fiber = null;
|
||||||
if (fiber.bdom) {
|
if (fiber.bdom) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { ComponentNode } from "./component_node";
|
|
||||||
import { fibersInError } from "./error_handling";
|
import { fibersInError } from "./error_handling";
|
||||||
import { Fiber, RootFiber } from "./fibers";
|
import { Fiber, RootFiber } from "./fibers";
|
||||||
import { STATUS } from "./status";
|
import { STATUS } from "./status";
|
||||||
@@ -15,7 +14,6 @@ export class Scheduler {
|
|||||||
requestAnimationFrame: Window["requestAnimationFrame"];
|
requestAnimationFrame: Window["requestAnimationFrame"];
|
||||||
frame: number = 0;
|
frame: number = 0;
|
||||||
delayedRenders: Fiber[] = [];
|
delayedRenders: Fiber[] = [];
|
||||||
cancelledNodes: Set<ComponentNode> = new Set();
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
|
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
|
||||||
@@ -25,13 +23,6 @@ export class Scheduler {
|
|||||||
this.tasks.add(fiber.root!);
|
this.tasks.add(fiber.root!);
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleDestroy(node: ComponentNode) {
|
|
||||||
this.cancelledNodes.add(node);
|
|
||||||
if (this.frame === 0) {
|
|
||||||
this.frame = this.requestAnimationFrame(() => this.processTasks());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process all current tasks. This only applies to the fibers that are ready.
|
* Process all current tasks. This only applies to the fibers that are ready.
|
||||||
* Other tasks are left unchanged.
|
* Other tasks are left unchanged.
|
||||||
@@ -48,23 +39,15 @@ export class Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.frame === 0) {
|
if (this.frame === 0) {
|
||||||
this.frame = this.requestAnimationFrame(() => this.processTasks());
|
this.frame = this.requestAnimationFrame(() => {
|
||||||
}
|
this.frame = 0;
|
||||||
}
|
this.tasks.forEach((fiber) => this.processFiber(fiber));
|
||||||
|
for (let task of this.tasks) {
|
||||||
processTasks() {
|
if (task.node.status === STATUS.DESTROYED) {
|
||||||
this.frame = 0;
|
this.tasks.delete(task);
|
||||||
for (let node of this.cancelledNodes) {
|
}
|
||||||
node._destroy();
|
}
|
||||||
}
|
});
|
||||||
this.cancelledNodes.clear();
|
|
||||||
for (let task of this.tasks) {
|
|
||||||
this.processFiber(task);
|
|
||||||
}
|
|
||||||
for (let task of this.tasks) {
|
|
||||||
if (task.node.status === STATUS.DESTROYED) {
|
|
||||||
this.tasks.delete(task);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,20 +7,15 @@ import type { Component } from "./component";
|
|||||||
export const enum STATUS {
|
export const enum STATUS {
|
||||||
NEW,
|
NEW,
|
||||||
MOUNTED, // is ready, and in DOM. It has a valid el
|
MOUNTED, // is ready, and in DOM. It has a valid el
|
||||||
// component has been created, but has been replaced by a newer component before being mounted
|
|
||||||
// it is cancelled until the next animation frame where it will be destroyed
|
|
||||||
CANCELLED,
|
|
||||||
DESTROYED,
|
DESTROYED,
|
||||||
}
|
}
|
||||||
|
|
||||||
type STATUS_DESCR = "new" | "mounted" | "cancelled" | "destroyed";
|
type STATUS_DESCR = "new" | "mounted" | "destroyed";
|
||||||
|
|
||||||
export function status(component: Component): STATUS_DESCR {
|
export function status(component: Component): STATUS_DESCR {
|
||||||
switch (component.__owl__.status) {
|
switch (component.__owl__.status) {
|
||||||
case STATUS.NEW:
|
case STATUS.NEW:
|
||||||
return "new";
|
return "new";
|
||||||
case STATUS.CANCELLED:
|
|
||||||
return "cancelled";
|
|
||||||
case STATUS.MOUNTED:
|
case STATUS.MOUNTED:
|
||||||
return "mounted";
|
return "mounted";
|
||||||
case STATUS.DESTROYED:
|
case STATUS.DESTROYED:
|
||||||
|
|||||||
@@ -60,26 +60,18 @@ function withKey(elem: any, k: string) {
|
|||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareList(collection: unknown): [unknown[], unknown[], number, undefined[]] {
|
function prepareList(collection: any): [any[], any[], number, any[]] {
|
||||||
let keys: unknown[];
|
let keys: any[];
|
||||||
let values: unknown[];
|
let values: any[];
|
||||||
|
|
||||||
if (Array.isArray(collection)) {
|
if (Array.isArray(collection)) {
|
||||||
keys = collection;
|
keys = collection;
|
||||||
values = collection;
|
values = collection;
|
||||||
} else if (collection instanceof Map) {
|
} else if (collection) {
|
||||||
keys = [...collection.keys()];
|
values = Object.keys(collection);
|
||||||
values = [...collection.values()];
|
keys = Object.values(collection);
|
||||||
} else if (collection && typeof collection === "object") {
|
|
||||||
if (Symbol.iterator in collection) {
|
|
||||||
keys = [...(<Iterable<unknown>>collection)];
|
|
||||||
values = keys;
|
|
||||||
} else {
|
|
||||||
values = Object.keys(collection);
|
|
||||||
keys = Object.values(collection);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new OwlError(`Invalid loop expression: "${collection}" is not iterable`);
|
throw new OwlError("Invalid loop expression");
|
||||||
}
|
}
|
||||||
const n = values.length;
|
const n = values.length;
|
||||||
return [keys, values, n, new Array(n)];
|
return [keys, values, n, new Array(n)];
|
||||||
|
|||||||
+14
-7
@@ -9,13 +9,20 @@ export type Callback = () => void;
|
|||||||
* @returns a batched version of the original callback
|
* @returns a batched version of the original callback
|
||||||
*/
|
*/
|
||||||
export function batched(callback: Callback): Callback {
|
export function batched(callback: Callback): Callback {
|
||||||
let scheduled = false;
|
let called = false;
|
||||||
return async (...args) => {
|
return async () => {
|
||||||
if (!scheduled) {
|
// This await blocks all calls to the callback here, then releases them sequentially
|
||||||
scheduled = true;
|
// in the next microtick. This line decides the granularity of the batch.
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
scheduled = false;
|
if (!called) {
|
||||||
callback(...args);
|
called = true;
|
||||||
|
// wait for all calls in this microtick to fall through before resetting "called"
|
||||||
|
// so that only the first call to the batched function calls the original callback.
|
||||||
|
// Schedule this before calling the callback so that calls to the batched function
|
||||||
|
// within the callback will proceed only after resetting called to false, and have
|
||||||
|
// a chance to execute the callback again
|
||||||
|
Promise.resolve().then(() => (called = false));
|
||||||
|
callback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
// do not modify manually. This file is generated by the release script.
|
// do not modify manually. This file is generated by the release script.
|
||||||
export const version = "2.2.3";
|
export const version = "2.1.4";
|
||||||
|
|||||||
@@ -15,34 +15,6 @@ exports[`app App supports env with getters/setters 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`app app: clear scheduler tasks and destroy cancelled nodes immediately on destroy 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
const comp1 = app.createComponent(\`B\`, true, false, false, []);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let b2,b3;
|
|
||||||
b2 = text(\`A\`);
|
|
||||||
if (ctx['state'].value) {
|
|
||||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
|
||||||
}
|
|
||||||
return multi([b2, b3]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`app app: clear scheduler tasks and destroy cancelled nodes immediately on destroy 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`B\`);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`app can configure an app with props 1`] = `
|
exports[`app can configure an app with props 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
+2
-50
@@ -1,14 +1,6 @@
|
|||||||
import { App, Component, mount, onWillStart, useState, xml } from "../../src";
|
import { App, Component, mount, xml } from "../../src";
|
||||||
import { status } from "../../src/runtime/status";
|
import { status } from "../../src/runtime/status";
|
||||||
import {
|
import { makeTestFixture, snapshotEverything, nextTick, elem } from "../helpers";
|
||||||
makeTestFixture,
|
|
||||||
snapshotEverything,
|
|
||||||
nextTick,
|
|
||||||
elem,
|
|
||||||
useLogLifecycle,
|
|
||||||
makeDeferred,
|
|
||||||
nextMicroTick,
|
|
||||||
} from "../helpers";
|
|
||||||
|
|
||||||
let fixture: HTMLElement;
|
let fixture: HTMLElement;
|
||||||
|
|
||||||
@@ -102,44 +94,4 @@ describe("app", () => {
|
|||||||
expect(iframeDoc.contains(div)).toBe(false);
|
expect(iframeDoc.contains(div)).toBe(false);
|
||||||
expect(status(comp)).toBe("destroyed");
|
expect(status(comp)).toBe("destroyed");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("app: clear scheduler tasks and destroy cancelled nodes immediately on destroy", async () => {
|
|
||||||
let def = makeDeferred();
|
|
||||||
class B extends Component {
|
|
||||||
static template = xml`B`;
|
|
||||||
setup() {
|
|
||||||
useLogLifecycle();
|
|
||||||
onWillStart(() => def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class A extends Component {
|
|
||||||
static template = xml`A<t t-if="state.value"><B/></t>`;
|
|
||||||
static components = { B };
|
|
||||||
state = useState({ value: false });
|
|
||||||
setup() {
|
|
||||||
useLogLifecycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new App(A);
|
|
||||||
const comp = await app.mount(fixture);
|
|
||||||
expect(["A:setup", "A:willStart", "A:willRender", "A:rendered", "A:mounted"]).toBeLogged();
|
|
||||||
|
|
||||||
comp.state.value = true;
|
|
||||||
await nextTick();
|
|
||||||
expect(["A:willRender", "B:setup", "B:willStart", "A:rendered"]).toBeLogged();
|
|
||||||
|
|
||||||
// rerender to force the instantiation of a new B component (and cancelling the first)
|
|
||||||
comp.render();
|
|
||||||
await nextMicroTick();
|
|
||||||
expect(["A:willRender", "B:setup", "B:willStart", "A:rendered"]).toBeLogged();
|
|
||||||
|
|
||||||
app.destroy();
|
|
||||||
expect([
|
|
||||||
"A:willUnmount",
|
|
||||||
"B:willDestroy",
|
|
||||||
"A:willDestroy",
|
|
||||||
"B:willDestroy", // make sure the 2 B instances have been destroyed synchronously
|
|
||||||
]).toBeLogged();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,670 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`t-for destructuring array items 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for ([ctx['key'],ctx['value']] of Object.entries({a:1,b:2})) {
|
|
||||||
const key1 = ctx['key'];
|
|
||||||
const b3 = text(\`(\`);
|
|
||||||
const b4 = text(ctx['key']);
|
|
||||||
const b5 = text(\`: \`);
|
|
||||||
const b6 = text(ctx['value']);
|
|
||||||
const b7 = text(\`)\`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for destructuring array items: rest 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for ([ctx['head'],...ctx['tail']] of [[1,2,3],[4,5,6]]) {
|
|
||||||
const key1 = ctx['head'];
|
|
||||||
const b3 = text(\`(\`);
|
|
||||||
const b4 = text(ctx['head']);
|
|
||||||
const b5 = text(\`;\`);
|
|
||||||
const b6 = text(ctx['tail']);
|
|
||||||
const b7 = text(\`)\`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for destructuring object items 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for ({k:ctx['k'],v:ctx['v']} of [{k:'a',v:1},{k:'b',v:2}]) {
|
|
||||||
const key1 = ctx['k'];
|
|
||||||
const b3 = text(\`(\`);
|
|
||||||
const b4 = text(ctx['k']);
|
|
||||||
const b5 = text(\`: \`);
|
|
||||||
const b6 = text(ctx['v']);
|
|
||||||
const b7 = text(\`)\`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for destructuring object items: rest 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for ({k:ctx['k'],v:ctx['v']} of [{k:'a',v:1},{k:'b',v:2}]) {
|
|
||||||
const key1 = ctx['k'];
|
|
||||||
const b3 = text(\`(\`);
|
|
||||||
const b4 = text(ctx['k']);
|
|
||||||
const b5 = text(\`: \`);
|
|
||||||
const b6 = text(ctx['v']);
|
|
||||||
const b7 = text(\`)\`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for does not pollute the rendering context 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of [1]) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
c_block2[i1] = withKey(text(ctx['item']), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for iterate on items (on a element node) 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
let block3 = createBlock(\`<span><block-text-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of [1,2]) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
let txt1 = ctx['item'];
|
|
||||||
c_block2[i1] = withKey(block3([txt1]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for iterate, Map param 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for ([ctx['key'],ctx['value']] of ctx['map']) {
|
|
||||||
const key1 = ctx['key'];
|
|
||||||
const b3 = text(\` [\`);
|
|
||||||
const b4 = text(ctx['key']);
|
|
||||||
const b5 = text(\`: \`);
|
|
||||||
const b6 = text(ctx['value']);
|
|
||||||
const b7 = text(\`] \`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for iterate, Set param 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of ctx['set']) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
c_block1[i1] = withKey(text(ctx['item']), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for iterate, generator param 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of ctx['gen']()) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
c_block1[i1] = withKey(text(ctx['item']), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for iterate, iterable param 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of ctx['map'].values()) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
c_block1[i1] = withKey(text(ctx['item']), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for nested destructuring 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for ([ctx['key'],{left:ctx['left'],right:ctx['right']}] of Object.entries(ctx['obj'])) {
|
|
||||||
const key1 = ctx['key'];
|
|
||||||
const b3 = text(\`(\`);
|
|
||||||
const b4 = text(ctx['key']);
|
|
||||||
const b5 = text(\`: [\`);
|
|
||||||
const b6 = text(ctx['left']);
|
|
||||||
const b7 = text(\`, \`);
|
|
||||||
const b8 = text(ctx['right']);
|
|
||||||
const b9 = text(\`])\`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7, b8, b9]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for simple iteration (in a node) 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of [3,2,1]) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
c_block2[i1] = withKey(text(ctx['item']), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for simple iteration 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of [3,2,1]) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
c_block1[i1] = withKey(text(ctx['item']), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for simple iteration with two nodes inside 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block3 = createBlock(\`<span>a<block-text-0/></span>\`);
|
|
||||||
let block4 = createBlock(\`<span>b<block-text-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of [3,2,1]) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
let txt1 = ctx['item'];
|
|
||||||
const b3 = block3([txt1]);
|
|
||||||
let txt2 = ctx['item'];
|
|
||||||
const b4 = block4([txt2]);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-call with body in t-for in t-for 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
|
||||||
const callTemplate_1 = app.getTemplate(\`sub\`);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/><span>[<block-text-0/>][<block-text-1/>][<block-text-2/>]</span></div>\`);
|
|
||||||
let block6 = createBlock(\`<span><block-text-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
ctx[isBoundary] = 1
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['a'] of ctx['numbers']) {
|
|
||||||
const key1 = ctx['a'];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block4 = [];
|
|
||||||
let i2 = 0;
|
|
||||||
for (ctx['b'] of ctx['letters']) {
|
|
||||||
const key2 = ctx['b'];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
ctx[isBoundary] = 1;
|
|
||||||
setContextValue(ctx, \\"c\\", 'x'+'_'+ctx['a']+'_'+ctx['b']);
|
|
||||||
c_block4[i2] = withKey(callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}__\${key2}\`), key2);
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
i2++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
const b4 = list(c_block4);
|
|
||||||
let txt1 = ctx['c'];
|
|
||||||
const b6 = block6([txt1]);
|
|
||||||
c_block2[i1] = withKey(multi([b4, b6]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
let txt2 = ctx['a'];
|
|
||||||
let txt3 = ctx['b'];
|
|
||||||
let txt4 = ctx['c'];
|
|
||||||
return block1([txt2, txt3, txt4], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-call with body in t-for in t-for 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
const b2 = text(\` [\`);
|
|
||||||
const b3 = text(ctx['a']);
|
|
||||||
const b4 = text(\`] [\`);
|
|
||||||
const b5 = text(ctx['b']);
|
|
||||||
const b6 = text(\`] [\`);
|
|
||||||
const b7 = text(ctx['c']);
|
|
||||||
const b8 = text(\`] \`);
|
|
||||||
return multi([b2, b3, b4, b5, b6, b7, b8]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-call without body in t-for in t-for 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const callTemplate_1 = app.getTemplate(\`sub\`);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/><span>[<block-text-0/>][<block-text-1/>][<block-text-2/>]</span></div>\`);
|
|
||||||
let block6 = createBlock(\`<span><block-text-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['a'] of ctx['numbers']) {
|
|
||||||
const key1 = ctx['a'];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block4 = [];
|
|
||||||
let i2 = 0;
|
|
||||||
for (ctx['b'] of ctx['letters']) {
|
|
||||||
const key2 = ctx['b'];
|
|
||||||
c_block4[i2] = withKey(callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}__\${key2}\`), key2);
|
|
||||||
i2++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
const b4 = list(c_block4);
|
|
||||||
let txt1 = ctx['c'];
|
|
||||||
const b6 = block6([txt1]);
|
|
||||||
c_block2[i1] = withKey(multi([b4, b6]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
let txt2 = ctx['a'];
|
|
||||||
let txt3 = ctx['b'];
|
|
||||||
let txt4 = ctx['c'];
|
|
||||||
return block1([txt2, txt3, txt4], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-call without body in t-for in t-for 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { isBoundary, withDefault, setContextValue } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
ctx[isBoundary] = 1
|
|
||||||
setContextValue(ctx, \\"c\\", 'x'+'_'+ctx['a']+'_'+ctx['b']);
|
|
||||||
const b2 = text(\` [\`);
|
|
||||||
const b3 = text(ctx['a']);
|
|
||||||
const b4 = text(\`] [\`);
|
|
||||||
const b5 = text(ctx['b']);
|
|
||||||
const b6 = text(\`] [\`);
|
|
||||||
const b7 = text(ctx['c']);
|
|
||||||
const b8 = text(\`] \`);
|
|
||||||
return multi([b2, b3, b4, b5, b6, b7, b8]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-for in t-for 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['number'] of ctx['numbers']) {
|
|
||||||
const key1 = ctx['number'];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block3 = [];
|
|
||||||
let i2 = 0;
|
|
||||||
for (ctx['letter'] of ctx['letters']) {
|
|
||||||
const key2 = ctx['letter'];
|
|
||||||
const b5 = text(\` [\`);
|
|
||||||
const b6 = text(ctx['number']);
|
|
||||||
const b7 = text(ctx['letter']);
|
|
||||||
const b8 = text(\`] \`);
|
|
||||||
c_block3[i2] = withKey(multi([b5, b6, b7, b8]), key2);
|
|
||||||
i2++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
c_block2[i1] = withKey(list(c_block3), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-for in t-foreach 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { prepareList, withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['numbers']);;
|
|
||||||
for (let i1 = 0; i1 < l_block2; i1++) {
|
|
||||||
ctx[\`number\`] = v_block2[i1];
|
|
||||||
const key1 = ctx['number'];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block3 = [];
|
|
||||||
let i2 = 0;
|
|
||||||
for (ctx['letter'] of ctx['letters']) {
|
|
||||||
const key2 = ctx['letter'];
|
|
||||||
const b5 = text(\` [\`);
|
|
||||||
const b6 = text(ctx['number']);
|
|
||||||
const b7 = text(ctx['letter']);
|
|
||||||
const b8 = text(\`] \`);
|
|
||||||
c_block3[i2] = withKey(multi([b5, b6, b7, b8]), key2);
|
|
||||||
i2++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
c_block2[i1] = withKey(list(c_block3), key1);
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-for with t-if inside (no external node) 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block3 = createBlock(\`<span><block-text-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for ({id:ctx['id'],text:ctx['text']} of ctx['elems']) {
|
|
||||||
const key1 = ctx['id'];
|
|
||||||
let b3;
|
|
||||||
if (ctx['id']<3) {
|
|
||||||
let txt1 = ctx['text'];
|
|
||||||
b3 = block3([txt1]);
|
|
||||||
}
|
|
||||||
c_block1[i1] = withKey(multi([b3]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-for with t-if inside 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
let block4 = createBlock(\`<span><block-text-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for ({id:ctx['id'],text:ctx['text']} of ctx['elems']) {
|
|
||||||
const key1 = ctx['id'];
|
|
||||||
let b4;
|
|
||||||
if (ctx['id']<3) {
|
|
||||||
let txt1 = ctx['text'];
|
|
||||||
b4 = block4([txt1]);
|
|
||||||
}
|
|
||||||
c_block2[i1] = withKey(multi([b4]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-foreach in t-for 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { prepareList, withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['number'] of ctx['numbers']) {
|
|
||||||
const key1 = ctx['number'];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const [k_block3, v_block3, l_block3, c_block3] = prepareList(ctx['letters']);;
|
|
||||||
for (let i2 = 0; i2 < l_block3; i2++) {
|
|
||||||
ctx[\`letter\`] = v_block3[i2];
|
|
||||||
const key2 = ctx['letter'];
|
|
||||||
const b5 = text(\` [\`);
|
|
||||||
const b6 = text(ctx['number']);
|
|
||||||
const b7 = text(ctx['letter']);
|
|
||||||
const b8 = text(\`] \`);
|
|
||||||
c_block3[i2] = withKey(multi([b5, b6, b7, b8]), key2);
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
c_block2[i1] = withKey(list(c_block3), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for t-key on t-for 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
let block3 = createBlock(\`<span/>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['thing'] of ctx['things']) {
|
|
||||||
const key1 = ctx['thing'];
|
|
||||||
c_block2[i1] = withKey(block3(), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-for throws error if invalid loop expression 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
let block3 = createBlock(\`<span/>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of ctx['abc']) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
const tKey_1 = ctx['item'];
|
|
||||||
c_block2[i1] = withKey(block3(), tKey_1 + key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
@@ -77,62 +77,6 @@ exports[`t-foreach iterate on items 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`t-foreach iterate, Map param 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { prepareList, withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['value']);;
|
|
||||||
for (let i1 = 0; i1 < l_block1; i1++) {
|
|
||||||
ctx[\`item\`] = v_block1[i1];
|
|
||||||
ctx[\`item_index\`] = i1;
|
|
||||||
ctx[\`item_value\`] = k_block1[i1];
|
|
||||||
const key1 = ctx['item_index'];
|
|
||||||
const b3 = text(\` [\`);
|
|
||||||
const b4 = text(ctx['item_index']);
|
|
||||||
const b5 = text(\`: \`);
|
|
||||||
const b6 = text(ctx['item']);
|
|
||||||
const b7 = text(\` \`);
|
|
||||||
const b8 = text(ctx['item_value']);
|
|
||||||
const b9 = text(\`] \`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7, b8, b9]), key1);
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-foreach iterate, Set param 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { prepareList, withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['value']);;
|
|
||||||
for (let i1 = 0; i1 < l_block1; i1++) {
|
|
||||||
ctx[\`item\`] = v_block1[i1];
|
|
||||||
ctx[\`item_index\`] = i1;
|
|
||||||
ctx[\`item_value\`] = k_block1[i1];
|
|
||||||
const key1 = ctx['item_index'];
|
|
||||||
const b3 = text(\` [\`);
|
|
||||||
const b4 = text(ctx['item_index']);
|
|
||||||
const b5 = text(\`: \`);
|
|
||||||
const b6 = text(ctx['item']);
|
|
||||||
const b7 = text(\` \`);
|
|
||||||
const b8 = text(ctx['item_value']);
|
|
||||||
const b9 = text(\`] \`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7, b8, b9]), key1);
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-foreach iterate, dict param 1`] = `
|
exports[`t-foreach iterate, dict param 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -164,62 +108,6 @@ exports[`t-foreach iterate, dict param 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`t-foreach iterate, generator param 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { prepareList, withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['gen']());;
|
|
||||||
for (let i1 = 0; i1 < l_block1; i1++) {
|
|
||||||
ctx[\`item\`] = v_block1[i1];
|
|
||||||
ctx[\`item_index\`] = i1;
|
|
||||||
ctx[\`item_value\`] = k_block1[i1];
|
|
||||||
const key1 = ctx['item_index'];
|
|
||||||
const b3 = text(\` [\`);
|
|
||||||
const b4 = text(ctx['item_index']);
|
|
||||||
const b5 = text(\`: \`);
|
|
||||||
const b6 = text(ctx['item']);
|
|
||||||
const b7 = text(\` \`);
|
|
||||||
const b8 = text(ctx['item_value']);
|
|
||||||
const b9 = text(\`] \`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7, b8, b9]), key1);
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-foreach iterate, iterable param 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { prepareList, withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['map'].values());;
|
|
||||||
for (let i1 = 0; i1 < l_block1; i1++) {
|
|
||||||
ctx[\`item\`] = v_block1[i1];
|
|
||||||
ctx[\`item_index\`] = i1;
|
|
||||||
ctx[\`item_value\`] = k_block1[i1];
|
|
||||||
const key1 = ctx['item_index'];
|
|
||||||
const b3 = text(\` [\`);
|
|
||||||
const b4 = text(ctx['item_index']);
|
|
||||||
const b5 = text(\`: \`);
|
|
||||||
const b6 = text(ctx['item']);
|
|
||||||
const b7 = text(\` \`);
|
|
||||||
const b8 = text(ctx['item_value']);
|
|
||||||
const b9 = text(\`] \`);
|
|
||||||
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7, b8, b9]), key1);
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-foreach iterate, position 1`] = `
|
exports[`t-foreach iterate, position 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -335,99 +335,6 @@ exports[`t-set t-set evaluates an expression only once 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`t-set t-set outside modified in t-for 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
|
|
||||||
let block3 = createBlock(\`<p>InLoop: <block-text-0/></p>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
ctx[isBoundary] = 1
|
|
||||||
setContextValue(ctx, \\"iter\\", 0);
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['val'] of ['a','b']) {
|
|
||||||
const key1 = ctx['val'];
|
|
||||||
let txt1 = ctx['iter'];
|
|
||||||
c_block2[i1] = withKey(block3([txt1]), key1);
|
|
||||||
setContextValue(ctx, \\"iter\\", ctx['iter']+1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
let txt2 = ctx['iter'];
|
|
||||||
return block1([txt2], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-set t-set outside modified in t-for increment-after operator 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
|
|
||||||
let block3 = createBlock(\`<p>InLoop: <block-text-0/></p>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
ctx[isBoundary] = 1
|
|
||||||
setContextValue(ctx, \\"iter\\", 0);
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['val'] of ['a','b']) {
|
|
||||||
const key1 = ctx['val'];
|
|
||||||
let txt1 = ctx['iter'];
|
|
||||||
c_block2[i1] = withKey(block3([txt1]), key1);
|
|
||||||
setContextValue(ctx, \\"iter\\", ctx['iter']++);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
let txt2 = ctx['iter'];
|
|
||||||
return block1([txt2], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-set t-set outside modified in t-for increment-before operator 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
|
|
||||||
let block3 = createBlock(\`<p>InLoop: <block-text-0/></p>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
ctx[isBoundary] = 1
|
|
||||||
setContextValue(ctx, \\"iter\\", 0);
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['val'] of ['a','b']) {
|
|
||||||
const key1 = ctx['val'];
|
|
||||||
let txt1 = ctx['iter'];
|
|
||||||
c_block2[i1] = withKey(block3([txt1]), key1);
|
|
||||||
setContextValue(ctx, \\"iter\\", ++ctx['iter']);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
let txt2 = ctx['iter'];
|
|
||||||
return block1([txt2], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-set t-set outside modified in t-foreach 1`] = `
|
exports[`t-set t-set outside modified in t-foreach 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -547,35 +454,6 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`t-set t-set should reuse variable if possible: for..of 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
let block3 = createBlock(\`<div><span>v<block-text-0/></span></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
ctx[isBoundary] = 1
|
|
||||||
setContextValue(ctx, \\"v\\", 1);
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['elem'] of ctx['list']) {
|
|
||||||
const key1 = ctx['elem_index'];
|
|
||||||
let txt1 = ctx['v'];
|
|
||||||
setContextValue(ctx, \\"v\\", ctx['elem']);
|
|
||||||
c_block2[i1] = withKey(block3([txt1]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-set t-set with content and sub t-esc 1`] = `
|
exports[`t-set t-set with content and sub t-esc 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1569,12 +1569,6 @@ describe("qweb parser", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("component with t-out", async () => {
|
|
||||||
expect(parse(`<MyComponent t-out="someValue"/>`)).toEqual(
|
|
||||||
parse(`<MyComponent><t t-out="someValue"/></MyComponent>`)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("component with t-esc and content", async () => {
|
test("component with t-esc and content", async () => {
|
||||||
expect(() => parse(`<MyComponent t-esc="someValue">Some content</MyComponent>`)).toThrow(
|
expect(() => parse(`<MyComponent t-esc="someValue">Some content</MyComponent>`)).toThrow(
|
||||||
"Cannot have t-esc on a component that already has content"
|
"Cannot have t-esc on a component that already has content"
|
||||||
@@ -1997,8 +1991,8 @@ describe("qweb parser", () => {
|
|||||||
baseExpr: "state",
|
baseExpr: "state",
|
||||||
expr: "'stuff'",
|
expr: "'stuff'",
|
||||||
eventType: "click",
|
eventType: "click",
|
||||||
shouldNumberize: true,
|
shouldNumberize: false,
|
||||||
shouldTrim: true,
|
shouldTrim: false,
|
||||||
targetAttr: "value",
|
targetAttr: "value",
|
||||||
hasDynamicChildren: false,
|
hasDynamicChildren: false,
|
||||||
specialInitTargetAttr: "checked",
|
specialInitTargetAttr: "checked",
|
||||||
|
|||||||
@@ -1,317 +0,0 @@
|
|||||||
import {
|
|
||||||
renderToBdom,
|
|
||||||
renderToString,
|
|
||||||
snapshotEverything,
|
|
||||||
TestContext,
|
|
||||||
makeTestFixture,
|
|
||||||
} from "../helpers";
|
|
||||||
import { mount, patch } from "../../src/runtime/blockdom";
|
|
||||||
|
|
||||||
snapshotEverything();
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// t-for
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
describe("t-for", () => {
|
|
||||||
test("simple iteration", () => {
|
|
||||||
const template = `<t t-for="item" t-of="[3, 2, 1]" t-key="item"><t t-esc="item"/></t>`;
|
|
||||||
expect(renderToString(template)).toBe("321");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("simple iteration with two nodes inside", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-for="item" t-of="[3, 2, 1]" t-key="item">
|
|
||||||
<span>a<t t-esc="item"/></span>
|
|
||||||
<span>b<t t-esc="item"/></span>
|
|
||||||
</t>`;
|
|
||||||
const expected =
|
|
||||||
"<span>a3</span><span>b3</span><span>a2</span><span>b2</span><span>a1</span><span>b1</span>";
|
|
||||||
expect(renderToString(template)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("destructuring array items", () => {
|
|
||||||
const template = `<t t-for="[key, value]" t-of="Object.entries({ a: 1, b: 2 })" t-key="key">(<t t-esc="key"/>: <t t-esc="value"/>)</t>`;
|
|
||||||
expect(renderToString(template)).toBe("(a: 1)(b: 2)");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("destructuring array items: rest", () => {
|
|
||||||
const template = `<t t-for="[head, ...tail]" t-of="[[1, 2, 3], [4, 5, 6]]" t-key="head">(<t t-esc="head"/>;<t t-esc="tail"/>)</t>`;
|
|
||||||
expect(renderToString(template)).toBe("(1;2,3)(4;5,6)");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("destructuring object items", () => {
|
|
||||||
const template = `<t t-for="{ k, v }" t-of="[{ k: 'a', v: 1 }, { k: 'b', v: 2 }]" t-key="k">(<t t-esc="k"/>: <t t-esc="v"/>)</t>`;
|
|
||||||
expect(renderToString(template)).toBe("(a: 1)(b: 2)");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("destructuring object items: rest", () => {
|
|
||||||
const template = `<t t-for="{ k, v }" t-of="[{ k: 'a', v: 1 }, { k: 'b', v: 2 }]" t-key="k">(<t t-esc="k"/>: <t t-esc="v"/>)</t>`;
|
|
||||||
expect(renderToString(template)).toBe("(a: 1)(b: 2)");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("nested destructuring", () => {
|
|
||||||
const template = `<t t-for="[key, {left, right}]" t-of="Object.entries(obj)" t-key="key">(<t t-esc="key"/>: [<t t-esc="left"/>, <t t-esc="right"/>])</t>`;
|
|
||||||
expect(
|
|
||||||
renderToString(template, {
|
|
||||||
obj: {
|
|
||||||
a: { left: 1, right: 2 },
|
|
||||||
b: { left: 3, right: 4 },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
).toBe("(a: [1, 2])(b: [3, 4])");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-key on t-for", async () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-for="thing" t-of="things" t-key="thing">
|
|
||||||
<span/>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
const fixture = makeTestFixture();
|
|
||||||
|
|
||||||
const vnode1 = renderToBdom(template, { things: [1, 2] });
|
|
||||||
mount(vnode1, fixture);
|
|
||||||
let elm = fixture;
|
|
||||||
expect(elm.innerHTML).toBe("<div><span></span><span></span></div>");
|
|
||||||
const first = elm.querySelectorAll("span")[0];
|
|
||||||
const second = elm.querySelectorAll("span")[1];
|
|
||||||
|
|
||||||
const vnode2 = renderToBdom(template, { things: [2, 1] });
|
|
||||||
patch(vnode1, vnode2);
|
|
||||||
|
|
||||||
expect(elm.innerHTML).toBe("<div><span></span><span></span></div>");
|
|
||||||
expect(first).toBe(elm.querySelectorAll("span")[1]);
|
|
||||||
expect(second).toBe(elm.querySelectorAll("span")[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("simple iteration (in a node)", () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-for="item" t-of="[3, 2, 1]" t-key="item"><t t-esc="item"/></t>
|
|
||||||
</div>`;
|
|
||||||
expect(renderToString(template)).toBe("<div>321</div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("iterate on items (on a element node)", () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<span t-for="item" t-of="[1, 2]" t-key="item"><t t-esc="item"/></span>
|
|
||||||
</div>`;
|
|
||||||
const expected = `<div><span>1</span><span>2</span></div>`;
|
|
||||||
expect(renderToString(template)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("iterate, Map param", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-for="[key, value]" t-of="map" t-key="key">
|
|
||||||
[<t t-esc="key"/>: <t t-esc="value"/>]
|
|
||||||
</t>`;
|
|
||||||
const expected = ` [a: 1] [b: 2] [c: 3] `;
|
|
||||||
const context = {
|
|
||||||
map: new Map([
|
|
||||||
["a", 1],
|
|
||||||
["b", 2],
|
|
||||||
["c", 3],
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("iterate, Set param", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-for="item" t-of="set" t-key="item">
|
|
||||||
<t t-esc="item"/>
|
|
||||||
</t>`;
|
|
||||||
const expected = `123`;
|
|
||||||
const context = { set: new Set([1, 2, 3]) };
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("iterate, iterable param", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-for="item" t-of="map.values()" t-key="item">
|
|
||||||
<t t-esc="item"/>
|
|
||||||
</t>`;
|
|
||||||
const expected = `123`;
|
|
||||||
const context = {
|
|
||||||
map: new Map([
|
|
||||||
["a", 1],
|
|
||||||
["b", 2],
|
|
||||||
["c", 3],
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("iterate, generator param", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-for="item" t-of="gen()" t-key="item">
|
|
||||||
<t t-esc="item"/>
|
|
||||||
</t>`;
|
|
||||||
const expected = `123`;
|
|
||||||
const context = {
|
|
||||||
*gen() {
|
|
||||||
yield 1;
|
|
||||||
yield 2;
|
|
||||||
yield 3;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("does not pollute the rendering context", () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-for="item" t-of="[1]" t-key="item"><t t-esc="item"/></t>
|
|
||||||
</div>`;
|
|
||||||
const context = { __owl__: {} };
|
|
||||||
renderToString(template, context);
|
|
||||||
expect(Object.keys(context)).toEqual(["__owl__"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-for in t-for", () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-for="number" t-of="numbers" t-key="number">
|
|
||||||
<t t-for="letter" t-of="letters" t-key="letter">
|
|
||||||
[<t t-esc="number"/><t t-esc="letter"/>]
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
const context = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
|
||||||
const expected = "<div> [1a] [1b] [2a] [2b] [3a] [3b] </div>";
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-for in t-foreach", () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-foreach="numbers" t-as="number" t-key="number">
|
|
||||||
<t t-for="letter" t-of="letters" t-key="letter">
|
|
||||||
[<t t-esc="number"/><t t-esc="letter"/>]
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
const context = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
|
||||||
const expected = "<div> [1a] [1b] [2a] [2b] [3a] [3b] </div>";
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-foreach in t-for", () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-for="number" t-of="numbers" t-key="number">
|
|
||||||
<t t-foreach="letters" t-as="letter" t-key="letter">
|
|
||||||
[<t t-esc="number"/><t t-esc="letter"/>]
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
const context = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
|
||||||
const expected = "<div> [1a] [1b] [2a] [2b] [3a] [3b] </div>";
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-call without body in t-for in t-for", () => {
|
|
||||||
const context = new TestContext();
|
|
||||||
const sub = `
|
|
||||||
<t>
|
|
||||||
<t t-set="c" t-value="'x' + '_' + a + '_'+ b" />
|
|
||||||
[<t t-esc="a" />]
|
|
||||||
[<t t-esc="b" />]
|
|
||||||
[<t t-esc="c" />]
|
|
||||||
</t>`;
|
|
||||||
|
|
||||||
const main = `
|
|
||||||
<div>
|
|
||||||
<t t-for="a" t-of="numbers" t-key="a">
|
|
||||||
<t t-for="b" t-of="letters" t-key="b">
|
|
||||||
<t t-call="sub" />
|
|
||||||
</t>
|
|
||||||
<span t-esc="c"/>
|
|
||||||
</t>
|
|
||||||
<span>[<t t-esc="a" />][<t t-esc="b" />][<t t-esc="c" />]</span>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
context.addTemplate("sub", sub);
|
|
||||||
context.addTemplate("main", main);
|
|
||||||
|
|
||||||
const ctx = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
|
||||||
const expected =
|
|
||||||
"<div> [1] [a] [x_1_a] [1] [b] [x_1_b] <span></span> [2] [a] [x_2_a] [2] [b] [x_2_b] <span></span> [3] [a] [x_3_a] [3] [b] [x_3_b] <span></span><span>[][][]</span></div>";
|
|
||||||
expect(context.renderToString("main", ctx)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-call with body in t-for in t-for", () => {
|
|
||||||
const context = new TestContext();
|
|
||||||
const sub = `
|
|
||||||
<t>
|
|
||||||
[<t t-esc="a" />]
|
|
||||||
[<t t-esc="b" />]
|
|
||||||
[<t t-esc="c" />]
|
|
||||||
</t>`;
|
|
||||||
|
|
||||||
const main = `
|
|
||||||
<div>
|
|
||||||
<t t-for="a" t-of="numbers" t-key="a">
|
|
||||||
<t t-for="b" t-of="letters" t-key="b">
|
|
||||||
<t t-call="sub" >
|
|
||||||
<t t-set="c" t-value="'x' + '_' + a + '_'+ b" />
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
<span t-esc="c"/>
|
|
||||||
</t>
|
|
||||||
<span>[<t t-esc="a" />][<t t-esc="b" />][<t t-esc="c" />]</span>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
context.addTemplate("sub", sub);
|
|
||||||
context.addTemplate("main", main);
|
|
||||||
|
|
||||||
const ctx = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
|
||||||
const expected =
|
|
||||||
"<div> [1] [a] [x_1_a] [1] [b] [x_1_b] <span></span> [2] [a] [x_2_a] [2] [b] [x_2_b] <span></span> [3] [a] [x_3_a] [3] [b] [x_3_b] <span></span><span>[][][]</span></div>";
|
|
||||||
expect(context.renderToString("main", ctx)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("throws error if invalid loop expression", () => {
|
|
||||||
const test = `<div><t t-for="item" t-of="abc" t-key="item"><span t-key="item"/></t></div>`;
|
|
||||||
expect(() => renderToString(test)).toThrow("ctx.abc is not iterable");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-for with t-if inside", () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-for="{ id, text }" t-of="elems" t-key="id">
|
|
||||||
<span t-if="id lt 3"><t t-esc="text"/></span>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
const ctx = {
|
|
||||||
elems: [
|
|
||||||
{ id: 1, text: "a" },
|
|
||||||
{ id: 2, text: "b" },
|
|
||||||
{ id: 3, text: "c" },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
expect(renderToString(template, ctx)).toBe("<div><span>a</span><span>b</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-for with t-if inside (no external node)", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-for="{ id, text }" t-of="elems" t-key="id">
|
|
||||||
<span t-if="id lt 3"><t t-esc="text"/></span>
|
|
||||||
</t>`;
|
|
||||||
const ctx = {
|
|
||||||
elems: [
|
|
||||||
{ id: 1, text: "a" },
|
|
||||||
{ id: 2, text: "b" },
|
|
||||||
{ id: 3, text: "c" },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
expect(renderToString(template, ctx)).toBe("<span>a</span><span>b</span>");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -105,64 +105,6 @@ describe("t-foreach", () => {
|
|||||||
expect(renderToString(template, context)).toBe(expected);
|
expect(renderToString(template, context)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("iterate, Map param", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-foreach="value" t-as="item" t-key="item_index">
|
|
||||||
[<t t-esc="item_index"/>: <t t-esc="item"/> <t t-esc="item_value"/>]
|
|
||||||
</t>`;
|
|
||||||
const expected = ` [0: 1 a] [1: 2 b] [2: 3 c] `;
|
|
||||||
const context = {
|
|
||||||
value: new Map([
|
|
||||||
["a", 1],
|
|
||||||
["b", 2],
|
|
||||||
["c", 3],
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("iterate, Set param", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-foreach="value" t-as="item" t-key="item_index">
|
|
||||||
[<t t-esc="item_index"/>: <t t-esc="item"/> <t t-esc="item_value"/>]
|
|
||||||
</t>`;
|
|
||||||
const expected = ` [0: 1 1] [1: 2 2] [2: 3 3] `;
|
|
||||||
const context = { value: new Set([1, 2, 3]) };
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("iterate, iterable param", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-foreach="map.values()" t-as="item" t-key="item_index">
|
|
||||||
[<t t-esc="item_index"/>: <t t-esc="item"/> <t t-esc="item_value"/>]
|
|
||||||
</t>`;
|
|
||||||
const expected = ` [0: 1 1] [1: 2 2] [2: 3 3] `;
|
|
||||||
const context = {
|
|
||||||
map: new Map([
|
|
||||||
["a", 1],
|
|
||||||
["b", 2],
|
|
||||||
["c", 3],
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("iterate, generator param", () => {
|
|
||||||
const template = `
|
|
||||||
<t t-foreach="gen()" t-as="item" t-key="item_index">
|
|
||||||
[<t t-esc="item_index"/>: <t t-esc="item"/> <t t-esc="item_value"/>]
|
|
||||||
</t>`;
|
|
||||||
const expected = ` [0: 1 1] [1: 2 2] [2: 3 3] `;
|
|
||||||
const context = {
|
|
||||||
*gen() {
|
|
||||||
yield 1;
|
|
||||||
yield 2;
|
|
||||||
yield 3;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
expect(renderToString(template, context)).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("does not pollute the rendering context", () => {
|
test("does not pollute the rendering context", () => {
|
||||||
const template = `
|
const template = `
|
||||||
<div>
|
<div>
|
||||||
@@ -251,9 +193,7 @@ describe("t-foreach", () => {
|
|||||||
|
|
||||||
test("throws error if invalid loop expression", () => {
|
test("throws error if invalid loop expression", () => {
|
||||||
const test = `<div><t t-foreach="abc" t-as="item" t-key="item"><span t-key="item_index"/></t></div>`;
|
const test = `<div><t t-foreach="abc" t-as="item" t-key="item"><span t-key="item_index"/></t></div>`;
|
||||||
expect(() => renderToString(test)).toThrow(
|
expect(() => renderToString(test)).toThrow("Invalid loop expression");
|
||||||
'Invalid loop expression: "undefined" is not iterable'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-foreach with t-if inside", () => {
|
test("t-foreach with t-if inside", () => {
|
||||||
|
|||||||
@@ -122,19 +122,6 @@ describe("t-set", () => {
|
|||||||
expect(renderToString(template, { list: ["a", "b"] })).toBe(expected);
|
expect(renderToString(template, { list: ["a", "b"] })).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-set should reuse variable if possible: for..of", () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-set="v" t-value="1"/>
|
|
||||||
<div t-for="elem" t-of="list" t-key="elem_index">
|
|
||||||
<span>v<t t-esc="v"/></span>
|
|
||||||
<t t-set="v" t-value="elem"/>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
const expected = "<div><div><span>v1</span></div><div><span>va</span></div></div>";
|
|
||||||
expect(renderToString(template, { list: ["a", "b"] })).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-set with content and sub t-esc", () => {
|
test("t-set with content and sub t-esc", () => {
|
||||||
const template = `
|
const template = `
|
||||||
<div>
|
<div>
|
||||||
@@ -245,22 +232,6 @@ describe("t-set", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-set outside modified in t-for", async () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-set="iter" t-value="0"/>
|
|
||||||
<t t-for="val" t-of="['a','b']" t-key="val">
|
|
||||||
<p>InLoop: <t t-esc="iter"/></p>
|
|
||||||
<t t-set="iter" t-value="iter + 1"/>
|
|
||||||
</t>
|
|
||||||
<p>EndLoop: <t t-esc="iter"/></p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
expect(renderToString(template)).toBe(
|
|
||||||
"<div><p>InLoop: 0</p><p>InLoop: 1</p><p>EndLoop: 2</p></div>"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-set outside modified in t-foreach increment-after operator", async () => {
|
test("t-set outside modified in t-foreach increment-after operator", async () => {
|
||||||
const template = `
|
const template = `
|
||||||
<div>
|
<div>
|
||||||
@@ -277,22 +248,6 @@ describe("t-set", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-set outside modified in t-for increment-after operator", async () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-set="iter" t-value="0"/>
|
|
||||||
<t t-for="val" t-of="['a','b']" t-key="val">
|
|
||||||
<p>InLoop: <t t-esc="iter"/></p>
|
|
||||||
<t t-set="iter" t-value="iter++"/>
|
|
||||||
</t>
|
|
||||||
<p>EndLoop: <t t-esc="iter"/></p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
expect(renderToString(template)).toBe(
|
|
||||||
"<div><p>InLoop: 0</p><p>InLoop: 0</p><p>EndLoop: 0</p></div>"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-set outside modified in t-foreach increment-before operator", async () => {
|
test("t-set outside modified in t-foreach increment-before operator", async () => {
|
||||||
const template = `
|
const template = `
|
||||||
<div>
|
<div>
|
||||||
@@ -309,22 +264,6 @@ describe("t-set", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-set outside modified in t-for increment-before operator", async () => {
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<t t-set="iter" t-value="0"/>
|
|
||||||
<t t-for="val" t-of="['a','b']" t-key="val">
|
|
||||||
<p>InLoop: <t t-esc="iter"/></p>
|
|
||||||
<t t-set="iter" t-value="++iter"/>
|
|
||||||
</t>
|
|
||||||
<p>EndLoop: <t t-esc="iter"/></p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
expect(renderToString(template)).toBe(
|
|
||||||
"<div><p>InLoop: 0</p><p>InLoop: 1</p><p>EndLoop: 0</p></div>"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-set can't alter from within callee", async () => {
|
test("t-set can't alter from within callee", async () => {
|
||||||
const context = new TestContext();
|
const context = new TestContext();
|
||||||
const sub = `<div><t t-esc="iter"/><t t-set="iter" t-value="'called'"/><t t-esc="iter"/></div>`;
|
const sub = `<div><t t-esc="iter"/><t t-set="iter" t-value="'called'"/><t t-esc="iter"/></div>`;
|
||||||
|
|||||||
@@ -37,22 +37,4 @@ describe("basic validation", () => {
|
|||||||
const template = `<div t-best-beer="rochefort 10">test</div>`;
|
const template = `<div t-best-beer="rochefort 10">test</div>`;
|
||||||
expect(() => renderToString(template)).toThrow("Unknown QWeb directive: 't-best-beer'");
|
expect(() => renderToString(template)).toThrow("Unknown QWeb directive: 't-best-beer'");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("compilation error", () => {
|
|
||||||
const template = `<div t-att-class="a b">test</div>`;
|
|
||||||
expect(() => renderToString(template))
|
|
||||||
.toThrow(`Failed to compile anonymous template: Unexpected identifier
|
|
||||||
|
|
||||||
generated code:
|
|
||||||
function(app, bdom, helpers) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div block-attribute-0="class">test</div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = "") {
|
|
||||||
let attr1 = ctx['a']ctx['b'];
|
|
||||||
return block1([attr1]);
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -212,73 +212,6 @@ exports[`changing state before first render does not trigger a render 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`component destroyed just after render 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
const comp1 = app.createComponent(\`B\`, true, false, false, []);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return comp1({}, key + \`__1\`, node, this, null);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`component destroyed just after render 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
const b2 = text(\`B\`);
|
|
||||||
const b3 = text(ctx['state'].value);
|
|
||||||
return multi([b2, b3]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components are not destroyed between animation frame 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
const comp1 = app.createComponent(\`B\`, true, false, false, []);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let b2,b3;
|
|
||||||
b2 = text(\`A\`);
|
|
||||||
if (ctx['state'].flag) {
|
|
||||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
|
||||||
}
|
|
||||||
return multi([b2, b3]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components are not destroyed between animation frame 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
const comp1 = app.createComponent(\`C\`, true, false, false, []);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
const b2 = text(\`B\`);
|
|
||||||
const b3 = comp1({}, key + \`__1\`, node, this, null);
|
|
||||||
return multi([b2, b3]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components are not destroyed between animation frame 3`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`C\`);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`concurrent renderings scenario 1 1`] = `
|
exports[`concurrent renderings scenario 1 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -12,18 +12,6 @@ exports[`basics display a nice error if a component is not a component 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`basics display a nice error if a non-root component template fails to compile 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return comp1({}, key + \`__1\`, node, this, null);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`basics display a nice error if it cannot find component (in dev mode) 1`] = `
|
exports[`basics display a nice error if it cannot find component (in dev mode) 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,493 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`list of components components in a node in a t-for 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"item\\"]);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><ul><block-child-0/></ul></div>\`);
|
|
||||||
let block3 = createBlock(\`<li><block-child-0/></li>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of ctx['items']) {
|
|
||||||
const key1 = 'li_'+ctx['item'];
|
|
||||||
const b4 = comp1({item: ctx['item']}, key + \`__1__\${key1}\`, node, this, null);
|
|
||||||
c_block2[i1] = withKey(block3([], [b4]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components components in a node in a t-for 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['props'].item;
|
|
||||||
return block1([txt1]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components crash on duplicate key in dev mode 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { OwlError, withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const keys1 = new Set();
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of [1,2]) {
|
|
||||||
const key1 = 'child';
|
|
||||||
if (keys1.has(String(key1))) { throw new OwlError(\`Got duplicate key in t-for: \${key1}\`)}
|
|
||||||
keys1.add(String(key1));
|
|
||||||
const props1 = {};
|
|
||||||
helpers.validateProps(\`Child\`, props1, this);
|
|
||||||
c_block1[i1] = withKey(comp1(props1, key + \`__1__\${key1}\`, node, this, null), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components crash on duplicate key in dev mode 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`\`);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components crash when using object as keys that serialize to the same string 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { OwlError, withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const keys1 = new Set();
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['item'] of [{},{}]) {
|
|
||||||
const key1 = ctx['item'];
|
|
||||||
if (keys1.has(String(key1))) { throw new OwlError(\`Got duplicate key in t-for: \${key1}\`)}
|
|
||||||
keys1.add(String(key1));
|
|
||||||
const props1 = {};
|
|
||||||
helpers.validateProps(\`Child\`, props1, this);
|
|
||||||
c_block1[i1] = withKey(comp1(props1, key + \`__1__\${key1}\`, node, this, null), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components crash when using object as keys that serialize to the same string 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`\`);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components list of sub components inside other nodes 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`SubComponent\`, true, false, false, []);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
let block3 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['blip'] of ctx['state'].blips) {
|
|
||||||
const key1 = ctx['blip'].id;
|
|
||||||
const b4 = comp1({}, key + \`__1__\${key1}\`, node, this, null);
|
|
||||||
c_block2[i1] = withKey(block3([], [b4]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components list of sub components inside other nodes 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<span>asdf</span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return block1();
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components order is correct when slots are not of same type 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { capture, markRaw } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, true, false, []);
|
|
||||||
|
|
||||||
let block2 = createBlock(\`<div>A</div>\`);
|
|
||||||
|
|
||||||
function slot1(ctx, node, key = \\"\\") {
|
|
||||||
let b2;
|
|
||||||
if (!ctx['state'].active) {
|
|
||||||
b2 = block2();
|
|
||||||
}
|
|
||||||
return multi([b2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function slot2(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`B\`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function slot3(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`C\`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
const ctx1 = capture(ctx);
|
|
||||||
return comp1({slots: markRaw({'a': {__render: slot1.bind(this), __ctx: ctx1, active: !ctx['state'].active}, 'b': {__render: slot2.bind(this), __ctx: ctx1, active: true}, 'c': {__render: slot3.bind(this), __ctx: ctx1, active: ctx['state'].active}})}, key + \`__1\`, node, this, null);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components order is correct when slots are not of same type 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { callSlot, withKey } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['slotName'] of ctx['slotNames']) {
|
|
||||||
const key1 = ctx['slotName'];
|
|
||||||
const slot1 = (ctx['slotName']);
|
|
||||||
c_block1[i1] = withKey(toggler(slot1, callSlot(ctx, node, key1 + \`__1__\${key1}\`, slot1, true, {})), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components reconciliation alg works for t-for in t-for 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"blip\\"]);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['section'] of ctx['state'].s) {
|
|
||||||
const key1 = ctx['section'];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block3 = [];
|
|
||||||
let i2 = 0;
|
|
||||||
for (ctx['blip'] of ctx['section'].blips) {
|
|
||||||
const key2 = ctx['blip'];
|
|
||||||
c_block3[i2] = withKey(comp1({blip: ctx['blip']}, key + \`__1__\${key1}__\${key2}\`, node, this, null), key2);
|
|
||||||
i2++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
c_block2[i1] = withKey(list(c_block3), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components reconciliation alg works for t-for in t-for 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['props'].blip;
|
|
||||||
return block1([txt1]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components reconciliation alg works for t-for in t-for, 2 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"row\\",\\"col\\"]);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
let block3 = createBlock(\`<p><block-child-0/></p>\`);
|
|
||||||
let block5 = createBlock(\`<p><block-child-0/></p>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['row'] of ctx['state'].rows) {
|
|
||||||
const key1 = ctx['row'];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block4 = [];
|
|
||||||
let i2 = 0;
|
|
||||||
for (ctx['col'] of ctx['state'].cols) {
|
|
||||||
const key2 = ctx['col'];
|
|
||||||
const b6 = comp1({row: ctx['row'],col: ctx['col']}, key + \`__1__\${key1}__\${key2}\`, node, this, null);
|
|
||||||
c_block4[i2] = withKey(block5([], [b6]), key2);
|
|
||||||
i2++;
|
|
||||||
}
|
|
||||||
ctx = ctx.__proto__;
|
|
||||||
const b4 = list(c_block4);
|
|
||||||
c_block2[i1] = withKey(block3([], [b4]), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components reconciliation alg works for t-for in t-for, 2 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['props'].row+'_'+ctx['props'].col;
|
|
||||||
return block1([txt1]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components simple list 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"value\\"]);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block1 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['elem'] of ctx['state'].elems) {
|
|
||||||
const key1 = ctx['elem'].id;
|
|
||||||
c_block1[i1] = withKey(comp1({value: ctx['elem'].value}, key + \`__1__\${key1}\`, node, this, null), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
return list(c_block1);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components simple list 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<span><block-text-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['props'].value;
|
|
||||||
return block1([txt1]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components sub components rendered in a loop 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"n\\"]);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['number'] of ctx['state'].numbers) {
|
|
||||||
const key1 = ctx['number'];
|
|
||||||
c_block2[i1] = withKey(comp1({n: ctx['number']}, key + \`__1__\${key1}\`, node, this, null), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components sub components rendered in a loop 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<p><block-text-0/></p>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['props'].n;
|
|
||||||
return block1([txt1]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components sub components with some state rendered in a loop 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['number'] of ctx['state'].numbers) {
|
|
||||||
const key1 = ctx['number'];
|
|
||||||
c_block2[i1] = withKey(comp1({}, key + \`__1__\${key1}\`, node, this, null), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components sub components with some state rendered in a loop 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<p><block-text-0/></p>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['state'].n;
|
|
||||||
return block1([txt1]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components switch component position 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"key\\"]);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['c'] of ctx['clist']) {
|
|
||||||
const key1 = ctx['c'];
|
|
||||||
c_block2[i1] = withKey(comp1({key: ctx['c']}, key + \`__1__\${key1}\`, node, this, null), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components switch component position 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['props'].key;
|
|
||||||
return block1([txt1]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components t-for with t-component, and update 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { withKey } = helpers;
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"val\\"]);
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const c_block2 = [];
|
|
||||||
let i1 = 0;
|
|
||||||
for (ctx['n'] of [0,1]) {
|
|
||||||
const key1 = ctx['n'];
|
|
||||||
c_block2[i1] = withKey(comp1({val: ctx['n']}, key + \`__1__\${key1}\`, node, this, null), key1);
|
|
||||||
i1++;
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`list of components t-for with t-component, and update 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<span><block-text-0/><block-text-1/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['state'].val;
|
|
||||||
let txt2 = ctx['props'].val;
|
|
||||||
return block1([txt1, txt2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
@@ -468,36 +468,6 @@ exports[`t-model directive t-model on select with static options 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`t-model directive t-model with dynamic number values on select options in foreach 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { toNumber, prepareList, withKey } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<select block-handler-0=\\"change\\"><block-child-0/></select>\`);
|
|
||||||
let block3 = createBlock(\`<option block-attribute-0=\\"value\\" block-attribute-1=\\"selected\\"><block-text-2/></option>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
const bExpr1 = ctx['state'];
|
|
||||||
const expr1 = 'value';
|
|
||||||
const bValue1 = bExpr1[expr1];
|
|
||||||
let hdlr1 = [(ev) => { bExpr1[expr1] = toNumber(ev.target.value); }];
|
|
||||||
ctx = Object.create(ctx);
|
|
||||||
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['state'].options);;
|
|
||||||
for (let i1 = 0; i1 < l_block2; i1++) {
|
|
||||||
ctx[\`o\`] = v_block2[i1];
|
|
||||||
const key1 = ctx['o'].value;
|
|
||||||
let attr1 = ctx['o'].value;
|
|
||||||
let attr2 = bValue1 === ctx['o'].value;
|
|
||||||
let txt1 = ctx['o'].value;
|
|
||||||
c_block2[i1] = withKey(block3([attr1, attr2, txt1]), key1);
|
|
||||||
}
|
|
||||||
const b2 = list(c_block2);
|
|
||||||
return block1([hdlr1], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-model directive t-model with dynamic values on select options -- 2 1`] = `
|
exports[`t-model directive t-model with dynamic values on select options -- 2 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -115,7 +115,13 @@ test("destroying/recreating a subwidget with different props (if start is not ov
|
|||||||
await nextMicroTick();
|
await nextMicroTick();
|
||||||
expect(n).toBe(2);
|
expect(n).toBe(2);
|
||||||
|
|
||||||
expect(["W:willRender", "Child:setup", "Child:willStart", "W:rendered"]).toBeLogged();
|
expect([
|
||||||
|
"Child:willDestroy",
|
||||||
|
"W:willRender",
|
||||||
|
"Child:setup",
|
||||||
|
"Child:willStart",
|
||||||
|
"W:rendered",
|
||||||
|
]).toBeLogged();
|
||||||
|
|
||||||
def.resolve();
|
def.resolve();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
@@ -124,7 +130,6 @@ test("destroying/recreating a subwidget with different props (if start is not ov
|
|||||||
expect([
|
expect([
|
||||||
"Child:willRender",
|
"Child:willRender",
|
||||||
"Child:rendered",
|
"Child:rendered",
|
||||||
"Child:willDestroy",
|
|
||||||
"W:willPatch",
|
"W:willPatch",
|
||||||
"Child:mounted",
|
"Child:mounted",
|
||||||
"W:patched",
|
"W:patched",
|
||||||
@@ -173,13 +178,13 @@ test("destroying/recreating a subcomponent, other scenario", async () => {
|
|||||||
"Child:setup",
|
"Child:setup",
|
||||||
"Child:willStart",
|
"Child:willStart",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
|
"Child:willDestroy",
|
||||||
"Parent:willRender",
|
"Parent:willRender",
|
||||||
"Child:setup",
|
"Child:setup",
|
||||||
"Child:willStart",
|
"Child:willStart",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
"Child:willRender",
|
"Child:willRender",
|
||||||
"Child:rendered",
|
"Child:rendered",
|
||||||
"Child:willDestroy",
|
|
||||||
"Parent:willPatch",
|
"Parent:willPatch",
|
||||||
"Child:mounted",
|
"Child:mounted",
|
||||||
"Parent:patched",
|
"Parent:patched",
|
||||||
@@ -246,13 +251,13 @@ test("creating two async components, scenario 1", async () => {
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("");
|
expect(fixture.innerHTML).toBe("");
|
||||||
expect([
|
expect([
|
||||||
|
"ChildA:willDestroy",
|
||||||
"Parent:willRender",
|
"Parent:willRender",
|
||||||
"ChildA:setup",
|
"ChildA:setup",
|
||||||
"ChildA:willStart",
|
"ChildA:willStart",
|
||||||
"ChildB:setup",
|
"ChildB:setup",
|
||||||
"ChildB:willStart",
|
"ChildB:willStart",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
"ChildA:willDestroy",
|
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
|
|
||||||
defB.resolve();
|
defB.resolve();
|
||||||
@@ -499,6 +504,48 @@ test("update a sub-component twice in the same frame", async () => {
|
|||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.only("abcde", async () => {
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<span t-esc="state.x"/>`;
|
||||||
|
state = useState({ x: 1 });
|
||||||
|
setup() {
|
||||||
|
useLogLifecycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _doIt() {
|
||||||
|
await Promise.resolve();
|
||||||
|
this.state.x++;
|
||||||
|
}
|
||||||
|
|
||||||
|
async doIt() {
|
||||||
|
await this._doIt();
|
||||||
|
this.state.x++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
expect([
|
||||||
|
"Parent:setup",
|
||||||
|
"Parent:willStart",
|
||||||
|
"Parent:willRender",
|
||||||
|
"Parent:rendered",
|
||||||
|
"Parent:mounted",
|
||||||
|
]).toBeLogged();
|
||||||
|
expect(fixture.innerHTML).toBe("<span>1</span>");
|
||||||
|
|
||||||
|
parent.doIt();
|
||||||
|
await nextTick();
|
||||||
|
expect([
|
||||||
|
"Parent:willRender",
|
||||||
|
"Parent:rendered",
|
||||||
|
"Parent:willRender",
|
||||||
|
"Parent:rendered",
|
||||||
|
"Parent:willPatch",
|
||||||
|
"Parent:patched",
|
||||||
|
]).toBeLogged();
|
||||||
|
expect(fixture.innerHTML).toBe("<span>3</span>");
|
||||||
|
});
|
||||||
|
|
||||||
test("update a sub-component twice in the same frame, 2", async () => {
|
test("update a sub-component twice in the same frame, 2", async () => {
|
||||||
class ChildA extends Component {
|
class ChildA extends Component {
|
||||||
static template = xml`<span><t t-esc="val()"/></span>`;
|
static template = xml`<span><t t-esc="val()"/></span>`;
|
||||||
@@ -698,13 +745,13 @@ test("rendering component again in next microtick", async () => {
|
|||||||
"Child:setup",
|
"Child:setup",
|
||||||
"Child:willStart",
|
"Child:willStart",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
|
"Child:willDestroy",
|
||||||
"Parent:willRender",
|
"Parent:willRender",
|
||||||
"Child:setup",
|
"Child:setup",
|
||||||
"Child:willStart",
|
"Child:willStart",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
"Child:willRender",
|
"Child:willRender",
|
||||||
"Child:rendered",
|
"Child:rendered",
|
||||||
"Child:willDestroy",
|
|
||||||
"Parent:willPatch",
|
"Parent:willPatch",
|
||||||
"Child:mounted",
|
"Child:mounted",
|
||||||
"Parent:patched",
|
"Parent:patched",
|
||||||
@@ -1727,9 +1774,9 @@ test("concurrent renderings scenario 10", async () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><p></p></div>");
|
expect(fixture.innerHTML).toBe("<div><p></p></div>");
|
||||||
expect([
|
expect([
|
||||||
"ComponentA:willRender",
|
"ComponentA:willRender",
|
||||||
|
"ComponentC:willDestroy",
|
||||||
"ComponentB:willUpdateProps",
|
"ComponentB:willUpdateProps",
|
||||||
"ComponentA:rendered",
|
"ComponentA:rendered",
|
||||||
"ComponentC:willDestroy",
|
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
|
|
||||||
defB.resolve();
|
defB.resolve();
|
||||||
@@ -2277,6 +2324,7 @@ test("concurrent renderings scenario 16", async () => {
|
|||||||
"D:setup",
|
"D:setup",
|
||||||
"D:willStart",
|
"D:willStart",
|
||||||
"C:rendered",
|
"C:rendered",
|
||||||
|
"D:willDestroy",
|
||||||
"B:willRender",
|
"B:willRender",
|
||||||
"C:willUpdateProps",
|
"C:willUpdateProps",
|
||||||
"B:rendered",
|
"B:rendered",
|
||||||
@@ -2284,7 +2332,6 @@ test("concurrent renderings scenario 16", async () => {
|
|||||||
"D:setup",
|
"D:setup",
|
||||||
"D:willStart",
|
"D:willStart",
|
||||||
"C:rendered",
|
"C:rendered",
|
||||||
"D:willDestroy",
|
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
|
|
||||||
// at this point, C rendering is still pending, and nothing should have been
|
// at this point, C rendering is still pending, and nothing should have been
|
||||||
@@ -2992,11 +3039,11 @@ test("t-key on dom node having a component", async () => {
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||||
expect([
|
expect([
|
||||||
|
"Child (2):willDestroy",
|
||||||
"Child (3):setup",
|
"Child (3):setup",
|
||||||
"Child (3):willStart",
|
"Child (3):willStart",
|
||||||
"Child (3):willRender",
|
"Child (3):willRender",
|
||||||
"Child (3):rendered",
|
"Child (3):rendered",
|
||||||
"Child (2):willDestroy",
|
|
||||||
"Child (1):willUnmount",
|
"Child (1):willUnmount",
|
||||||
"Child (1):willDestroy",
|
"Child (1):willDestroy",
|
||||||
"Child (3):mounted",
|
"Child (3):mounted",
|
||||||
@@ -3050,11 +3097,11 @@ test("t-key on dynamic async component (toggler is never patched)", async () =>
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||||
expect([
|
expect([
|
||||||
|
"Child (2):willDestroy",
|
||||||
"Child (3):setup",
|
"Child (3):setup",
|
||||||
"Child (3):willStart",
|
"Child (3):willStart",
|
||||||
"Child (3):willRender",
|
"Child (3):willRender",
|
||||||
"Child (3):rendered",
|
"Child (3):rendered",
|
||||||
"Child (2):willDestroy",
|
|
||||||
"Child (1):willUnmount",
|
"Child (1):willUnmount",
|
||||||
"Child (1):willDestroy",
|
"Child (1):willDestroy",
|
||||||
"Child (3):mounted",
|
"Child (3):mounted",
|
||||||
@@ -3109,11 +3156,11 @@ test("t-foreach with dynamic async component", async () => {
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||||
expect([
|
expect([
|
||||||
|
"Child (2):willDestroy",
|
||||||
"Child (3):setup",
|
"Child (3):setup",
|
||||||
"Child (3):willStart",
|
"Child (3):willStart",
|
||||||
"Child (3):willRender",
|
"Child (3):willRender",
|
||||||
"Child (3):rendered",
|
"Child (3):rendered",
|
||||||
"Child (2):willDestroy",
|
|
||||||
"Child (1):willUnmount",
|
"Child (1):willUnmount",
|
||||||
"Child (1):willDestroy",
|
"Child (1):willDestroy",
|
||||||
"Child (3):mounted",
|
"Child (3):mounted",
|
||||||
@@ -3796,7 +3843,7 @@ test("destroyed component causes other soon to be destroyed component to rerende
|
|||||||
static template = xml`<t t-esc="state.val + props.value"/>`;
|
static template = xml`<t t-esc="state.val + props.value"/>`;
|
||||||
state = useState({ val: 0 });
|
state = useState({ val: 0 });
|
||||||
setup() {
|
setup() {
|
||||||
c = c || this;
|
c = this;
|
||||||
useLogLifecycle();
|
useLogLifecycle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3841,6 +3888,8 @@ test("destroyed component causes other soon to be destroyed component to rerende
|
|||||||
parent.state.valueB = 2;
|
parent.state.valueB = 2;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect([
|
expect([
|
||||||
|
"B:willDestroy",
|
||||||
|
"C:willDestroy",
|
||||||
"A:willRender",
|
"A:willRender",
|
||||||
"B:setup",
|
"B:setup",
|
||||||
"B:willStart",
|
"B:willStart",
|
||||||
@@ -3851,8 +3900,6 @@ test("destroyed component causes other soon to be destroyed component to rerende
|
|||||||
"B:rendered",
|
"B:rendered",
|
||||||
"C:willRender",
|
"C:willRender",
|
||||||
"C:rendered",
|
"C:rendered",
|
||||||
"B:willDestroy",
|
|
||||||
"C:willDestroy",
|
|
||||||
"A:willPatch",
|
"A:willPatch",
|
||||||
"C:mounted",
|
"C:mounted",
|
||||||
"B:mounted",
|
"B:mounted",
|
||||||
@@ -4195,116 +4242,6 @@ test("delayed render is not cancelled by upcoming render", async () => {
|
|||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("components are not destroyed between animation frame", async () => {
|
|
||||||
const def = makeDeferred();
|
|
||||||
class C extends Component {
|
|
||||||
static template = xml`C`;
|
|
||||||
setup() {
|
|
||||||
useLogLifecycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class B extends Component {
|
|
||||||
static template = xml`B<C/>`;
|
|
||||||
static components = { C };
|
|
||||||
setup() {
|
|
||||||
useLogLifecycle();
|
|
||||||
onWillStart(() => {
|
|
||||||
return def;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class A extends Component {
|
|
||||||
static template = xml`A<B t-if="state.flag"/>`;
|
|
||||||
static components = { B };
|
|
||||||
|
|
||||||
state = useState({ flag: false });
|
|
||||||
setup() {
|
|
||||||
useLogLifecycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const a = await mount(A, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("A");
|
|
||||||
expect(["A:setup", "A:willStart", "A:willRender", "A:rendered", "A:mounted"]).toBeLogged();
|
|
||||||
|
|
||||||
// turn the flag on, this will render A and stops at B because of def
|
|
||||||
a.state.flag = true;
|
|
||||||
await nextTick();
|
|
||||||
expect(["A:willRender", "B:setup", "B:willStart", "A:rendered"]).toBeLogged();
|
|
||||||
|
|
||||||
// force a render of A
|
|
||||||
// => owl will need to create a new B component
|
|
||||||
// => initial B component will be cancelled
|
|
||||||
a.render();
|
|
||||||
await nextMicroTick();
|
|
||||||
expect([
|
|
||||||
// note that B is not destroyed here. It is cancelled instead
|
|
||||||
"A:willRender",
|
|
||||||
"B:setup",
|
|
||||||
"B:willStart",
|
|
||||||
"A:rendered",
|
|
||||||
]).toBeLogged();
|
|
||||||
|
|
||||||
// resolve def, so B render is unblocked
|
|
||||||
def.resolve();
|
|
||||||
await nextTick();
|
|
||||||
expect([
|
|
||||||
"B:willRender",
|
|
||||||
"C:setup",
|
|
||||||
"C:willStart",
|
|
||||||
"B:rendered",
|
|
||||||
"C:willRender",
|
|
||||||
"C:rendered",
|
|
||||||
// animation frame callback starts here
|
|
||||||
"B:willDestroy", // B is destroyed here
|
|
||||||
"A:willPatch",
|
|
||||||
"C:mounted",
|
|
||||||
"B:mounted",
|
|
||||||
"A:patched",
|
|
||||||
]).toBeLogged();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("component destroyed just after render", async () => {
|
|
||||||
let stateB: any;
|
|
||||||
|
|
||||||
class B extends Component {
|
|
||||||
static template = xml`B<t t-esc="state.value"/>`;
|
|
||||||
state = useState({ value: 1 });
|
|
||||||
setup() {
|
|
||||||
stateB = this.state;
|
|
||||||
useLogLifecycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class A extends Component {
|
|
||||||
static template = xml`<B/>`;
|
|
||||||
static components = { B };
|
|
||||||
setup() {
|
|
||||||
useLogLifecycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const a = await mount(A, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("B1");
|
|
||||||
expect([
|
|
||||||
"A:setup",
|
|
||||||
"A:willStart",
|
|
||||||
"A:willRender",
|
|
||||||
"B:setup",
|
|
||||||
"B:willStart",
|
|
||||||
"A:rendered",
|
|
||||||
"B:willRender",
|
|
||||||
"B:rendered",
|
|
||||||
"B:mounted",
|
|
||||||
"A:mounted",
|
|
||||||
]).toBeLogged();
|
|
||||||
|
|
||||||
stateB!.value++; // force a render of B
|
|
||||||
await nextMicroTick(); // wait for B render to actually start
|
|
||||||
a.__owl__.app.destroy();
|
|
||||||
expect(["A:willUnmount", "B:willUnmount", "B:willDestroy", "A:willDestroy"]).toBeLogged();
|
|
||||||
await nextTick();
|
|
||||||
// check that B was not rendered after being destroyed
|
|
||||||
expect([]).toBeLogged();
|
|
||||||
});
|
|
||||||
|
|
||||||
// test.skip("components with shouldUpdate=false", async () => {
|
// test.skip("components with shouldUpdate=false", async () => {
|
||||||
// const state = { p: 1, cc: 10 };
|
// const state = { p: 1, cc: 10 };
|
||||||
|
|
||||||
|
|||||||
@@ -143,66 +143,6 @@ describe("basics", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("display a nice error if the root component template fails to compile", async () => {
|
|
||||||
// This is a special case: mount throws synchronously and we don't have any
|
|
||||||
// node which can handle the error, hence the different structure of this test
|
|
||||||
class Comp extends Component {
|
|
||||||
static template = xml`<div t-att-class="a b">test</div>`;
|
|
||||||
}
|
|
||||||
const app = new App(Comp);
|
|
||||||
let error: Error;
|
|
||||||
try {
|
|
||||||
await app.mount(fixture);
|
|
||||||
} catch (e) {
|
|
||||||
error = e as Error;
|
|
||||||
}
|
|
||||||
const expectedErrorMessage = `Failed to compile anonymous template: Unexpected identifier
|
|
||||||
|
|
||||||
generated code:
|
|
||||||
function(app, bdom, helpers) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div block-attribute-0="class">test</div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = "") {
|
|
||||||
let attr1 = ctx['a']ctx['b'];
|
|
||||||
return block1([attr1]);
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
expect(error!).toBeDefined();
|
|
||||||
expect(error!.message).toBe(expectedErrorMessage);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("display a nice error if a non-root component template fails to compile", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<div t-att-class="a b">test</div>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`<Child/>`;
|
|
||||||
}
|
|
||||||
const expectedErrorMessage = `Failed to compile anonymous template: Unexpected identifier
|
|
||||||
|
|
||||||
generated code:
|
|
||||||
function(app, bdom, helpers) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div block-attribute-0="class">test</div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = "") {
|
|
||||||
let attr1 = ctx['a']ctx['b'];
|
|
||||||
return block1([attr1]);
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
const app = new App(Parent as typeof Component);
|
|
||||||
let error: Error;
|
|
||||||
const mountProm = app.mount(fixture).catch((e: Error) => (error = e));
|
|
||||||
await expect(nextAppError(app)).resolves.toThrow(expectedErrorMessage);
|
|
||||||
await mountProm;
|
|
||||||
expect(error!).toBeDefined();
|
|
||||||
expect(error!.message).toBe(expectedErrorMessage);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("simple catchError", async () => {
|
test("simple catchError", async () => {
|
||||||
class Boom extends Component {
|
class Boom extends Component {
|
||||||
static template = xml`<div t-esc="a.b.c"/>`;
|
static template = xml`<div t-esc="a.b.c"/>`;
|
||||||
@@ -1504,7 +1444,6 @@ describe("can catch errors", () => {
|
|||||||
"Parent:willPatch",
|
"Parent:willPatch",
|
||||||
"Child:willUnmount",
|
"Child:willUnmount",
|
||||||
"Child:willDestroy",
|
"Child:willDestroy",
|
||||||
"Parent:patched",
|
|
||||||
"Parent:willRender",
|
"Parent:willRender",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
"Parent:willPatch",
|
"Parent:willPatch",
|
||||||
@@ -1567,15 +1506,12 @@ describe("can catch errors", () => {
|
|||||||
parent.state.hasChild = false;
|
parent.state.hasChild = false;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect([
|
expect([
|
||||||
"Parent:willRender",
|
|
||||||
"Parent:rendered",
|
|
||||||
"Child:willDestroy",
|
"Child:willDestroy",
|
||||||
"Parent:willRender",
|
"Parent:willRender",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
|
"Parent:willPatch",
|
||||||
|
"Parent:patched",
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
expect(fixture.innerHTML).toBe("1");
|
|
||||||
await nextTick();
|
|
||||||
expect(["Parent:willPatch", "Parent:patched"]).toBeLogged();
|
|
||||||
expect(fixture.innerHTML).toBe("2");
|
expect(fixture.innerHTML).toBe("2");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,391 +0,0 @@
|
|||||||
import { App, Component, mount, onMounted, useState, xml } from "../../src/index";
|
|
||||||
import {
|
|
||||||
makeTestFixture,
|
|
||||||
nextAppError,
|
|
||||||
nextTick,
|
|
||||||
snapshotEverything,
|
|
||||||
useLogLifecycle,
|
|
||||||
} from "../helpers";
|
|
||||||
|
|
||||||
snapshotEverything();
|
|
||||||
|
|
||||||
let originalconsoleWarn = console.warn;
|
|
||||||
let mockConsoleWarn: any;
|
|
||||||
|
|
||||||
let fixture: HTMLElement;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = makeTestFixture();
|
|
||||||
mockConsoleWarn = jest.fn(() => {});
|
|
||||||
console.warn = mockConsoleWarn;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
console.warn = originalconsoleWarn;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("list of components", () => {
|
|
||||||
test("simple list", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<span><t t-esc="props.value"/></span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<t t-for="elem" t-of="state.elems" t-key="elem.id">
|
|
||||||
<Child value="elem.value"/>
|
|
||||||
</t>`;
|
|
||||||
static components = { Child };
|
|
||||||
|
|
||||||
state = useState({
|
|
||||||
elems: [
|
|
||||||
{ id: 1, value: "a" },
|
|
||||||
{ id: 2, value: "b" },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<span>a</span><span>b</span>");
|
|
||||||
parent.state.elems.push({ id: 4, value: "d" });
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("<span>a</span><span>b</span><span>d</span>");
|
|
||||||
|
|
||||||
parent.state.elems.pop();
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("<span>a</span><span>b</span>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("components in a node in a t-for ", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<div><t t-esc="props.item"/></div>`;
|
|
||||||
setup() {
|
|
||||||
useLogLifecycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<ul>
|
|
||||||
<t t-for="item" t-of="items" t-key="'li_'+item">
|
|
||||||
<li>
|
|
||||||
<Child item="item"/>
|
|
||||||
</li>
|
|
||||||
</t>
|
|
||||||
</ul>
|
|
||||||
</div>`;
|
|
||||||
static components = { Child };
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
useLogLifecycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
get items() {
|
|
||||||
return [1, 2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await mount(Parent, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
"<div><ul><li><div>1</div></li><li><div>2</div></li></ul></div>"
|
|
||||||
);
|
|
||||||
expect([
|
|
||||||
"Parent:setup",
|
|
||||||
"Parent:willStart",
|
|
||||||
"Parent:willRender",
|
|
||||||
"Child:setup",
|
|
||||||
"Child:willStart",
|
|
||||||
"Child:setup",
|
|
||||||
"Child:willStart",
|
|
||||||
"Parent:rendered",
|
|
||||||
"Child:willRender",
|
|
||||||
"Child:rendered",
|
|
||||||
"Child:willRender",
|
|
||||||
"Child:rendered",
|
|
||||||
"Child:mounted",
|
|
||||||
"Child:mounted",
|
|
||||||
"Parent:mounted",
|
|
||||||
]).toBeLogged();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("reconciliation alg works for t-for in t-for", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<div><t t-esc="props.blip"/></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-for="section" t-of="state.s" t-key="section">
|
|
||||||
<t t-for="blip" t-of="section.blips" t-key="blip">
|
|
||||||
<Child blip="blip"/>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
static components = { Child };
|
|
||||||
state = { s: [{ blips: ["a1", "a2"] }, { blips: ["b1"] }] };
|
|
||||||
}
|
|
||||||
|
|
||||||
await mount(Parent, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div>a1</div><div>a2</div><div>b1</div></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("reconciliation alg works for t-for in t-for, 2", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<div><t t-esc="props.row + '_' + props.col"/></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<p t-for="row" t-of="state.rows" t-key="row">
|
|
||||||
<p t-for="col" t-of="state.cols" t-key="col">
|
|
||||||
<Child row="row" col="col"/>
|
|
||||||
</p>
|
|
||||||
</p>
|
|
||||||
</div>`;
|
|
||||||
static components = { Child };
|
|
||||||
state = useState({ rows: [1, 2], cols: ["a", "b"] });
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
"<div><p><p><div>1_a</div></p><p><div>1_b</div></p></p><p><p><div>2_a</div></p><p><div>2_b</div></p></p></div>"
|
|
||||||
);
|
|
||||||
parent.state.rows = [2, 1];
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
"<div><p><p><div>2_a</div></p><p><div>2_b</div></p></p><p><p><div>1_a</div></p><p><div>1_b</div></p></p></div>"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("sub components rendered in a loop", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<p><t t-esc="props.n"/></p>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-for="number" t-of="state.numbers" t-key="number" >
|
|
||||||
<Child n="number"/>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
static components = { Child };
|
|
||||||
|
|
||||||
state = useState({ numbers: [1, 2, 3] });
|
|
||||||
}
|
|
||||||
await mount(Parent, fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(`<div><p>1</p><p>2</p><p>3</p></div>`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("sub components with some state rendered in a loop", async () => {
|
|
||||||
let n = 1;
|
|
||||||
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<p><t t-esc="state.n"/></p>`;
|
|
||||||
state: any;
|
|
||||||
setup() {
|
|
||||||
this.state = useState({ n });
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-for="number" t-of="state.numbers" t-key="number">
|
|
||||||
<Child/>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
static components = { Child };
|
|
||||||
|
|
||||||
state = useState({
|
|
||||||
numbers: [1, 2, 3],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
|
|
||||||
parent.state.numbers = [1, 3];
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe(`<div><p>1</p><p>3</p></div>`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("list of sub components inside other nodes", async () => {
|
|
||||||
// this confuses the patching algorithm...
|
|
||||||
class SubComponent extends Component {
|
|
||||||
static template = xml`<span>asdf</span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<div t-for="blip" t-of="state.blips" t-key="blip.id">
|
|
||||||
<SubComponent />
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
static components = { SubComponent };
|
|
||||||
state = useState({
|
|
||||||
blips: [
|
|
||||||
{ a: "a", id: 1 },
|
|
||||||
{ b: "b", id: 2 },
|
|
||||||
{ c: "c", id: 4 },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
"<div><div><span>asdf</span></div><div><span>asdf</span></div><div><span>asdf</span></div></div>"
|
|
||||||
);
|
|
||||||
parent.state.blips.splice(0, 1);
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
"<div><div><span>asdf</span></div><div><span>asdf</span></div></div>"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-for with t-component, and update", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<span>
|
|
||||||
<t t-esc="state.val"/>
|
|
||||||
<t t-esc="props.val"/>
|
|
||||||
</span>`;
|
|
||||||
state = useState({ val: "A" });
|
|
||||||
setup() {
|
|
||||||
onMounted(() => {
|
|
||||||
this.state.val = "B";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Parent extends Component {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-for="n" t-of="[0, 1]" t-key="n">
|
|
||||||
<Child val="n"/>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await mount(Parent, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
|
||||||
|
|
||||||
await nextTick(); // wait for changes triggered in mounted to be applied
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("switch component position", async () => {
|
|
||||||
const childInstances = [];
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<div t-esc="props.key"></div>`;
|
|
||||||
setup() {
|
|
||||||
childInstances.push(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`<span>
|
|
||||||
<t t-for="c" t-of="clist" t-key="c">
|
|
||||||
<Child key="c"/>
|
|
||||||
</t>
|
|
||||||
</span>`;
|
|
||||||
|
|
||||||
clist = [1, 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<span><div>1</div><div>2</div></span>");
|
|
||||||
parent.clist = [2, 1];
|
|
||||||
parent.render();
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("<span><div>2</div><div>1</div></span>");
|
|
||||||
expect(childInstances.length).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("crash on duplicate key in dev mode", async () => {
|
|
||||||
const consoleInfo = console.info;
|
|
||||||
console.info = jest.fn();
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml``;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<t t-for="item" t-of="[1, 2]" t-key="'child'">
|
|
||||||
<Child/>
|
|
||||||
</t>
|
|
||||||
`;
|
|
||||||
static components = { Child };
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new App(Parent, { test: true });
|
|
||||||
const mountProm = expect(app.mount(fixture)).rejects.toThrow(
|
|
||||||
"Got duplicate key in t-for: child"
|
|
||||||
);
|
|
||||||
await expect(nextAppError(app)).resolves.toThrow("Got duplicate key in t-for: child");
|
|
||||||
await mountProm;
|
|
||||||
console.info = consoleInfo;
|
|
||||||
expect(mockConsoleWarn).toBeCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("crash when using object as keys that serialize to the same string", async () => {
|
|
||||||
const consoleInfo = console.info;
|
|
||||||
console.info = jest.fn();
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml``;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<t t-for="item" t-of="[{}, {}]" t-key="item">
|
|
||||||
<Child/>
|
|
||||||
</t>
|
|
||||||
`;
|
|
||||||
static components = { Child };
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new App(Parent, { test: true });
|
|
||||||
const mountProm = expect(app.mount(fixture)).rejects.toThrow(
|
|
||||||
"Got duplicate key in t-for: [object Object]"
|
|
||||||
);
|
|
||||||
await expect(nextAppError(app)).resolves.toThrow("Got duplicate key in t-for: [object Object]");
|
|
||||||
await mountProm;
|
|
||||||
console.info = consoleInfo;
|
|
||||||
expect(mockConsoleWarn).toBeCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("order is correct when slots are not of same type", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<t t-slot="{{ slotName }}" t-for="slotName" t-of="slotNames" t-key="slotName"/>
|
|
||||||
`;
|
|
||||||
get slotNames() {
|
|
||||||
return Object.entries(this.props.slots)
|
|
||||||
.filter((entry: any) => entry[1].active)
|
|
||||||
.map((entry) => entry[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<Child>
|
|
||||||
<t t-set-slot="a" active="!state.active"><div t-if="!state.active">A</div></t>
|
|
||||||
<t t-set-slot="b" active="true">B</t>
|
|
||||||
<t t-set-slot="c" active="state.active">C</t>
|
|
||||||
</Child>
|
|
||||||
`;
|
|
||||||
static components = { Child };
|
|
||||||
state = useState({ active: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
expect(fixture.textContent).toBe("AB");
|
|
||||||
parent.state.active = true;
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.textContent).toBe("BC");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -626,39 +626,6 @@ describe("t-model directive", () => {
|
|||||||
expect(fixture.querySelector("select")!.value).toEqual("b");
|
expect(fixture.querySelector("select")!.value).toEqual("b");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-model with dynamic number values on select options in foreach", async () => {
|
|
||||||
class Test extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<select t-model.number="state.value">
|
|
||||||
<t t-foreach="state.options" t-as="o" t-key="o.value">
|
|
||||||
<option t-att-value="o.value" t-esc="o.value"/>
|
|
||||||
</t>
|
|
||||||
</select>
|
|
||||||
`;
|
|
||||||
state: any;
|
|
||||||
setup() {
|
|
||||||
this.state = useState({
|
|
||||||
value: 2,
|
|
||||||
options: [{ value: 1 }, { value: 2 }, { value: 3 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const comp = await mount(Test, fixture);
|
|
||||||
// check that we have a value of 2 selected
|
|
||||||
expect(fixture.querySelector("select")!.value).toEqual("2");
|
|
||||||
expect(comp.state.value).toBe(2);
|
|
||||||
|
|
||||||
// emulate a click on the option=3 element
|
|
||||||
fixture.querySelectorAll("option")[2].selected = true;
|
|
||||||
fixture.querySelector("select")!.dispatchEvent(new Event("change"));
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
// check that we have now selected the number 3 (and not the string)
|
|
||||||
expect(fixture.querySelector("select")!.value).toEqual("3");
|
|
||||||
expect(comp.state.value).toBe(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-model is applied before t-on-input", async () => {
|
test("t-model is applied before t-on-input", async () => {
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
|
|||||||
@@ -988,7 +988,7 @@ describe("Portal: Props validation", () => {
|
|||||||
error = e as Error;
|
error = e as Error;
|
||||||
}
|
}
|
||||||
expect(error!).toBeDefined();
|
expect(error!).toBeDefined();
|
||||||
expect(error!.message).toContain(`Unexpected token ','`);
|
expect(error!.message).toBe(`Unexpected token ','`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("target must be a valid selector", async () => {
|
test("target must be a valid selector", async () => {
|
||||||
|
|||||||
@@ -1519,9 +1519,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If nothing was found, return the path of the first root component found in the apps
|
// If nothing was found, return the first app's root component path
|
||||||
const appIndex = [...this.apps].findIndex((app) => app.root);
|
return ["0", "root"];
|
||||||
return [appIndex.toString(), "root"];
|
|
||||||
}
|
}
|
||||||
// Returns the tree of components of the inspected page in a parsed format
|
// Returns the tree of components of the inspected page in a parsed format
|
||||||
// Use inspectedPath to specify the path of the selected component
|
// Use inspectedPath to specify the path of the selected component
|
||||||
|
|||||||
+7
-6
@@ -82,12 +82,6 @@ async function startRelease() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
log(`Step ${step++}/${STEPS}: updating package.json...`);
|
|
||||||
await writeFile("package.json", JSON.stringify({...package, version: next}, null, 2) + "\n");
|
|
||||||
await writeFile("package-lock.json", JSON.stringify({...packageLock, version: next}, null, 2) + "\n");
|
|
||||||
await writeFile("./src/version.ts", `// do not modify manually. This file is generated by the release script.\nexport const version = "${next}";\n`);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
log(`Step ${step++}/${STEPS}: building owl...`);
|
log(`Step ${step++}/${STEPS}: building owl...`);
|
||||||
await execCommand("rm -rf dist/");
|
await execCommand("rm -rf dist/");
|
||||||
@@ -114,6 +108,13 @@ async function startRelease() {
|
|||||||
await execCommand("cd dist && zip -r owl-devtools.zip devtools-chrome devtools-firefox && cd ..");
|
await execCommand("cd dist && zip -r owl-devtools.zip devtools-chrome devtools-firefox && cd ..");
|
||||||
await execCommand("rm -r dist/devtools-chrome dist/devtools-firefox && rm dist/compiler.js");
|
await execCommand("rm -r dist/devtools-chrome dist/devtools-firefox && rm dist/compiler.js");
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
log(`Step ${step++}/${STEPS}: updating package.json...`);
|
||||||
|
await writeFile("package.json", JSON.stringify({...package, version: next}, null, 2) + "\n");
|
||||||
|
await writeFile("package-lock.json", JSON.stringify({...packageLock, version: next}, null, 2) + "\n");
|
||||||
|
await writeFile("./src/version.ts", `// do not modify manually. This file is generated by the release script.\nexport const version = "${next}";\n`);
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
log(`Step ${step++}/${STEPS}: updating owl on github page...`);
|
log(`Step ${step++}/${STEPS}: updating owl on github page...`);
|
||||||
await fs.copyFileSync("dist/owl.es.js", "docs/owl.js");
|
await fs.copyFileSync("dist/owl.es.js", "docs/owl.js");
|
||||||
|
|||||||
Reference in New Issue
Block a user