diff --git a/doc/qweb.md b/doc/qweb.md index 7ee88951..3aee3e69 100644 --- a/doc/qweb.md +++ b/doc/qweb.md @@ -25,3 +25,11 @@ result = qweb.render("sometemplate", { name: "world" }); - t-on directive - t-widget, t-props, t-key - t-ref + +## Note on white spaces + +White spaces in a templates are handled in a special way: + +- consecutive whitespaces are always condensed to a single whitespace +- if a whitespace-only text node contains a linebreak, it is ignored +- the previous rules do not apply if we are in a `
` tag
diff --git a/examples/todoapp/components/TodoApp.js b/examples/todoapp/components/TodoApp.js
index ab3aca07..636630a9 100644
--- a/examples/todoapp/components/TodoApp.js
+++ b/examples/todoapp/components/TodoApp.js
@@ -33,7 +33,8 @@ class TodoApp extends Component {
}
get remainingText() {
- return (this.remaining < 2 ? "item" : "items") + " left";
+ const items = this.remaining < 2 ? "item" : "items";
+ return ` ${items} left`;
}
addTodo(ev) {
diff --git a/examples/web/static/tests/ui/__snapshots__/home_menu.test.ts.snap b/examples/web/static/tests/ui/__snapshots__/home_menu.test.ts.snap
index 1b89d629..1ea0da1b 100644
--- a/examples/web/static/tests/ui/__snapshots__/home_menu.test.ts.snap
+++ b/examples/web/static/tests/ui/__snapshots__/home_menu.test.ts.snap
@@ -1,24 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`can be rendered 1`] = `
-""
-`;
+exports[`can be rendered 1`] = `""`;
diff --git a/examples/web/static/tests/ui/__snapshots__/navbar.test.ts.snap b/examples/web/static/tests/ui/__snapshots__/navbar.test.ts.snap
index 92ae0b43..c59d87fd 100644
--- a/examples/web/static/tests/ui/__snapshots__/navbar.test.ts.snap
+++ b/examples/web/static/tests/ui/__snapshots__/navbar.test.ts.snap
@@ -1,45 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`can be rendered (in home menu, no app) 1`] = `
-""
-`;
+exports[`can be rendered (in home menu, no app) 1`] = `""`;
-exports[`can be rendered (in home menu, one active app) 1`] = `
-""
-`;
+exports[`can be rendered (in home menu, one active app) 1`] = `""`;
-exports[`can render one menu item 1`] = `
-""
-`;
+exports[`can render one menu item 1`] = `""`;
-exports[`mobile mode: navbar is different 1`] = `
-""
-`;
+exports[`mobile mode: navbar is different 1`] = `""`;
diff --git a/examples/web/static/tests/ui/__snapshots__/notification.test.ts.snap b/examples/web/static/tests/ui/__snapshots__/notification.test.ts.snap
index 778e21d7..b847fb7e 100644
--- a/examples/web/static/tests/ui/__snapshots__/notification.test.ts.snap
+++ b/examples/web/static/tests/ui/__snapshots__/notification.test.ts.snap
@@ -1,27 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`can be rendered (type = warning) 1`] = `
-"
-
-
-
- title
-
-
- message
-
- "
-`;
+exports[`can be rendered (type = warning) 1`] = `"titlemessage"`;
-exports[`can be rendered 1`] = `
-"
-
-
-
- title
-
-
- message
-
- "
-`;
+exports[`can be rendered 1`] = `"titlemessage"`;
diff --git a/examples/web/static/tests/ui/__snapshots__/root.test.ts.snap b/examples/web/static/tests/ui/__snapshots__/root.test.ts.snap
index e01ee020..2fdecf1d 100644
--- a/examples/web/static/tests/ui/__snapshots__/root.test.ts.snap
+++ b/examples/web/static/tests/ui/__snapshots__/root.test.ts.snap
@@ -1,257 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`can be rendered (in home menu) 1`] = `
-"
-
-
-
-
-
-
- Loading
- "
-`;
+exports[`can be rendered (in home menu) 1`] = `"Loading"`;
-exports[`clicks on client action with invalid key => empty widget is rendered + warning 1`] = `
-"
-
-
-
-
-
-
-
-
-
- Invalid Client Action
-
-
- Cannot find widget 'mail.discuss' in the action registry
-
-
-
-
- Loading
- "
-`;
+exports[`clicks on client action with invalid key => empty widget is rendered + warning 1`] = `"Invalid Client ActionCannot find widget 'mail.discuss' in the action registryLoading"`;
-exports[`if url has action_id, will render action and navigate to proper menu_id 1`] = `
-"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Value: 4
-
-
-
-
- Value: 400
-
-
-
-
- Current Color:
- red
-
-
-
-
-
-
-
-
-
-
- Loading
- "
-`;
+exports[`if url has action_id, will render action and navigate to proper menu_id 1`] = `"Value: 4Value: 400Current Color: redLoading"`;
-exports[`open act window action with invalid viewtype => empty widget is rendered + warning 1`] = `
-"
-
-
-
-
-
-
-
-
-
- Invalid View type
-
-
- Cannot find view of type 'kanban' in the view registry
-
-
-
-
- Loading
- "
-`;
+exports[`open act window action with invalid viewtype => empty widget is rendered + warning 1`] = `"Invalid View typeCannot find view of type 'kanban' in the view registryLoading"`;
-exports[`start with no action => clicks on client action => discuss is rendered 1`] = `
-"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Value: 4
-
-
-
-
- Value: 400
-
-
-
-
- Current Color:
- red
-
-
-
-
-
-
-
-
-
-
- Loading
- "
-`;
+exports[`start with no action => clicks on client action => discuss is rendered 1`] = `"Value: 4Value: 400Current Color: redLoading"`;
diff --git a/src/qweb.ts b/src/qweb.ts
index ec7d796d..de3d7715 100644
--- a/src/qweb.ts
+++ b/src/qweb.ts
@@ -32,6 +32,9 @@ const DISABLED_TAGS = [
"optgroup"
];
+const lineBreakRE = /[\r\n]/;
+const whitespaceRE = /\s+/g;
+
//------------------------------------------------------------------------------
// Compilation Context
//------------------------------------------------------------------------------
@@ -49,6 +52,7 @@ export class Context {
shouldDefineOwner: boolean = false;
shouldProtectContext: boolean = false;
inLoop: boolean = false;
+ inPreTag: boolean = false;
constructor() {
this.rootContext = this;
@@ -84,6 +88,12 @@ export class Context {
return newContext;
}
+ withInPre(): Context {
+ const newContext = Object.create(this);
+ newContext.inPreTag = true;
+ return newContext;
+ }
+
withCaller(node: Element): Context {
const newContext = Object.create(this);
newContext.caller = node;
@@ -336,6 +346,12 @@ export class QWeb {
if (!(node instanceof Element)) {
// this is a text node, there are no directive to apply
let text = node.textContent!;
+ if (!ctx.inPreTag) {
+ if (lineBreakRE.test(text) && !text.trim()) {
+ return;
+ }
+ text = text.replace(whitespaceRE, " ");
+ }
if (ctx.parentNode) {
ctx.addLine(`c${ctx.parentNode}.push({text: \`${text}\`});`);
} else {
@@ -410,6 +426,9 @@ export class QWeb {
}
}
}
+ if (node.nodeName === "pre") {
+ ctx = ctx.withInPre();
+ }
this._compileChildren(node, ctx);
diff --git a/tests/__snapshots__/component.test.ts.snap b/tests/__snapshots__/component.test.ts.snap
index af1c3683..a96a52b6 100644
--- a/tests/__snapshots__/component.test.ts.snap
+++ b/tests/__snapshots__/component.test.ts.snap
@@ -8,8 +8,6 @@ exports[`composition sub widgets with some state rendered in a loop 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let _2 = context['state'].numbers;
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
@@ -22,8 +20,6 @@ exports[`composition sub widgets with some state rendered in a loop 1`] = `
context.number_index = i;
context.number = _3[i];
context.number_value = _4[i];
- c1.push({text: \`
- \`});
//WIDGET
let key8 = context['number'];
let _5_index = c1.length;
@@ -60,11 +56,7 @@ exports[`composition sub widgets with some state rendered in a loop 1`] = `
def6 = def6.then(()=>{if (w7.__widget__.isDestroyed) {return};let vnode;if (!w7.__widget__.vnode){vnode=w7.__widget__.pvnode} else { vnode=h(w7.__widget__.vnode.sel, {key: key8});vnode.elm=w7.el;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w7.el,a.elm);a.elm=w7.el;w7.__mount();},remove(){w7.destroy()}}}c1[_5_index]=vnode;});
}
extra.promises.push(def6);
- c1.push({text: \`
- \`});
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
diff --git a/tests/__snapshots__/qweb.test.ts.snap b/tests/__snapshots__/qweb.test.ts.snap
index 8da0137c..32f1fab4 100644
--- a/tests/__snapshots__/qweb.test.ts.snap
+++ b/tests/__snapshots__/qweb.test.ts.snap
@@ -250,8 +250,6 @@ exports[`foreach does not pollute the rendering context 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let _2 = [1];
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
@@ -269,8 +267,6 @@ exports[`foreach does not pollute the rendering context 1`] = `
c1.push({text: e5});
}
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -282,8 +278,6 @@ exports[`foreach iterate on items (on a element node) 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let _2 = [1, 2];
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
@@ -304,8 +298,6 @@ exports[`foreach iterate on items (on a element node) 1`] = `
c5.push({text: e6});
}
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -317,8 +309,6 @@ exports[`foreach iterate on items 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let _2 = [3, 2, 1];
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
@@ -331,8 +321,7 @@ exports[`foreach iterate on items 1`] = `
context.item_index = i;
context.item = _3[i];
context.item_value = _4[i];
- c1.push({text: \`
- [\`});
+ c1.push({text: \` [\`});
let e5 = context['item_index'];
if (e5 || e5 === 0) {
c1.push({text: e5});
@@ -347,11 +336,8 @@ exports[`foreach iterate on items 1`] = `
if (e7 || e7 === 0) {
c1.push({text: e7});
}
- c1.push({text: \`]
- \`});
+ c1.push({text: \`] \`});
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -363,8 +349,6 @@ exports[`foreach iterate, dict param 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let _2 = context['value'];
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
@@ -377,8 +361,7 @@ exports[`foreach iterate, dict param 1`] = `
context.item_index = i;
context.item = _3[i];
context.item_value = _4[i];
- c1.push({text: \`
- [\`});
+ c1.push({text: \` [\`});
let e5 = context['item_index'];
if (e5 || e5 === 0) {
c1.push({text: e5});
@@ -398,11 +381,8 @@ exports[`foreach iterate, dict param 1`] = `
if (e8 || e8 === 0) {
c1.push({text: e8});
}
- c1.push({text: \`]
- \`});
+ c1.push({text: \`] \`});
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -426,8 +406,7 @@ exports[`foreach iterate, integer param 1`] = `
context.item_index = i;
context.item = _3[i];
context.item_value = _4[i];
- c1.push({text: \`
- [\`});
+ c1.push({text: \` [\`});
let e5 = context['item_index'];
if (e5 || e5 === 0) {
c1.push({text: e5});
@@ -442,8 +421,7 @@ exports[`foreach iterate, integer param 1`] = `
if (e7 || e7 === 0) {
c1.push({text: e7});
}
- c1.push({text: \`]
- \`});
+ c1.push({text: \`] \`});
}
return vn1;
}"
@@ -456,8 +434,6 @@ exports[`foreach iterate, position 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let _2 = 5;
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
@@ -470,8 +446,7 @@ exports[`foreach iterate, position 1`] = `
context.elem_index = i;
context.elem = _3[i];
context.elem_value = _4[i];
- c1.push({text: \`
- -\`});
+ c1.push({text: \` -\`});
if (context['elem_first']) {
c1.push({text: \` first\`});
}
@@ -483,11 +458,8 @@ exports[`foreach iterate, position 1`] = `
if (e5 || e5 === 0) {
c1.push({text: e5});
}
- c1.push({text: \`)
- \`});
+ c1.push({text: \`) \`});
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -517,8 +489,6 @@ exports[`misc global 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let _2 = [4,5,6];
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
@@ -531,8 +501,6 @@ exports[`misc global 1`] = `
context.value_index = i;
context.value = _3[i];
context.value_value = _4[i];
- c1.push({text: \`
- \`});
let c5 = [], p5 = {key:5,on:{}};
let vn5 = h('span', p5, c5);
c1.push(vn5);
@@ -540,14 +508,10 @@ exports[`misc global 1`] = `
if (e6 || e6 === 0) {
c5.push({text: e6});
}
- c1.push({text: \`
- \`});
let _7 = 'agüero';
let c8 = [], p8 = {key:8,attrs:{\\"falló\\": _7},on:{}};
let vn8 = h('Año', p8, c8);
c1.push(vn8);
- c8.push({text: \`
- \`});
let c9 = [], p9 = {key:9,on:{}};
let vn9 = h('span', p9, c9);
c8.push(vn9);
@@ -557,8 +521,6 @@ exports[`misc global 1`] = `
} else {
c9.push({text: \`foo default\`});
}
- c8.push({text: \`
- \`});
let c11 = [], p11 = {key:11,on:{}};
let vn11 = h('span', p11, c11);
c8.push(vn11);
@@ -568,10 +530,6 @@ exports[`misc global 1`] = `
} else {
c11.push({text: \`foo default\`});
}
- c8.push({text: \`
- \`});
- c8.push({text: \`
- \`});
let c13 = [], p13 = {key:13,on:{}};
let vn13 = h('span', p13, c13);
c8.push(vn13);
@@ -581,13 +539,7 @@ exports[`misc global 1`] = `
} else {
c13.push({text: \`foo default\`});
}
- c8.push({text: \`
- \`});
- c1.push({text: \`
- \`});
}
- c1.push({text: \`
- \`});
let c15 = [], p15 = {key:15,on:{}};
let vn15 = h('div', p15, c15);
c1.push(vn15);
@@ -602,8 +554,6 @@ exports[`misc global 1`] = `
} else {
c15.push({text: \`toto default\`});
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -626,52 +576,36 @@ exports[`special cases for some boolean html attributes/properties various boole
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let _2 = 'checkbox';
let _3 = 'checked';
let c4 = [], p4 = {key:4,attrs:{type: _2,checked: _3},props:{checked: _3}};
let vn4 = h('input', p4, c4);
c1.push(vn4);
- c1.push({text: \`
- \`});
let _5 = 'checked';
let c6 = [], p6 = {key:6,attrs:{checked: _5}};
let vn6 = h('input', p6, c6);
c1.push(vn6);
- c1.push({text: \`
- \`});
let _7 = 'checked';
let c8 = [], p8 = {key:8,attrs:{checked: _7}};
let vn8 = h('div', p8, c8);
c1.push(vn8);
- c1.push({text: \`
- \`});
let _9 = 'selected';
let c10 = [], p10 = {key:10,attrs:{selected: _9}};
let vn10 = h('div', p10, c10);
c1.push(vn10);
- c1.push({text: \`
- \`});
let _11 = 'selected';
let _12 = '1';
let c13 = [], p13 = {key:13,attrs:{selected: _11,other: _12},props:{selected: _11}};
let vn13 = h('option', p13, c13);
c1.push(vn13);
- c1.push({text: \`
- \`});
let _14 = 'readonly';
let c15 = [], p15 = {key:15,attrs:{readonly: _14},props:{readonly: _14}};
let vn15 = h('input', p15, c15);
c1.push(vn15);
- c1.push({text: \`
- \`});
let _16 = 'disabled';
let c17 = [], p17 = {key:17,attrs:{disabled: _16},props:{disabled: _16}};
let vn17 = h('button', p17, c17);
c1.push(vn17);
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -751,17 +685,11 @@ exports[`t-call (template calling scoped parameters 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
c1.push({text: \`ok\`});
- c1.push({text: \`
- \`});
let e2 = context['foo'];
if (e2 || e2 === 0) {
c1.push({text: e2});
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -915,28 +843,20 @@ exports[`t-if boolean value condition else 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
let c2 = [], p2 = {key:2};
let vn2 = h('span', p2, c2);
c1.push(vn2);
c2.push({text: \`begin\`});
- c1.push({text: \`
- \`});
if (context['condition']) {
c1.push({text: \`ok\`});
}
else {
c1.push({text: \`ok-else\`});
}
- c1.push({text: \`
- \`});
let c3 = [], p3 = {key:3};
let vn3 = h('span', p3, c3);
c1.push(vn3);
c3.push({text: \`end\`});
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -1010,48 +930,30 @@ exports[`t-if can use some boolean operators in expressions 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
- c1.push({text: \`
- \`});
if (context['cond1'] && context['cond2']) {
c1.push({text: \`and\`});
}
- c1.push({text: \`
- \`});
if (context['cond1'] && context['cond3']) {
c1.push({text: \`nope\`});
}
- c1.push({text: \`
- \`});
if (context['cond1'] || context['cond3']) {
c1.push({text: \`or\`});
}
- c1.push({text: \`
- \`});
if (context['cond3'] || context['cond4']) {
c1.push({text: \`nope\`});
}
- c1.push({text: \`
- \`});
if (context['m'] > 3) {
c1.push({text: \`mgt\`});
}
- c1.push({text: \`
- \`});
if (context['n'] > 3) {
c1.push({text: \`ngt\`});
}
- c1.push({text: \`
- \`});
if (context['m'] < 3) {
c1.push({text: \`mlt\`});
}
- c1.push({text: \`
- \`});
if (context['n'] < 3) {
c1.push({text: \`nlt\`});
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -1117,8 +1019,6 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
let h = this.utils.h;
let c1 = [], p1 = {key:1};
let vn1 = h('ul', p1, c1);
- c1.push({text: \`
- \`});
let _2 = ['someval'];
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
@@ -1140,8 +1040,6 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
p6.on['click'] = context['activate'].bind(owner, context['action']);
c6.push({text: \`link\`});
}
- c1.push({text: \`
- \`});
return vn1;
}"
`;
@@ -1387,3 +1285,76 @@ exports[`t-set value priority 1`] = `
return vn1;
}"
`;
+
+exports[`whitespace handling consecutives whitespaces are condensed into a single space 1`] = `
+"function anonymous(context,extra
+) {
+ let h = this.utils.h;
+ let c1 = [], p1 = {key:1};
+ let vn1 = h('div', p1, c1);
+ c1.push({text: \` abc \`});
+ return vn1;
+}"
+`;
+
+exports[`whitespace handling nothing is done in pre tags 1`] = `
+"function anonymous(context,extra
+) {
+ let h = this.utils.h;
+ let c1 = [], p1 = {key:1};
+ let vn1 = h('pre', p1, c1);
+ c1.push({text: \` \`});
+ return vn1;
+}"
+`;
+
+exports[`whitespace handling nothing is done in pre tags 2`] = `
+"function anonymous(context,extra
+) {
+ let h = this.utils.h;
+ let c1 = [], p1 = {key:1};
+ let vn1 = h('pre', p1, c1);
+ c1.push({text: \`
+ some text
+ \`});
+ return vn1;
+}"
+`;
+
+exports[`whitespace handling nothing is done in pre tags 3`] = `
+"function anonymous(context,extra
+) {
+ let h = this.utils.h;
+ let c1 = [], p1 = {key:1};
+ let vn1 = h('pre', p1, c1);
+ c1.push({text: \`
+
+ \`});
+ return vn1;
+}"
+`;
+
+exports[`whitespace handling white space only text nodes are condensed into a single space 1`] = `
+"function anonymous(context,extra
+) {
+ let h = this.utils.h;
+ let c1 = [], p1 = {key:1};
+ let vn1 = h('div', p1, c1);
+ c1.push({text: \` \`});
+ return vn1;
+}"
+`;
+
+exports[`whitespace handling whitespace only text nodes with newlines are removed 1`] = `
+"function anonymous(context,extra
+) {
+ let h = this.utils.h;
+ let c1 = [], p1 = {key:1};
+ let vn1 = h('div', p1, c1);
+ let c2 = [], p2 = {key:2};
+ let vn2 = h('span', p2, c2);
+ c1.push(vn2);
+ c2.push({text: \`abc\`});
+ return vn1;
+}"
+`;
diff --git a/tests/__snapshots__/store.test.ts.snap b/tests/__snapshots__/store.test.ts.snap
index 392d8141..62eeff61 100644
--- a/tests/__snapshots__/store.test.ts.snap
+++ b/tests/__snapshots__/store.test.ts.snap
@@ -1,15 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`connecting a component to store connecting a component works 1`] = `
-"
-
- "
-`;
+exports[`connecting a component to store connecting a component works 1`] = `""`;
-exports[`connecting a component to store connecting a component works 2`] = `
-"
-
- hello
-
- "
-`;
+exports[`connecting a component to store connecting a component works 2`] = `"hello"`;
diff --git a/tests/qweb.test.ts b/tests/qweb.test.ts
index 64041d63..60f2c793 100644
--- a/tests/qweb.test.ts
+++ b/tests/qweb.test.ts
@@ -921,3 +921,48 @@ describe("special cases for some boolean html attributes/properties", () => {
renderToString(qweb, "test", { flag: true });
});
});
+
+describe("whitespace handling", () => {
+ test("white space only text nodes are condensed into a single space", () => {
+ qweb.addTemplate("test", ` `);
+ const result = renderToString(qweb, "test");
+ expect(result).toBe(` `);
+ });
+
+ test("consecutives whitespaces are condensed into a single space", () => {
+ qweb.addTemplate("test", ` abc `);
+ const result = renderToString(qweb, "test");
+ expect(result).toBe(` abc `);
+ });
+
+ test("whitespace only text nodes with newlines are removed", () => {
+ qweb.addTemplate(
+ "test",
+ `
+ abc
+ `
+ );
+ const result = renderToString(qweb, "test");
+ expect(result).toBe(`abc`);
+ });
+
+ test("nothing is done in pre tags", () => {
+ qweb.addTemplate("test", `
`);
+ const result = renderToString(qweb, "test");
+ expect(result).toBe(`
`);
+
+ const pretagtext = `
+ some text
+
`;
+ qweb.addTemplate("test2", pretagtext);
+ const result2 = renderToString(qweb, "test2");
+ expect(result2).toBe(pretagtext);
+
+ const pretagwithonlywhitespace = `
+
+
`;
+ qweb.addTemplate("test3", pretagwithonlywhitespace);
+ const result3 = renderToString(qweb, "test3");
+ expect(result3).toBe(pretagwithonlywhitespace);
+ });
+});