mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
@@ -827,6 +827,153 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-model directive .lazy modifier 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('input', p2, c2);
|
||||
c1.push(vn2);
|
||||
p2.props = {value: context.state['text']};
|
||||
extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context.state['text'] = ev.target.value});
|
||||
p2.on['change'] = extra.handlers['change' + 2];
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('span', p3, c3);
|
||||
c1.push(vn3);
|
||||
var _4 = context['state'].text;
|
||||
if (_4 || _4 === 0) {
|
||||
c3.push({text: _4});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-model directive basic use, on an input 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('input', p2, c2);
|
||||
c1.push(vn2);
|
||||
p2.props = {value: context.state['text']};
|
||||
extra.handlers['input' + 2] = extra.handlers['input' + 2] || ((ev) => {context.state['text'] = ev.target.value});
|
||||
p2.on['input'] = extra.handlers['input' + 2];
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('span', p3, c3);
|
||||
c1.push(vn3);
|
||||
var _4 = context['state'].text;
|
||||
if (_4 || _4 === 0) {
|
||||
c3.push({text: _4});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-model directive on a select 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('select', p2, c2);
|
||||
c1.push(vn2);
|
||||
p2.props = {value: context.state['color']};
|
||||
extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context.state['color'] = ev.target.value});
|
||||
p2.on['change'] = extra.handlers['change' + 2];
|
||||
var _3 = '';
|
||||
let c4 = [], p4 = {key:4,attrs:{value: _3}};
|
||||
var vn4 = h('option', p4, c4);
|
||||
c2.push(vn4);
|
||||
c4.push({text: \`Please select one\`});
|
||||
var _5 = 'red';
|
||||
let c6 = [], p6 = {key:6,attrs:{value: _5}};
|
||||
var vn6 = h('option', p6, c6);
|
||||
c2.push(vn6);
|
||||
c6.push({text: \`Red\`});
|
||||
var _7 = 'blue';
|
||||
let c8 = [], p8 = {key:8,attrs:{value: _7}};
|
||||
var vn8 = h('option', p8, c8);
|
||||
c2.push(vn8);
|
||||
c8.push({text: \`Blue\`});
|
||||
let c9 = [], p9 = {key:9};
|
||||
var vn9 = h('span', p9, c9);
|
||||
c1.push(vn9);
|
||||
c9.push({text: \`Choice: \`});
|
||||
var _10 = context['state'].color;
|
||||
if (_10 || _10 === 0) {
|
||||
c9.push({text: _10});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-model directive on an input type=radio 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 'radio';
|
||||
var _3 = 'one';
|
||||
var _4 = 'One';
|
||||
let c5 = [], p5 = {key:5,attrs:{type: _2,id: _3,value: _4},on:{}};
|
||||
var vn5 = h('input', p5, c5);
|
||||
c1.push(vn5);
|
||||
p5.props = {checked:context.state['choice'] === 'One'};
|
||||
extra.handlers['click' + 5] = extra.handlers['click' + 5] || ((ev) => {context.state['choice'] = ev.target.value});
|
||||
p5.on['click'] = extra.handlers['click' + 5];
|
||||
var _6 = 'radio';
|
||||
var _7 = 'two';
|
||||
var _8 = 'Two';
|
||||
let c9 = [], p9 = {key:9,attrs:{type: _6,id: _7,value: _8},on:{}};
|
||||
var vn9 = h('input', p9, c9);
|
||||
c1.push(vn9);
|
||||
p9.props = {checked:context.state['choice'] === 'Two'};
|
||||
extra.handlers['click' + 9] = extra.handlers['click' + 9] || ((ev) => {context.state['choice'] = ev.target.value});
|
||||
p9.on['click'] = extra.handlers['click' + 9];
|
||||
let c10 = [], p10 = {key:10};
|
||||
var vn10 = h('span', p10, c10);
|
||||
c1.push(vn10);
|
||||
c10.push({text: \`Choice: \`});
|
||||
var _11 = context['state'].choice;
|
||||
if (_11 || _11 === 0) {
|
||||
c10.push({text: _11});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-model directive on an input, type=checkbox 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 'checkbox';
|
||||
let c3 = [], p3 = {key:3,attrs:{type: _2},on:{}};
|
||||
var vn3 = h('input', p3, c3);
|
||||
c1.push(vn3);
|
||||
p3.props = {checked: context.state['flag']};
|
||||
extra.handlers['input' + 3] = extra.handlers['input' + 3] || ((ev) => {context.state['flag'] = ev.target.checked});
|
||||
p3.on['input'] = extra.handlers['input' + 3];
|
||||
let c4 = [], p4 = {key:4};
|
||||
var vn4 = h('span', p4, c4);
|
||||
c1.push(vn4);
|
||||
if (context['state'].flag) {
|
||||
c4.push({text: \`yes\`});
|
||||
}
|
||||
else {
|
||||
c4.push({text: \`no\`});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive can define and call slots 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
|
||||
+234
-1
@@ -6,7 +6,8 @@ import {
|
||||
makeTestEnv,
|
||||
nextMicroTick,
|
||||
nextTick,
|
||||
normalize
|
||||
normalize,
|
||||
editInput
|
||||
} from "./helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -2902,3 +2903,235 @@ describe("t-slot directive", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("t-model directive", () => {
|
||||
test("basic use, on an input", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model="text"/>
|
||||
<span><t t-esc="state.text"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { text: "" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><input><span></span></div>");
|
||||
|
||||
const input = fixture.querySelector("input")!;
|
||||
await editInput(input, "test");
|
||||
expect(comp.state.text).toBe("test");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("on an input, type=checkbox", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input type="checkbox" t-model="flag"/>
|
||||
<span>
|
||||
<t t-if="state.flag">yes</t>
|
||||
<t t-else="1">no</t>
|
||||
</span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { flag: false };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><input type="checkbox"><span>no</span></div>'
|
||||
);
|
||||
|
||||
let input = fixture.querySelector("input")!;
|
||||
input.click();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><input type="checkbox"><span>yes</span></div>'
|
||||
);
|
||||
expect(comp.state.flag).toBe(true);
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
|
||||
input.click();
|
||||
await nextTick();
|
||||
expect(comp.state.flag).toBe(false);
|
||||
});
|
||||
|
||||
test("on an textarea", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<textarea t-model="text"/>
|
||||
<span><t t-esc="state.text"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { text: "" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><textarea></textarea><span></span></div>"
|
||||
);
|
||||
|
||||
const textarea = fixture.querySelector("textarea")!;
|
||||
await editInput(textarea, "test");
|
||||
expect(comp.state.text).toBe("test");
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><textarea></textarea><span>test</span></div>"
|
||||
);
|
||||
});
|
||||
|
||||
test("on an input type=radio", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input type="radio" id="one" value="One" t-model="choice"/>
|
||||
<input type="radio" id="two" value="Two" t-model="choice"/>
|
||||
<span>Choice: <t t-esc="state.choice"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { choice: "" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><input type="radio" id="one" value="One"><input type="radio" id="two" value="Two"><span>Choice: </span></div>'
|
||||
);
|
||||
|
||||
const firstInput = fixture.querySelector("input")!;
|
||||
firstInput.click();
|
||||
await nextTick();
|
||||
expect(comp.state.choice).toBe("One");
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><input type="radio" id="one" value="One"><input type="radio" id="two" value="Two"><span>Choice: One</span></div>'
|
||||
);
|
||||
|
||||
const secondInput = fixture.querySelectorAll("input")[1];
|
||||
secondInput.click();
|
||||
await nextTick();
|
||||
expect(comp.state.choice).toBe("Two");
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><input type="radio" id="one" value="One"><input type="radio" id="two" value="Two"><span>Choice: Two</span></div>'
|
||||
);
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("on a select", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<select t-model="color">
|
||||
<option value="">Please select one</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="blue">Blue</option>
|
||||
</select>
|
||||
<span>Choice: <t t-esc="state.color"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { color: "" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><select><option value="">Please select one</option><option value="red">Red</option><option value="blue">Blue</option></select><span>Choice: </span></div>'
|
||||
);
|
||||
|
||||
const select = fixture.querySelector("select")!;
|
||||
select.value = "red";
|
||||
select.dispatchEvent(new Event("change"));
|
||||
await nextTick();
|
||||
|
||||
expect(comp.state.color).toBe("red");
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><select><option value="">Please select one</option><option value="red">Red</option><option value="blue">Blue</option></select><span>Choice: red</span></div>'
|
||||
);
|
||||
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test(".lazy modifier", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model.lazy="text"/>
|
||||
<span><t t-esc="state.text"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { text: "" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><input><span></span></div>");
|
||||
|
||||
const input = fixture.querySelector("input")!;
|
||||
input.value = "test";
|
||||
input.dispatchEvent(new Event("input"));
|
||||
await nextTick();
|
||||
expect(comp.state.text).toBe("");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span></span></div>");
|
||||
input.dispatchEvent(new Event("change"));
|
||||
await nextTick();
|
||||
expect(comp.state.text).toBe("test");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test(".trim modifier", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model.trim="text"/>
|
||||
<span><t t-esc="state.text"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { text: "" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
|
||||
const input = fixture.querySelector("input")!;
|
||||
await editInput(input, " test ");
|
||||
expect(comp.state.text).toBe("test");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||
});
|
||||
|
||||
test(".number modifier", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model.number="number"/>
|
||||
<span><t t-esc="state.number"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { number: 0 };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>0</span></div>");
|
||||
|
||||
const input = fixture.querySelector("input")!;
|
||||
await editInput(input, "13");
|
||||
expect(comp.state.number).toBe(13);
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>13</span></div>");
|
||||
|
||||
await editInput(input, "invalid");
|
||||
expect(comp.state.number).toBe("invalid");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>invalid</span></div>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,3 +91,13 @@ export function patchNextFrame(f: Function) {
|
||||
export function unpatchNextFrame() {
|
||||
UTILS.nextFrame = nextFrame;
|
||||
}
|
||||
|
||||
export async function editInput(
|
||||
input: HTMLInputElement | HTMLTextAreaElement,
|
||||
value: string
|
||||
) {
|
||||
input.value = value;
|
||||
input.dispatchEvent(new Event("input"));
|
||||
input.dispatchEvent(new Event("change"));
|
||||
return nextTick();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user