Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d0a46c5dc | |||
| 34489a2494 | |||
| 398df543fe | |||
| aa6a4a5d46 | |||
| db3499f5e5 | |||
| 166cada8ff | |||
| acbe316689 | |||
| fb013ccc72 | |||
| fee3eecd7f | |||
| 6037018dd2 | |||
| eec7cc4ea7 | |||
| 035895b043 | |||
| ff5ef82ea2 | |||
| 0048205636 | |||
| 0d341d9e7b | |||
| 4c7f572dbe | |||
| 610c272104 | |||
| 32e4565131 | |||
| 9b9c15e4a9 | |||
| b1690f19cc | |||
| 5dcee2564c | |||
| 752160fd85 | |||
| 3937966b74 | |||
| e7ebb92104 | |||
| c78e070636 | |||
| 610ed02373 | |||
| 4b23f51974 | |||
| b25e988628 | |||
| e0758c0e9e | |||
| ba34811f71 | |||
| 28672096a2 | |||
| 9823a4e7dd |
@@ -15,7 +15,7 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
#ide's
|
||||
.vscode
|
||||
**/.vscode/*
|
||||
.idea
|
||||
|
||||
node_modules
|
||||
@@ -26,3 +26,10 @@ release-notes.md
|
||||
|
||||
# useful in some cases
|
||||
/temp
|
||||
|
||||
# owl-vision
|
||||
*/owl-vision/out/
|
||||
*/owl-vision/.vs/
|
||||
**/*.vsix
|
||||
!*/owl-vision/.vscode/launch.json
|
||||
!*/owl-vision/.vscode/tasks.json
|
||||
|
||||
@@ -357,7 +357,7 @@ cleaning operation, since the component may be destroyed before it has even been
|
||||
mounted. The `willDestroy` hook is useful in that situation, since it is always
|
||||
called.
|
||||
|
||||
The `onWillUnmount` hook is used to register a function that will be executed at
|
||||
The `onWillDestroy` hook is used to register a function that will be executed at
|
||||
this moment:
|
||||
|
||||
```javascript
|
||||
|
||||
@@ -51,8 +51,8 @@ that render its content, and a fallback if an error happened.
|
||||
```js
|
||||
class ErrorBoundary extends Component {
|
||||
static template = xml`
|
||||
<t t-if="error" t-slot="fallback">An error occurred</t>
|
||||
<t t-else="" t-slot="content"`;
|
||||
<t t-if="state.error" t-slot="fallback">An error occurred</t>
|
||||
<t t-else="" t-slot="default"/>`;
|
||||
|
||||
setup() {
|
||||
this.state = useState({ error: false });
|
||||
|
||||
@@ -190,12 +190,13 @@ will then be updated accordingly.
|
||||
### `useExternalListener`
|
||||
|
||||
The `useExternalListener` hook helps solve a very common problem: adding and removing
|
||||
a listener on some target whenever a component is mounted/unmounted. For example,
|
||||
a listener on some target whenever a component is mounted/unmounted. It takes a target
|
||||
as its first argument, forwards the other arguments to `addEventListener`. For example,
|
||||
a dropdown menu (or its parent) may need to listen to a `click` event on `window`
|
||||
to be closed:
|
||||
|
||||
```js
|
||||
useExternalListener(window, "click", this.closeMenu);
|
||||
useExternalListener(window, "click", this.closeMenu, { capture: true });
|
||||
```
|
||||
|
||||
### `useComponent`
|
||||
|
||||
@@ -35,7 +35,7 @@ The components tab is separated into two sub windows: the components tree in the
|
||||
the component details in the right. The components tree will display all the different
|
||||
components that are present in the tab in the form of a tree. The root of this tree is
|
||||
actually the app which is not a component but can still be inspected by the devtools like
|
||||
one. There can also be multiple apps loaded in the page like in the following:
|
||||
one. There can also be multiple apps loaded in the page like in website:
|
||||
|
||||
<img src="screenshots/multi_apps.png"/>
|
||||
|
||||
@@ -64,25 +64,18 @@ as its env, props, observed states and all the other variables that are present
|
||||
While the props and the env are already present on the actual instance of the component and are
|
||||
pretty explicit by themselves, the observed state value is a bit more complicated to grasp.
|
||||
|
||||
The observed state is actually information about which variables are observed by the component
|
||||
which will trigger a rerender of the component when it is modified. The keys represent which part of the
|
||||
variable is actually observed and the target is the actual variable. For simplicity, the properties
|
||||
that are not observed by the component are greyed out while the others are in bold. This means that
|
||||
editing bold ones will trigger a rerender while the greyed out ones will not.
|
||||
The observed state is actually information about which variables are being observed by the component:
|
||||
when any property of a reactive object is being read by the component, the component will subscribe
|
||||
to this property which means it will listen to any change that can occur on the property and render
|
||||
when such a change occurs. This can be visualized easily within the devtools inside of the observed
|
||||
state section: observed properties of the reactive object(s) are displayed in bold while the others
|
||||
are greyed out. Do keep in mind that a greyed out property in the observed state of one component
|
||||
may be observed by another and the other way around is also possible. Here is an example for some
|
||||
user Field component:
|
||||
|
||||
<img src="screenshots/states.png"/>
|
||||
|
||||
In the given example, we have two keys/target pairs for two different variables. The first one indicates
|
||||
that adding or removing an element to the array will trigger a rerender since the length will have changed.
|
||||
Replacing the element at index 0, 1 or 2 will also have the same effect as implied by the keys. It doesn't
|
||||
mean that editing the properties of element at index 0, 1 or 2 will rerender the component though. It may
|
||||
be the case for some but this will be described in another keys/target pair. The second keys/target pair
|
||||
is actually the element at index 0 of the first pair. It only has id in the keys meaning that only the
|
||||
id property will actually trigger a rerender the component when modified. Be aware however that the other
|
||||
properties may be in the observed state of another component like a child one in this case. A greyed out
|
||||
property only implies it is not reactive for the selected component and not for the others.
|
||||
|
||||
The navigation inside the properties is also similar to the one in console variables: properties have
|
||||
Navigation inside the properties is also similar to the one in console variables: properties have
|
||||
their prototype displayed and getters will get their value when clicked on (...). It is also possible to
|
||||
send any property to the console using the right-click context menu on it and functions can be inspected
|
||||
in the sources tab as well.
|
||||
@@ -96,8 +89,10 @@ component's name. Using the left click on the component's name will focus it in
|
||||
It is also possible to edit any of the leaf node properties. To do so, you must double click on the
|
||||
property's value and modify it using the freshly created input then press enter to apply the changes.
|
||||
Do note that the modified values should be written in JSON format in order to be valid (examples:
|
||||
89, "yes", undefined, null, \["hello", 15\], {"a": 1}, true, ...). Whether it has an impact on the
|
||||
component or not and whether it produces an error is the responsability of the user.
|
||||
89, "yes", undefined, null, \["hello", 15\], {"a": 1}, true, ...). Editing any value will produce a
|
||||
manual render of the component (or the root component of the application in the case of env values).
|
||||
Whether the edition has an impact on the component or not and whether it produces an error is the
|
||||
responsability of the user.
|
||||
|
||||
<img src="screenshots/edit.png"/>
|
||||
|
||||
@@ -117,7 +112,8 @@ are intercepted by the devtools using the record button.
|
||||
The second button is used to clear all the events that have been recorded. The select can be used to
|
||||
switch between the tree view (which shows the causality between renders) and the events log view which
|
||||
simply displays the events in the exact order they were triggered. In this view, you can expand the create,
|
||||
update and destroy events which reveals the component that initiated the event.
|
||||
update and destroy events which reveals the component that initiated the event. Also, a transition line will
|
||||
appear each time a new animation frame has been loaded between events.
|
||||
|
||||
<img src="screenshots/events_log.png"/>
|
||||
|
||||
@@ -131,20 +127,29 @@ There is also the Trace Renderings and Trace Subscriptions features. These featu
|
||||
recording of events and have no effect on the profiler tab. The Trace Renderings option is used to log in
|
||||
the console all the render events and allows to show their traceback information. Similarly, the Trace
|
||||
Subscriptions option logs all the properties that caused a render event and also allows to see the traceback
|
||||
of the modification
|
||||
of the modification.
|
||||
|
||||
<img src="screenshots/trace_rendering.png"/>
|
||||
<img src="screenshots/trace_subscriptions.png"/>
|
||||
|
||||
The Owl Devtools also allow to inspect iframes coded in Owl: when an Owl iframe is detected in the page,
|
||||
the iframe selector will appear next to the tabs. This allows to switch from an iframe to another easily.
|
||||
Be aware that switching iframes will clear all record events from the profiler tab. Iframes detection is
|
||||
currently not working in the firefox version, we are aware of this issue and will try to address it in the
|
||||
future.
|
||||
|
||||
<img src="screenshots/iframes.png"/>
|
||||
|
||||
## Options
|
||||
|
||||
The owl devtools extension has a dark mode feature which defaults to your general devtools settings and can
|
||||
be toggled using the sun/moon icon at the top-right corner of the tab. All the examples above were created
|
||||
with the dark mode enabled. There is also a refresh button to completely reset the owl devtools.
|
||||
be toggled using the sun/moon icon at the top-right corner of the tab. There is also a refresh button to
|
||||
completely reset the owl devtools.
|
||||
|
||||
<img src="screenshots/darkmode.png"/>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the feedback from the page to the devtools seems to be cut, just close the devtools and refresh the page.
|
||||
If the feedback from the page to the devtools seems to be cut, you can first try to use the refresh
|
||||
button mentioned above but if it still doesn't seem to work, just close the devtools and refresh the page.
|
||||
This will eventually happen any time a tab stays opened for too long without being refreshed.
|
||||
|
||||
|
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 545 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 387 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 266 KiB |
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 320 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 329 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 536 KiB |
|
Before Width: | Height: | Size: 336 KiB After Width: | Height: | Size: 488 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 182 KiB |
@@ -710,12 +710,7 @@ function buildTree(node, parent = null, domParentTree = null) {
|
||||
info.push({ type: "child", idx: index });
|
||||
el = document.createTextNode("");
|
||||
}
|
||||
const attrs = node.attributes;
|
||||
const ns = attrs.getNamedItem("block-ns");
|
||||
if (ns) {
|
||||
attrs.removeNamedItem("block-ns");
|
||||
currentNS = ns.value;
|
||||
}
|
||||
currentNS || (currentNS = node.namespaceURI);
|
||||
if (!el) {
|
||||
el = currentNS
|
||||
? document.createElementNS(currentNS, tagName)
|
||||
@@ -731,6 +726,7 @@ function buildTree(node, parent = null, domParentTree = null) {
|
||||
const fragment = document.createElement("template").content;
|
||||
fragment.appendChild(el);
|
||||
}
|
||||
const attrs = node.attributes;
|
||||
for (let i = 0; i < attrs.length; i++) {
|
||||
const attrName = attrs[i].name;
|
||||
const attrValue = attrs[i].value;
|
||||
@@ -2181,7 +2177,7 @@ function delegateAndNotify(setterName, getterName, target) {
|
||||
if (hadKey !== hasKey) {
|
||||
notifyReactives(target, KEYCHANGES);
|
||||
}
|
||||
if (originalValue !== value) {
|
||||
if (originalValue !== target[getterName](key)) {
|
||||
notifyReactives(target, key);
|
||||
}
|
||||
return ret;
|
||||
@@ -3002,15 +2998,13 @@ function prepareList(collection) {
|
||||
keys = [...collection.keys()];
|
||||
values = [...collection.values()];
|
||||
}
|
||||
else if (Symbol.iterator in Object(collection)) {
|
||||
keys = [...collection];
|
||||
values = keys;
|
||||
}
|
||||
else if (collection && typeof collection === "object") {
|
||||
if (Symbol.iterator in collection) {
|
||||
keys = [...collection];
|
||||
values = keys;
|
||||
}
|
||||
else {
|
||||
values = Object.values(collection);
|
||||
keys = Object.keys(collection);
|
||||
}
|
||||
values = Object.values(collection);
|
||||
keys = Object.keys(collection);
|
||||
}
|
||||
else {
|
||||
throw new OwlError(`Invalid loop expression: "${collection}" is not iterable`);
|
||||
@@ -3211,6 +3205,10 @@ class TemplateSet {
|
||||
}
|
||||
addTemplate(name, template) {
|
||||
if (name in this.rawTemplates) {
|
||||
// this check can be expensive, just silently ignore double definitions outside dev mode
|
||||
if (!this.dev) {
|
||||
return;
|
||||
}
|
||||
const rawTemplate = this.rawTemplates[name];
|
||||
const currentAsString = typeof rawTemplate === "string"
|
||||
? rawTemplate
|
||||
@@ -3854,7 +3852,7 @@ class CodeGenerator {
|
||||
createBlock(parentBlock, type, ctx) {
|
||||
const hasRoot = this.target.hasRoot;
|
||||
const block = new BlockDescription(this.target, type);
|
||||
if (!hasRoot && !ctx.preventRoot) {
|
||||
if (!hasRoot) {
|
||||
this.target.hasRoot = true;
|
||||
block.isRoot = true;
|
||||
}
|
||||
@@ -3877,7 +3875,7 @@ class CodeGenerator {
|
||||
if (ctx.tKeyExpr) {
|
||||
blockExpr = `toggler(${ctx.tKeyExpr}, ${blockExpr})`;
|
||||
}
|
||||
if (block.isRoot && !ctx.preventRoot) {
|
||||
if (block.isRoot) {
|
||||
if (this.target.on) {
|
||||
blockExpr = this.wrapWithEventCatcher(blockExpr, this.target.on);
|
||||
}
|
||||
@@ -4053,11 +4051,6 @@ class CodeGenerator {
|
||||
}
|
||||
// attributes
|
||||
const attrs = {};
|
||||
const nameSpace = ast.ns || ctx.nameSpace;
|
||||
if (nameSpace && isNewBlock) {
|
||||
// specific namespace uri
|
||||
attrs["block-ns"] = nameSpace;
|
||||
}
|
||||
for (let key in ast.attrs) {
|
||||
let expr, attrName;
|
||||
if (key.startsWith("t-attf")) {
|
||||
@@ -4173,7 +4166,10 @@ class CodeGenerator {
|
||||
const idx = block.insertData(setRefStr, "ref");
|
||||
attrs["block-ref"] = String(idx);
|
||||
}
|
||||
const dom = xmlDoc.createElement(ast.tag);
|
||||
const nameSpace = ast.ns || ctx.nameSpace;
|
||||
const dom = nameSpace
|
||||
? xmlDoc.createElementNS(nameSpace, ast.tag)
|
||||
: xmlDoc.createElement(ast.tag);
|
||||
for (const [attr, val] of Object.entries(attrs)) {
|
||||
if (!(attr === "class" && val === "")) {
|
||||
dom.setAttribute(attr, val);
|
||||
@@ -4215,7 +4211,7 @@ class CodeGenerator {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
|
||||
this.addLine(`let ${block.children.map((c) => c.varName).join(", ")};`, codeIdx);
|
||||
}
|
||||
}
|
||||
return block.varName;
|
||||
@@ -4318,7 +4314,7 @@ class CodeGenerator {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
|
||||
this.addLine(`let ${block.children.map((c) => c.varName).join(", ")};`, codeIdx);
|
||||
}
|
||||
// note: this part is duplicated from end of compilemulti:
|
||||
const args = block.children.map((c) => c.varName).join(", ");
|
||||
@@ -4433,7 +4429,6 @@ class CodeGenerator {
|
||||
block,
|
||||
index,
|
||||
forceNewBlock: !isTSet,
|
||||
preventRoot: ctx.preventRoot,
|
||||
isLast: ctx.isLast && i === l - 1,
|
||||
});
|
||||
this.compileAST(child, subCtx);
|
||||
@@ -4442,21 +4437,19 @@ class CodeGenerator {
|
||||
}
|
||||
}
|
||||
if (isNewBlock) {
|
||||
if (block.hasDynamicChildren) {
|
||||
if (block.children.length) {
|
||||
const code = this.target.code;
|
||||
const children = block.children.slice();
|
||||
let current = children.shift();
|
||||
for (let i = codeIdx; i < code.length; i++) {
|
||||
if (code[i].trimStart().startsWith(`const ${current.varName} `)) {
|
||||
code[i] = code[i].replace(`const ${current.varName}`, current.varName);
|
||||
current = children.shift();
|
||||
if (!current)
|
||||
break;
|
||||
}
|
||||
if (block.hasDynamicChildren && block.children.length) {
|
||||
const code = this.target.code;
|
||||
const children = block.children.slice();
|
||||
let current = children.shift();
|
||||
for (let i = codeIdx; i < code.length; i++) {
|
||||
if (code[i].trimStart().startsWith(`const ${current.varName} `)) {
|
||||
code[i] = code[i].replace(`const ${current.varName}`, current.varName);
|
||||
current = children.shift();
|
||||
if (!current)
|
||||
break;
|
||||
}
|
||||
this.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
|
||||
}
|
||||
this.addLine(`let ${block.children.map((c) => c.varName).join(", ")};`, codeIdx);
|
||||
}
|
||||
const args = block.children.map((c) => c.varName).join(", ");
|
||||
this.insertBlock(`multi([${args}])`, block, ctx);
|
||||
@@ -4470,24 +4463,23 @@ class CodeGenerator {
|
||||
ctxVar = generateId("ctx");
|
||||
this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`);
|
||||
}
|
||||
const isDynamic = INTERP_REGEXP.test(ast.name);
|
||||
const subTemplate = isDynamic ? interpolate(ast.name) : "`" + ast.name + "`";
|
||||
if (block && !forceNewBlock) {
|
||||
this.insertAnchor(block);
|
||||
}
|
||||
block = this.createBlock(block, "multi", ctx);
|
||||
if (ast.body) {
|
||||
this.addLine(`${ctxVar} = Object.create(${ctxVar});`);
|
||||
this.addLine(`${ctxVar}[isBoundary] = 1;`);
|
||||
this.helpers.add("isBoundary");
|
||||
const subCtx = createContext(ctx, { preventRoot: true, ctxVar });
|
||||
const subCtx = createContext(ctx, { ctxVar });
|
||||
const bl = this.compileMulti({ type: 3 /* Multi */, content: ast.body }, subCtx);
|
||||
if (bl) {
|
||||
this.helpers.add("zero");
|
||||
this.addLine(`${ctxVar}[zero] = ${bl};`);
|
||||
}
|
||||
}
|
||||
const isDynamic = INTERP_REGEXP.test(ast.name);
|
||||
const subTemplate = isDynamic ? interpolate(ast.name) : "`" + ast.name + "`";
|
||||
if (block) {
|
||||
if (!forceNewBlock) {
|
||||
this.insertAnchor(block);
|
||||
}
|
||||
}
|
||||
const key = `key + \`${this.generateComponentKey()}\``;
|
||||
if (isDynamic) {
|
||||
const templateVar = generateId("template");
|
||||
@@ -4495,7 +4487,6 @@ class CodeGenerator {
|
||||
this.staticDefs.push({ id: "call", expr: `app.callTemplate.bind(app)` });
|
||||
}
|
||||
this.define(templateVar, subTemplate);
|
||||
block = this.createBlock(block, "multi", ctx);
|
||||
this.insertBlock(`call(this, ${templateVar}, ${ctxVar}, node, ${key})`, block, {
|
||||
...ctx,
|
||||
forceNewBlock: !block,
|
||||
@@ -4504,7 +4495,6 @@ class CodeGenerator {
|
||||
else {
|
||||
const id = generateId(`callTemplate_`);
|
||||
this.staticDefs.push({ id, expr: `app.getTemplate(${subTemplate})` });
|
||||
block = this.createBlock(block, "multi", ctx);
|
||||
this.insertBlock(`${id}.call(this, ${ctxVar}, node, ${key})`, block, {
|
||||
...ctx,
|
||||
forceNewBlock: !block,
|
||||
@@ -4826,7 +4816,7 @@ function parse(xml) {
|
||||
}
|
||||
function _parse(xml) {
|
||||
normalizeXML(xml);
|
||||
const ctx = { inPreTag: false, inSVG: false };
|
||||
const ctx = { inPreTag: false };
|
||||
return parseNode(xml, ctx) || { type: 0 /* Text */, value: "" };
|
||||
}
|
||||
function parseNode(node, ctx) {
|
||||
@@ -4917,9 +4907,7 @@ function parseDOMNode(node, ctx) {
|
||||
if (tagName === "pre") {
|
||||
ctx.inPreTag = true;
|
||||
}
|
||||
const shouldAddSVGNS = ROOT_SVG_TAGS.has(tagName) && !ctx.inSVG;
|
||||
ctx.inSVG = ctx.inSVG || shouldAddSVGNS;
|
||||
const ns = shouldAddSVGNS ? "http://www.w3.org/2000/svg" : null;
|
||||
let ns = !ctx.nameSpace && ROOT_SVG_TAGS.has(tagName) ? "http://www.w3.org/2000/svg" : null;
|
||||
const ref = node.getAttribute("t-ref");
|
||||
node.removeAttribute("t-ref");
|
||||
const nodeAttrsNames = node.getAttributeNames();
|
||||
@@ -4981,6 +4969,9 @@ function parseDOMNode(node, ctx) {
|
||||
else if (attr.startsWith("block-")) {
|
||||
throw new OwlError(`Invalid attribute: '${attr}'`);
|
||||
}
|
||||
else if (attr === "xmlns") {
|
||||
ns = value;
|
||||
}
|
||||
else if (attr !== "t-name") {
|
||||
if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
|
||||
throw new OwlError(`Unknown QWeb directive: '${attr}'`);
|
||||
@@ -4993,6 +4984,9 @@ function parseDOMNode(node, ctx) {
|
||||
attrs[attr] = value;
|
||||
}
|
||||
}
|
||||
if (ns) {
|
||||
ctx.nameSpace = ns;
|
||||
}
|
||||
const children = parseChildren(node, ctx);
|
||||
return {
|
||||
type: 2 /* DomNode */,
|
||||
@@ -5561,7 +5555,7 @@ function compile(template, options = {}) {
|
||||
}
|
||||
|
||||
// do not modify manually. This file is generated by the release script.
|
||||
const version = "2.2.4";
|
||||
const version = "2.2.6";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Scheduler
|
||||
@@ -5650,13 +5644,8 @@ const DEV_MSG = () => {
|
||||
This is not suitable for production use.
|
||||
See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`;
|
||||
};
|
||||
window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = {
|
||||
apps: new Set(),
|
||||
Fiber: Fiber,
|
||||
RootFiber: RootFiber,
|
||||
toRaw: toRaw,
|
||||
reactive: reactive,
|
||||
});
|
||||
const apps = new Set();
|
||||
window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps, Fiber, RootFiber, toRaw, reactive });
|
||||
class App extends TemplateSet {
|
||||
constructor(Root, config = {}) {
|
||||
super(config);
|
||||
@@ -5664,7 +5653,7 @@ class App extends TemplateSet {
|
||||
this.root = null;
|
||||
this.name = config.name || "";
|
||||
this.Root = Root;
|
||||
window.__OWL_DEVTOOLS__.apps.add(this);
|
||||
apps.add(this);
|
||||
if (config.test) {
|
||||
this.dev = true;
|
||||
}
|
||||
@@ -5721,7 +5710,7 @@ class App extends TemplateSet {
|
||||
this.root.destroy();
|
||||
this.scheduler.processTasks();
|
||||
}
|
||||
window.__OWL_DEVTOOLS__.apps.delete(this);
|
||||
apps.delete(this);
|
||||
}
|
||||
createComponent(name, isStatic, hasSlotsProp, hasDynamicPropList, propList) {
|
||||
const isDynamic = !isStatic;
|
||||
@@ -5796,6 +5785,7 @@ class App extends TemplateSet {
|
||||
}
|
||||
}
|
||||
App.validateTarget = validateTarget;
|
||||
App.apps = apps;
|
||||
App.version = version;
|
||||
async function mount(C, target, config = {}) {
|
||||
return new App(C, config).mount(target, config);
|
||||
@@ -5994,6 +5984,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 };
|
||||
|
||||
|
||||
__info__.date = '2023-08-02T06:20:03.634Z';
|
||||
__info__.hash = '8f9ad98';
|
||||
__info__.date = '2023-09-25T11:48:01.531Z';
|
||||
__info__.hash = '752160f';
|
||||
__info__.url = 'https://github.com/odoo/owl';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@odoo/owl",
|
||||
"version": "2.2.4",
|
||||
"version": "2.2.6",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@odoo/owl",
|
||||
"version": "2.2.4",
|
||||
"version": "2.2.6",
|
||||
"description": "Odoo Web Library (OWL)",
|
||||
"main": "dist/owl.cjs.js",
|
||||
"module": "dist/owl.es.js",
|
||||
|
||||
@@ -160,7 +160,6 @@ interface Context {
|
||||
block: BlockDescription | null;
|
||||
index: number | string;
|
||||
forceNewBlock: boolean;
|
||||
preventRoot?: boolean;
|
||||
isLast?: boolean;
|
||||
translate: boolean;
|
||||
tKeyExpr: string | null;
|
||||
@@ -377,7 +376,7 @@ export class CodeGenerator {
|
||||
): BlockDescription {
|
||||
const hasRoot = this.target.hasRoot;
|
||||
const block = new BlockDescription(this.target, type);
|
||||
if (!hasRoot && !ctx.preventRoot) {
|
||||
if (!hasRoot) {
|
||||
this.target.hasRoot = true;
|
||||
block.isRoot = true;
|
||||
}
|
||||
@@ -403,7 +402,7 @@ export class CodeGenerator {
|
||||
blockExpr = `toggler(${ctx.tKeyExpr}, ${blockExpr})`;
|
||||
}
|
||||
|
||||
if (block.isRoot && !ctx.preventRoot) {
|
||||
if (block.isRoot) {
|
||||
if (this.target.on) {
|
||||
blockExpr = this.wrapWithEventCatcher(blockExpr, this.target.on);
|
||||
}
|
||||
@@ -586,11 +585,6 @@ export class CodeGenerator {
|
||||
}
|
||||
// attributes
|
||||
const attrs: Attrs = {};
|
||||
const nameSpace = ast.ns || ctx.nameSpace;
|
||||
if (nameSpace && isNewBlock) {
|
||||
// specific namespace uri
|
||||
attrs["block-ns"] = nameSpace;
|
||||
}
|
||||
|
||||
for (let key in ast.attrs) {
|
||||
let expr, attrName;
|
||||
@@ -719,7 +713,10 @@ export class CodeGenerator {
|
||||
attrs["block-ref"] = String(idx);
|
||||
}
|
||||
|
||||
const dom = xmlDoc.createElement(ast.tag);
|
||||
const nameSpace = ast.ns || ctx.nameSpace;
|
||||
const dom = nameSpace
|
||||
? xmlDoc.createElementNS(nameSpace, ast.tag)
|
||||
: xmlDoc.createElement(ast.tag);
|
||||
for (const [attr, val] of Object.entries(attrs)) {
|
||||
if (!(attr === "class" && val === "")) {
|
||||
dom.setAttribute(attr, val);
|
||||
@@ -761,7 +758,7 @@ export class CodeGenerator {
|
||||
if (!current) break;
|
||||
}
|
||||
}
|
||||
this.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
|
||||
this.addLine(`let ${block!.children.map((c) => c.varName).join(", ")};`, codeIdx);
|
||||
}
|
||||
}
|
||||
return block!.varName;
|
||||
@@ -863,7 +860,7 @@ export class CodeGenerator {
|
||||
if (!current) break;
|
||||
}
|
||||
}
|
||||
this.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
|
||||
this.addLine(`let ${block!.children.map((c) => c.varName).join(", ")};`, codeIdx);
|
||||
}
|
||||
|
||||
// note: this part is duplicated from end of compilemulti:
|
||||
@@ -989,7 +986,6 @@ export class CodeGenerator {
|
||||
block,
|
||||
index,
|
||||
forceNewBlock: !isTSet,
|
||||
preventRoot: ctx.preventRoot,
|
||||
isLast: ctx.isLast && i === l - 1,
|
||||
});
|
||||
this.compileAST(child, subCtx);
|
||||
@@ -998,20 +994,18 @@ export class CodeGenerator {
|
||||
}
|
||||
}
|
||||
if (isNewBlock) {
|
||||
if (block!.hasDynamicChildren) {
|
||||
if (block!.children.length) {
|
||||
const code = this.target.code;
|
||||
const children = block!.children.slice();
|
||||
let current = children.shift();
|
||||
for (let i = codeIdx; i < code.length; i++) {
|
||||
if (code[i].trimStart().startsWith(`const ${current!.varName} `)) {
|
||||
code[i] = code[i].replace(`const ${current!.varName}`, current!.varName);
|
||||
current = children.shift();
|
||||
if (!current) break;
|
||||
}
|
||||
if (block!.hasDynamicChildren && block!.children.length) {
|
||||
const code = this.target.code;
|
||||
const children = block!.children.slice();
|
||||
let current = children.shift();
|
||||
for (let i = codeIdx; i < code.length; i++) {
|
||||
if (code[i].trimStart().startsWith(`const ${current!.varName} `)) {
|
||||
code[i] = code[i].replace(`const ${current!.varName}`, current!.varName);
|
||||
current = children.shift();
|
||||
if (!current) break;
|
||||
}
|
||||
this.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
|
||||
}
|
||||
this.addLine(`let ${block!.children.map((c) => c.varName).join(", ")};`, codeIdx);
|
||||
}
|
||||
|
||||
const args = block!.children.map((c) => c.varName).join(", ");
|
||||
@@ -1027,24 +1021,24 @@ export class CodeGenerator {
|
||||
ctxVar = generateId("ctx");
|
||||
this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`);
|
||||
}
|
||||
const isDynamic = INTERP_REGEXP.test(ast.name);
|
||||
const subTemplate = isDynamic ? interpolate(ast.name) : "`" + ast.name + "`";
|
||||
if (block && !forceNewBlock) {
|
||||
this.insertAnchor(block);
|
||||
}
|
||||
block = this.createBlock(block, "multi", ctx);
|
||||
if (ast.body) {
|
||||
this.addLine(`${ctxVar} = Object.create(${ctxVar});`);
|
||||
this.addLine(`${ctxVar}[isBoundary] = 1;`);
|
||||
this.helpers.add("isBoundary");
|
||||
const subCtx = createContext(ctx, { preventRoot: true, ctxVar });
|
||||
const subCtx = createContext(ctx, { ctxVar });
|
||||
const bl = this.compileMulti({ type: ASTType.Multi, content: ast.body }, subCtx);
|
||||
if (bl) {
|
||||
this.helpers.add("zero");
|
||||
this.addLine(`${ctxVar}[zero] = ${bl};`);
|
||||
}
|
||||
}
|
||||
const isDynamic = INTERP_REGEXP.test(ast.name);
|
||||
const subTemplate = isDynamic ? interpolate(ast.name) : "`" + ast.name + "`";
|
||||
if (block) {
|
||||
if (!forceNewBlock) {
|
||||
this.insertAnchor(block);
|
||||
}
|
||||
}
|
||||
|
||||
const key = `key + \`${this.generateComponentKey()}\``;
|
||||
if (isDynamic) {
|
||||
const templateVar = generateId("template");
|
||||
@@ -1052,7 +1046,6 @@ export class CodeGenerator {
|
||||
this.staticDefs.push({ id: "call", expr: `app.callTemplate.bind(app)` });
|
||||
}
|
||||
this.define(templateVar, subTemplate);
|
||||
block = this.createBlock(block, "multi", ctx);
|
||||
this.insertBlock(`call(this, ${templateVar}, ${ctxVar}, node, ${key})`, block!, {
|
||||
...ctx,
|
||||
forceNewBlock: !block,
|
||||
@@ -1060,7 +1053,6 @@ export class CodeGenerator {
|
||||
} else {
|
||||
const id = generateId(`callTemplate_`);
|
||||
this.staticDefs.push({ id, expr: `app.getTemplate(${subTemplate})` });
|
||||
block = this.createBlock(block, "multi", ctx);
|
||||
this.insertBlock(`${id}.call(this, ${ctxVar}, node, ${key})`, block!, {
|
||||
...ctx,
|
||||
forceNewBlock: !block,
|
||||
|
||||
@@ -213,14 +213,14 @@ export function parse(xml: string | Element): AST {
|
||||
|
||||
function _parse(xml: Element): AST {
|
||||
normalizeXML(xml);
|
||||
const ctx = { inPreTag: false, inSVG: false };
|
||||
const ctx = { inPreTag: false };
|
||||
return parseNode(xml, ctx) || { type: ASTType.Text, value: "" };
|
||||
}
|
||||
|
||||
interface ParsingContext {
|
||||
tModelInfo?: TModelInfo | null;
|
||||
nameSpace?: string;
|
||||
inPreTag: boolean;
|
||||
inSVG: boolean;
|
||||
}
|
||||
|
||||
function parseNode(node: Node, ctx: ParsingContext): AST | null {
|
||||
@@ -323,9 +323,8 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
||||
if (tagName === "pre") {
|
||||
ctx.inPreTag = true;
|
||||
}
|
||||
const shouldAddSVGNS = ROOT_SVG_TAGS.has(tagName) && !ctx.inSVG;
|
||||
ctx.inSVG = ctx.inSVG || shouldAddSVGNS;
|
||||
const ns = shouldAddSVGNS ? "http://www.w3.org/2000/svg" : null;
|
||||
|
||||
let ns = !ctx.nameSpace && ROOT_SVG_TAGS.has(tagName) ? "http://www.w3.org/2000/svg" : null;
|
||||
const ref = node.getAttribute("t-ref");
|
||||
node.removeAttribute("t-ref");
|
||||
|
||||
@@ -389,6 +388,8 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
||||
}
|
||||
} else if (attr.startsWith("block-")) {
|
||||
throw new OwlError(`Invalid attribute: '${attr}'`);
|
||||
} else if (attr === "xmlns") {
|
||||
ns = value;
|
||||
} else if (attr !== "t-name") {
|
||||
if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
|
||||
throw new OwlError(`Unknown QWeb directive: '${attr}'`);
|
||||
@@ -401,6 +402,9 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
||||
attrs[attr] = value;
|
||||
}
|
||||
}
|
||||
if (ns) {
|
||||
ctx.nameSpace = ns;
|
||||
}
|
||||
|
||||
const children = parseChildren(node, ctx);
|
||||
return {
|
||||
|
||||
@@ -35,6 +35,8 @@ This is not suitable for production use.
|
||||
See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`;
|
||||
};
|
||||
|
||||
const apps = new Set<App>();
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__OWL_DEVTOOLS__: {
|
||||
@@ -47,13 +49,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
window.__OWL_DEVTOOLS__ ||= {
|
||||
apps: new Set<App>(),
|
||||
Fiber: Fiber,
|
||||
RootFiber: RootFiber,
|
||||
toRaw: toRaw,
|
||||
reactive: reactive,
|
||||
};
|
||||
window.__OWL_DEVTOOLS__ ||= { apps, Fiber, RootFiber, toRaw, reactive };
|
||||
|
||||
export class App<
|
||||
T extends abstract new (...args: any) => any = any,
|
||||
@@ -61,6 +57,7 @@ export class App<
|
||||
E = any
|
||||
> extends TemplateSet {
|
||||
static validateTarget = validateTarget;
|
||||
static apps = apps;
|
||||
static version = version;
|
||||
|
||||
name: string;
|
||||
@@ -75,7 +72,7 @@ export class App<
|
||||
super(config);
|
||||
this.name = config.name || "";
|
||||
this.Root = Root;
|
||||
window.__OWL_DEVTOOLS__.apps.add(this);
|
||||
apps.add(this);
|
||||
if (config.test) {
|
||||
this.dev = true;
|
||||
}
|
||||
@@ -140,7 +137,7 @@ export class App<
|
||||
this.root.destroy();
|
||||
this.scheduler.processTasks();
|
||||
}
|
||||
window.__OWL_DEVTOOLS__.apps.delete(this);
|
||||
apps.delete(this);
|
||||
}
|
||||
|
||||
createComponent<P extends Props>(
|
||||
|
||||
@@ -144,12 +144,7 @@ function buildTree(
|
||||
info.push({ type: "child", idx: index });
|
||||
el = document.createTextNode("");
|
||||
}
|
||||
const attrs = (node as Element).attributes;
|
||||
const ns = attrs.getNamedItem("block-ns");
|
||||
if (ns) {
|
||||
attrs.removeNamedItem("block-ns");
|
||||
currentNS = ns.value;
|
||||
}
|
||||
currentNS ||= (node as Element).namespaceURI;
|
||||
if (!el) {
|
||||
el = currentNS
|
||||
? document.createElementNS(currentNS, tagName)
|
||||
@@ -165,6 +160,7 @@ function buildTree(
|
||||
const fragment = document.createElement("template").content;
|
||||
fragment.appendChild(el);
|
||||
}
|
||||
const attrs = (node as Element).attributes;
|
||||
for (let i = 0; i < attrs.length; i++) {
|
||||
const attrName = attrs[i].name;
|
||||
const attrValue = attrs[i].value;
|
||||
|
||||
@@ -59,7 +59,7 @@ export function useChildSubEnv(envExtension: Env) {
|
||||
// useEffect
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type EffectDeps<T extends any[]> = T | (T extends [...infer H, never] ? EffectDeps<H> : never);
|
||||
type EffectDeps<T extends unknown[]> = T | (T extends [...infer H, never] ? EffectDeps<H> : never);
|
||||
|
||||
/**
|
||||
* @template T
|
||||
@@ -67,7 +67,7 @@ type EffectDeps<T extends any[]> = T | (T extends [...infer H, never] ? EffectDe
|
||||
* @returns {void|(()=>void)} a cleanup function that reverses the side
|
||||
* effects of the effect callback.
|
||||
*/
|
||||
type Effect<T extends [...T]> = (...dependencies: EffectDeps<T>) => void | (() => void);
|
||||
type Effect<T extends unknown[]> = (...dependencies: EffectDeps<T>) => void | (() => void);
|
||||
|
||||
/**
|
||||
* This hook will run a callback when a component is mounted and patched, and
|
||||
@@ -76,15 +76,15 @@ type Effect<T extends [...T]> = (...dependencies: EffectDeps<T>) => void | (() =
|
||||
*
|
||||
* @template T
|
||||
* @param {Effect<T>} effect the effect to run on component mount and/or patch
|
||||
* @param {()=>T} [computeDependencies=()=>[NaN]] a callback to compute
|
||||
* @param {()=>[...T]} [computeDependencies=()=>[NaN]] a callback to compute
|
||||
* dependencies that will decide if the effect needs to be cleaned up and
|
||||
* run again. If the dependencies did not change, the effect will not run
|
||||
* again. The default value returns an array containing only NaN because
|
||||
* NaN !== NaN, which will cause the effect to rerun on every patch.
|
||||
*/
|
||||
export function useEffect<T extends [...T]>(
|
||||
export function useEffect<T extends unknown[]>(
|
||||
effect: Effect<T>,
|
||||
computeDependencies: () => T = () => [NaN] as never
|
||||
computeDependencies: () => [...T] = () => [NaN] as never
|
||||
) {
|
||||
let cleanup: (() => void) | void;
|
||||
let dependencies: T;
|
||||
|
||||
@@ -249,7 +249,7 @@ function basicProxyHandler<T extends Target>(callback: Callback): ProxyHandler<T
|
||||
set(target, key, value, receiver) {
|
||||
const hadKey = objectHasOwnProperty.call(target, key);
|
||||
const originalValue = Reflect.get(target, key, receiver);
|
||||
const ret = Reflect.set(target, key, value, receiver);
|
||||
const ret = Reflect.set(target, key, toRaw(value), receiver);
|
||||
if (!hadKey && objectHasOwnProperty.call(target, key)) {
|
||||
notifyReactives(target, KEYCHANGES);
|
||||
}
|
||||
@@ -368,7 +368,7 @@ function delegateAndNotify(
|
||||
if (hadKey !== hasKey) {
|
||||
notifyReactives(target, KEYCHANGES);
|
||||
}
|
||||
if (originalValue !== value) {
|
||||
if (originalValue !== target[getterName](key)) {
|
||||
notifyReactives(target, key);
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -70,14 +70,12 @@ function prepareList(collection: unknown): [unknown[], unknown[], number, undefi
|
||||
} else if (collection instanceof Map) {
|
||||
keys = [...collection.keys()];
|
||||
values = [...collection.values()];
|
||||
} else if (Symbol.iterator in Object(collection)) {
|
||||
keys = [...(<Iterable<unknown>>collection)];
|
||||
values = keys;
|
||||
} else if (collection && typeof collection === "object") {
|
||||
if (Symbol.iterator in collection) {
|
||||
keys = [...(<Iterable<unknown>>collection)];
|
||||
values = keys;
|
||||
} else {
|
||||
values = Object.values(collection);
|
||||
keys = Object.keys(collection);
|
||||
}
|
||||
values = Object.values(collection);
|
||||
keys = Object.keys(collection);
|
||||
} else {
|
||||
throw new OwlError(`Invalid loop expression: "${collection}" is not iterable`);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,10 @@ export class TemplateSet {
|
||||
|
||||
addTemplate(name: string, template: string | Element) {
|
||||
if (name in this.rawTemplates) {
|
||||
// this check can be expensive, just silently ignore double definitions outside dev mode
|
||||
if (!this.dev) {
|
||||
return;
|
||||
}
|
||||
const rawTemplate = this.rawTemplates[name];
|
||||
const currentAsString =
|
||||
typeof rawTemplate === "string"
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// do not modify manually. This file is generated by the release script.
|
||||
export const version = "2.2.4";
|
||||
export const version = "2.2.6";
|
||||
|
||||
@@ -22,7 +22,7 @@ exports[`app app: clear scheduler tasks and destroy cancelled nodes immediately
|
||||
const comp1 = app.createComponent(\`B\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\`A\`);
|
||||
if (ctx['state'].value) {
|
||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
||||
@@ -32,7 +32,7 @@ exports[`app app: clear scheduler tasks and destroy cancelled nodes immediately
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`app app: clear scheduler tasks and destroy cancelled nodes immediately on destroy 2`] = `
|
||||
exports[`app app: clear scheduler tasks and destroy cancelled nodes immediately on destroy 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
useLogLifecycle,
|
||||
makeDeferred,
|
||||
nextMicroTick,
|
||||
steps,
|
||||
} from "../helpers";
|
||||
|
||||
let fixture: HTMLElement;
|
||||
@@ -123,23 +124,47 @@ describe("app", () => {
|
||||
|
||||
const app = new App(A);
|
||||
const comp = await app.mount(fixture);
|
||||
expect(["A:setup", "A:willStart", "A:willRender", "A:rendered", "A:mounted"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"A:rendered",
|
||||
"A:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
comp.state.value = true;
|
||||
await nextTick();
|
||||
expect(["A:willRender", "B:setup", "B:willStart", "A:rendered"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"A:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
// 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();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"A:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
app.destroy();
|
||||
expect([
|
||||
"A:willUnmount",
|
||||
"B:willDestroy",
|
||||
"A:willDestroy",
|
||||
"B:willDestroy", // make sure the 2 B instances have been destroyed synchronously
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:willUnmount",
|
||||
"B:willDestroy",
|
||||
"A:willDestroy",
|
||||
"B:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -244,12 +244,14 @@ describe("misc", () => {
|
||||
});
|
||||
|
||||
test("namespace is not propagated to siblings", () => {
|
||||
const block = createBlock(`<div><svg block-ns="someNameSpace"><g/></svg><div></div></div>`);
|
||||
const block = createBlock(`<div><svg xmlns="someNameSpace"><g/></svg><div></div></div>`);
|
||||
|
||||
const fixture = makeTestFixture();
|
||||
mount(block(), fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><svg><g></g></svg><div></div></div>");
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><svg xmlns="someNameSpace"><g></g></svg><div></div></div>'
|
||||
);
|
||||
expect(fixture.querySelector("svg")!.namespaceURI).toBe("someNameSpace");
|
||||
expect(fixture.querySelector("g")!.namespaceURI).toBe("someNameSpace");
|
||||
const allDivs = fixture.querySelectorAll("div");
|
||||
|
||||
@@ -30,22 +30,22 @@ describe("namespace", () => {
|
||||
expect(fixture.firstElementChild!.namespaceURI).toBe(XHTML_URI);
|
||||
});
|
||||
|
||||
test("namespace can be changed with block-ns", () => {
|
||||
const block = createBlock(`<tag block-ns="${SVG_URI}"/>`);
|
||||
test("namespace can be changed with xmlns", () => {
|
||||
const block = createBlock(`<tag xmlns="${SVG_URI}"/>`);
|
||||
const tree = block();
|
||||
mount(tree, fixture);
|
||||
expect(fixture.innerHTML).toBe("<tag></tag>");
|
||||
expect(fixture.innerHTML).toBe(`<tag xmlns="${SVG_URI}"></tag>`);
|
||||
expect(fixture.firstElementChild!.namespaceURI).toBe(SVG_URI);
|
||||
});
|
||||
|
||||
test("namespace is kept for children", () => {
|
||||
const block = createBlock(
|
||||
`<parent block-ns="${SVG_URI}"><child><subchild/></child><child/></parent>`
|
||||
`<parent xmlns="${SVG_URI}"><child><subchild/></child><child/></parent>`
|
||||
);
|
||||
const tree = block();
|
||||
mount(tree, fixture);
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<parent><child><subchild></subchild></child><child></child></parent>"
|
||||
`<parent xmlns="${SVG_URI}"><child><subchild></subchild></child><child></child></parent>`
|
||||
);
|
||||
const parent = fixture.firstElementChild!;
|
||||
const child1 = parent.firstElementChild!;
|
||||
@@ -58,10 +58,10 @@ describe("namespace", () => {
|
||||
});
|
||||
|
||||
test("various namespaces in same block", () => {
|
||||
const block = createBlock(`<none><one block-ns="one"/><two block-ns="two"/></none>`);
|
||||
const block = createBlock(`<none><one xmlns="one"/><two xmlns="two"/></none>`);
|
||||
const tree = block();
|
||||
mount(tree, fixture);
|
||||
expect(fixture.innerHTML).toBe("<none><one></one><two></two></none>");
|
||||
expect(fixture.innerHTML).toBe('<none><one xmlns="one"></one><two xmlns="two"></two></none>');
|
||||
const none = fixture.firstElementChild!;
|
||||
const one = none.firstElementChild!;
|
||||
const two = one.nextElementSibling!;
|
||||
|
||||
@@ -34,7 +34,7 @@ exports[`comments properly handle comments between t-if/t-else 1`] = `
|
||||
let block3 = createBlock(\`<span>owl</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (true) {
|
||||
b2 = block2();
|
||||
} else {
|
||||
|
||||
@@ -18,7 +18,7 @@ exports[`misc complex template 1`] = `
|
||||
let block13 = createBlock(\`<i class=\\"fa fa-fw fa-clock-o\\" title=\\"This commit is the head of a base branch\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4,b6,b8;
|
||||
let b2, b3, b4, b6, b8;
|
||||
let attr1 = \`batch_tile \${ctx['options'].more?'more':'nomore'}\`;
|
||||
let attr2 = \`card bg-\${ctx['klass']}-light\`;
|
||||
let attr3 = \`/runbot/batch/\${ctx['batch'].id}\`;
|
||||
@@ -53,7 +53,7 @@ exports[`misc complex template 1`] = `
|
||||
for (let i1 = 0; i1 < l_block8; i1++) {
|
||||
ctx[\`commit_link\`] = k_block8[i1];
|
||||
const key1 = ctx['commit_link'].id;
|
||||
let b10,b11,b12,b13;
|
||||
let b10, b11, b12, b13;
|
||||
let attr5 = \`/runbot/commit/\${ctx['commit_link'].commit_id}\`;
|
||||
let attr6 = \`badge badge-light batch_commit match_type_\${ctx['commit_link'].match_type}\`;
|
||||
if (ctx['commit_link'].match_type=='new') {
|
||||
@@ -112,16 +112,16 @@ exports[`misc global 1`] = `
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
setContextValue(ctx, \\"foo\\", 'aaa');
|
||||
const b6 = callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}\`);
|
||||
const b7 = callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}\`);
|
||||
ctx = ctx.__proto__;
|
||||
const b7 = callTemplate_2.call(this, ctx, node, key + \`__2__\${key1}\`);
|
||||
const b8 = callTemplate_2.call(this, ctx, node, key + \`__2__\${key1}\`);
|
||||
setContextValue(ctx, \\"foo\\", 'bbb');
|
||||
const b8 = callTemplate_3.call(this, ctx, node, key + \`__3__\${key1}\`);
|
||||
const b5 = multi([b6, b7, b8]);
|
||||
ctx[zero] = b5;
|
||||
const b9 = callTemplate_4.call(this, ctx, node, key + \`__4__\${key1}\`);
|
||||
const b9 = callTemplate_3.call(this, ctx, node, key + \`__3__\${key1}\`);
|
||||
const b6 = multi([b7, b8, b9]);
|
||||
ctx[zero] = b6;
|
||||
const b5 = callTemplate_4.call(this, ctx, node, key + \`__4__\${key1}\`);
|
||||
ctx = ctx.__proto__;
|
||||
c_block2[i1] = withKey(multi([b4, b9]), key1);
|
||||
c_block2[i1] = withKey(multi([b4, b5]), key1);
|
||||
}
|
||||
ctx = ctx.__proto__;
|
||||
const b2 = list(c_block2);
|
||||
@@ -217,7 +217,7 @@ exports[`misc other complex template 1`] = `
|
||||
let block25 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b4,b14,b17,b22,b23,b24,b25;
|
||||
let b2, b4, b14, b17, b22, b23, b24, b25;
|
||||
let attr1 = \`/runbot/\${ctx['project'].slug}\`;
|
||||
let txt1 = ctx['project'].name;
|
||||
ctx = Object.create(ctx);
|
||||
@@ -232,12 +232,12 @@ exports[`misc other complex template 1`] = `
|
||||
ctx = ctx.__proto__;
|
||||
b2 = list(c_block2);
|
||||
if (ctx['user']) {
|
||||
let b5,b6;
|
||||
let b5, b6;
|
||||
if (ctx['user'].public) {
|
||||
let attr2 = \`/web/login?redirect=/\`;
|
||||
b5 = block5([attr2]);
|
||||
} else {
|
||||
let b7,b10,b13;
|
||||
let b7, b10, b13;
|
||||
if (ctx['nb_assigned_errors']&&ctx['nb_assigned_errors']>0) {
|
||||
let attr3 = \`You have \${ctx['nb_assigned_errors']} random bug assigned\`;
|
||||
let txt3 = ctx['nb_assigned_errors'];
|
||||
|
||||
@@ -5,7 +5,7 @@ exports[`properly support svg add proper namespace to g tags 1`] = `
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<g block-ns=\\"http://www.w3.org/2000/svg\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/> </g>\`);
|
||||
let block1 = createBlock(\`<g xmlns=\\"http://www.w3.org/2000/svg\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/> </g>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
@@ -18,7 +18,7 @@ exports[`properly support svg add proper namespace to svg 1`] = `
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\" width=\\"100px\\" height=\\"90px\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/> </svg>\`);
|
||||
let block1 = createBlock(\`<svg xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"100px\\" height=\\"90px\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/> </svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
@@ -31,7 +31,7 @@ exports[`properly support svg namespace to g tags not added if already in svg na
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><g/></svg>\`);
|
||||
let block1 = createBlock(\`<svg xmlns=\\"http://www.w3.org/2000/svg\\"><g/></svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
@@ -44,7 +44,7 @@ exports[`properly support svg namespace to svg tags added even if already in svg
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><svg/></svg>\`);
|
||||
let block1 = createBlock(\`<svg xmlns=\\"http://www.w3.org/2000/svg\\"><svg/></svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
@@ -58,8 +58,8 @@ exports[`properly support svg svg creates new block if it is within html -- 2 1`
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block2 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><polygon fill=\\"#000000\\" points=\\"0 0 4 4 8 0\\" transform=\\"translate(5 7)\\"/><block-child-0/></svg>\`);
|
||||
let block3 = createBlock(\`<path block-ns=\\"http://www.w3.org/2000/svg\\"/>\`);
|
||||
let block2 = createBlock(\`<svg xmlns=\\"http://www.w3.org/2000/svg\\"><polygon fill=\\"#000000\\" points=\\"0 0 4 4 8 0\\" transform=\\"translate(5 7)\\"/><block-child-0 xmlns=\\"\\"/></svg>\`);
|
||||
let block3 = createBlock(\`<path xmlns=\\"http://www.w3.org/2000/svg\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b3;
|
||||
@@ -78,7 +78,7 @@ exports[`properly support svg svg creates new block if it is within html 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block2 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><polygon fill=\\"#000000\\" points=\\"0 0 4 4 8 0\\" transform=\\"translate(5 7)\\"/></svg>\`);
|
||||
let block2 = createBlock(\`<svg xmlns=\\"http://www.w3.org/2000/svg\\"><polygon fill=\\"#000000\\" points=\\"0 0 4 4 8 0\\" transform=\\"translate(5 7)\\"/></svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const b2 = block2();
|
||||
@@ -93,7 +93,7 @@ exports[`properly support svg svg namespace added to sub templates if root tag i
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
const callTemplate_1 = app.getTemplate(\`path\`);
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><block-child-0/></svg>\`);
|
||||
let block1 = createBlock(\`<svg xmlns=\\"http://www.w3.org/2000/svg\\"><block-child-0 xmlns=\\"\\"/></svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
@@ -107,7 +107,7 @@ exports[`properly support svg svg namespace added to sub templates if root tag i
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<path block-ns=\\"http://www.w3.org/2000/svg\\"/>\`);
|
||||
let block1 = createBlock(\`<path xmlns=\\"http://www.w3.org/2000/svg\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
@@ -120,8 +120,8 @@ exports[`properly support svg svg namespace added to sub-blocks 1`] = `
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><block-child-0/></svg>\`);
|
||||
let block2 = createBlock(\`<path block-ns=\\"http://www.w3.org/2000/svg\\"/>\`);
|
||||
let block1 = createBlock(\`<svg xmlns=\\"http://www.w3.org/2000/svg\\"><block-child-0 xmlns=\\"\\"/></svg>\`);
|
||||
let block2 = createBlock(\`<path xmlns=\\"http://www.w3.org/2000/svg\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2;
|
||||
|
||||
@@ -61,19 +61,19 @@ exports[`t-call (template calling) call with several sub nodes on same line 1`]
|
||||
const callTemplate_1 = app.getTemplate(\`sub\`);
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block3 = createBlock(\`<span>hey</span>\`);
|
||||
let block5 = createBlock(\`<span>yay</span>\`);
|
||||
let block4 = createBlock(\`<span>hey</span>\`);
|
||||
let block6 = createBlock(\`<span>yay</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b3 = block3();
|
||||
const b4 = text(\` \`);
|
||||
const b5 = block5();
|
||||
const b2 = multi([b3, b4, b5]);
|
||||
ctx[zero] = b2;
|
||||
const b6 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b6]);
|
||||
const b4 = block4();
|
||||
const b5 = text(\` \`);
|
||||
const b6 = block6();
|
||||
const b3 = multi([b4, b5, b6]);
|
||||
ctx[zero] = b3;
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -101,19 +101,19 @@ exports[`t-call (template calling) cascading t-call t-out='0' 1`] = `
|
||||
const callTemplate_1 = app.getTemplate(\`subTemplate\`);
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block3 = createBlock(\`<span>hey</span>\`);
|
||||
let block5 = createBlock(\`<span>yay</span>\`);
|
||||
let block4 = createBlock(\`<span>hey</span>\`);
|
||||
let block6 = createBlock(\`<span>yay</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b3 = block3();
|
||||
const b4 = text(\` \`);
|
||||
const b5 = block5();
|
||||
const b2 = multi([b3, b4, b5]);
|
||||
ctx[zero] = b2;
|
||||
const b6 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b6]);
|
||||
const b4 = block4();
|
||||
const b5 = text(\` \`);
|
||||
const b6 = block6();
|
||||
const b3 = multi([b4, b5, b6]);
|
||||
ctx[zero] = b3;
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -126,17 +126,17 @@ exports[`t-call (template calling) cascading t-call t-out='0' 2`] = `
|
||||
const callTemplate_1 = app.getTemplate(\`subSubTemplate\`);
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block3 = createBlock(\`<span>cascade 0</span>\`);
|
||||
let block4 = createBlock(\`<span>cascade 0</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b3 = block3();
|
||||
const b4 = ctx[zero];
|
||||
const b2 = multi([b3, b4]);
|
||||
ctx[zero] = b2;
|
||||
const b5 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b5]);
|
||||
const b4 = block4();
|
||||
const b5 = ctx[zero];
|
||||
const b3 = multi([b4, b5]);
|
||||
ctx[zero] = b3;
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -149,17 +149,17 @@ exports[`t-call (template calling) cascading t-call t-out='0' 3`] = `
|
||||
const callTemplate_1 = app.getTemplate(\`finalTemplate\`);
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block3 = createBlock(\`<span>cascade 1</span>\`);
|
||||
let block4 = createBlock(\`<span>cascade 1</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b3 = block3();
|
||||
const b4 = ctx[zero];
|
||||
const b2 = multi([b3, b4]);
|
||||
ctx[zero] = b2;
|
||||
const b5 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b5]);
|
||||
const b4 = block4();
|
||||
const b5 = ctx[zero];
|
||||
const b3 = multi([b4, b5]);
|
||||
ctx[zero] = b3;
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -186,17 +186,17 @@ exports[`t-call (template calling) cascading t-call t-out='0', without external
|
||||
let { isBoundary, zero } = helpers;
|
||||
const callTemplate_1 = app.getTemplate(\`subTemplate\`);
|
||||
|
||||
let block2 = createBlock(\`<span>hey</span>\`);
|
||||
let block4 = createBlock(\`<span>yay</span>\`);
|
||||
let block3 = createBlock(\`<span>hey</span>\`);
|
||||
let block5 = createBlock(\`<span>yay</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b2 = block2();
|
||||
const b3 = text(\` \`);
|
||||
const b4 = block4();
|
||||
const b1 = multi([b2, b3, b4]);
|
||||
ctx[zero] = b1;
|
||||
const b3 = block3();
|
||||
const b4 = text(\` \`);
|
||||
const b5 = block5();
|
||||
const b2 = multi([b3, b4, b5]);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
@@ -209,15 +209,15 @@ exports[`t-call (template calling) cascading t-call t-out='0', without external
|
||||
let { isBoundary, zero } = helpers;
|
||||
const callTemplate_1 = app.getTemplate(\`subSubTemplate\`);
|
||||
|
||||
let block2 = createBlock(\`<span>cascade 0</span>\`);
|
||||
let block3 = createBlock(\`<span>cascade 0</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b2 = block2();
|
||||
const b3 = ctx[zero];
|
||||
const b1 = multi([b2, b3]);
|
||||
ctx[zero] = b1;
|
||||
const b3 = block3();
|
||||
const b4 = ctx[zero];
|
||||
const b2 = multi([b3, b4]);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
@@ -230,15 +230,15 @@ exports[`t-call (template calling) cascading t-call t-out='0', without external
|
||||
let { isBoundary, zero } = helpers;
|
||||
const callTemplate_1 = app.getTemplate(\`finalTemplate\`);
|
||||
|
||||
let block2 = createBlock(\`<span>cascade 1</span>\`);
|
||||
let block3 = createBlock(\`<span>cascade 1</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b2 = block2();
|
||||
const b3 = ctx[zero];
|
||||
const b1 = multi([b2, b3]);
|
||||
ctx[zero] = b1;
|
||||
const b3 = block3();
|
||||
const b4 = ctx[zero];
|
||||
const b2 = multi([b3, b4]);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
@@ -342,15 +342,15 @@ exports[`t-call (template calling) nested t-calls with magic variable 0 1`] = `
|
||||
const callTemplate_1 = app.getTemplate(\`grandchild\`);
|
||||
const callTemplate_2 = app.getTemplate(\`child\`);
|
||||
|
||||
let block1 = createBlock(\`<p>Some content...</p>\`);
|
||||
let block3 = createBlock(\`<p>Some content...</p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b1 = block1();
|
||||
ctx[zero] = b1;
|
||||
const b3 = block3();
|
||||
ctx[zero] = b3;
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
ctx = ctx.__proto__;
|
||||
ctx[zero] = b2;
|
||||
@@ -571,6 +571,134 @@ exports[`t-call (template calling) recursive template, part 4: with t-set recurs
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) root t-call with body: t-foreach 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
let { isBoundary, prepareList, withKey, zero } = helpers;
|
||||
const callTemplate_1 = app.getTemplate(\`subTemplate\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
ctx = Object.create(ctx);
|
||||
const [k_block2, v_block2, l_block2, c_block2] = prepareList([1]);;
|
||||
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||
ctx[\`i\`] = k_block2[i1];
|
||||
const key1 = ctx['i'];
|
||||
c_block2[i1] = withKey(text(\`1\`), key1);
|
||||
}
|
||||
ctx = ctx.__proto__;
|
||||
const b2 = list(c_block2);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) root t-call with body: t-foreach 2`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return text(\`sub\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) root t-call with body: t-if false 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
let { isBoundary, zero } = helpers;
|
||||
const callTemplate_1 = app.getTemplate(\`subTemplate\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
let b3;
|
||||
if (false) {
|
||||
b3 = text(\`zero\`);
|
||||
}
|
||||
const b2 = multi([b3]);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) root t-call with body: t-if false 2`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return text(\`sub\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) root t-call with body: t-if true 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
let { isBoundary, zero } = helpers;
|
||||
const callTemplate_1 = app.getTemplate(\`subTemplate\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
let b3;
|
||||
if (true) {
|
||||
b3 = text(\`zero\`);
|
||||
}
|
||||
const b2 = multi([b3]);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) root t-call with body: t-if true 2`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return text(\`sub\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) root t-call with body: t-out with default 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
let { isBoundary, safeOutput, zero } = helpers;
|
||||
const callTemplate_1 = app.getTemplate(\`subTemplate\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b2 = safeOutput(ctx['nothing']);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) root t-call with body: t-out with default 2`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return text(\`sub\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling) scoped parameters 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
@@ -705,13 +833,13 @@ exports[`t-call (template calling) t-call with body content as root of a templat
|
||||
let { isBoundary, zero } = helpers;
|
||||
const callTemplate_1 = app.getTemplate(\`antony\`);
|
||||
|
||||
let block1 = createBlock(\`<p>antony</p>\`);
|
||||
let block2 = createBlock(\`<p>antony</p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b1 = block1();
|
||||
ctx[zero] = b1;
|
||||
const b2 = block2();
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
@@ -928,7 +1056,7 @@ exports[`t-call (template calling) t-call, conditional and t-set in t-call body
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
setContextValue(ctx, \\"v1\\", 'elif');
|
||||
if (ctx['v1']==='if') {
|
||||
b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
@@ -1075,8 +1203,8 @@ exports[`t-call (template calling) with unused body 1`] = `
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b1 = text(\`WHEEE\`);
|
||||
ctx[zero] = b1;
|
||||
const b2 = text(\`WHEEE\`);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
@@ -1136,8 +1264,8 @@ exports[`t-call (template calling) with used body 1`] = `
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b1 = text(\`ok\`);
|
||||
ctx[zero] = b1;
|
||||
const b2 = text(\`ok\`);
|
||||
ctx[zero] = b2;
|
||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
}"
|
||||
|
||||
@@ -180,15 +180,15 @@ exports[`t-esc t-esc=0 is escaped 1`] = `
|
||||
const callTemplate_1 = app.getTemplate(\`sub\`);
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block2 = createBlock(\`<p>escaped</p>\`);
|
||||
let block3 = createBlock(\`<p>escaped</p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b2 = block2();
|
||||
ctx[zero] = b2;
|
||||
const b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b3]);
|
||||
const b3 = block3();
|
||||
ctx[zero] = b3;
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -237,7 +237,7 @@ exports[`t-foreach iterate, position 1`] = `
|
||||
ctx[\`elem_last\`] = i1 === k_block2.length - 1;
|
||||
ctx[\`elem_index\`] = i1;
|
||||
const key1 = ctx['elem'];
|
||||
let b4,b5,b6,b7,b8,b9;
|
||||
let b4, b5, b6, b7, b8, b9;
|
||||
b4 = text(\` -\`);
|
||||
if (ctx['elem_first']) {
|
||||
b5 = text(\` first\`);
|
||||
@@ -256,6 +256,34 @@ exports[`t-foreach iterate, position 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-foreach iterate, string 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('abc');;
|
||||
for (let i1 = 0; i1 < l_block1; i1++) {
|
||||
ctx[\`item\`] = k_block1[i1];
|
||||
ctx[\`item_index\`] = i1;
|
||||
ctx[\`item_value\`] = v_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 simple iteration (in a node) 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -8,7 +8,7 @@ exports[`t-if a t-if next to a div 1`] = `
|
||||
let block2 = createBlock(\`<div>foo</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = block2();
|
||||
if (ctx['cond']) {
|
||||
b3 = text(\`1\`);
|
||||
@@ -44,7 +44,7 @@ exports[`t-if boolean value condition elif (no outside node) 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4,b5;
|
||||
let b2, b3, b4, b5;
|
||||
if (ctx['color']=='black') {
|
||||
b2 = text(\`black pearl\`);
|
||||
} else if (ctx['color']=='yellow') {
|
||||
@@ -67,7 +67,7 @@ exports[`t-if boolean value condition elif 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-2/><block-child-3/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4,b5;
|
||||
let b2, b3, b4, b5;
|
||||
if (ctx['color']=='black') {
|
||||
b2 = text(\`black pearl\`);
|
||||
} else if (ctx['color']=='yellow') {
|
||||
@@ -90,7 +90,7 @@ exports[`t-if boolean value condition else 1`] = `
|
||||
let block1 = createBlock(\`<div><span>begin</span><block-child-0/><block-child-1/><span>end</span></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['condition']) {
|
||||
b2 = text(\`ok\`);
|
||||
} else {
|
||||
@@ -109,7 +109,7 @@ exports[`t-if boolean value condition false else 1`] = `
|
||||
let block1 = createBlock(\`<div><span>begin</span><block-child-0/><block-child-1/><span>end</span></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['condition']) {
|
||||
b2 = text(\`fail\`);
|
||||
} else {
|
||||
@@ -145,7 +145,7 @@ exports[`t-if can use some boolean operators in expressions 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-2/><block-child-3/><block-child-4/><block-child-5/><block-child-6/><block-child-7/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4,b5,b6,b7,b8,b9;
|
||||
let b2, b3, b4, b5, b6, b7, b8, b9;
|
||||
if (ctx['cond1']&&ctx['cond2']) {
|
||||
b2 = text(\`and\`);
|
||||
}
|
||||
@@ -239,7 +239,7 @@ exports[`t-if simple t-if/t-else 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['condition']) {
|
||||
b2 = text(\`1\`);
|
||||
} else {
|
||||
@@ -258,7 +258,7 @@ exports[`t-if simple t-if/t-else in a div 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['condition']) {
|
||||
b2 = text(\`1\`);
|
||||
} else {
|
||||
@@ -277,7 +277,7 @@ exports[`t-if t-esc with t-elif 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (false) {
|
||||
b2 = text(\`abc\`);
|
||||
} else {
|
||||
@@ -314,7 +314,7 @@ exports[`t-if t-if and t-else with two nodes 1`] = `
|
||||
let block5 = createBlock(\`<span>b</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['condition']) {
|
||||
b2 = text(\`1\`);
|
||||
} else {
|
||||
@@ -372,7 +372,7 @@ exports[`t-if t-if with empty content 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\`hello\`);
|
||||
if (ctx['condition']) {
|
||||
b3 = text(\`\`);
|
||||
@@ -388,7 +388,7 @@ exports[`t-if t-if/t-else with more content 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['condition']) {
|
||||
if (ctx['condition']) {
|
||||
b2 = text(\`asf\`);
|
||||
@@ -458,7 +458,7 @@ exports[`t-if t-set, then t-if, part 3 1`] = `
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
setContextValue(ctx, \\"y\\", false);
|
||||
setContextValue(ctx, \\"x\\", ctx['y']);
|
||||
if (ctx['x']) {
|
||||
@@ -477,7 +477,7 @@ exports[`t-if two consecutive t-if 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['cond1']) {
|
||||
b2 = text(\`1\`);
|
||||
}
|
||||
@@ -497,7 +497,7 @@ exports[`t-if two consecutive t-if in a div 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['cond1']) {
|
||||
b2 = text(\`1\`);
|
||||
}
|
||||
@@ -520,7 +520,7 @@ exports[`t-if two t-ifs next to each other 1`] = `
|
||||
let block5 = createBlock(\`<p>2</p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['condition']) {
|
||||
let txt1 = ctx['text'];
|
||||
b2 = block2([txt1]);
|
||||
|
||||
@@ -79,7 +79,7 @@ exports[`t-key t-key on sub dom node pushes a child block in its parent 1`] = `
|
||||
let block3 = createBlock(\`<div><h1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['hasSpan']) {
|
||||
b2 = block2();
|
||||
}
|
||||
|
||||
@@ -35,15 +35,15 @@ exports[`t-out multiple calls to t-out 1`] = `
|
||||
const callTemplate_1 = app.getTemplate(\`sub\`);
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block2 = createBlock(\`<span>coucou</span>\`);
|
||||
let block3 = createBlock(\`<span>coucou</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b2 = block2();
|
||||
ctx[zero] = b2;
|
||||
const b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b3]);
|
||||
const b3 = block3();
|
||||
ctx[zero] = b3;
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -102,15 +102,15 @@ exports[`t-out t-out 0 1`] = `
|
||||
const callTemplate_1 = app.getTemplate(\`_basic-callee\`);
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block2 = createBlock(\`<div>zero</div>\`);
|
||||
let block3 = createBlock(\`<div>zero</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b2 = block2();
|
||||
ctx[zero] = b2;
|
||||
const b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b3]);
|
||||
const b3 = block3();
|
||||
ctx[zero] = b3;
|
||||
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -309,7 +309,7 @@ exports[`t-out t-out switch markup on bdom 1`] = `
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1
|
||||
let b3,b5;
|
||||
let b3, b5;
|
||||
ctx[\`bdom\`] = new LazyValue(value1, ctx, this, node, key);
|
||||
if (ctx['hasBdom']) {
|
||||
const b4 = safeOutput(ctx['bdom']);
|
||||
|
||||
@@ -94,7 +94,7 @@ exports[`t-set set from body literal (with t-if/t-else 1`] = `
|
||||
let { isBoundary, withDefault, LazyValue } = helpers;
|
||||
|
||||
function value1(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['condition']) {
|
||||
b2 = text(\`true\`);
|
||||
} else {
|
||||
|
||||
@@ -9,20 +9,20 @@ describe("properly support svg", () => {
|
||||
test("add proper namespace to svg", () => {
|
||||
const template = `<svg width="100px" height="90px"><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/> </svg>`;
|
||||
expect(renderToString(template)).toBe(
|
||||
`<svg width=\"100px\" height=\"90px\"><circle cx=\"50\" cy=\"50\" r=\"4\" stroke=\"green\" stroke-width=\"1\" fill=\"yellow\"></circle> </svg>`
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="100px" height="90px"><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"></circle> </svg>`
|
||||
);
|
||||
});
|
||||
|
||||
test("add proper namespace to g tags", () => {
|
||||
const template = `<g><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/> </g>`;
|
||||
expect(renderToString(template)).toBe(
|
||||
`<g><circle cx=\"50\" cy=\"50\" r=\"4\" stroke=\"green\" stroke-width=\"1\" fill=\"yellow\"></circle> </g>`
|
||||
`<g xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"></circle> </g>`
|
||||
);
|
||||
});
|
||||
|
||||
test("namespace to g tags not added if already in svg namespace", () => {
|
||||
const template = `<svg><g/></svg>`;
|
||||
expect(renderToString(template)).toBe(`<svg><g></g></svg>`);
|
||||
expect(renderToString(template)).toBe(`<svg xmlns="http://www.w3.org/2000/svg"><g></g></svg>`);
|
||||
});
|
||||
|
||||
test("namespace to svg tags added even if already in svg namespace", () => {
|
||||
@@ -41,8 +41,13 @@ describe("properly support svg", () => {
|
||||
test("svg namespace added to sub-blocks", () => {
|
||||
const template = `<svg><path t-if="path"/></svg>`;
|
||||
|
||||
expect(renderToString(template, { path: false })).toBe(`<svg></svg>`);
|
||||
expect(renderToString(template, { path: true })).toBe(`<svg><path></path></svg>`);
|
||||
expect(renderToString(template, { path: false })).toBe(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg"></svg>`
|
||||
);
|
||||
// Because the path is its own block, it has its own xmlns attribute
|
||||
expect(renderToString(template, { path: true })).toBe(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg"><path xmlns="http://www.w3.org/2000/svg"></path></svg>`
|
||||
);
|
||||
|
||||
const bdom = renderToBdom(template, { path: true });
|
||||
const fixture = makeTestFixture();
|
||||
|
||||
@@ -430,6 +430,48 @@ describe("t-call (template calling)", () => {
|
||||
expect(context.renderToString("main")).toBe(expected);
|
||||
});
|
||||
|
||||
test("root t-call with body: t-if true", () => {
|
||||
const context = new TestContext();
|
||||
const subTemplate = `sub`;
|
||||
const main = `<t t-call="subTemplate"><t t-if="true">zero</t></t>`;
|
||||
context.addTemplate("subTemplate", subTemplate);
|
||||
context.addTemplate("main", main);
|
||||
const expected = "sub";
|
||||
expect(context.renderToString("main")).toBe(expected);
|
||||
});
|
||||
|
||||
test("root t-call with body: t-if false", () => {
|
||||
const context = new TestContext();
|
||||
const subTemplate = `sub`;
|
||||
const main = `<t t-call="subTemplate"><t t-if="false">zero</t></t>`;
|
||||
context.addTemplate("subTemplate", subTemplate);
|
||||
context.addTemplate("main", main);
|
||||
const expected = "sub";
|
||||
expect(context.renderToString("main")).toBe(expected);
|
||||
});
|
||||
|
||||
test("root t-call with body: t-out with default", () => {
|
||||
const context = new TestContext();
|
||||
const subTemplate = `sub`;
|
||||
const main = `<t t-call="subTemplate"><t t-out="nothing">default</t></t>`;
|
||||
context.addTemplate("subTemplate", subTemplate);
|
||||
context.addTemplate("main", main);
|
||||
const expected = "sub";
|
||||
expect(context.renderToString("main")).toBe(expected);
|
||||
});
|
||||
|
||||
test("root t-call with body: t-foreach", () => {
|
||||
const context = new TestContext();
|
||||
const subTemplate = `sub`;
|
||||
const main = `<t t-call="subTemplate">
|
||||
<t t-foreach="[1]" t-as="i" t-key="i">1</t>
|
||||
</t>`;
|
||||
context.addTemplate("subTemplate", subTemplate);
|
||||
context.addTemplate("main", main);
|
||||
const expected = "sub";
|
||||
expect(context.renderToString("main")).toBe(expected);
|
||||
});
|
||||
|
||||
test("dynamic t-call", () => {
|
||||
const context = new TestContext();
|
||||
const foo = `<foo><t t-esc="val"/></foo>`;
|
||||
|
||||
@@ -131,6 +131,15 @@ describe("t-foreach", () => {
|
||||
expect(renderToString(template, context)).toBe(expected);
|
||||
});
|
||||
|
||||
test("iterate, string param", () => {
|
||||
const template = `
|
||||
<t t-foreach="'abc'" 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: a a] [1: b b] [2: c c] `;
|
||||
expect(renderToString(template)).toBe(expected);
|
||||
});
|
||||
|
||||
test("iterate, iterable param", () => {
|
||||
const template = `
|
||||
<t t-foreach="map.values()" t-as="item" t-key="item_index">
|
||||
|
||||
@@ -11,8 +11,8 @@ describe("basic validation", () => {
|
||||
expect(() => context.getTemplate("invalidname")).toThrow("Missing template");
|
||||
});
|
||||
|
||||
test("cannot add a different template with the same name", () => {
|
||||
const context = new TemplateSet();
|
||||
test("cannot add a different template with the same name in dev mode", () => {
|
||||
const context = new TemplateSet({ dev: true });
|
||||
context.addTemplate("test", `<t/>`);
|
||||
// Same template with the same name is fine
|
||||
expect(() => context.addTemplate("test", "<t/>")).not.toThrow();
|
||||
@@ -20,6 +20,13 @@ describe("basic validation", () => {
|
||||
expect(() => context.addTemplate("test", "<div/>")).toThrow("already defined");
|
||||
});
|
||||
|
||||
test("adding different template with same name outside dev mode silently ignores it", () => {
|
||||
const context = new TemplateSet({ dev: false });
|
||||
context.addTemplate("test", `<t/>`);
|
||||
expect(() => context.addTemplate("test", "<div/>")).not.toThrow();
|
||||
expect(context.rawTemplates.test).toBe("<t/>");
|
||||
});
|
||||
|
||||
test("invalid xml", () => {
|
||||
const template = "<div>";
|
||||
expect(() => snapshotTemplate(template)).toThrow("Invalid XML in template");
|
||||
|
||||
@@ -440,7 +440,7 @@ exports[`basics higher order components parent and child 2`] = `
|
||||
const comp2 = app.createComponent(\`ChildB\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['props'].child==='a') {
|
||||
b2 = comp1({}, key + \`__1\`, node, this, null);
|
||||
} else {
|
||||
@@ -738,7 +738,7 @@ exports[`basics sub components between t-ifs 1`] = `
|
||||
let block5 = createBlock(\`<span>test</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4,b5;
|
||||
let b2, b3, b4, b5;
|
||||
if (ctx['state'].flag) {
|
||||
b2 = block2();
|
||||
} else {
|
||||
@@ -776,7 +776,7 @@ exports[`basics t-elif works with t-component 1`] = `
|
||||
let block2 = createBlock(\`<div>somediv</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].flag) {
|
||||
b2 = block2();
|
||||
} else if (!ctx['state'].flag) {
|
||||
@@ -810,7 +810,7 @@ exports[`basics t-else with empty string works with t-component 1`] = `
|
||||
let block2 = createBlock(\`<div>somediv</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].flag) {
|
||||
b2 = block2();
|
||||
} else {
|
||||
@@ -844,7 +844,7 @@ exports[`basics t-else works with t-component 1`] = `
|
||||
let block2 = createBlock(\`<div>somediv</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].flag) {
|
||||
b2 = block2();
|
||||
} else {
|
||||
@@ -909,7 +909,7 @@ exports[`basics t-key on a component with t-if, and a sibling component 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (false) {
|
||||
const tKey_1 = 'str';
|
||||
b2 = toggler(tKey_1, comp1({}, tKey_1 + key + \`__1\`, node, this, null));
|
||||
@@ -1248,7 +1248,7 @@ exports[`support svg components add proper namespace to svg 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
const comp1 = app.createComponent(\`GComp\`, true, false, false, []);
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><block-child-0/></svg>\`);
|
||||
let block1 = createBlock(\`<svg xmlns=\\"http://www.w3.org/2000/svg\\"><block-child-0 xmlns=\\"\\"/></svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const b2 = comp1({}, key + \`__1\`, node, this, null);
|
||||
@@ -1262,7 +1262,7 @@ exports[`support svg components add proper namespace to svg 2`] = `
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<g block-ns=\\"http://www.w3.org/2000/svg\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/></g>\`);
|
||||
let block1 = createBlock(\`<g xmlns=\\"http://www.w3.org/2000/svg\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/></g>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
|
||||
@@ -61,7 +61,7 @@ exports[`another scenario with delayed rendering 1`] = `
|
||||
const comp1 = app.createComponent(\`B\`, true, false, false, [\\"value\\"]);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\`A\`);
|
||||
if (ctx['state'].value<15) {
|
||||
b3 = comp1({value: ctx['state'].value}, key + \`__1\`, node, this, null);
|
||||
@@ -184,7 +184,7 @@ exports[`changing state before first render does not trigger a render (with pare
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`changing state before first render does not trigger a render (with parent) 2`] = `
|
||||
exports[`changing state before first render does not trigger a render (with parent) 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -244,7 +244,7 @@ exports[`components are not destroyed between animation frame 1`] = `
|
||||
const comp1 = app.createComponent(\`B\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\`A\`);
|
||||
if (ctx['state'].flag) {
|
||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
||||
@@ -254,7 +254,7 @@ exports[`components are not destroyed between animation frame 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`components are not destroyed between animation frame 2`] = `
|
||||
exports[`components are not destroyed between animation frame 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -268,7 +268,7 @@ exports[`components are not destroyed between animation frame 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`components are not destroyed between animation frame 3`] = `
|
||||
exports[`components are not destroyed between animation frame 5`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -748,7 +748,7 @@ exports[`concurrent renderings scenario 10 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`concurrent renderings scenario 10 3`] = `
|
||||
exports[`concurrent renderings scenario 10 4`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -831,7 +831,7 @@ exports[`concurrent renderings scenario 13 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = comp1({}, key + \`__1\`, node, this, null);
|
||||
if (ctx['state'].bool) {
|
||||
b3 = comp2({}, key + \`__2\`, node, this, null);
|
||||
@@ -978,7 +978,7 @@ exports[`concurrent renderings scenario 16 3`] = `
|
||||
const comp1 = app.createComponent(\`D\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4,b5,b6,b7,b8;
|
||||
let b2, b3, b4, b5, b6, b7, b8;
|
||||
b2 = text(ctx['props'].fromA);
|
||||
b3 = text(\`:\`);
|
||||
b4 = text(ctx['props'].fromB);
|
||||
@@ -993,7 +993,7 @@ exports[`concurrent renderings scenario 16 3`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`concurrent renderings scenario 16 4`] = `
|
||||
exports[`concurrent renderings scenario 16 6`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1012,7 +1012,7 @@ exports[`creating two async components, scenario 1 1`] = `
|
||||
const comp2 = app.createComponent(\`ChildB\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].flagA) {
|
||||
b2 = comp1({}, key + \`__1\`, node, this, null);
|
||||
}
|
||||
@@ -1024,7 +1024,7 @@ exports[`creating two async components, scenario 1 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`creating two async components, scenario 1 2`] = `
|
||||
exports[`creating two async components, scenario 1 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1038,7 +1038,7 @@ exports[`creating two async components, scenario 1 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`creating two async components, scenario 1 3`] = `
|
||||
exports[`creating two async components, scenario 1 5`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1061,7 +1061,7 @@ exports[`creating two async components, scenario 2 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = comp1({val: ctx['state'].valA}, key + \`__1\`, node, this, null);
|
||||
if (ctx['state'].flagB) {
|
||||
b3 = comp2({val: ctx['state'].valB}, key + \`__2\`, node, this, null);
|
||||
@@ -1085,7 +1085,7 @@ exports[`creating two async components, scenario 2 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`creating two async components, scenario 2 3`] = `
|
||||
exports[`creating two async components, scenario 2 5`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1109,7 +1109,7 @@ exports[`creating two async components, scenario 3 (patching in the same frame)
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = comp1({val: ctx['state'].valA}, key + \`__1\`, node, this, null);
|
||||
if (ctx['state'].flagB) {
|
||||
b3 = comp2({val: ctx['state'].valB}, key + \`__2\`, node, this, null);
|
||||
@@ -1133,7 +1133,7 @@ exports[`creating two async components, scenario 3 (patching in the same frame)
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`creating two async components, scenario 3 (patching in the same frame) 3`] = `
|
||||
exports[`creating two async components, scenario 3 (patching in the same frame) 5`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1308,7 +1308,7 @@ exports[`delayed render does not go through when t-component value changed 2`] =
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`delayed render does not go through when t-component value changed 3`] = `
|
||||
exports[`delayed render does not go through when t-component value changed 4`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1421,7 +1421,7 @@ exports[`delayed rendering, destruction, stuff happens 2`] = `
|
||||
const comp1 = app.createComponent(\`C\`, true, false, false, [\\"value\\"]);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\`B\`);
|
||||
if (ctx['state'].hasChild) {
|
||||
b3 = comp1({value: ctx['state'].someValue+ctx['props'].value}, key + \`__1\`, node, this, null);
|
||||
@@ -1514,7 +1514,7 @@ exports[`delayed rendering, reusing fiber then component is destroyed and stuff
|
||||
const comp1 = app.createComponent(\`B\`, true, false, false, [\\"value\\"]);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\`A\`);
|
||||
if (ctx['state'].value<15) {
|
||||
b3 = comp1({value: ctx['state'].value}, key + \`__1\`, node, this, null);
|
||||
@@ -1572,7 +1572,7 @@ exports[`delayed rendering, then component is destroyed and stuff 2`] = `
|
||||
const comp1 = app.createComponent(\`C\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(ctx['props'].value);
|
||||
if (ctx['props'].value<10) {
|
||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
||||
@@ -1605,7 +1605,7 @@ exports[`destroyed component causes other soon to be destroyed component to rere
|
||||
const comp2 = app.createComponent(\`C\`, true, false, false, [\\"value\\"]);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\` A \`);
|
||||
if (ctx['state'].flag) {
|
||||
const b4 = comp1({value: ctx['state'].valueB}, key + \`__1\`, node, this, null);
|
||||
@@ -1617,7 +1617,7 @@ exports[`destroyed component causes other soon to be destroyed component to rere
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`destroyed component causes other soon to be destroyed component to rerender, weird stuff happens 2`] = `
|
||||
exports[`destroyed component causes other soon to be destroyed component to rerender, weird stuff happens 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1628,7 +1628,7 @@ exports[`destroyed component causes other soon to be destroyed component to rere
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`destroyed component causes other soon to be destroyed component to rerender, weird stuff happens 3`] = `
|
||||
exports[`destroyed component causes other soon to be destroyed component to rerender, weird stuff happens 4`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1646,7 +1646,7 @@ exports[`destroying/recreating a subcomponent, other scenario 1`] = `
|
||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\`parent\`);
|
||||
if (ctx['state'].hasChild) {
|
||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
||||
@@ -1656,7 +1656,7 @@ exports[`destroying/recreating a subcomponent, other scenario 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`destroying/recreating a subcomponent, other scenario 2`] = `
|
||||
exports[`destroying/recreating a subcomponent, other scenario 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1685,7 +1685,7 @@ exports[`destroying/recreating a subwidget with different props (if start is not
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`destroying/recreating a subwidget with different props (if start is not over) 2`] = `
|
||||
exports[`destroying/recreating a subwidget with different props (if start is not over) 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1787,7 +1787,7 @@ exports[`rendering component again in next microtick 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`rendering component again in next microtick 2`] = `
|
||||
exports[`rendering component again in next microtick 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -1846,7 +1846,7 @@ exports[`renderings, destruction, patch, stuff, ... yet another variation 2`] =
|
||||
const comp1 = app.createComponent(\`C\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(\`B\`);
|
||||
if (ctx['props'].value===33) {
|
||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
||||
|
||||
@@ -103,7 +103,7 @@ exports[`basics simple catchError 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['error']) {
|
||||
b2 = text(\`Error\`);
|
||||
} else {
|
||||
@@ -201,7 +201,7 @@ exports[`can catch errors an error in onWillDestroy 1`] = `
|
||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(ctx['state'].value);
|
||||
if (ctx['state'].hasChild) {
|
||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
||||
@@ -231,7 +231,7 @@ exports[`can catch errors an error in onWillDestroy, variation 1`] = `
|
||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
b2 = text(ctx['state'].value);
|
||||
if (ctx['state'].hasChild) {
|
||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
||||
@@ -241,7 +241,7 @@ exports[`can catch errors an error in onWillDestroy, variation 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`can catch errors an error in onWillDestroy, variation 2`] = `
|
||||
exports[`can catch errors an error in onWillDestroy, variation 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -295,7 +295,7 @@ exports[`can catch errors can catch an error in a component render function 2`]
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -350,7 +350,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -394,7 +394,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -474,7 +474,7 @@ exports[`can catch errors can catch an error in the initial call of a component
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -532,7 +532,7 @@ exports[`can catch errors can catch an error in the initial call of a component
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -593,7 +593,7 @@ exports[`can catch errors can catch an error in the mounted call (in child of ch
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -626,7 +626,7 @@ exports[`can catch errors can catch an error in the mounted call (in root compon
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -680,7 +680,7 @@ exports[`can catch errors can catch an error in the mounted call 2`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -735,7 +735,7 @@ exports[`can catch errors can catch an error in the willPatch call 2`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -790,7 +790,7 @@ exports[`can catch errors can catch an error in the willStart call 2`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -847,7 +847,7 @@ exports[`can catch errors can catch an error origination from a child's willStar
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -893,7 +893,7 @@ exports[`can catch errors catchError in catchError 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['error']) {
|
||||
b2 = text(\`Error\`);
|
||||
} else {
|
||||
@@ -1123,7 +1123,7 @@ exports[`can catch errors error in mounted on a component with a sibling (proper
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].error) {
|
||||
b2 = text(\`Error handled\`);
|
||||
} else {
|
||||
@@ -1167,7 +1167,7 @@ exports[`can catch errors onError in class inheritance is called if rethrown 2`]
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (!ctx['state'].error) {
|
||||
b2 = text(ctx['this'].will.crash);
|
||||
} else {
|
||||
@@ -1198,7 +1198,7 @@ exports[`can catch errors onError in class inheritance is not called if no rethr
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (!ctx['state'].error) {
|
||||
b2 = text(ctx['this'].will.crash);
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,7 @@ exports[`basics can select a sub widget 1`] = `
|
||||
const comp2 = app.createComponent(\`OtherChild\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['env'].options.flag) {
|
||||
b2 = comp1({}, key + \`__1\`, node, this, null);
|
||||
}
|
||||
@@ -80,7 +80,7 @@ exports[`basics can select a sub widget, part 2 1`] = `
|
||||
const comp2 = app.createComponent(\`OtherChild\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].flag) {
|
||||
b2 = comp1({}, key + \`__1\`, node, this, null);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ exports[`lifecycle hooks component semantics 3`] = `
|
||||
let block1 = createBlock(\`<div>C<block-child-0/><block-child-1/><block-child-2/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4;
|
||||
let b2, b3, b4;
|
||||
b2 = comp1({}, key + \`__1\`, node, this, null);
|
||||
if (ctx['state'].flag) {
|
||||
b3 = comp2({}, key + \`__2\`, node, this, null);
|
||||
@@ -92,7 +92,7 @@ exports[`lifecycle hooks component semantics 5`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks component semantics 6`] = `
|
||||
exports[`lifecycle hooks component semantics 7`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -174,7 +174,7 @@ exports[`lifecycle hooks destroy new children before being mountged 1`] = `
|
||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4;
|
||||
let b2, b3, b4;
|
||||
b2 = text(\`before\`);
|
||||
if (ctx['state'].flag) {
|
||||
b3 = comp1({}, key + \`__1\`, node, this, null);
|
||||
@@ -185,7 +185,7 @@ exports[`lifecycle hooks destroy new children before being mountged 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks destroy new children before being mountged 2`] = `
|
||||
exports[`lifecycle hooks destroy new children before being mountged 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -291,7 +291,7 @@ exports[`lifecycle hooks lifecycle semantics, part 2 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks lifecycle semantics, part 2 2`] = `
|
||||
exports[`lifecycle hooks lifecycle semantics, part 2 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -303,7 +303,7 @@ exports[`lifecycle hooks lifecycle semantics, part 2 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks lifecycle semantics, part 2 3`] = `
|
||||
exports[`lifecycle hooks lifecycle semantics, part 2 4`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -348,7 +348,7 @@ exports[`lifecycle hooks lifecycle semantics, part 4 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks lifecycle semantics, part 4 2`] = `
|
||||
exports[`lifecycle hooks lifecycle semantics, part 4 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -360,7 +360,7 @@ exports[`lifecycle hooks lifecycle semantics, part 4 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks lifecycle semantics, part 4 3`] = `
|
||||
exports[`lifecycle hooks lifecycle semantics, part 4 4`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
@@ -826,7 +826,7 @@ exports[`lifecycle hooks willStart, mounted on subwidget rendered after main is
|
||||
let block3 = createBlock(\`<div/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].ok) {
|
||||
b2 = comp1({}, key + \`__1\`, node, this, null);
|
||||
} else {
|
||||
|
||||
@@ -39,7 +39,7 @@ exports[`default props can set default boolean values 2`] = `
|
||||
let block1 = createBlock(\`<span><block-child-0/><block-child-1/></span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['props'].p) {
|
||||
b2 = text(\`hey\`);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ exports[`refs can use 2 refs with same name in a t-if/t-else situation 1`] = `
|
||||
let block3 = createBlock(\`<span block-ref=\\"0\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].value) {
|
||||
let ref1 = (el) => this.__owl__.setRef((\`coucou\`), el);
|
||||
b2 = block2([ref1]);
|
||||
|
||||
@@ -509,7 +509,7 @@ exports[`slots default slot with slot scope: shorthand syntax 1`] = `
|
||||
const comp1 = app.createComponent(\`Child\`, true, true, false, []);
|
||||
|
||||
function slot1(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['slotScope'].bool) {
|
||||
b2 = text(\`some text\`);
|
||||
} else {
|
||||
@@ -633,7 +633,7 @@ exports[`slots dynamic slot in multiple locations 2`] = `
|
||||
let block2 = createBlock(\`<p><block-child-0/></p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b4;
|
||||
let b2, b4;
|
||||
if (ctx['props'].location===1) {
|
||||
const slot1 = ('coffee');
|
||||
const b3 = toggler(slot1, callSlot(ctx, node, key + \`__1\`, slot1, true, {}));
|
||||
@@ -1462,7 +1462,7 @@ exports[`slots simple default slot with params 1`] = `
|
||||
const comp1 = app.createComponent(\`Child\`, true, true, false, []);
|
||||
|
||||
function slot1(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['slotScope'].bool) {
|
||||
b2 = text(\`some text\`);
|
||||
} else {
|
||||
@@ -1558,7 +1558,7 @@ exports[`slots simple dynamic slot with slot scope 1`] = `
|
||||
const comp1 = app.createComponent(\`Child\`, true, true, false, []);
|
||||
|
||||
function slot1(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['slotScope'].bool) {
|
||||
b2 = text(\`some text\`);
|
||||
} else {
|
||||
@@ -1665,7 +1665,7 @@ exports[`slots simple slot with slot scope 1`] = `
|
||||
const comp1 = app.createComponent(\`Child\`, true, true, false, []);
|
||||
|
||||
function slot1(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['slotScope'].bool) {
|
||||
b2 = text(\`some text\`);
|
||||
} else {
|
||||
@@ -2082,7 +2082,7 @@ exports[`slots slot in multiple locations 2`] = `
|
||||
let block2 = createBlock(\`<p><block-child-0/></p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b4;
|
||||
let b2, b4;
|
||||
if (ctx['props'].location===1) {
|
||||
const b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||
b2 = block2([], [b3]);
|
||||
@@ -2887,7 +2887,7 @@ exports[`slots t-slot in recursive templates 1`] = `
|
||||
ctx[\`item_index\`] = i1;
|
||||
ctx[\`item_value\`] = v_block3[i1];
|
||||
const key1 = ctx['item'].name;
|
||||
let b5,b6;
|
||||
let b5, b6;
|
||||
if (!ctx['item'].children.length) {
|
||||
b5 = text(ctx['item'].name);
|
||||
} else {
|
||||
|
||||
@@ -166,7 +166,7 @@ exports[`style and class handling class on sub component, which is switched to a
|
||||
const comp2 = app.createComponent(\`ChildB\`, true, false, false, [\\"class\\"]);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['props'].child==='a') {
|
||||
b2 = comp1({class: ctx['props'].class}, key + \`__1\`, node, this, null);
|
||||
} else {
|
||||
|
||||
@@ -10,8 +10,8 @@ exports[`t-call dynamic t-call 1`] = `
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
ctx[isBoundary] = 1;
|
||||
const b1 = text(\` owl \`);
|
||||
ctx[zero] = b1;
|
||||
const b2 = text(\` owl \`);
|
||||
ctx[zero] = b2;
|
||||
const template1 = (ctx['current'].template);
|
||||
return call(this, template1, ctx, node, key + \`__1\`);
|
||||
}
|
||||
@@ -364,7 +364,7 @@ exports[`t-call sub components in two t-calls 1`] = `
|
||||
let block3 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].val===1) {
|
||||
b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
||||
} else {
|
||||
|
||||
@@ -180,7 +180,7 @@ exports[`t-component switching dynamic component 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-component switching dynamic component 3`] = `
|
||||
exports[`t-component switching dynamic component 4`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
@@ -376,7 +376,7 @@ exports[`t-model directive on an input, type=checkbox 1`] = `
|
||||
let block1 = createBlock(\`<div><input type=\\"checkbox\\" block-property-0=\\"checked\\" block-handler-1=\\"input\\"/><span><block-child-0/><block-child-1/></span></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
const bExpr1 = ctx['state'];
|
||||
const expr1 = 'flag';
|
||||
let prop1 = bExpr1[expr1];
|
||||
@@ -640,7 +640,7 @@ exports[`t-model directive two inputs in a div alternating with a t-if 1`] = `
|
||||
let block3 = createBlock(\`<input class=\\"b\\" block-property-0=\\"value\\" block-handler-1=\\"input\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['state'].flag) {
|
||||
const bExpr1 = ctx['state'];
|
||||
const expr1 = 'text1';
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
nextAppError,
|
||||
nextTick,
|
||||
snapshotEverything,
|
||||
steps,
|
||||
useLogLifecycle,
|
||||
} from "../helpers";
|
||||
import { markup } from "../../src/runtime/utils";
|
||||
@@ -868,19 +869,26 @@ describe("basics", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(Object.keys(parent.__owl__.children).length).toStrictEqual(1);
|
||||
expect([
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.ifVar = false;
|
||||
parent.render();
|
||||
await nextTick();
|
||||
expect(Object.keys(parent.__owl__.children).length).toStrictEqual(0);
|
||||
expect(["Child:willUnmount", "Child:willDestroy"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("component children doesn't leak (t-key case)", async () => {
|
||||
@@ -899,27 +907,31 @@ describe("basics", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(Object.keys(parent.__owl__.children).length).toStrictEqual(1);
|
||||
expect([
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.keyVar = 2;
|
||||
parent.render();
|
||||
await nextTick();
|
||||
expect(Object.keys(parent.__owl__.children).length).toStrictEqual(1);
|
||||
expect([
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Child:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Child:mounted",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("GrandChild display is controlled by its GrandParent", async () => {
|
||||
@@ -943,20 +955,27 @@ describe("basics", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div></div>");
|
||||
expect([
|
||||
"GrandChild:setup",
|
||||
"GrandChild:willStart",
|
||||
"GrandChild:willRender",
|
||||
"GrandChild:rendered",
|
||||
"GrandChild:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"GrandChild:setup",
|
||||
"GrandChild:willStart",
|
||||
"GrandChild:willRender",
|
||||
"GrandChild:rendered",
|
||||
"GrandChild:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.displayGrandChild = false;
|
||||
parent.render();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
expect(["GrandChild:willUnmount", "GrandChild:willDestroy"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"GrandChild:willUnmount",
|
||||
"GrandChild:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1024,7 +1043,7 @@ describe("support svg components", () => {
|
||||
await mount(Svg, fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<svg><g><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"></circle></g></svg>'
|
||||
'<svg xmlns="http://www.w3.org/2000/svg"><g xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"></circle></g></svg>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
snapshotEverything,
|
||||
useLogLifecycle,
|
||||
nextAppError,
|
||||
steps,
|
||||
} from "../helpers";
|
||||
import { OwlError } from "../../src/common/owl_error";
|
||||
|
||||
@@ -948,26 +949,28 @@ describe("can catch errors", () => {
|
||||
}
|
||||
await mount(Root, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
expect([
|
||||
"Root:setup",
|
||||
"Root:willStart",
|
||||
"Root:willRender",
|
||||
"ErrorBoundary:setup",
|
||||
"ErrorBoundary:willStart",
|
||||
"Root:rendered",
|
||||
"ErrorBoundary:willRender",
|
||||
"ErrorComponent:setup",
|
||||
"ErrorComponent:willStart",
|
||||
"ErrorBoundary:rendered",
|
||||
"ErrorComponent:willRender",
|
||||
"ErrorComponent:rendered",
|
||||
"ErrorComponent:mounted",
|
||||
"boom",
|
||||
"ErrorBoundary:willRender",
|
||||
"ErrorBoundary:rendered",
|
||||
"ErrorBoundary:mounted",
|
||||
"Root:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Root:setup",
|
||||
"Root:willStart",
|
||||
"Root:willRender",
|
||||
"ErrorBoundary:setup",
|
||||
"ErrorBoundary:willStart",
|
||||
"Root:rendered",
|
||||
"ErrorBoundary:willRender",
|
||||
"ErrorComponent:setup",
|
||||
"ErrorComponent:willStart",
|
||||
"ErrorBoundary:rendered",
|
||||
"ErrorComponent:willRender",
|
||||
"ErrorComponent:rendered",
|
||||
"ErrorComponent:mounted",
|
||||
"boom",
|
||||
"ErrorBoundary:willRender",
|
||||
"ErrorBoundary:rendered",
|
||||
"ErrorBoundary:mounted",
|
||||
"Root:mounted",
|
||||
]
|
||||
`);
|
||||
expect(mockConsoleError).toBeCalledTimes(0);
|
||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||
});
|
||||
@@ -998,21 +1001,23 @@ describe("can catch errors", () => {
|
||||
}
|
||||
await mount(Root, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>Error handled</div>");
|
||||
expect([
|
||||
"Root:setup",
|
||||
"Root:willStart",
|
||||
"Root:willRender",
|
||||
"ErrorComponent:setup",
|
||||
"ErrorComponent:willStart",
|
||||
"Root:rendered",
|
||||
"ErrorComponent:willRender",
|
||||
"ErrorComponent:rendered",
|
||||
"ErrorComponent:mounted",
|
||||
"boom",
|
||||
"Root:willRender",
|
||||
"Root:rendered",
|
||||
"Root:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Root:setup",
|
||||
"Root:willStart",
|
||||
"Root:willRender",
|
||||
"ErrorComponent:setup",
|
||||
"ErrorComponent:willStart",
|
||||
"Root:rendered",
|
||||
"ErrorComponent:willRender",
|
||||
"ErrorComponent:rendered",
|
||||
"ErrorComponent:mounted",
|
||||
"boom",
|
||||
"Root:willRender",
|
||||
"Root:rendered",
|
||||
"Root:mounted",
|
||||
]
|
||||
`);
|
||||
expect(mockConsoleError).toBeCalledTimes(0);
|
||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||
});
|
||||
@@ -1059,31 +1064,33 @@ describe("can catch errors", () => {
|
||||
}
|
||||
await mount(A, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
expect([
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"Boom:setup",
|
||||
"Boom:willStart",
|
||||
"C:rendered",
|
||||
"Boom:willRender",
|
||||
"Boom:rendered",
|
||||
"Boom:mounted",
|
||||
"boom",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"Boom:setup",
|
||||
"Boom:willStart",
|
||||
"C:rendered",
|
||||
"Boom:willRender",
|
||||
"Boom:rendered",
|
||||
"Boom:mounted",
|
||||
"boom",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]
|
||||
`);
|
||||
expect(mockConsoleError).toBeCalledTimes(0);
|
||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||
});
|
||||
@@ -1130,31 +1137,33 @@ describe("can catch errors", () => {
|
||||
}
|
||||
await mount(Root, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>OK<div>Error handled</div></div>");
|
||||
expect([
|
||||
"Root:setup",
|
||||
"Root:willStart",
|
||||
"Root:willRender",
|
||||
"OK:setup",
|
||||
"OK:willStart",
|
||||
"ErrorBoundary:setup",
|
||||
"ErrorBoundary:willStart",
|
||||
"Root:rendered",
|
||||
"OK:willRender",
|
||||
"OK:rendered",
|
||||
"ErrorBoundary:willRender",
|
||||
"ErrorComponent:setup",
|
||||
"ErrorComponent:willStart",
|
||||
"ErrorBoundary:rendered",
|
||||
"ErrorComponent:willRender",
|
||||
"ErrorComponent:rendered",
|
||||
"ErrorComponent:mounted",
|
||||
"boom",
|
||||
"ErrorBoundary:willRender",
|
||||
"ErrorBoundary:rendered",
|
||||
"ErrorBoundary:mounted",
|
||||
"OK:mounted",
|
||||
"Root:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Root:setup",
|
||||
"Root:willStart",
|
||||
"Root:willRender",
|
||||
"OK:setup",
|
||||
"OK:willStart",
|
||||
"ErrorBoundary:setup",
|
||||
"ErrorBoundary:willStart",
|
||||
"Root:rendered",
|
||||
"OK:willRender",
|
||||
"OK:rendered",
|
||||
"ErrorBoundary:willRender",
|
||||
"ErrorComponent:setup",
|
||||
"ErrorComponent:willStart",
|
||||
"ErrorBoundary:rendered",
|
||||
"ErrorComponent:willRender",
|
||||
"ErrorComponent:rendered",
|
||||
"ErrorComponent:mounted",
|
||||
"boom",
|
||||
"ErrorBoundary:willRender",
|
||||
"ErrorBoundary:rendered",
|
||||
"ErrorBoundary:mounted",
|
||||
"OK:mounted",
|
||||
"Root:mounted",
|
||||
]
|
||||
`);
|
||||
expect(mockConsoleError).toBeCalledTimes(0);
|
||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||
});
|
||||
@@ -1481,35 +1490,39 @@ describe("can catch errors", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("1<div>abc</div>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
parent.state.hasChild = false;
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
expect(fixture.innerHTML).toBe("2");
|
||||
});
|
||||
|
||||
@@ -1542,13 +1555,15 @@ describe("can catch errors", () => {
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("1");
|
||||
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.hasChild = true;
|
||||
await nextMicroTick();
|
||||
@@ -1556,26 +1571,35 @@ describe("can catch errors", () => {
|
||||
await nextMicroTick();
|
||||
await nextMicroTick();
|
||||
await nextMicroTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
]
|
||||
`);
|
||||
parent.state.hasChild = false;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Child:willDestroy",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Child:willDestroy",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
]
|
||||
`);
|
||||
expect(fixture.innerHTML).toBe("1");
|
||||
await nextTick();
|
||||
expect(["Parent:willPatch", "Parent:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
expect(fixture.innerHTML).toBe("2");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -641,6 +641,56 @@ describe("hooks", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("effect types are inferred from dependencies", async () => {
|
||||
// @ts-ignore (declared but never used)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class MyComponent extends Component {
|
||||
static template = xml`<div/>`;
|
||||
|
||||
setup() {
|
||||
useEffect(
|
||||
(a, b) => {
|
||||
expectType<number>(a);
|
||||
expectType<string>(b);
|
||||
},
|
||||
() => [3, "hello"]
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("effect type allows an effect with partial dependencies parameters", async () => {
|
||||
// @ts-ignore (declared but never used)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class MyComponent extends Component {
|
||||
static template = xml`<div/>`;
|
||||
|
||||
setup() {
|
||||
useEffect(
|
||||
(a) => {
|
||||
expectType<number>(a);
|
||||
},
|
||||
() => [3, "hello"]
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("effect type allows an effect with no dependency parameter", async () => {
|
||||
// @ts-ignore (declared but never used)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class MyComponent extends Component {
|
||||
static template = xml`<div/>`;
|
||||
|
||||
setup() {
|
||||
useEffect(
|
||||
() => {},
|
||||
() => [3, "hello"]
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("properly behaves when the effect function throws", async () => {
|
||||
let originalconsoleError = console.error;
|
||||
let originalconsoleWarn = console.warn;
|
||||
@@ -673,3 +723,5 @@ describe("hooks", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function expectType<T>(t: T) {}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
nextMicroTick,
|
||||
nextTick,
|
||||
snapshotEverything,
|
||||
steps,
|
||||
useLogLifecycle,
|
||||
} from "../helpers";
|
||||
|
||||
@@ -455,45 +456,51 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>0</span></div>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.increment();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.toggleSubWidget();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("hooks are called in proper order in widget creation/destruction", async () => {
|
||||
@@ -514,26 +521,30 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
const app = new App(Parent);
|
||||
await app.mount(fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
app.destroy();
|
||||
expect([
|
||||
"Parent:willUnmount",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willUnmount",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("willUpdateProps hook is called", async () => {
|
||||
@@ -632,26 +643,30 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
const app = new App(Parent);
|
||||
await app.mount(fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
app.destroy();
|
||||
expect([
|
||||
"Parent:willUnmount",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willUnmount",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("lifecycle semantics, part 2", async () => {
|
||||
@@ -680,42 +695,48 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
const app = new App(Parent);
|
||||
const parent = await app.mount(fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.hasChild = true;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"GrandChild:setup",
|
||||
"GrandChild:willStart",
|
||||
"Child:rendered",
|
||||
"GrandChild:willRender",
|
||||
"GrandChild:rendered",
|
||||
"Parent:willPatch",
|
||||
"GrandChild:mounted",
|
||||
"Child:mounted",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"GrandChild:setup",
|
||||
"GrandChild:willStart",
|
||||
"Child:rendered",
|
||||
"GrandChild:willRender",
|
||||
"GrandChild:rendered",
|
||||
"Parent:willPatch",
|
||||
"GrandChild:mounted",
|
||||
"Child:mounted",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
|
||||
app.destroy();
|
||||
expect([
|
||||
"Parent:willUnmount",
|
||||
"Child:willUnmount",
|
||||
"GrandChild:willUnmount",
|
||||
"GrandChild:willDestroy",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willUnmount",
|
||||
"Child:willUnmount",
|
||||
"GrandChild:willUnmount",
|
||||
"GrandChild:willDestroy",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("lifecycle semantics, part 3", async () => {
|
||||
@@ -744,19 +765,26 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
const app = new App(Parent);
|
||||
const parent = await app.mount(fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.hasChild = true;
|
||||
// immediately destroy everything
|
||||
app.destroy();
|
||||
await nextTick();
|
||||
expect(["Parent:willUnmount", "Parent:willDestroy"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willUnmount",
|
||||
"Parent:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("lifecycle semantics, part 4", async () => {
|
||||
@@ -789,34 +817,40 @@ describe("lifecycle hooks", () => {
|
||||
const app = new App(Parent);
|
||||
const parent = await app.mount(fixture);
|
||||
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.hasChild = true;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"GrandChild:setup",
|
||||
"GrandChild:willStart",
|
||||
"Child:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"GrandChild:setup",
|
||||
"GrandChild:willStart",
|
||||
"Child:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
app.destroy();
|
||||
expect([
|
||||
"Parent:willUnmount",
|
||||
"GrandChild:willDestroy",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willUnmount",
|
||||
"GrandChild:willDestroy",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("lifecycle semantics, part 5", async () => {
|
||||
@@ -837,29 +871,33 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.hasChild = false;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("lifecycle semantics, part 6", async () => {
|
||||
@@ -880,32 +918,36 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.value = 2;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("onWillRender", async () => {
|
||||
@@ -937,43 +979,53 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<button>1</button>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.value++; // to block child render
|
||||
await nextTick();
|
||||
expect(["Parent:willRender", "Child:willUpdateProps", "Parent:rendered"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
fixture.querySelector("button")!.click();
|
||||
await nextTick();
|
||||
expect([]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`Array []`);
|
||||
|
||||
fixture.querySelector("button")!.click();
|
||||
await nextTick();
|
||||
expect([]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`Array []`);
|
||||
expect(fixture.innerHTML).toBe("<button>1</button>");
|
||||
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<button>3</button>");
|
||||
expect([
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
// TODO: rename (remove? seems covered by lifecycle semantics)
|
||||
@@ -1054,50 +1106,54 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
await mount(A, fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div>A<div>B</div><div>C<div>D</div><div>E</div></div></div>`);
|
||||
expect([
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"D:setup",
|
||||
"D:willStart",
|
||||
"E:setup",
|
||||
"E:willStart",
|
||||
"C:rendered",
|
||||
"D:willRender",
|
||||
"D:rendered",
|
||||
"E:willRender",
|
||||
"E:rendered",
|
||||
"E:mounted",
|
||||
"D:mounted",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"D:setup",
|
||||
"D:willStart",
|
||||
"E:setup",
|
||||
"E:willStart",
|
||||
"C:rendered",
|
||||
"D:willRender",
|
||||
"D:rendered",
|
||||
"E:willRender",
|
||||
"E:rendered",
|
||||
"E:mounted",
|
||||
"D:mounted",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
// update
|
||||
c!.state.flag = false;
|
||||
await nextTick();
|
||||
expect([
|
||||
"C:willRender",
|
||||
"F:setup",
|
||||
"F:willStart",
|
||||
"C:rendered",
|
||||
"F:willRender",
|
||||
"F:rendered",
|
||||
"C:willPatch",
|
||||
"E:willUnmount",
|
||||
"E:willDestroy",
|
||||
"F:mounted",
|
||||
"C:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"C:willRender",
|
||||
"F:setup",
|
||||
"F:willStart",
|
||||
"C:rendered",
|
||||
"F:willRender",
|
||||
"F:rendered",
|
||||
"C:willPatch",
|
||||
"E:willUnmount",
|
||||
"E:willDestroy",
|
||||
"F:mounted",
|
||||
"C:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("mounted hook is called on every mount, not just the first one", async () => {
|
||||
@@ -1118,43 +1174,49 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.hasChild = false;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.hasChild = true;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:mounted",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:mounted",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("render in mounted", async () => {
|
||||
@@ -1172,19 +1234,26 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<span></span>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<span>Patched</span>");
|
||||
expect(["Parent:willPatch", "Parent:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("render in patched", async () => {
|
||||
@@ -1205,29 +1274,38 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<span></span>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.render();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<span></span>");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<span>Patched</span>");
|
||||
expect(["Parent:willPatch", "Parent:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("render in willPatch", async () => {
|
||||
@@ -1248,29 +1326,38 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<span></span>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.render();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<span></span>");
|
||||
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
await nextTick();
|
||||
expect(["Parent:willPatch", "Parent:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
expect(fixture.innerHTML).toBe("<span>Patched</span>");
|
||||
});
|
||||
|
||||
@@ -1313,19 +1400,21 @@ describe("lifecycle hooks", () => {
|
||||
await nextTick();
|
||||
app.destroy();
|
||||
|
||||
expect([
|
||||
"onWillStart",
|
||||
"onWillRender",
|
||||
"onRendered",
|
||||
"onMounted",
|
||||
"onWillUpdateProps",
|
||||
"onWillRender",
|
||||
"onRendered",
|
||||
"onWillPatch",
|
||||
"onPatched",
|
||||
"onWillUnmount",
|
||||
"onWillDestroy",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"onWillStart",
|
||||
"onWillRender",
|
||||
"onRendered",
|
||||
"onMounted",
|
||||
"onWillUpdateProps",
|
||||
"onWillRender",
|
||||
"onRendered",
|
||||
"onWillPatch",
|
||||
"onPatched",
|
||||
"onWillUnmount",
|
||||
"onWillDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("destroy new children before being mountged", async () => {
|
||||
@@ -1357,26 +1446,30 @@ describe("lifecycle hooks", () => {
|
||||
const app = new App(Parent);
|
||||
const parent = await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("beforeafter");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.flag = true;
|
||||
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Parent:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Parent:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:willDestroy",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, mount, onWillUpdateProps, useState, xml } from "../../src";
|
||||
import { makeTestFixture, nextTick, snapshotEverything, useLogLifecycle } from "../helpers";
|
||||
import { makeTestFixture, nextTick, snapshotEverything, steps, useLogLifecycle } from "../helpers";
|
||||
|
||||
let fixture: HTMLElement;
|
||||
|
||||
@@ -272,26 +272,30 @@ test("bound functions are considered 'alike'", async () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("1child");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
parent.state.val = 3;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
expect(fixture.innerHTML).toBe("3child");
|
||||
});
|
||||
|
||||
@@ -330,29 +334,33 @@ test(".alike suffix in a simple case", async () => {
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
expect(fixture.innerHTML).toBe("01");
|
||||
parent.state.counter++;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("11");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test(".alike suffix in a list", async () => {
|
||||
@@ -388,36 +396,40 @@ test(".alike suffix in a list", async () => {
|
||||
}
|
||||
|
||||
await mount(Parent, fixture);
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Todo:setup",
|
||||
"Todo:willStart",
|
||||
"Todo:setup",
|
||||
"Todo:willStart",
|
||||
"Parent:rendered",
|
||||
"Todo:willRender",
|
||||
"Todo:rendered",
|
||||
"Todo:willRender",
|
||||
"Todo:rendered",
|
||||
"Todo:mounted",
|
||||
"Todo:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Todo:setup",
|
||||
"Todo:willStart",
|
||||
"Todo:setup",
|
||||
"Todo:willStart",
|
||||
"Parent:rendered",
|
||||
"Todo:willRender",
|
||||
"Todo:rendered",
|
||||
"Todo:willRender",
|
||||
"Todo:rendered",
|
||||
"Todo:mounted",
|
||||
"Todo:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<button>1</button><button>2V</button>");
|
||||
fixture.querySelector("button")?.click();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<button>1V</button><button>2V</button>");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Todo:willRender",
|
||||
"Todo:rendered",
|
||||
"Todo:willPatch",
|
||||
"Todo:patched",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Todo:willRender",
|
||||
"Todo:rendered",
|
||||
"Todo:willPatch",
|
||||
"Todo:patched",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
xml,
|
||||
toRaw,
|
||||
} from "../../src";
|
||||
import { makeTestFixture, nextTick, snapshotEverything, useLogLifecycle } from "../helpers";
|
||||
import { makeTestFixture, nextTick, snapshotEverything, steps, useLogLifecycle } from "../helpers";
|
||||
|
||||
let fixture: HTMLElement;
|
||||
|
||||
@@ -175,30 +175,34 @@ describe("reactivity in lifecycle", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("2");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.content = null;
|
||||
parent.state.renderChild = false;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("Component is automatically subscribed to reactive object received as prop", async () => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
useLogLifecycle,
|
||||
makeDeferred,
|
||||
nextMicroTick,
|
||||
steps,
|
||||
} from "../helpers";
|
||||
|
||||
let fixture: HTMLElement;
|
||||
@@ -41,28 +42,32 @@ describe("rendering semantics", () => {
|
||||
const parent = await mount(Parent, fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("Achild");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.value = "B";
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("Bchild");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("can force a render to update sub tree", async () => {
|
||||
@@ -167,18 +172,20 @@ describe("rendering semantics", () => {
|
||||
const parent = await mount(Parent, fixture, { env });
|
||||
|
||||
expect(fixture.innerHTML).toBe("parentAchild3");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
value = 4;
|
||||
parent.render(true);
|
||||
@@ -187,30 +194,34 @@ describe("rendering semantics", () => {
|
||||
await nextMicroTick();
|
||||
await nextMicroTick();
|
||||
await nextMicroTick();
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.value = "B";
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(fixture.innerHTML).toBe("parentBchild4");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Child:willUpdateProps",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("props are reactive", async () => {
|
||||
@@ -235,23 +246,32 @@ describe("rendering semantics", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("1");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.b = 3;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("3");
|
||||
expect(["Child:willRender", "Child:rendered", "Child:willPatch", "Child:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("props are reactive (nested prop)", async () => {
|
||||
@@ -278,37 +298,48 @@ describe("rendering semantics", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("1");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.b.c = 3; // parent is now subscribed to 'b' key
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("3");
|
||||
expect(["Child:willRender", "Child:rendered", "Child:willPatch", "Child:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.b = { c: 444 }; // triggers a parent and a child render
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("444");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("works as expected for dynamic number of props", async () => {
|
||||
@@ -366,41 +397,45 @@ describe("rendering semantics", () => {
|
||||
|
||||
const parent = await mount(A, fixture);
|
||||
expect(fixture.innerHTML).toBe("11");
|
||||
expect([
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.obj.val = 3;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("33");
|
||||
expect([
|
||||
"A:willRender",
|
||||
"A:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"A:willPatch",
|
||||
"A:patched",
|
||||
"C:willPatch",
|
||||
"C:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:willRender",
|
||||
"A:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"A:willPatch",
|
||||
"A:patched",
|
||||
"C:willPatch",
|
||||
"C:patched",
|
||||
]
|
||||
`);
|
||||
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
expect([]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`Array []`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -431,51 +466,67 @@ test("force render in case of existing render", async () => {
|
||||
}
|
||||
const parent = await mount(A, fixture);
|
||||
expect(fixture.innerHTML).toBe("C1");
|
||||
expect([
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
// trigger a new rendering, blocked in B
|
||||
parent.state.val = 2;
|
||||
await nextTick();
|
||||
expect(["A:willRender", "B:willUpdateProps", "A:rendered"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:willRender",
|
||||
"B:willUpdateProps",
|
||||
"A:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
// initiate a new render with deep=true. it should cancel the current render
|
||||
// and also be blocked in B
|
||||
parent.render(true);
|
||||
await nextTick();
|
||||
expect(["A:willRender", "B:willUpdateProps", "A:rendered"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"A:willRender",
|
||||
"B:willUpdateProps",
|
||||
"A:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
// we check here that the render reaches C (so, that it was properly forced)
|
||||
expect([
|
||||
"B:willRender",
|
||||
"C:willUpdateProps",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"A:willPatch",
|
||||
"B:willPatch",
|
||||
"C:willPatch",
|
||||
"C:patched",
|
||||
"B:patched",
|
||||
"A:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"B:willRender",
|
||||
"C:willUpdateProps",
|
||||
"B:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"A:willPatch",
|
||||
"B:willPatch",
|
||||
"C:willPatch",
|
||||
"C:patched",
|
||||
"B:patched",
|
||||
"A:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("children, default props and renderings", async () => {
|
||||
@@ -503,26 +554,30 @@ test("children, default props and renderings", async () => {
|
||||
const parent = await mount(Parent, fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("Achild");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.value = "B";
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("Bchild");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, mount, useState, xml } from "../../src";
|
||||
import { makeTestFixture, nextTick, snapshotEverything, useLogLifecycle } from "../helpers";
|
||||
import { makeTestFixture, nextTick, snapshotEverything, steps, useLogLifecycle } from "../helpers";
|
||||
|
||||
let fixture: HTMLElement;
|
||||
|
||||
@@ -29,18 +29,20 @@ describe("t-component", () => {
|
||||
await mount(Parent, fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div>child</div>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("switching dynamic component", async () => {
|
||||
@@ -68,36 +70,40 @@ describe("t-component", () => {
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>child a</div>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"ChildA:setup",
|
||||
"ChildA:willStart",
|
||||
"Parent:rendered",
|
||||
"ChildA:willRender",
|
||||
"ChildA:rendered",
|
||||
"ChildA:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"ChildA:setup",
|
||||
"ChildA:willStart",
|
||||
"Parent:rendered",
|
||||
"ChildA:willRender",
|
||||
"ChildA:rendered",
|
||||
"ChildA:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.Child = ChildB;
|
||||
parent.render();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("child b");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"ChildB:setup",
|
||||
"ChildB:willStart",
|
||||
"Parent:rendered",
|
||||
"ChildB:willRender",
|
||||
"ChildB:rendered",
|
||||
"Parent:willPatch",
|
||||
"ChildA:willUnmount",
|
||||
"ChildA:willDestroy",
|
||||
"ChildB:mounted",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"ChildB:setup",
|
||||
"ChildB:willStart",
|
||||
"Parent:rendered",
|
||||
"ChildB:willRender",
|
||||
"ChildB:rendered",
|
||||
"Parent:willPatch",
|
||||
"ChildA:willUnmount",
|
||||
"ChildA:willDestroy",
|
||||
"ChildB:mounted",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("can switch between dynamic components without the need for a t-key", async () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
nextAppError,
|
||||
nextTick,
|
||||
snapshotEverything,
|
||||
steps,
|
||||
useLogLifecycle,
|
||||
} from "../helpers";
|
||||
|
||||
@@ -91,23 +92,25 @@ describe("list of components", () => {
|
||||
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();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"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",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("reconciliation alg works for t-foreach in t-foreach", async () => {
|
||||
|
||||
@@ -137,7 +137,7 @@ export function snapshotEverything() {
|
||||
};
|
||||
}
|
||||
|
||||
const steps: string[] = [];
|
||||
export const steps: string[] = [];
|
||||
|
||||
export function logStep(step: string) {
|
||||
steps.push(step);
|
||||
|
||||
@@ -345,7 +345,7 @@ exports[`Portal conditional use of Portal (with sub Component) 1`] = `
|
||||
}
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b4;
|
||||
let b2, b4;
|
||||
b2 = block2();
|
||||
if (ctx['state'].hasPortal) {
|
||||
b4 = comp2({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal);
|
||||
@@ -384,7 +384,7 @@ exports[`Portal conditional use of Portal 1`] = `
|
||||
}
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b4;
|
||||
let b2, b4;
|
||||
b2 = block2();
|
||||
if (ctx['state'].hasPortal) {
|
||||
b4 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
|
||||
@@ -699,7 +699,7 @@ exports[`Portal portal with dynamic body 1`] = `
|
||||
let block4 = createBlock(\`<div/>\`);
|
||||
|
||||
function slot1(ctx, node, key = \\"\\") {
|
||||
let b3,b4;
|
||||
let b3, b4;
|
||||
if (ctx['state'].val) {
|
||||
let txt1 = ctx['state'].val;
|
||||
b3 = block3([txt1]);
|
||||
@@ -848,7 +848,7 @@ exports[`Portal simple catchError with portal 1`] = `
|
||||
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
let b2, b3;
|
||||
if (ctx['error']) {
|
||||
b2 = text(\`Error\`);
|
||||
} else {
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
nextMicroTick,
|
||||
nextTick,
|
||||
snapshotEverything,
|
||||
steps,
|
||||
useLogLifecycle,
|
||||
} from "./helpers";
|
||||
|
||||
@@ -992,32 +993,33 @@ describe("Reactivity", () => {
|
||||
const obj2 = createReactive({ b: {} }, () => n2++);
|
||||
const obj3 = createReactive({ c: {} }, () => n3++);
|
||||
|
||||
// assign the same object should'nt notify reactivity
|
||||
obj2.b = obj2.b;
|
||||
obj2.b;
|
||||
obj3.c = obj3.c;
|
||||
obj3.c;
|
||||
expect(n1).toBe(0);
|
||||
expect(n2).toBe(1);
|
||||
expect(n3).toBe(1);
|
||||
expect(n2).toBe(0);
|
||||
expect(n3).toBe(0);
|
||||
|
||||
obj2.b = obj1;
|
||||
obj2.b;
|
||||
obj3.c = obj1;
|
||||
obj3.c;
|
||||
expect(n1).toBe(0);
|
||||
expect(n2).toBe(2);
|
||||
expect(n3).toBe(2);
|
||||
expect(n2).toBe(1);
|
||||
expect(n3).toBe(1);
|
||||
|
||||
obj1.a = obj1.a + 2;
|
||||
obj1.a;
|
||||
expect(n1).toBe(1);
|
||||
expect(n2).toBe(2);
|
||||
expect(n3).toBe(2);
|
||||
expect(n2).toBe(1);
|
||||
expect(n3).toBe(1);
|
||||
|
||||
obj2.b.a = obj2.b.a + 1;
|
||||
expect(n1).toBe(2);
|
||||
expect(n2).toBe(3);
|
||||
expect(n3).toBe(2);
|
||||
expect(n2).toBe(2);
|
||||
expect(n3).toBe(1);
|
||||
});
|
||||
|
||||
test("reactive inside other: reading the inner reactive from outer doesn't affect the inner's subscriptions", async () => {
|
||||
@@ -1292,6 +1294,12 @@ describe("Collections", () => {
|
||||
|
||||
state.add(3); // setting unobserved key doesn't notify
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
expect(state.has(3)).toBe(true); // subscribe to 3
|
||||
state.add(3); // adding observed key doesn't notify if key was already present
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
expect(state.has(4)).toBe(false); // subscribe to 4
|
||||
state.delete(4); // deleting observed key doesn't notify if key was already not present
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test("iterating on keys returns reactives", async () => {
|
||||
@@ -1485,6 +1493,12 @@ describe("Collections", () => {
|
||||
|
||||
state.set(3, 4); // setting unobserved key doesn't notify
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
expect(state.has(3)).toBe(true); // subscribe to 3
|
||||
state.set(3, 4); // setting the same value doesn't notify
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
expect(state.has(4)).toBe(false); // subscribe to 4
|
||||
state.delete(4); // deleting observed key doesn't notify if key was already not present
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test("checking for a key with 'get' subscribes the callback to changes to that key", () => {
|
||||
@@ -1510,6 +1524,12 @@ describe("Collections", () => {
|
||||
|
||||
state.set(3, 4); // setting unobserved key doesn't notify
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
expect(state.get(3)).toBe(4); // subscribe to 3
|
||||
state.set(3, 4); // setting the same value doesn't notify
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
expect(state.get(4)).toBe(undefined); // subscribe to 4
|
||||
state.delete(4); // deleting observed key doesn't notify if key was already not present
|
||||
expect(observer).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test("getting values returns a reactive", async () => {
|
||||
@@ -1832,37 +1852,41 @@ describe("Reactivity: useState", () => {
|
||||
}
|
||||
}
|
||||
await mount(Parent, fixture);
|
||||
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();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"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",
|
||||
]
|
||||
`);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span><span>123</span></div>");
|
||||
testContext.value = 321;
|
||||
await nextTick();
|
||||
expect([
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]
|
||||
`);
|
||||
expect(fixture.innerHTML).toBe("<div><span>321</span><span>321</span></div>");
|
||||
});
|
||||
|
||||
@@ -1886,38 +1910,49 @@ describe("Reactivity: useState", () => {
|
||||
}
|
||||
|
||||
await mount(Parent, fixture);
|
||||
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();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"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",
|
||||
]
|
||||
`);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span><span>123</span></div>");
|
||||
testContext.value = 321;
|
||||
await nextMicroTick();
|
||||
await nextMicroTick();
|
||||
expect([
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
]
|
||||
`);
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span><span>123</span></div>");
|
||||
|
||||
await nextTick();
|
||||
expect(["Child:willPatch", "Child:patched", "Child:willPatch", "Child:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]
|
||||
`);
|
||||
expect(fixture.innerHTML).toBe("<div><span>321</span><span>321</span></div>");
|
||||
});
|
||||
|
||||
@@ -1951,43 +1986,54 @@ describe("Reactivity: useState", () => {
|
||||
|
||||
await mount(GrandFather, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span><div><span>123</span></div></div>");
|
||||
expect([
|
||||
"GrandFather:setup",
|
||||
"GrandFather:willStart",
|
||||
"GrandFather:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"GrandFather:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
"Child:mounted",
|
||||
"GrandFather:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"GrandFather:setup",
|
||||
"GrandFather:willStart",
|
||||
"GrandFather:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"GrandFather:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
"Child:mounted",
|
||||
"GrandFather:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
testContext.value = 321;
|
||||
await nextMicroTick();
|
||||
await nextMicroTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span><div><span>123</span></div></div>");
|
||||
expect([
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
]
|
||||
`);
|
||||
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>321</span><div><span>321</span></div></div>");
|
||||
expect(["Child:willPatch", "Child:patched", "Child:willPatch", "Child:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("one components can subscribe twice to same context", async () => {
|
||||
@@ -2163,38 +2209,49 @@ describe("Reactivity: useState", () => {
|
||||
}
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span></div>");
|
||||
expect([
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:setup",
|
||||
"Parent:willStart",
|
||||
"Parent:willRender",
|
||||
"Child:setup",
|
||||
"Child:willStart",
|
||||
"Parent:rendered",
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:mounted",
|
||||
"Parent:mounted",
|
||||
]
|
||||
`);
|
||||
|
||||
testContext.a = 321;
|
||||
await nextTick();
|
||||
expect(["Child:willRender", "Child:rendered", "Child:willPatch", "Child:patched"]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Child:willRender",
|
||||
"Child:rendered",
|
||||
"Child:willPatch",
|
||||
"Child:patched",
|
||||
]
|
||||
`);
|
||||
|
||||
parent.state.flag = false;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div></div>");
|
||||
expect([
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Parent:willRender",
|
||||
"Parent:rendered",
|
||||
"Parent:willPatch",
|
||||
"Child:willUnmount",
|
||||
"Child:willDestroy",
|
||||
"Parent:patched",
|
||||
]
|
||||
`);
|
||||
|
||||
testContext.a = 456;
|
||||
await nextTick();
|
||||
expect([]).toBeLogged();
|
||||
expect(steps.splice(0)).toMatchInlineSnapshot(`Array []`);
|
||||
});
|
||||
|
||||
test("destroyed component before being mounted is inactive", async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Owl devtools",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.2",
|
||||
"manifest_version": 3,
|
||||
"description": "Chrome devtools extension for Odoo Owl framework",
|
||||
"icons": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Owl devtools",
|
||||
"version": "1.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Firefox devtools extension for Odoo Owl framework",
|
||||
"manifest_version": 2,
|
||||
"browser_specific_settings": {
|
||||
@@ -29,7 +29,7 @@
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
"devtools_page": "devtools_app/devtools.html",
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval' blob:; object-src 'self'",
|
||||
"content_security_policy": "script-src 'self'; object-src 'self'",
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { IS_FIREFOX, getActiveTabURL } from "./utils";
|
||||
import { IS_FIREFOX, getActiveTabURL, browserInstance } from "./utils";
|
||||
|
||||
let owlStatus = 0;
|
||||
|
||||
const browserInstance = IS_FIREFOX ? browser : chrome;
|
||||
|
||||
// Used to keep track of the tabs where the owl devtools have been opened
|
||||
const activePanels = new Map();
|
||||
|
||||
@@ -71,6 +69,9 @@ function checkOwlStatus(tabId) {
|
||||
browserInstance.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
|
||||
// Send back the owl status to the sender
|
||||
if (message.type === "getOwlStatus") {
|
||||
if (IS_FIREFOX) {
|
||||
return { result: owlStatus };
|
||||
}
|
||||
sendResponse({ result: owlStatus });
|
||||
return true;
|
||||
} else if (message.type === "owlStatus") {
|
||||
@@ -112,11 +113,10 @@ browserInstance.runtime.onMessage.addListener(async (message, sender, sendRespon
|
||||
}, 750);
|
||||
activePanels.set(message.id, { port: port, expirationTimeout: expirationTimeout });
|
||||
// This is solely for firefox which doesnt allow access to the chrome.tabs api inside devtools
|
||||
// We therefore only use the firefox syntax to send the response here
|
||||
} else if (message.type === "getActiveTabURL") {
|
||||
getActiveTabURL().then((tab) => {
|
||||
sendResponse({ result: tab });
|
||||
});
|
||||
return true;
|
||||
const tab = await getActiveTabURL();
|
||||
return { result: tab };
|
||||
} else {
|
||||
const destinationPanel = activePanels.get(sender.tab.id);
|
||||
if (destinationPanel) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import globalHook from "./page_scripts/owl_devtools_global_hook";
|
||||
import { IS_FIREFOX } from "./utils";
|
||||
import { IS_FIREFOX, browserInstance } from "./utils";
|
||||
|
||||
// Relays the owlDevtools__... type top window messages to the background script so that it can relay it to the devtools app
|
||||
window.addEventListener(
|
||||
@@ -7,7 +7,7 @@ window.addEventListener(
|
||||
function (event) {
|
||||
if (event.data.type && event.data.source === "owl-devtools") {
|
||||
try {
|
||||
chrome.runtime.sendMessage(
|
||||
browserInstance.runtime.sendMessage(
|
||||
event.data.data
|
||||
? { type: event.data.type, data: event.data.data, origin: event.data.origin }
|
||||
: { type: event.data.type, origin: event.data.origin }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { IS_FIREFOX } from "../utils";
|
||||
import { IS_FIREFOX, browserInstance } from "../utils";
|
||||
|
||||
let created = false;
|
||||
let browserInstance = IS_FIREFOX ? browser : chrome;
|
||||
|
||||
// Try to load the owl panel each 1000 ms in case it (re)appears on the page later on
|
||||
const checkInterval = setInterval(createPanelsIfOwl, 1000);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="devtools.ComponentSearchBar" owl="1">
|
||||
<div class="pointer-icon ms-1 px-2 py-1" t-on-click.stop='() => this.store.toggleSelector()'>
|
||||
<i title="Select an element in the page to inspect the corresponding component" class="fa fa-mouse-pointer" t-attf-style="color: {{store.componentSearch.activeSelector ? 'var(--active-icon)' : 'var(--text-color)'}};"></i>
|
||||
<div class="mouse-icon p-1" t-on-click.stop='() => this.store.toggleSelector()'>
|
||||
<i title="Select an element in the page to inspect the corresponding component" class="fa fa-fw fa-mouse-pointer" t-attf-style="color: {{store.componentSearch.activeSelector ? 'var(--active-icon)' : 'var(--text-color)'}};"></i>
|
||||
</div>
|
||||
<div class="icons-separator"/>
|
||||
<div class="d-flex align-items-center ms-2 flex-grow-1">
|
||||
|
||||
@@ -15,6 +15,7 @@ export class ComponentsTab extends Component {
|
||||
this.store = useStore();
|
||||
this.flushRendersTimeout = false;
|
||||
useExternalListener(document, "keydown", this.onKeyboardEvent);
|
||||
useExternalListener(window, "resize", this.onWindowResize);
|
||||
|
||||
onWillUnmount(() => {
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
@@ -53,9 +54,11 @@ export class ComponentsTab extends Component {
|
||||
|
||||
// Adjust the position of the split between the left and right right window of the components tab
|
||||
onMouseMove = (event) => {
|
||||
const minWidth = (147 / window.innerWidth) * 100;
|
||||
const maxWidth = 100 - (100 / window.innerWidth) * 100;
|
||||
this.store.splitPosition = Math.max(
|
||||
Math.min((event.clientX / window.innerWidth) * 100, 85),
|
||||
15
|
||||
Math.min((event.clientX / window.innerWidth) * 100, maxWidth),
|
||||
minWidth
|
||||
);
|
||||
};
|
||||
|
||||
@@ -64,4 +67,11 @@ export class ComponentsTab extends Component {
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
};
|
||||
|
||||
onWindowResize = () => {
|
||||
const minWidth = (147 / window.innerWidth) * 100;
|
||||
if (minWidth <= 100) {
|
||||
this.store.splitPosition = Math.max(this.store.splitPosition, minWidth);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="devtools.ObjectTreeElement" owl="1">
|
||||
<div class="m-0 p-0 text-nowrap w-100 object-line"
|
||||
t-att-class="props.class"
|
||||
t-on-click.stop="() => this.store.toggleObjectTreeElementsDisplay(this.props.object)"
|
||||
t-att-class="props.class + (props.object.hasChildren ? ' bg-feedback' : '')"
|
||||
t-on-click.stop="() => this.store.toggleObjectTreeElementsDisplay(this.props.object)"
|
||||
t-on-contextmenu.prevent="openMenu"
|
||||
>
|
||||
<div t-attf-style="padding-left: {{objectPadding}}rem">
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { isElementInCenterViewport, minimizeKey, IS_FIREFOX } from "../../../../utils";
|
||||
import { isElementInCenterViewport, minimizeKey, browserInstance } from "../../../../utils";
|
||||
import { useStore } from "../../../store/store";
|
||||
import { HighlightText } from "./highlight_text/highlight_text";
|
||||
|
||||
const browserInstance = IS_FIREFOX ? browser : chrome;
|
||||
|
||||
const { Component, useRef, useState, useEffect, onMounted } = owl;
|
||||
|
||||
export class TreeElement extends Component {
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
<option t-att-value="frame"><t t-esc="frame"/></option>
|
||||
</t>
|
||||
</select>
|
||||
<i class="ms-auto p-1 me-1 lg-icon fa fa-question-circle pointer-icon" title="Open devtools doc" t-on-click.stop="() => this.store.openDocumentation()"></i>
|
||||
<i class="p-1 me-1 lg-icon fa pointer-icon" title="Toggle dark mode" t-att-class="{ 'fa-sun-o': store.settings.darkMode, 'fa-moon-o' : !store.settings.darkMode}" t-on-click.stop="() => this.store.toggleDarkMode()"></i>
|
||||
<i class="p-1 me-1 lg-icon fa fa-repeat pointer-icon" title="Refresh extension" t-on-click.stop="() => this.store.refreshExtension()"></i>
|
||||
<i class="ms-auto p-1 me-1 lg-icon fa fa-question-circle navbar-icon" title="Open devtools doc" t-on-click.stop="() => this.store.openDocumentation()"></i>
|
||||
<i class="p-1 me-1 lg-icon fa navbar-icon" title="Toggle dark mode" t-att-class="{ 'fa-sun-o': store.settings.darkMode, 'fa-moon-o' : !store.settings.darkMode}" t-on-click.stop="() => this.store.toggleDarkMode()"></i>
|
||||
<i class="p-1 me-1 lg-icon fa fa-repeat navbar-icon" title="Refresh extension" t-on-click.stop="() => this.store.refreshExtension()"></i>
|
||||
</div>
|
||||
<ComponentsTab t-if="store.page === 'ComponentsTab'"/>
|
||||
<ProfilerTab t-if="store.page === 'ProfilerTab'"/>
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
<t t-name="devtools.ProfilerTab" owl="1">
|
||||
<div class="position-relative overflow-hidden d-flex flex-column h-100">
|
||||
<div class="panel-top d-flex align-items-center">
|
||||
<i title="Start/Stop Recording" class="fa fa-circle pointer-icon ms-1 p-1" t-attf-style="color: {{store.activeRecorder ? 'var(--active-recorder)' : 'var(--text-color)'}};" t-on-click.stop="() => this.store.toggleRecording()" aria-hidden="true"></i>
|
||||
<i title="Clear events" class="fa fa-ban pointer-icon p-1 px-2" t-on-click.stop="() => this.store.clearEventsConsole()" aria-hidden="true"></i>
|
||||
<div class="icons-separator mx-1"/>
|
||||
<select class="form-select form-select-sm custom-select border-0" t-on-change="selectDisplayMode">
|
||||
<i title="Start/Stop Recording" class="fa fa-circle profiler-icon p-2" t-attf-style="color: {{store.activeRecorder ? 'var(--active-recorder)' : 'var(--text-color)'}};" t-on-click.stop="() => this.store.toggleRecording()" aria-hidden="true"></i>
|
||||
<i title="Clear events" class="fa fa-ban profiler-icon p-2" t-on-click.stop="() => this.store.clearEventsConsole()" aria-hidden="true"></i>
|
||||
<div class="icons-separator"/>
|
||||
<select class="form-select form-select-sm custom-select pointer-icon border-0" t-on-change="selectDisplayMode">
|
||||
<option t-att-selected="store.eventsTreeView" value="Tree">Tree view</option>
|
||||
<option t-att-selected="!store.eventsTreeView" value="List">Events log</option>
|
||||
</select>
|
||||
<i title="Collapse All" type="button" class="fa fa-list me-2" t-on-click="() => this.store.collapseAll()" t-attf-style="{{store.eventsTreeView ? '' : 'visibility: hidden;'}}"></i>
|
||||
<i title="Collapse All" class="fa fa-list p-2 profiler-icon" t-on-click="() => this.store.collapseAll()" t-attf-style="{{store.eventsTreeView ? '' : 'display: none;'}}"></i>
|
||||
<div class="icons-separator"/>
|
||||
<label class="mx-2 form-check-label pointer-icon" title="Trace renderings in console">
|
||||
<input type="checkbox" class="form-check-input me-1" t-att-checked="store.traceRenderings" t-on-input="() => this.store.toggleTracing()"/> Trace Renderings
|
||||
<label class="p-1 mx-1 form-check-label pointer-icon" title="Trace renderings in console">
|
||||
<input type="checkbox" class="form-check-input me-1 pointer-icon" t-att-checked="store.traceRenderings" t-on-input="() => this.store.toggleTracing()"/> Trace Renderings
|
||||
</label>
|
||||
<label class="mx-2 form-check-label pointer-icon" title="Trace subscriptions in console (warning: it is VERY verbose)">
|
||||
<input type="checkbox" class="form-check-input me-1" t-att-checked="store.traceSubscriptions" t-on-input="() => this.store.toggleSubscriptionTracing()"/> Trace Subscriptions
|
||||
<label class="p-1 mx-1 form-check-label pointer-icon" title="Trace subscriptions in console (warning: it is VERY verbose)">
|
||||
<input type="checkbox" class="form-check-input me-1 pointer-icon" t-att-checked="store.traceSubscriptions" t-on-input="() => this.store.toggleSubscriptionTracing()"/> Trace Subscriptions
|
||||
</label>
|
||||
<!-- <EventSearchBar/> -->
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
const { reactive, useState, toRaw } = owl;
|
||||
import { fuzzySearch, IS_FIREFOX, getActiveTabURL } from "../../utils";
|
||||
import { fuzzySearch, IS_FIREFOX, getActiveTabURL, browserInstance } from "../../utils";
|
||||
import globalHook from "../../page_scripts/owl_devtools_global_hook";
|
||||
|
||||
const browserInstance = IS_FIREFOX ? browser : chrome;
|
||||
|
||||
// Main store which contains all states that needs to be maintained throughout all components in the devtools app
|
||||
export const store = reactive({
|
||||
devtoolsId: 0,
|
||||
@@ -115,6 +113,9 @@ export const store = reactive({
|
||||
}
|
||||
keepEnvLit(details);
|
||||
this.activeComponent = details;
|
||||
if (this.componentSearch.search.length) {
|
||||
this.updateSearch(this.componentSearch.search);
|
||||
}
|
||||
},
|
||||
|
||||
// Select a component by retrieving its details from the page based on its path
|
||||
@@ -146,6 +147,7 @@ export const store = reactive({
|
||||
}
|
||||
component.selected = true;
|
||||
highlightChildren(component);
|
||||
this.highlightComponent(path);
|
||||
const details = await evalFunctionInWindow(
|
||||
"getComponentDetails",
|
||||
[component.path],
|
||||
@@ -346,7 +348,7 @@ export const store = reactive({
|
||||
|
||||
// Expand the children of the input object property and load it from page if necessary
|
||||
async toggleObjectTreeElementsDisplay(obj) {
|
||||
if (!obj.hasChildren) {
|
||||
if (!obj.hasChildren || window.getSelection().toString().length) {
|
||||
return;
|
||||
}
|
||||
// Since it is sometimes impossible (and always ineffective) to load all descendants of a property
|
||||
@@ -939,10 +941,9 @@ function arraysEqual(arr1, arr2) {
|
||||
|
||||
async function getTabURL() {
|
||||
if (IS_FIREFOX) {
|
||||
// This happens in firefox when the method is called inside devtools so we ask the background to execute it instead
|
||||
browserInstance.runtime.sendMessage({ type: "getActiveTabURL" }).then((response) => {
|
||||
return response.result;
|
||||
});
|
||||
// It is not possible to run getActiveTabURL inside the devtools when using firefox so we ask the background to execute it instead
|
||||
const response = await browserInstance.runtime.sendMessage({ type: "getActiveTabURL" });
|
||||
return response.result;
|
||||
} else {
|
||||
return await getActiveTabURL();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
--text-selected: white;
|
||||
--menu-highlight-bg: rgb(201, 201, 201);
|
||||
--version-bg: teal;
|
||||
--hover-bg: #ebebeb;
|
||||
--navbar-hover-bg: #d3d3d3;
|
||||
/* to change the color here, put it in stroke='%23[color in hexadecimal]' */
|
||||
--select-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23444444' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
|
||||
}
|
||||
@@ -52,6 +54,8 @@
|
||||
--text-selected: #c9c9c9;
|
||||
--menu-highlight-bg: rgb(65, 65, 65);
|
||||
--version-bg: #805900ad;
|
||||
--hover-bg: #575757;
|
||||
--navbar-hover-bg: #6b6b6b;
|
||||
color-scheme: dark;
|
||||
/* to change the color here, put it in stroke='%23[color in hexadecimal]' */
|
||||
--select-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23c9c9c9' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
|
||||
@@ -82,6 +86,10 @@
|
||||
color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.navbar-btn {
|
||||
height: 23px;
|
||||
padding: 0.5rem;
|
||||
@@ -244,7 +252,7 @@
|
||||
.search-input {
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
padding: 0.4rem 0rem;
|
||||
padding: 0.37rem 0rem;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
@@ -255,6 +263,35 @@
|
||||
.utility-icon {
|
||||
cursor: pointer;
|
||||
font-size: 1.2em;
|
||||
border-radius: 35%;
|
||||
}
|
||||
|
||||
.mouse-icon {
|
||||
border-radius: 35%;
|
||||
cursor: pointer;
|
||||
padding-left: 0.4rem !important;
|
||||
}
|
||||
|
||||
.profiler-icon {
|
||||
border-radius: 35%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.utility-icon:hover, .mouse-icon:hover, .profiler-icon:hover, .bg-feedback:hover {
|
||||
background-color: var(--hover-bg);
|
||||
}
|
||||
|
||||
.navbar-icon {
|
||||
border-radius: 35%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navbar-icon:hover {
|
||||
background-color: var(--navbar-hover-bg);
|
||||
}
|
||||
|
||||
.bg-feedback {
|
||||
border-radius: 5%;
|
||||
}
|
||||
|
||||
.lg-icon {
|
||||
@@ -281,7 +318,7 @@
|
||||
color: var(--text-color);
|
||||
background-color: var(--background-color);
|
||||
border: 1px solid gray;
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
box-shadow: 1px 2px 5px #888;
|
||||
font-family: var(--bs-font-sans-serif);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
this.traceRenderings = false;
|
||||
this.traceSubscriptions = false;
|
||||
this.requestedFrame = false;
|
||||
this.enabledSelector = false;
|
||||
this.eventsBatch = [];
|
||||
// Object which defines how different types of data should be displayed when passed to the devtools
|
||||
this.serializer = {
|
||||
@@ -175,6 +176,7 @@
|
||||
|
||||
initDevtools(frame = "top") {
|
||||
if (!this.devtoolsInit) {
|
||||
document.addEventListener("mouseover", this.HTMLSelector, { capture: true });
|
||||
this.frame = frame;
|
||||
const self = this;
|
||||
// Flush the events batcher when a root render is completed
|
||||
@@ -606,28 +608,33 @@
|
||||
// Identify the hovered component based on the corresponding DOM element and send the Select message
|
||||
// when the target changes
|
||||
HTMLSelector = (ev) => {
|
||||
const target = ev.target;
|
||||
if (!this.currentSelectedElement || !target.isEqualNode(this.currentSelectedElement)) {
|
||||
const path = this.getElementPath(target);
|
||||
this.highlightComponent(path);
|
||||
this.currentSelectedElement = target;
|
||||
window.top.postMessage({
|
||||
source: "owl-devtools",
|
||||
type: "SelectElement",
|
||||
data: path,
|
||||
});
|
||||
if (this.enabledSelector) {
|
||||
const target = ev.target;
|
||||
if (!this.currentSelectedElement || !target.isEqualNode(this.currentSelectedElement)) {
|
||||
const path = this.getElementPath(target);
|
||||
this.highlightComponent(path);
|
||||
this.currentSelectedElement = target;
|
||||
window.top.postMessage({
|
||||
source: "owl-devtools",
|
||||
type: "SelectElement",
|
||||
data: path,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.removeHighlights();
|
||||
}
|
||||
};
|
||||
|
||||
// Activate the HTML selector tool
|
||||
enableHTMLSelector() {
|
||||
document.addEventListener("mouseover", this.HTMLSelector, { capture: true });
|
||||
this.enabledSelector = true;
|
||||
document.addEventListener("click", this.disableHTMLSelector, { capture: true });
|
||||
document.addEventListener("mouseout", this.removeHighlights, { capture: true });
|
||||
document.addEventListener("scroll", this.removeHighlights, { capture: true });
|
||||
}
|
||||
|
||||
// Diasble the HTML selector tool
|
||||
disableHTMLSelector = (ev = undefined) => {
|
||||
this.enabledSelector = false;
|
||||
if (ev) {
|
||||
if (!ev.isTrusted) {
|
||||
return;
|
||||
@@ -636,9 +643,8 @@
|
||||
ev.preventDefault();
|
||||
}
|
||||
this.removeHighlights();
|
||||
document.removeEventListener("mouseover", this.HTMLSelector, { capture: true });
|
||||
document.removeEventListener("click", this.disableHTMLSelector, { capture: true });
|
||||
document.removeEventListener("mouseout", this.removeHighlights, { capture: true });
|
||||
document.removeEventListener("scroll", this.removeHighlights, { capture: true });
|
||||
window.top.postMessage({
|
||||
source: "owl-devtools",
|
||||
type: "StopSelector",
|
||||
@@ -748,7 +754,7 @@
|
||||
child.contentType = "object";
|
||||
child.content = this.serializer.serializeItem(Object.getPrototypeOf(parentObj), true);
|
||||
child.hasChildren = true;
|
||||
if (!oldTree && type === "env") {
|
||||
if (!oldTree && type === "env" && Object.getPrototypeOf(parentObj) !== Object.prototype) {
|
||||
child.toggled = true;
|
||||
}
|
||||
break;
|
||||
@@ -1396,7 +1402,7 @@
|
||||
return this.getDOMElementsRecursive(node.content);
|
||||
}
|
||||
if (node.hasOwnProperty("el")) {
|
||||
if (node.el instanceof HTMLElement || node.el instanceof Text) {
|
||||
if (node.el instanceof Element || node.el instanceof Text) {
|
||||
return [node.el];
|
||||
}
|
||||
}
|
||||
@@ -1415,7 +1421,7 @@
|
||||
}
|
||||
}
|
||||
if (node.hasOwnProperty("parentEl")) {
|
||||
if (node.parentEl instanceof HTMLElement) {
|
||||
if (node.parentEl instanceof Element) {
|
||||
return [node.parentEl];
|
||||
}
|
||||
}
|
||||
@@ -1527,6 +1533,9 @@
|
||||
// Use inspectedPath to specify the path of the selected component
|
||||
getComponentsTree(inspectedPath = null, oldTrees = null, oldDetails = null) {
|
||||
const appsArray = [...this.apps];
|
||||
if (inspectedPath && !this.getComponentNode(inspectedPath)) {
|
||||
inspectedPath = null;
|
||||
}
|
||||
const trees = appsArray.map((app, index) => {
|
||||
let oldTree;
|
||||
if (oldTrees) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const IS_FIREFOX = navigator.userAgent.indexOf("Firefox") !== -1;
|
||||
|
||||
const browserInstance = IS_FIREFOX ? browser : chrome;
|
||||
export const browserInstance = IS_FIREFOX ? browser : chrome;
|
||||
|
||||
export async function getOwlStatus() {
|
||||
const response = await browserInstance.runtime.sendMessage({ type: "getOwlStatus" });
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/naming-convention": "warn",
|
||||
"@typescript-eslint/semi": "warn",
|
||||
"curly": "warn",
|
||||
"eqeqeq": "warn",
|
||||
"no-throw-literal": "warn",
|
||||
"semi": "off"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"out",
|
||||
"dist",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/*.vsix
|
||||
scripts/**
|
||||
@@ -0,0 +1,32 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the "owl-vision" extension will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
|
||||
## [0.0.2] - 2023-2-11
|
||||
|
||||
### Added
|
||||
|
||||
- Switch Below command
|
||||
- Basic syntax highlight for xpaths
|
||||
- Syntax builder scripts to make syntaxes easier to read and edit
|
||||
- Syntax highlight in single quote attributes
|
||||
- Syntax highlight for slot props
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added missing space in component's snippet indentation
|
||||
- Using `Switch Besides` or `Switch Below` does not open a new panel if one was already open
|
||||
|
||||
## [0.0.1] - 2023-03-10
|
||||
|
||||
- Initial release
|
||||
|
||||
### Added
|
||||
|
||||
- Owl templates syntax highlight in xml files
|
||||
- `Find Template` Command - Finds the template file of the currently selected element.
|
||||
- `Find Component` Command - Finds the selected component definition.
|
||||
- `Switch` Command - Finds the corresponding template or component file depending on the current file.
|
||||
- `Switch Besides` Command - Finds the corresponding template or component file depending on the current file and opens it besides.
|
||||
@@ -0,0 +1,856 @@
|
||||
OWL-VISION is published under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3
|
||||
(LGPLv3), as included below. Since the LGPL is a set of additional
|
||||
permissions on top of the GPL, the text of the GPL is included at the
|
||||
bottom as well.
|
||||
|
||||
Some external libraries and contributions bundled with OWL-VISION may be published
|
||||
under other GPL-compatible licenses. For these, please refer to the relevant
|
||||
source files and/or license files, in the source code tree.
|
||||
|
||||
**************************************************************************
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
||||
**************************************************************************
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
|
||||
**************************************************************************
|
||||
@@ -0,0 +1,30 @@
|
||||
# 🦉 Owl Vision 🕶️
|
||||
|
||||
Owl Vision is an extension for the amazing [Owl framework](https://github.com/odoo/owl) that complements your templates with beautiful colors and allows you to more easily navigate between components and templates.
|
||||
|
||||

|
||||
|
||||
This extension also adds:
|
||||
- A basic component snippent.
|
||||
- "Go to definition" providers for component tags in xml or in inline templates.
|
||||
|
||||
## Commands
|
||||
|
||||
* `Owl Vision: Find Template`:
|
||||
- If the cursor is on a template name, finds the corresponding template.
|
||||
- If the cursor is on a component, finds the template of the selected component.
|
||||
* `Owl Vision: Find Component`: Finds the selected component definition.
|
||||
* `Owl Vision: Switch`: Finds the corresponding template or component depending on the current file.
|
||||
* `Owl Vision: Switch (Besides)`: Finds the corresponding template or component depending on the current file and opens it besides.
|
||||
* `Owl Vision: Switch (Below)`: Finds the corresponding template or component depending on the current file and opens it below.
|
||||
|
||||
## Settings
|
||||
|
||||
* `owl-vision.include`: Glob filter for files to include while searching.
|
||||
* `owl-vision.exclude`: Glob filter for files to exclude while searching.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### *The extension cannot find my templates/components*
|
||||
|
||||
The include and exclude settings have default values that may not work with your project structure, try adapting them.
|
||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 138 KiB |
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"name": "owl-vision",
|
||||
"displayName": "Owl Vision",
|
||||
"description": "Owl framework extension that highlights templates and ease navigation between components and templates.",
|
||||
"publisher": "Odoo",
|
||||
"license": "LGPL-3.0-only",
|
||||
"version": "0.0.2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/odoo/owl/tree/master/tools/owl-vision"
|
||||
},
|
||||
"icon": "assets/icon128.png",
|
||||
"engines": {
|
||||
"vscode": "^1.73.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:xml",
|
||||
"onLanguage:js"
|
||||
],
|
||||
"main": "./out/main.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "owl-vision.switch",
|
||||
"title": "Switch",
|
||||
"category": "Owl Vision"
|
||||
},
|
||||
{
|
||||
"command": "owl-vision.switch-besides",
|
||||
"title": "Switch (Besides)",
|
||||
"category": "Owl Vision"
|
||||
},
|
||||
{
|
||||
"command": "owl-vision.switch-below",
|
||||
"title": "Switch (Below)",
|
||||
"category": "Owl Vision"
|
||||
},
|
||||
{
|
||||
"command": "owl-vision.find-component",
|
||||
"title": "Find Component",
|
||||
"category": "Owl Vision"
|
||||
},
|
||||
{
|
||||
"command": "owl-vision.find-template",
|
||||
"title": "Find Template",
|
||||
"category": "Owl Vision"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
"title": "Owl Vision",
|
||||
"properties": {
|
||||
"owl-vision.include": {
|
||||
"order": 0,
|
||||
"type": "string",
|
||||
"default": "**/*.js,**/*.ts,**/*.mjs,**/static/**/*.xml",
|
||||
"description": "Files to include in search"
|
||||
},
|
||||
"owl-vision.exclude": {
|
||||
"order": 1,
|
||||
"type": "string",
|
||||
"default": "**/node_modules/**,**/lib/**,**/tests/**",
|
||||
"description": "Files to exclude in search"
|
||||
}
|
||||
}
|
||||
},
|
||||
"snippets": [
|
||||
{
|
||||
"language": "javascript",
|
||||
"path": "./snippets/component.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"scopeName": "owl.template",
|
||||
"path": "./syntaxes/owl.template.json",
|
||||
"embeddedLanguages": {
|
||||
"meta.embedded.block.javascript": "source.js"
|
||||
},
|
||||
"injectTo": [
|
||||
"text.xml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scopeName": "owl.template.inline",
|
||||
"path": "./syntaxes/owl.template.inline.json",
|
||||
"embeddedLanguages": {
|
||||
"meta.embedded.block.xml": "xml"
|
||||
},
|
||||
"injectTo": [
|
||||
"source.js",
|
||||
"source.jsx",
|
||||
"source.ts",
|
||||
"source.tsx",
|
||||
"text.html"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scopeName": "owl.markup.inline",
|
||||
"path": "./syntaxes/owl.markup.inline.json",
|
||||
"embeddedLanguages": {
|
||||
"meta.embedded.block.html": "html"
|
||||
},
|
||||
"injectTo": [
|
||||
"source.js",
|
||||
"source.jsx",
|
||||
"source.ts",
|
||||
"source.tsx",
|
||||
"text.html"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "tsc -p ./",
|
||||
"package": "vsce package",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"lint": "eslint src --ext ts",
|
||||
"pretest": "npm run compile && npm run lint",
|
||||
"vscode:prepublish": "npm run esbuild-base -- --minify",
|
||||
"esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node",
|
||||
"esbuild": "npm run esbuild-base -- --sourcemap",
|
||||
"esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
|
||||
"build-syntax": "node ./scripts/owl_template_syntax.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.2.5",
|
||||
"@types/vscode": "^1.73.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||
"@typescript-eslint/parser": "^5.59.8",
|
||||
"@vscode/test-electron": "^2.3.2",
|
||||
"@vscode/vsce": "^2.20.1",
|
||||
"esbuild": "^0.19.5",
|
||||
"eslint": "^8.41.0",
|
||||
"typescript": "^5.1.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { createTagPattern, exportPatterns } from "./syntax_builder_utils.mjs";
|
||||
import {
|
||||
htmlAttributes,
|
||||
owlAttributesDynamic,
|
||||
owlAttributesFormattedString,
|
||||
owlAttributesStatic,
|
||||
propsAttributes
|
||||
} from "./syntax_parts/owl_attributes.mjs";
|
||||
import { xpathAttributes } from "./syntax_parts/xpath.mjs";
|
||||
|
||||
const componentsTags = createTagPattern("component-tags", {
|
||||
match: "[A-Z][a-zA-Z0-9_]*",
|
||||
name: "entity.name.type.class owl.component",
|
||||
patterns: [
|
||||
owlAttributesDynamic,
|
||||
owlAttributesDynamic,
|
||||
propsAttributes,
|
||||
],
|
||||
});
|
||||
|
||||
const htmlTags = createTagPattern("html-tags", {
|
||||
match: "[a-z][a-zA-Z0-9_:.]+|[abiqsuw]",
|
||||
name: "entity.name.tag.localname.xml owl.xml.tag",
|
||||
patterns: [
|
||||
owlAttributesFormattedString,
|
||||
xpathAttributes,
|
||||
owlAttributesDynamic,
|
||||
owlAttributesStatic,
|
||||
htmlAttributes,
|
||||
],
|
||||
});
|
||||
|
||||
const tTag = createTagPattern("t-tag", {
|
||||
match: "t(?![a-zA-Z])",
|
||||
name: "entity.name.tag.localname.xml owl.tag",
|
||||
patterns: [
|
||||
propsAttributes,
|
||||
owlAttributesFormattedString,
|
||||
owlAttributesDynamic,
|
||||
owlAttributesStatic,
|
||||
],
|
||||
});
|
||||
|
||||
exportPatterns(
|
||||
"L:text.xml -comment",
|
||||
"owl.template",
|
||||
[componentsTags, htmlTags, tTag]
|
||||
);
|
||||