Elliott Sprehn 9612dcf25f Don't set expression attributes before they're bound.
We were cloning elements with all the attributes that contained
expressions like attrName="{{ foo }}" which meant we'd go through the
reflection process converting that value and also call the
attrNameChanged() callback and the attributeChanged() with the braced
string which the element didn't really want to know about.

After this patch we create a "clone source node" which is a copy of the
original element without the attributes that have the expressions and
use that when cloning, then we assign the properties using data binding
later.

R=abarth@chromium.org, ojan@chromium.org

Review URL: https://codereview.chromium.org/868973002
2015-01-22 17:04:10 -08:00

225 lines
7.0 KiB
Plaintext

<sky>
<import src="/sky/tests//resources/chai.sky" />
<import src="/sky/tests/resources/mocha.sky" />
<import src="/sky/framework/sky-element/sky-element.sky" as="SkyElement" />
<import src="/sky/tests/resources/test-element.sky" as="TestElement" />
<div id="sandbox"></div>
<test-element id="parser-element" checked="true" size="10" name="foo bar" />
<sky-element name="test-element-parent">
<template>
<test-element size="{{ size }}" />
</template>
<script>
(class extends SkyElement {
created() {
this.size = 10;
}
}).register();
</script>
</sky-element>
<test-element-parent id="parent" />
<script>
describe("SkyElement", function() {
var element;
var sandbox = document.getElementById("sandbox");
beforeEach(function() {
element = new TestElement();
});
afterEach(function() {
element.remove();
});
it("should stamp when the element is inserted", function() {
assert.isNull(element.shadowRoot);
sandbox.appendChild(element);
assert.instanceOf(element.shadowRoot, ShadowRoot);
assert.ok(element.shadowRoot.getElementById("inside"));
});
it("should update isAttached when inserting", function() {
assert.isFalse(element.isAttached);
sandbox.appendChild(element);
assert.isTrue(element.isAttached);
element.remove();
assert.isFalse(element.isAttached);
});
it("should handle parser created elements with attributes", function() {
var element = document.getElementById("parser-element");
assert.isTrue(element.checked);
assert.isNumber(element.size);
assert.equal(element.size, 10);
assert.isString(element.name);
assert.equal(element.name, "foo bar");
});
it("should have defaults for all attributes", function() {
var element = new TestElement();
assert.isFalse(element.checked);
assert.isNumber(element.size);
assert.equal(element.size, 0);
assert.isString(element.name);
assert.equal(element.name, "");
});
it("should call change callbacks", function() {
var element = new TestElement();
element.size = 20;
element.name = "first name";
element.checked = true;
element.size = 10;
element.name = "second name";
assert.deepEqual(element.changes, [
{ name: 'size', oldValue: 0, newValue: 20 },
{ name: 'name', oldValue: "", newValue: "first name" },
{ name: 'checked', oldValue: false, newValue: true },
{ name: 'size', oldValue: 20, newValue: 10 },
{ name: 'name', oldValue: "first name", newValue: "second name" },
]);
});
it("should convert boolean reflected attributes", function() {
var element = new TestElement();
assert.isFalse(element.checked);
element.checked = null;
assert.isFalse(element.checked);
element.checked = "true";
assert.isTrue(element.checked);
element.checked = "false";
assert.isFalse(element.checked);
element.checked = {};
assert.isTrue(element.checked);
});
it("should convert string reflected attributes", function() {
var element = new TestElement();
assert.equal(element.name, "");
element.name = null;
assert.equal(element.name, "");
element.name = [1, 2];
assert.equal(element.name, "1,2");
element.name = false;
assert.equal(element.name, "false");
element.name = {};
assert.equal(element.name, "[object Object]");
element.name = "";
assert.equal(element.name, "");
});
it("should convert number reflected attributes", function() {
var element = new TestElement();
assert.isNumber(element.size);
element.size = 20;
assert.isNumber(element.size);
assert.equal(element.size, 20);
element.size = "08";
assert.equal(element.size, 8);
element.size = " 30 ";
assert.equal(element.size, 30);
element.size = "1.5";
assert.isNumber(element.size);
assert.equal(element.size, 1.5);
element.size = "false";
assert.isTrue(isNaN(element.size));
element.size = {};
assert.isTrue(isNaN(element.size));
});
it("should connect data binding", function(done) {
sandbox.appendChild(element);
var inside = element.shadowRoot.getElementById("inside");
Promise.resolve().then(function() {
assert.equal(inside.textContent, 10);
assert.equal(inside.lang, 10);
element.value = 20;
}).then(function() {
assert.equal(inside.textContent, 20);
assert.equal(inside.lang, 20);
done();
}).catch(function(e) {
done(e);
});
});
it("should two way bind attributes", function(done) {
sandbox.appendChild(element);
var checkbox = element.shadowRoot.getElementById("checkbox");
assert.isFalse(checkbox.checked);
assert.isFalse(element.checked);
element.checked = true;
assert.isTrue(element.checked);
assert.isFalse(checkbox.checked);
Promise.resolve().then(function() {
assert.isTrue(checkbox.checked);
checkbox.checked = false;
assert.isFalse(checkbox.checked);
return Promise.resolve().then(function() {
assert.isFalse(element.checked);
assert.isFalse(checkbox.checked);
checkbox.checked = true;
assert.isTrue(checkbox.checked);
return Promise.resolve().then(function() {
assert.isTrue(element.checked);
element.checked = true;
assert.isTrue(element.checked);
assert.isTrue(checkbox.checked);
element.checked = false;
assert.isFalse(element.checked);
assert.isTrue(checkbox.checked);
return Promise.resolve().then(function() {
assert.isFalse(checkbox.checked);
assert.isFalse(element.checked);
done();
});
});
});
}).catch(function(e) {
done(e);
});
});
it("should connect template event handlers", function() {
sandbox.appendChild(element);
var inside = element.shadowRoot.getElementById("inside");
inside.dispatchEvent(new CustomEvent("wrong-event"));
assert.isNull(element.lastEvent);
var event = new CustomEvent("test-event");
inside.dispatchEvent(event);
assert.equal(element.lastEvent, event);
});
it("should connect host event handlers", function() {
sandbox.appendChild(element);
element.dispatchEvent(new CustomEvent("wrong-event"));
assert.isNull(element.lastEvent);
var event = new CustomEvent("host-event");
element.dispatchEvent(event);
assert.equal(element.lastEvent, event);
});
it("should not call attributeChanged for binding expression values", function() {
var parent = document.getElementById('parent');
var element = parent.shadowRoot.querySelector('test-element');
assert.deepEqual(element.changes, [
{ name: 'size', oldValue: 0, newValue: 10 },
]);
});
it("should call shadowRootReady after creating the template instance", function() {
assert.equal(element.shadowRootReadyCount, 0);
sandbox.appendChild(element);
assert.equal(element.shadowRootReadyCount, 1);
element.remove();
sandbox.appendChild(element);
assert.equal(element.shadowRootReadyCount, 1);
});
});
</script>
</sky>