UNPKG

@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
<!doctype 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>