If we want to upgrade Typescript to version >4 (in this repo or another using
owl), we also need jest >25. Jest <26 do not support Typescript >4.
However, jest >26 has a few breaking changes that completely breaks owl in
tests.
This commit updates jest to the current latest version (27) and adapts the
code accordingly.
A few words on what changed
---------------------------
1. the default test environment is no longer `jsdom`. It is now manually
configured to restore the previous env.
2. the jsdom version has been upgraded. This brings a few breaking changes,
detailed later.
3. there's a bug in jsdom >16.4. Manually created `<t/>` (by qweb compilation)
are recognized as "T" and not "t". Qweb thinks it's a component (since the
first letter is capitalized), but it's not a component: boom, everything
breaks.
A fix has been proposed here jsdom/jsdom#3240. But it's not likely to land
in jest in the short term (jest would need to update its dependency to the
next major jsdom version). This commit works around the problem for now by
creating `<t/>` slighty differently such that they are in an XML document
from the start (and not HTML document).
4. the xml parser implementation changed to increase the strictness and
correctness of XML parsing, according to specifications. A few tests needed
to be adapted.
https://github.com/jsdom/jsdom/commit/c96decf837ece54bdc550dfb7dca7e5d6c97bc2d
5. jest matcher `toHaveProperty` now check inherited properties. This breaks a
few tests. Since the breaking assertions didn't bring a lot of value from a
behavior point of view (it was more "white box" technical tests), they are
removed in this commit
https://github.com/facebook/jest/commit/1256f76a5a83034b51c7524142b60b099f69a7ab
6. jsdom now implements the behavior of links (`<a/>`). One test was `click`ing
on such a link, with the right click. Jsdom now tries to navigate to the
pointed URl...but crashes because navigation is not implemented :(
It turns out the test is probably not a valid/realistic scenario. On every
tested browser (chrome - chromium - brave - edge -firefox - safari), a
right click with the mouse triggers a `contextmenu` event and no `click`
event.
https://github.com/jsdom/jsdom/commit/cc95abc576f596ff7f3eaf8245f376e1f21aa485
the low level method htmlelement.classList.add does not accept multiple
classes in one string, which is why, in owl, the expression
`<div t-att-class="{'a b c': value}" />`
did not work as one might expect. It is however very convenient in real
life templates, so this commit improve owl by adding support for this
feature.
closes#813
Before this commit, props and t-props were computed like
`Object.assign(props, t_props)`.
Now, it computes like `Object.assign(t_props, props)` so props will
override t-props.
closes https://github.com/odoo/owl/issues/886
Before this commit, params in paths had to be be between slashes and
comprise the entirety of the contents between those slashes (eg:
`/books/{{id}}/{{name}}`)
This is unnecessarily restrictive. This commit removes this restriction,
which allows for paths such as:
- `/books/{{id}}-{{name}}`
- `#books&id={{id}}&name={{name}}`
among others
closes#858
Previously, the order of resolution was:
- components defined on the class of the current component
- components defined on the QWeb instance
- the current instance's context
This is unnatural because it doesn't go from most specific to least, as
the current instance's context is the most specific. This is also
fragile, as adding components to the QWeb instance can break unrelated
components.
This commit fixes that by making the lookup start with the current
component's context instead
The initial problem solved by this commit is that it was possible to get
into a situation where a mounting/rendering was started, then the component was
updated, but then another mounting operation begins, and it tries to
reuse the previous rendering operation, which is no longer uptodate.
The underlying issue is that Owl did not track properly the various
internal state change of a component. These issue should be solved by
the introduction of the status enum, which currently tracks 6 possible
states:
- CREATED
- WILLSTARTED
- RENDERED
- MOUNTED
- UNMOUNTED
- DESTROYED
This status number replaces the isMounted and isDestroyed boolean flags.
It has the advantage of making sure that the component is in a
consistent state (it is no longer possible to be destroyed and mounted,
for example)
Another advantage is that it gives us an easy way to track the fact that
a component has been rendered, but is not in the DOM. This is a subtle
situation where some various events can happen, and we need to be able
to react to that case.
Note that there is a change of behaviour: if a component is mounted in a
specific target, then before the mounting is complete, the component is
mounted in another target, we no longer reject the first mounting
operation.
From the beginning, Owl was designed to only call event handler on
components that are mounted in the DOM. The main benefit is that if a
component is destroyed, we are guaranteed to not execute any useless (or
potentially dangerous code).
However, there is one downside: if a component tree is being mounted,
and a child component trigger an event in its mounted hook, then it
cannot be caught by the parent, since the parent is technically not yet
mounted.
This may not be a good situation, but the point is that Owl
unnecessarily prevent the handler to be called.
We can fix this issue by simply checking if the component is not destroyed
instead of checking that it is mounted.
closes#809
Strangely, the _compile method required a CompilationContext whenever it
was compiled as a sub template, but this parent context was actually not
really needed. I guess that it was the case in the past, but this was
changed at some point.
This commit makes another significant change: the xml element is no
longer mandatory. It is actually only required for slots (because the
template is not registered to qweb).
Finally, the interface for the whole method has been changed to use an
option object, which makes more sense with 3 optional paremeters.
In most cases, we just want Component<any, Env>. But since it was so
annoying to have always the type Env, we actually used
Component<any,any> everywhere.
With this commit, the generic types have a default (and their order is
swapped), so we can simply use Component in most cases, and
Component<Props> when we want to type the props.
The way keys were handled in QWeb was mostly ok, but a little naive. It
is fine when we deal with a list of dom nodes, since the reconciliation
algorithm need only to be able to differentiate/reconcile nodes in that
list, but it is an issue with components, which needs to globally be
able to find themselves in their parent's children map.
Because of the way this was handled, there were situations were Owl
internal virtual dom would crash, since components wrongly assumed that
they were already rendered in a different place.
To fix this, we generalize the way keys are generated, by concatenating
all sub keys coming from each iteration loops.
closes#584
While it is not tested, nor documented, the reference QWeb
implementation display the `false` value as "false". So, we have to
adapt the Owl implementation to match that behaviour.
At the same time, this commit uses 'let' instead of 'var' in various places,
to make the compiled code more consistant.
Before this commit, the handlers were bound to the current context,
which is often the component but not necessarily. In some cases, a sub
scope can be created (with t-foreach, or slots, or t-call), and the
context is actually an object with the actual component instance it its
prototype chain, but not the component.
- In some cases, we can simply inline a template key instead of assigning
it to a variable and then using that variable
- Add component name in generated code for components
This commit is a significant refactoring of the internal of QWeb. It
simplifies the way variables/scoping and slots interact together. The
main idea is that we use a simple scope object instead of a
context/var/scope object.
This commit also implement the actual correct QWeb semantic for the
t-call directive with a sub body. Before, we simply extracted the
variables from the body and injected them at the top of the sub
template. We now simply compile the body before the sub template.
This is a joint work with Lucas (lpe).
closes#541closes#544closes#545closes#557closes#556
This is a really interesting problem: there was a situation where a
component would not have an event handler properly bound. The reason was
that we were in a situation where the scheduler task queue was flushed
at an unfortunate timing:
- parent component template is rendered
- subcomponent is prepared:
- sub component is willStarted
- it is rendered (so, fiber counter is set to 0)
- scheduler task queue is flushed => we patch the DOM
- the code registered in the __prepare.then(handler) is executed, which
add the createHook to the vnode (but after the dom is patched)
Usually, the last two steps happen in a different order, but in an
environment with connected components, we sometimes flush the task queue
at various moments, so some code could be executed between the end of
__prepare and the registered handler.
The fix is interesting: we give a callback to the __prepare function
instead of registering a .then handler to the deferred. This callback
is then guaranteed to be called at exactly the proper timing.
Thanks seb for the hard work of finding the root cause of this issue
closes#520
Following 2922cee6ea
Previous commit was not correct: used children with a cancelled
fiber (for instance, with a new currentFiber) could be destroyed.
This commit fixes the issue differently, by directly detecting
children that are not used anymore (and not mounted), and destroy
them directly (no need to wait for willStart promise to be resolved
anymore).
It now takes two arguments: parent (optional, only for non-root
components) and props (optional). In the case of the root component,
the env is taken from the config (config.defaultEnv). If it doesn't
exists, the default env is created on the fly.
Closes#306
Big change! This commit introduces an xml function tag to easily define
inline templates.
This is a pretty big change toward single file owl components
Part of #284