@polymer/polymer
Version:
The Polymer library makes it easy to create your own web components. Give your element some markup and properties, and then use it on a site. Polymer provides features like dynamic templates and data binding to reduce the amount of boilerplate you need to
434 lines (347 loc) • 11.7 kB
HTML
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<meta charset="utf-8">
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../../web-component-tester/browser.js"></script>
<link rel="import" href="../../lib/mixins/properties-mixin.html">
<body>
<dom-module id="my-element">
<script>
HTMLImports.whenReady(function() {
class Glar {}
class Blar {}
class MyElement extends Polymer.PropertiesMixin(HTMLElement) {
static get properties() {
return {
prop: String,
noStomp: String,
id: String,
camelCase: String,
boo: Boolean,
num: Number,
glar: Glar,
blar: Blar
};
}
constructor() {
super();
this._propertiesChanged = sinon.spy();
this._tapListener = sinon.spy();
this._calledConstructor++;
this.prop = 'prop';
this.noStomp = 'stomped';
}
_deserializeValue(value, type) {
if (type == Glar) {
return 'glar';
}
return super._deserializeValue(value, type);
}
get noStomp() {
return 'noStomp';
}
set noStomp(value) {}
get id() {
return this._getProperty('id');
}
set id(value) {
this._setProperty('id', value);
// cannot call super cuz older browsers
this._propertyToAttribute('id');
}
connectedCallback() {
super.connectedCallback();
this._calledConnectedCallback++;
}
attributeChangedCallback() {
super.attributeChangedCallback.apply(this, arguments);
this._callAttributeChangedCallback++;
}
ready() {
super.ready();
this.addEventListener('click', (e) => this._tapListener(e));
this._calledReady++;
}
}
MyElement.prototype._calledConstructor = 0;
MyElement.prototype._calledConnectedCallback = 0;
MyElement.prototype._calledReady = 0;
MyElement.prototype._callAttributeChangedCallback = 0;
customElements.define('my-element', MyElement);
window.MyElement = MyElement;
});
</script>
</dom-module>
<dom-module id="sub-element">
<script>
HTMLImports.whenReady(function() {
class SubElement extends window.MyElement {
static get observedAttributes() {
return ['custom-observed-attr'].concat(super.observedAttributes);
}
static get properties() {
return {
prop2: String,
camelCase2: String
};
}
constructor() {
super();
this._calledConstructor++;
this.prop = 'sub';
this.prop2 = 'prop2';
}
connectedCallback() {
super.connectedCallback();
this._calledConnectedCallback++;
}
ready() {
super.ready();
this._calledReady++;
this.addEventListener('click', (e) => this._tapListener(e));
}
}
customElements.define('sub-element', SubElement);
window.SubElement = SubElement;
});
</script>
</dom-module>
<dom-module id="sub-mixin-element">
<script>
HTMLImports.whenReady(function() {
function MyMixin(Base) {
return class extends Base {
static get properties() {
return {
mixin: Number
};
}
constructor() {
super();
this._calledConstructor++;
this.mixin = 'mixin';
}
connectedCallback() {
super.connectedCallback();
this._calledConnectedCallback++;
}
ready() {
super.ready();
this._calledReady++;
this.addEventListener('click', (e) => this._tapListener(e));
}
};
}
class SubMixinElement extends MyMixin(window.SubElement) {
static get properties() {
return {
prop3: String,
camelCase: String
};
}
constructor() {
super();
this._calledConstructor++;
this.prop3 = 'prop3';
}
connectedCallback() {
super.connectedCallback();
this._calledConnectedCallback++;
}
ready() {
super.ready();
this._calledReady++;
this.addEventListener('click', (e) => this._tapListener(e));
}
}
customElements.define('sub-mixin-element', SubMixinElement);
window.SubMixinElement = SubMixinElement;
});
</script>
</dom-module>
<test-fixture id="my-element-attr">
<template>
<my-element prop="attr" camelCase="camelCase"></my-element>
</template>
</test-fixture>
<test-fixture id="sub-element-attr">
<template>
<sub-element prop="attr" prop2="attr" camelCase="camelCase" camelCase2="camelCase2" custom-observed-attr="custom"></sub-element>
</template>
</test-fixture>
<test-fixture id="sub-mixin-element-attr">
<template>
<sub-mixin-element prop="attr" prop2="attr" prop3="attr" mixin="5" custom-observed-attr="custom"></sub-mixin-element>
</template>
</test-fixture>
<script>
suite('class extends Polymer.PropertiesMixin', function() {
let el;
setup(function() {
el = document.createElement('my-element');
document.body.appendChild(el);
});
teardown(function() {
document.body.removeChild(el);
});
test('instanceof', function() {
assert.instanceOf(el, HTMLElement);
assert.instanceOf(el, window.MyElement);
});
test('lifecycle', function() {
assert.equal(el._calledConstructor, 1);
assert.equal(el._calledConnectedCallback, 1);
assert.equal(el._calledReady, 1);
assert.equal(el._callAttributeChangedCallback, 0);
});
test('listeners', function() {
el.dispatchEvent(new CustomEvent('click'));
assert.equal(el._tapListener.callCount, 1);
});
test('properties', function() {
assert.equal(el.prop, 'prop');
assert.equal(el.noStomp, 'noStomp');
assert.isTrue(el._propertiesChanged.calledOnce);
assert.deepEqual(el._propertiesChanged.args[0][0], {prop: 'prop'});
});
test('properties: accessor for native property', function() {
// old browsers with broken native properties on instances
// cannot pass this test.
if (el.hasOwnProperty('id')) {
this.skip();
}
el.setAttribute('id', 'yo');
assert.equal(el.id, 'yo');
el._flushProperties();
assert.isTrue(el._propertiesChanged.calledTwice);
assert.deepEqual(el._propertiesChanged.args[1][1], {id: 'yo'});
});
test('attributes', function() {
const fixtureEl = fixture('my-element-attr');
assert.equal(fixtureEl.prop, 'attr');
assert.equal(fixtureEl.camelCase, 'camelCase');
assert.equal(fixtureEl._callAttributeChangedCallback, 2);
fixtureEl.removeAttribute('prop');
assert.equal(fixtureEl.prop, null);
});
test('deserialize standard types', function() {
el.setAttribute('boo', '');
assert.equal(el.boo, true);
el.removeAttribute('boo');
assert.equal(el.boo, false);
el.setAttribute('prop', '1');
assert.equal(el.prop, '1');
el.setAttribute('num', '1');
assert.equal(el.num, 1);
el.setAttribute('num', '0');
assert.equal(el.num, 0);
});
test('deserialize class constructor type', function() {
el.setAttribute('blar', 'a');
assert.equal(el.blar, 'a');
});
test('deserialize custom type via `_deserializeValue`', function() {
el.setAttribute('glar', 'b');
assert.equal(el.glar, 'glar');
});
test('reflecting attributes', function() {
const fixtureEl = fixture('my-element-attr');
fixtureEl.prop = 'propValue';
fixtureEl._propertyToAttribute('prop');
assert.equal(fixtureEl.getAttribute('prop'), 'propValue');
});
});
suite('subclass', function() {
let el;
setup(function() {
el = document.createElement('sub-element');
document.body.appendChild(el);
});
teardown(function() {
document.body.removeChild(el);
});
test('instanceof', function() {
assert.instanceOf(el, HTMLElement);
assert.instanceOf(el, window.MyElement);
assert.instanceOf(el, window.SubElement);
});
test('lifecycle', function() {
assert.equal(el._calledConstructor, 2);
assert.equal(el._calledConnectedCallback, 2);
assert.equal(el._calledReady, 2);
assert.equal(el._callAttributeChangedCallback, 0);
});
test('listeners', function() {
el.dispatchEvent(new CustomEvent('click'));
assert.equal(el._tapListener.callCount, 2);
});
test('properties', function() {
assert.equal(el.prop, 'sub');
assert.equal(el.prop2, 'prop2');
assert.isTrue(el._propertiesChanged.calledOnce);
assert.deepEqual(el._propertiesChanged.args[0][0], {prop: 'sub', prop2: 'prop2'});
});
test('attributes', function() {
const fixtureEl = fixture('sub-element-attr');
assert.equal(fixtureEl.prop, 'attr');
assert.equal(fixtureEl.prop2, 'attr');
assert.equal(fixtureEl.camelCase, 'camelCase');
assert.equal(fixtureEl.camelCase2, 'camelCase2');
assert.equal(fixtureEl._callAttributeChangedCallback, 5);
});
});
suite('mixin', function() {
let el;
setup(function() {
el = document.createElement('sub-mixin-element');
document.body.appendChild(el);
});
teardown(function() {
document.body.removeChild(el);
});
test('instanceof', function() {
assert.instanceOf(el, HTMLElement);
assert.instanceOf(el, window.MyElement);
assert.instanceOf(el, window.SubElement);
assert.instanceOf(el, window.SubMixinElement);
});
test('lifecycle', function() {
assert.equal(el._calledConstructor, 4);
assert.equal(el._calledConnectedCallback, 4);
assert.equal(el._calledReady, 4);
assert.equal(el._callAttributeChangedCallback, 0);
});
test('listeners', function() {
el.dispatchEvent(new CustomEvent('click'));
assert.equal(el._tapListener.callCount, 4);
});
test('properties', function() {
assert.equal(el.prop, 'sub');
assert.equal(el.prop2, 'prop2');
assert.equal(el.mixin, 'mixin');
assert.equal(el.prop3, 'prop3');
assert.isTrue(el._propertiesChanged.calledOnce);
assert.deepEqual(el._propertiesChanged.args[0][0], {prop: 'sub', prop2: 'prop2', mixin: 'mixin', prop3: 'prop3'});
});
test('attributes', function() {
const fixtureEl = fixture('sub-mixin-element-attr');
assert.strictEqual(fixtureEl.prop, 'attr');
assert.strictEqual(fixtureEl.prop2, 'attr');
assert.strictEqual(fixtureEl.prop3, 'attr');
assert.strictEqual(fixtureEl.mixin, 5);
assert.equal(fixtureEl._callAttributeChangedCallback, 5);
});
});
</script>
</body>
</html>