From 050694414c8db39aa157edcf10ccf188def4abf7 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Tue, 21 May 2019 08:32:32 +0200 Subject: [PATCH] [REF] move animations tests into their own file --- tests/__snapshots__/animations.test.ts.snap | 115 ++++++++ tests/__snapshots__/component.test.ts.snap | 76 ----- tests/__snapshots__/qweb.test.ts.snap | 38 --- tests/animations.test.ts | 290 ++++++++++++++++++++ tests/component.test.ts | 216 +-------------- tests/helpers.ts | 44 ++- tests/qweb.test.ts | 100 +------ 7 files changed, 451 insertions(+), 428 deletions(-) create mode 100644 tests/__snapshots__/animations.test.ts.snap create mode 100644 tests/animations.test.ts diff --git a/tests/__snapshots__/animations.test.ts.snap b/tests/__snapshots__/animations.test.ts.snap new file mode 100644 index 00000000..d9f47343 --- /dev/null +++ b/tests/__snapshots__/animations.test.ts.snap @@ -0,0 +1,115 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`animations t-transition combined with t-widget 1`] = ` +"function anonymous(context,extra +) { + let utils = this.utils; + let owner = context; + var h = this.utils.h; + let c1 = [], p1 = {key:1}; + var vn1 = h('div', p1, c1); + //WIDGET + let _2_index = c1.length; + c1.push(null); + let def3; + let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false; + let props4 = {}; + if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode && props4 !== w4.__owl__.renderProps) { + w4.destroy(); + w4 = false + } + if (!w4) { + let W4 = context.widgets['Child']; + if (!W4) {throw new Error(\`Cannot find the definition of widget \\"Child\\"\`)} + w4 = new W4(owner, props4); + context.__owl__.cmap[4] = w4.__owl__.id; + def3 = w4._prepare(); + def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn.elm, 'chimay');},remove() {},destroy(vn) {let finalize = () => { + w4.destroy(); + }; + utils.transitionRemove(vn.elm, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + } else { + def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue); + def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + } + extra.promises.push(def3); + return vn1; +}" +`; + +exports[`animations t-transition combined with t-widget and t-if 1`] = ` +"function anonymous(context,extra +) { + let utils = this.utils; + let owner = context; + var h = this.utils.h; + let c1 = [], p1 = {key:1}; + var vn1 = h('div', p1, c1); + if (context['state'].display) { + //WIDGET + let _2_index = c1.length; + c1.push(null); + let def3; + let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false; + let props4 = {}; + if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode && props4 !== w4.__owl__.renderProps) { + w4.destroy(); + w4 = false + } + if (!w4) { + let W4 = context.widgets['Child']; + if (!W4) {throw new Error(\`Cannot find the definition of widget \\"Child\\"\`)} + w4 = new W4(owner, props4); + context.__owl__.cmap[4] = w4.__owl__.id; + def3 = w4._prepare(); + def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn.elm, 'chimay');},remove() {},destroy(vn) {let finalize = () => { + w4.destroy(); + }; + utils.transitionRemove(vn.elm, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + } else { + def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue); + def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + } + extra.promises.push(def3); + } + return vn1; +}" +`; + +exports[`animations t-transition with no delay/duration 1`] = ` +"function anonymous(context,extra +) { + var h = this.utils.h; + let c1 = [], p1 = {key:1}; + var vn1 = h('span', p1, c1); + p1.hook = { + insert: vn => { + this.utils.transitionInsert(vn.elm, 'jupiler'); + }, + remove: (vn, rm) => { + this.utils.transitionRemove(vn.elm, 'jupiler', rm); + }, + }; + c1.push({text: \`blue\`}); + return vn1; +}" +`; + +exports[`animations t-transition, on a simple node (insert) 1`] = ` +"function anonymous(context,extra +) { + var h = this.utils.h; + let c1 = [], p1 = {key:1}; + var vn1 = h('span', p1, c1); + p1.hook = { + insert: vn => { + this.utils.transitionInsert(vn.elm, 'chimay'); + }, + remove: (vn, rm) => { + this.utils.transitionRemove(vn.elm, 'chimay', rm); + }, + }; + c1.push({text: \`blue\`}); + return vn1; +}" +`; diff --git a/tests/__snapshots__/component.test.ts.snap b/tests/__snapshots__/component.test.ts.snap index f07dba6b..082a10e0 100644 --- a/tests/__snapshots__/component.test.ts.snap +++ b/tests/__snapshots__/component.test.ts.snap @@ -1,81 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`animations t-transition combined with t-widget 1`] = ` -"function anonymous(context,extra -) { - let utils = this.utils; - let owner = context; - var h = this.utils.h; - let c1 = [], p1 = {key:1}; - var vn1 = h('div', p1, c1); - //WIDGET - let _2_index = c1.length; - c1.push(null); - let def3; - let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false; - let props4 = {}; - if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode && props4 !== w4.__owl__.renderProps) { - w4.destroy(); - w4 = false - } - if (!w4) { - let W4 = context.widgets['Child']; - if (!W4) {throw new Error(\`Cannot find the definition of widget \\"Child\\"\`)} - w4 = new W4(owner, props4); - context.__owl__.cmap[4] = w4.__owl__.id; - def3 = w4._prepare(); - def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn.elm, 'chimay');},remove() {},destroy(vn) {let finalize = () => { - w4.destroy(); - }; - utils.transitionRemove(vn.elm, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); - } else { - def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); - } - extra.promises.push(def3); - return vn1; -}" -`; - -exports[`animations t-transition combined with t-widget and t-if 1`] = ` -"function anonymous(context,extra -) { - let utils = this.utils; - let owner = context; - var h = this.utils.h; - let c1 = [], p1 = {key:1}; - var vn1 = h('div', p1, c1); - if (context['state'].display) { - //WIDGET - let _2_index = c1.length; - c1.push(null); - let def3; - let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false; - let props4 = {}; - if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode && props4 !== w4.__owl__.renderProps) { - w4.destroy(); - w4 = false - } - if (!w4) { - let W4 = context.widgets['Child']; - if (!W4) {throw new Error(\`Cannot find the definition of widget \\"Child\\"\`)} - w4 = new W4(owner, props4); - context.__owl__.cmap[4] = w4.__owl__.id; - def3 = w4._prepare(); - def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn.elm, 'chimay');},remove() {},destroy(vn) {let finalize = () => { - w4.destroy(); - }; - utils.transitionRemove(vn.elm, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); - } else { - def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); - } - extra.promises.push(def3); - } - return vn1; -}" -`; - exports[`class and style attributes with t-widget dynamic t-att-style is properly added and updated on widget root el 1`] = ` "function anonymous(context,extra ) { diff --git a/tests/__snapshots__/qweb.test.ts.snap b/tests/__snapshots__/qweb.test.ts.snap index 5b1ca054..7d3f398d 100644 --- a/tests/__snapshots__/qweb.test.ts.snap +++ b/tests/__snapshots__/qweb.test.ts.snap @@ -1,43 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`animations t-transition with no delay/duration 1`] = ` -"function anonymous(context,extra -) { - var h = this.utils.h; - let c1 = [], p1 = {key:1}; - var vn1 = h('span', p1, c1); - p1.hook = { - insert: vn => { - this.utils.transitionInsert(vn.elm, 'jupiler'); - }, - remove: (vn, rm) => { - this.utils.transitionRemove(vn.elm, 'jupiler', rm); - }, - }; - c1.push({text: \`blue\`}); - return vn1; -}" -`; - -exports[`animations t-transition, on a simple node (insert) 1`] = ` -"function anonymous(context,extra -) { - var h = this.utils.h; - let c1 = [], p1 = {key:1}; - var vn1 = h('span', p1, c1); - p1.hook = { - insert: vn => { - this.utils.transitionInsert(vn.elm, 'chimay'); - }, - remove: (vn, rm) => { - this.utils.transitionRemove(vn.elm, 'chimay', rm); - }, - }; - c1.push({text: \`blue\`}); - return vn1; -}" -`; - exports[`attributes dynamic attribute falsy variable 1`] = ` "function anonymous(context,extra ) { diff --git a/tests/animations.test.ts b/tests/animations.test.ts new file mode 100644 index 00000000..2ff55f07 --- /dev/null +++ b/tests/animations.test.ts @@ -0,0 +1,290 @@ +import { Component, Env } from "../src/component"; +import { QWeb } from "../src/qweb_core"; +import { + makeDeferred, + makeTestFixture, + makeTestEnv, + nextTick, + patchNextFrame, + renderToDOM, + unpatchNextFrame +} from "./helpers"; + +//------------------------------------------------------------------------------ +// Setup and helpers +//------------------------------------------------------------------------------ + +// We create before each test: +// - fixture: a div, appended to the DOM, intended to be the target of dom +// manipulations. Note that it is removed after each test. +// - qweb: a new QWeb instance +// - env: a WEnv, necessary to create new widgets +// - cssEl: a stylesheet injected into the dom + +let fixture: HTMLElement; +let qweb: QWeb; +let env: Env; +let cssEl: HTMLElement; + +beforeEach(() => { + fixture = makeTestFixture(); + env = makeTestEnv(); + qweb = new QWeb(); +}); + +afterEach(() => { + fixture.remove(); +}); + +class Widget extends Component {} + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +describe("animations", () => { + beforeEach(() => { + cssEl = document.createElement("style"); + let css = ` + .chimay-enter-active, .chimay-leave-active { + transition-property: opacity; + transition-duration: 0.1s; + } + .chimay-enter, .chimay-leave-to { + opacity: 0; + } + `; + cssEl.textContent = css.trim(); + document.head.appendChild(cssEl); + }); + + afterEach(() => { + document.head.removeChild(cssEl); + unpatchNextFrame(); + }); + + test("t-transition, on a simple node (insert)", async () => { + expect.assertions(5); + qweb.addTemplate("test", `blue`); + + let def = makeDeferred(); + patchNextFrame(cb => { + expect(node.className).toBe("chimay-enter chimay-enter-active"); + cb(); + expect(node.className).toBe("chimay-enter-active chimay-enter-to"); + def.resolve(); + }); + let node: HTMLElement = renderToDOM(qweb, "test"); + + expect(node.className).toBe("chimay-enter chimay-enter-active"); + await def; // wait for the mocked repaint to be done + node.dispatchEvent(new Event("transitionend")); // mock end of css transition + expect(node.className).toBe(""); + }); + + test("t-transition with no delay/duration", async () => { + expect.assertions(4); + qweb.addTemplate("test", `blue`); + + let def = makeDeferred(); + patchNextFrame(cb => { + expect(node.className).toBe("jupiler-enter jupiler-enter-active"); + cb(); + expect(node.className).toBe(""); + def.resolve(); + }); + let node: HTMLElement = renderToDOM(qweb, "test"); + expect(node.className).toBe("jupiler-enter jupiler-enter-active"); + await def; + }); + + test("t-transition on a conditional node", async () => { + expect.assertions(7); + + env.qweb.addTemplate( + "TestWidget", + `
blue
` + ); + class TestWidget extends Widget { + state = { hide: false }; + } + const widget = new TestWidget(env); + + // insert widget into the DOM + let def = makeDeferred(); + var spanNode; + patchNextFrame(cb => { + expect(spanNode.className).toBe("chimay-enter chimay-enter-active"); + cb(); + expect(spanNode.className).toBe("chimay-enter-active chimay-enter-to"); + def.resolve(); + }); + await widget.mount(fixture); + spanNode = widget.el!.children[0]; + expect(spanNode.className).toBe("chimay-enter chimay-enter-active"); + await def; // wait for the mocked repaint to be done + spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition + expect(spanNode.className).toBe(""); + + // remove span from the DOM + def = makeDeferred(); + widget.state.hide = true; + patchNextFrame(cb => { + expect(spanNode.className).toBe("chimay-leave chimay-leave-active"); + cb(); + expect(spanNode.className).toBe("chimay-leave-active chimay-leave-to"); + def.resolve(); + }); + await def; // wait for the mocked repaint to be done + spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition + expect(spanNode.className).toBe(""); + }); + + test("t-transition combined with t-ref", async () => { + expect.assertions(5); + + env.qweb.addTemplate( + "TestWidget", + `
blue
` + ); + class TestWidget extends Widget { + state = { hide: false }; + } + const widget = new TestWidget(env); + + // insert widget into the DOM + let def = makeDeferred(); + var spanNode; + patchNextFrame(cb => { + expect(spanNode.className).toBe("chimay-enter chimay-enter-active"); + cb(); + expect(spanNode.className).toBe("chimay-enter-active chimay-enter-to"); + def.resolve(); + }); + await widget.mount(fixture); + spanNode = widget.el!.children[0]; + expect(widget.refs.span).toBe(spanNode); + expect(spanNode.className).toBe("chimay-enter chimay-enter-active"); + await def; // wait for the mocked repaint to be done + spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition + expect(spanNode.className).toBe(""); + }); + + test("t-transition combined with t-widget", async () => { + expect.assertions(5); + + env.qweb.addTemplate( + "Parent", + `
` + ); + env.qweb.addTemplate("Child", `blue`); + class Parent extends Widget { + widgets = { Child: Child }; + } + class Child extends Widget {} + const widget = new Parent(env); + + let def = makeDeferred(); + var spanNode; + patchNextFrame(cb => { + expect(fixture.innerHTML).toBe( + '
blue
' + ); + cb(); + expect(fixture.innerHTML).toBe( + '
blue
' + ); + def.resolve(); + }); + await widget.mount(fixture); + spanNode = widget.el!.children[0]; + + expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot(); + expect(fixture.innerHTML).toBe( + '
blue
' + ); + + await def; // wait for the mocked repaint to be done + spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition + expect(fixture.innerHTML).toBe('
blue
'); + }); + + test("t-transition combined with t-widget and t-if", async () => { + expect.assertions(8); + + env.qweb.addTemplate( + "Parent", + `
` + ); + env.qweb.addTemplate("Child", `blue`); + class Parent extends Widget { + widgets = { Child: Child }; + state = { display: true }; + } + class Child extends Widget {} + const widget = new Parent(env); + + let def = makeDeferred(); + var spanNode; + patchNextFrame(cb => { + expect(fixture.innerHTML).toBe( + '
blue
' + ); + cb(); + expect(fixture.innerHTML).toBe( + '
blue
' + ); + def.resolve(); + }); + await widget.mount(fixture); + spanNode = widget.el!.children[0]; + + expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot(); + expect(fixture.innerHTML).toBe( + '
blue
' + ); + + await def; // wait for the mocked repaint to be done + spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition + expect(fixture.innerHTML).toBe('
blue
'); + + // remove span from the DOM + def = makeDeferred(); + widget.state.display = false; + patchNextFrame(cb => { + expect(fixture.innerHTML).toBe( + '
blue
' + ); + cb(); + expect(fixture.innerHTML).toBe( + '
blue
' + ); + def.resolve(); + }); + await def; // wait for the mocked repaint to be done + spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition + expect(fixture.innerHTML).toBe("
"); + }); + + test("t-transition combined with t-mounted", async () => { + env.qweb.addTemplate( + "TestWidget", + `
blue
` + ); + class TestWidget extends Widget { + state = { flag: false }; + f() {} + } + const widget = new TestWidget(env); + widget.f = jest.fn(); + await widget.mount(fixture); + + patchNextFrame(cb => cb()); + expect(widget.f).toHaveBeenCalledTimes(0); + + widget.state.flag = true; + await nextTick(); + expect(widget.f).toHaveBeenCalledTimes(1); + unpatchNextFrame(); + }); +}); diff --git a/tests/component.test.ts b/tests/component.test.ts index d40f715a..da845c49 100644 --- a/tests/component.test.ts +++ b/tests/component.test.ts @@ -5,9 +5,7 @@ import { makeTestEnv, nextMicroTick, nextTick, - normalize, - patchNextFrame, - unpatchNextFrame + normalize } from "./helpers"; //------------------------------------------------------------------------------ @@ -21,7 +19,6 @@ import { let fixture: HTMLElement; let env: Env; -let cssEl: HTMLElement; beforeEach(() => { fixture = makeTestFixture(); @@ -2266,27 +2263,6 @@ describe("t-mounted directive", () => { expect(widget.refs.input).toBeDefined(); expect(widget.f).toHaveBeenCalledTimes(1); }); - - test("combined with a t-transition", async () => { - env.qweb.addTemplate( - "TestWidget", - `
blue
` - ); - class TestWidget extends Widget { - state = { flag: false }; - f() {} - } - const widget = new TestWidget(env); - widget.f = jest.fn(); - await widget.mount(fixture); - - patchNextFrame(cb => cb()); - expect(widget.f).toHaveBeenCalledTimes(0); - - widget.state.flag = true; - await nextTick(); - expect(widget.f).toHaveBeenCalledTimes(1); - }); }); describe("can deduce template from name", () => { @@ -2332,193 +2308,3 @@ describe("can deduce template from name", () => { expect(fixture.innerHTML).toBe("Rochefort 10"); }); }); - -describe("animations", () => { - beforeEach(() => { - cssEl = document.createElement("style"); - let css = ` - .chimay-enter-active, .chimay-leave-active { - transition-property: opacity; - transition-duration: 0.1s; - } - .chimay-enter, .chimay-leave-to { - opacity: 0; - } - `; - cssEl.textContent = css.trim(); - document.head.appendChild(cssEl); - }); - - afterEach(() => { - document.head.removeChild(cssEl); - unpatchNextFrame(); - }); - - test("t-transition on a conditional node", async () => { - expect.assertions(7); - - env.qweb.addTemplate( - "TestWidget", - `
blue
` - ); - class TestWidget extends Widget { - state = { hide: false }; - } - const widget = new TestWidget(env); - - // insert widget into the DOM - let def = makeDeferred(); - var spanNode; - patchNextFrame(cb => { - expect(spanNode.className).toBe("chimay-enter chimay-enter-active"); - cb(); - expect(spanNode.className).toBe("chimay-enter-active chimay-enter-to"); - def.resolve(); - }); - await widget.mount(fixture); - spanNode = widget.el!.children[0]; - expect(spanNode.className).toBe("chimay-enter chimay-enter-active"); - await def; // wait for the mocked repaint to be done - spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition - expect(spanNode.className).toBe(""); - - // remove span from the DOM - def = makeDeferred(); - widget.state.hide = true; - patchNextFrame(cb => { - expect(spanNode.className).toBe("chimay-leave chimay-leave-active"); - cb(); - expect(spanNode.className).toBe("chimay-leave-active chimay-leave-to"); - def.resolve(); - }); - await def; // wait for the mocked repaint to be done - spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition - expect(spanNode.className).toBe(""); - }); - - test("t-transition combined with t-ref", async () => { - expect.assertions(5); - - env.qweb.addTemplate( - "TestWidget", - `
blue
` - ); - class TestWidget extends Widget { - state = { hide: false }; - } - const widget = new TestWidget(env); - - // insert widget into the DOM - let def = makeDeferred(); - var spanNode; - patchNextFrame(cb => { - expect(spanNode.className).toBe("chimay-enter chimay-enter-active"); - cb(); - expect(spanNode.className).toBe("chimay-enter-active chimay-enter-to"); - def.resolve(); - }); - await widget.mount(fixture); - spanNode = widget.el!.children[0]; - expect(widget.refs.span).toBe(spanNode); - expect(spanNode.className).toBe("chimay-enter chimay-enter-active"); - await def; // wait for the mocked repaint to be done - spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition - expect(spanNode.className).toBe(""); - }); - - test("t-transition combined with t-widget", async () => { - expect.assertions(5); - - env.qweb.addTemplate( - "Parent", - `
` - ); - env.qweb.addTemplate("Child", `blue`); - class Parent extends Widget { - widgets = { Child: Child }; - } - class Child extends Widget {} - const widget = new Parent(env); - - let def = makeDeferred(); - var spanNode; - patchNextFrame(cb => { - expect(fixture.innerHTML).toBe( - '
blue
' - ); - cb(performance.now()); - expect(fixture.innerHTML).toBe( - '
blue
' - ); - def.resolve(); - }); - await widget.mount(fixture); - spanNode = widget.el!.children[0]; - - expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot(); - expect(fixture.innerHTML).toBe( - '
blue
' - ); - - await def; // wait for the mocked repaint to be done - spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition - expect(fixture.innerHTML).toBe('
blue
'); - }); - - test("t-transition combined with t-widget and t-if", async () => { - expect.assertions(8); - - env.qweb.addTemplate( - "Parent", - `
` - ); - env.qweb.addTemplate("Child", `blue`); - class Parent extends Widget { - widgets = { Child: Child }; - state = { display: true }; - } - class Child extends Widget {} - const widget = new Parent(env); - - let def = makeDeferred(); - var spanNode; - patchNextFrame(cb => { - expect(fixture.innerHTML).toBe( - '
blue
' - ); - cb(performance.now()); - expect(fixture.innerHTML).toBe( - '
blue
' - ); - def.resolve(); - }); - await widget.mount(fixture); - spanNode = widget.el!.children[0]; - - expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot(); - expect(fixture.innerHTML).toBe( - '
blue
' - ); - - await def; // wait for the mocked repaint to be done - spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition - expect(fixture.innerHTML).toBe('
blue
'); - - // remove span from the DOM - def = makeDeferred(); - widget.state.display = false; - patchNextFrame(cb => { - expect(fixture.innerHTML).toBe( - '
blue
' - ); - cb(performance.now()); - expect(fixture.innerHTML).toBe( - '
blue
' - ); - def.resolve(); - }); - await def; // wait for the mocked repaint to be done - spanNode.dispatchEvent(new Event("transitionend")); // mock end of css transition - expect(fixture.innerHTML).toBe("
"); - }); -}); diff --git a/tests/helpers.ts b/tests/helpers.ts index 6c8b278a..2c0e23fe 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -1,5 +1,6 @@ import { Env } from "../src/component"; -import { QWeb, UTILS } from "../src/qweb_core"; +import { EvalContext, QWeb, UTILS } from "../src/qweb_core"; +import { patch } from "../src/vdom"; import "../src/qweb_directives"; import "../src/qweb_extensions"; @@ -43,13 +44,50 @@ export function makeTestEnv(): Env { }; } +export function trim(str: string): string { + return str.replace(/\s/g, ""); +} + +export function renderToDOM( + qweb: QWeb, + template: string, + context: EvalContext = {}, + extra?: any +): HTMLElement | Text { + const vnode = qweb.render(template, context, extra); + + // we snapshot here the compiled code. This is useful to prevent unwanted code + // change. + expect(qweb.templates[template].fn.toString()).toMatchSnapshot(); + + if (vnode.sel === undefined) { + return document.createTextNode(vnode.text!); + } + const node = document.createElement(vnode.sel!); + const result = patch(node, vnode); + return result.elm as HTMLElement; +} + +export function renderToString( + qweb: QWeb, + t: string, + context: EvalContext = {} +): string { + const node = renderToDOM(qweb, t, context); + return node instanceof Text ? node.textContent! : node.outerHTML; +} + +// hereafter, we define two helpers to patch/unpatch the nextFrame utils. This +// is useful for animations tests, as we hook before repaints to trigger +// animations (thanks to requestAnimationFrame). Patching nextFrame allows to +// simulate calls to this hook. One must not forget to unpatch afterwards. let nextFrame = UTILS.nextFrame; -export function patchNextFrame(f: Function): void { +export function patchNextFrame(f: Function) { UTILS.nextFrame = (cb: () => void) => { setTimeout(() => f(cb)); }; } -export function unpatchNextFrame(): void { +export function unpatchNextFrame() { UTILS.nextFrame = nextFrame; } diff --git a/tests/qweb.test.ts b/tests/qweb.test.ts index 7fb5dd61..4ba216ea 100644 --- a/tests/qweb.test.ts +++ b/tests/qweb.test.ts @@ -1,10 +1,9 @@ -import { EvalContext, QWeb } from "../src/qweb_core"; -import { patch } from "../src/vdom"; +import { QWeb } from "../src/qweb_core"; import { - makeDeferred, normalize, - patchNextFrame, - unpatchNextFrame + renderToDOM, + renderToString, + trim } from "./helpers"; //------------------------------------------------------------------------------ @@ -15,45 +14,11 @@ import { // - qweb: a new QWeb instance let qweb: QWeb; -let cssEl: HTMLElement; beforeEach(() => { qweb = new QWeb(); }); -function trim(str: string): string { - return str.replace(/\s/g, ""); -} - -function renderToDOM( - qweb: QWeb, - template: string, - context: EvalContext = {}, - extra?: any -): HTMLElement | Text { - const vnode = qweb.render(template, context, extra); - - // we snapshot here the compiled code. This is useful to prevent unwanted code - // change. - expect(qweb.templates[template].fn.toString()).toMatchSnapshot(); - - if (vnode.sel === undefined) { - return document.createTextNode(vnode.text!); - } - const node = document.createElement(vnode.sel!); - const result = patch(node, vnode); - return result.elm as HTMLElement; -} - -function renderToString( - qweb: QWeb, - t: string, - context: EvalContext = {} -): string { - const node = renderToDOM(qweb, t, context); - return node instanceof Text ? node.textContent! : node.outerHTML; -} - //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ @@ -1285,60 +1250,3 @@ describe("debugging", () => { console.log = consoleLog; }); }); - -describe("animations", () => { - beforeEach(() => { - cssEl = document.createElement("style"); - let css = ` - .chimay-enter-active, .chimay-leave-active { - transition-property: opacity; - transition-duration: 0.1s; - } - .chimay-enter, .chimay-leave-to { - opacity: 0; - } - `; - cssEl.textContent = css.trim(); - document.head.appendChild(cssEl); - }); - - afterEach(() => { - document.head.removeChild(cssEl); - unpatchNextFrame(); - }); - - test("t-transition, on a simple node (insert)", async () => { - expect.assertions(5); - qweb.addTemplate("test", `blue`); - - let def = makeDeferred(); - patchNextFrame(cb => { - expect(node.className).toBe("chimay-enter chimay-enter-active"); - cb(); - expect(node.className).toBe("chimay-enter-active chimay-enter-to"); - def.resolve(); - }); - - let node: HTMLElement = renderToDOM(qweb, "test"); - expect(node.className).toBe("chimay-enter chimay-enter-active"); - await def; // wait for the mocked repaint to be done - node.dispatchEvent(new Event("transitionend")); // mock end of css transition - expect(node.className).toBe(""); - }); - - test("t-transition with no delay/duration", async () => { - expect.assertions(4); - qweb.addTemplate("test", `blue`); - - let def = makeDeferred(); - patchNextFrame(cb => { - expect(node.className).toBe("jupiler-enter jupiler-enter-active"); - cb(); - expect(node.className).toBe(""); - def.resolve(); - }); - let node: HTMLElement = renderToDOM(qweb, "test"); - expect(node.className).toBe("jupiler-enter jupiler-enter-active"); - await def; - }); -});