mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
609 lines
16 KiB
HTML
609 lines
16 KiB
HTML
<!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="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
|
|
<script src="wct-browser-config.js"></script>
|
|
<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
|
|
<script type="module" src="../../polymer-element.js"></script>
|
|
<body>
|
|
|
|
<dom-module id="my-element">
|
|
<template>
|
|
<div id="content">{{prop}}</div>
|
|
</template>
|
|
<script type="module">
|
|
import { PolymerElement } from '../../polymer-element.js';
|
|
|
|
class MyElement extends PolymerElement {
|
|
|
|
static get is() { return 'my-element'; }
|
|
|
|
static get properties() {
|
|
return {
|
|
prop: {
|
|
value: 'base',
|
|
observer: '_propObserver'
|
|
},
|
|
computedPropDep: {
|
|
value: true
|
|
},
|
|
computedProp: {
|
|
computed: '_compute(computedPropDep)'
|
|
},
|
|
accessor: String,
|
|
noStomp: String
|
|
};
|
|
}
|
|
|
|
static get observers() {
|
|
return [
|
|
'_methodObserver(prop)'
|
|
];
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this._propObserver = sinon.spy();
|
|
this._methodObserver = sinon.spy();
|
|
this._tapListener = sinon.spy();
|
|
this._calledConstructor++;
|
|
}
|
|
|
|
get noStomp() {
|
|
return 'noStomp';
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
this._calledConnectedCallback++;
|
|
}
|
|
|
|
attributeChangedCallback() {
|
|
super.attributeChangedCallback.apply(this, arguments);
|
|
this._callAttributeChangedCallback++;
|
|
}
|
|
|
|
ready() {
|
|
super.ready();
|
|
this._ensureAttribute('tabIndex', 0);
|
|
this.addEventListener('click', (e) => this._tapListener(e));
|
|
this._calledReady++;
|
|
}
|
|
|
|
_compute(value) {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
MyElement.prototype._calledConstructor = 0;
|
|
MyElement.prototype._calledConnectedCallback = 0;
|
|
MyElement.prototype._calledReady = 0;
|
|
MyElement.prototype._callAttributeChangedCallback = 0;
|
|
|
|
customElements.define(MyElement.is, MyElement);
|
|
|
|
window.MyElement = MyElement;
|
|
</script>
|
|
</dom-module>
|
|
|
|
|
|
<dom-module id="sub-element">
|
|
<script type="module">
|
|
import '../../polymer-element.js';
|
|
|
|
class SubElement extends window.MyElement {
|
|
|
|
static get is() { return 'sub-element'; }
|
|
|
|
static get properties() {
|
|
return {
|
|
prop: {
|
|
value: 'sub'
|
|
},
|
|
prop2: {
|
|
value: 'prop2',
|
|
observer: '_prop2Observer'
|
|
},
|
|
computedPropDep: {
|
|
computed: '_compute(prop2)'
|
|
}
|
|
};
|
|
}
|
|
|
|
static get observers() {
|
|
return [
|
|
'_subMethodObserver(prop, prop2)'
|
|
];
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this._calledConstructor++;
|
|
this._prop2Observer = sinon.spy();
|
|
this._subMethodObserver = sinon.spy();
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
this._calledConnectedCallback++;
|
|
}
|
|
|
|
ready() {
|
|
super.ready();
|
|
this._calledReady++;
|
|
this._ensureAttribute('role', 'button');
|
|
this.addEventListener('click', (e) => this._tapListener(e));
|
|
}
|
|
|
|
}
|
|
|
|
customElements.define(SubElement.is, SubElement);
|
|
|
|
window.SubElement = SubElement;
|
|
</script>
|
|
</dom-module>
|
|
|
|
<dom-module id="sub-mixin-element">
|
|
<script type="module">
|
|
import '../../polymer-element.js';
|
|
|
|
function MyMixin(Base) {
|
|
return class extends Base {
|
|
|
|
static get properties() {
|
|
return {
|
|
prop: {
|
|
value: 'mixin'
|
|
},
|
|
mixin: {
|
|
value: 'mixin',
|
|
observer: '_mixinObserver'
|
|
}
|
|
};
|
|
}
|
|
|
|
static get observers() {
|
|
return [
|
|
'_mixinMethodObserver(mixin, prop, prop2)'
|
|
];
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this._calledConstructor++;
|
|
this._mixinObserver = sinon.spy();
|
|
this._mixinMethodObserver = sinon.spy();
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
this._calledConnectedCallback++;
|
|
}
|
|
|
|
ready() {
|
|
super.ready();
|
|
this._calledReady++;
|
|
this._ensureAttribute('mixinattr', 'mixinattr');
|
|
this.addEventListener('click', (e) => this._tapListener(e));
|
|
}
|
|
|
|
};
|
|
}
|
|
|
|
class SubMixinElement extends MyMixin(window.SubElement) {
|
|
|
|
static get is() { return 'sub-mixin-element'; }
|
|
|
|
static get properties() {
|
|
return {
|
|
prop3: {
|
|
value: 'prop3',
|
|
observer: '_prop3Observer'
|
|
}
|
|
};
|
|
}
|
|
|
|
static get observers() {
|
|
return [
|
|
'_subMixinMethodObserver(mixin, prop, prop2, prop3)'
|
|
];
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this._calledConstructor++;
|
|
this._prop3Observer = sinon.spy();
|
|
this._subMixinMethodObserver = sinon.spy();
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
this._calledConnectedCallback++;
|
|
}
|
|
|
|
ready() {
|
|
super.ready();
|
|
this._calledReady++;
|
|
this._ensureAttribute('submixinattr', 'submixinattr');
|
|
this.addEventListener('click', (e) => this._tapListener(e));
|
|
}
|
|
|
|
}
|
|
|
|
customElements.define(SubMixinElement.is, SubMixinElement);
|
|
|
|
window.SubMixinElement = SubMixinElement;
|
|
</script>
|
|
</dom-module>
|
|
|
|
<script type="module">
|
|
import '../../polymer-element.js';
|
|
|
|
class SubNewTemplate extends window.MyElement {
|
|
static get template() {
|
|
return `
|
|
<h1>Sub template</h1>
|
|
<div id="subContent">{{prop2}}</div>`;
|
|
}
|
|
|
|
static get properties() {
|
|
return {
|
|
prop2: {
|
|
value: 'prop2',
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
customElements.define('sub-new-template', SubNewTemplate);
|
|
</script>
|
|
|
|
<template id="no-dom-module">
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
border: 10px solid rgb(123, 123, 123);
|
|
}
|
|
</style>
|
|
<div id="child">Content</div>
|
|
</template>
|
|
<script type="module">
|
|
import { PolymerElement } from '../../polymer-element.js';
|
|
class NoDomModule extends PolymerElement {
|
|
static get is() {return 'no-dom-module';}
|
|
static get template() {
|
|
return document.body.querySelector('template#no-dom-module');
|
|
}
|
|
}
|
|
customElements.define('no-dom-module', NoDomModule);
|
|
</script>
|
|
|
|
<dom-module id="sub-extended-template">
|
|
<script type="module">
|
|
import '../../polymer-element.js';
|
|
// Setting up property bind listeners in both base and sub class
|
|
// is required to catch regression bug
|
|
class ElementWithBindListeners extends window.MyElement {
|
|
static get is() { return 'element-with-bind-listeners'; }
|
|
}
|
|
class SubExtendedTemplate extends ElementWithBindListeners {
|
|
static get is() { return 'sub-extended-template'; }
|
|
static get template() {
|
|
if (!this.hasOwnProperty('_template')) {
|
|
const baseClass = Object.getPrototypeOf(this.prototype).constructor;
|
|
this._template = baseClass.template.cloneNode(true);
|
|
const newElement = document.createElement('div');
|
|
newElement.innerHTML = '<div id="subContent" imaginary-prop$="{{prop}}""></div>';
|
|
this._template.content.appendChild(newElement.firstChild);
|
|
}
|
|
return this._template;
|
|
}
|
|
}
|
|
customElements.define(ElementWithBindListeners.is, ElementWithBindListeners);
|
|
customElements.define(SubExtendedTemplate.is, SubExtendedTemplate);
|
|
</script>
|
|
</dom-module>
|
|
|
|
<test-fixture id="my-element-attr">
|
|
<template>
|
|
<my-element prop="attr" foo="foo"></my-element>
|
|
</template>
|
|
</test-fixture>
|
|
|
|
<test-fixture id="sub-element-attr">
|
|
<template>
|
|
<sub-element prop="attr" prop2="attr"></sub-element>
|
|
</template>
|
|
</test-fixture>
|
|
|
|
<test-fixture id="sub-mixin-element-attr">
|
|
<template>
|
|
<sub-mixin-element prop="attr" prop2="attr" prop3="attr"></sub-mixin-element>
|
|
</template>
|
|
</test-fixture>
|
|
|
|
<script type="module">
|
|
import { PolymerElement } from '../../polymer-element.js';
|
|
suite('class extends Polymer.Element', function() {
|
|
|
|
var 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, PolymerElement);
|
|
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('template', function() {
|
|
assert.equal(el.prop, 'base');
|
|
assert.equal(el.$.content.textContent, el.prop);
|
|
});
|
|
|
|
test('observers', function() {
|
|
assert.isTrue(el._propObserver.calledOnce);
|
|
assert.isTrue(el._propObserver.calledWith('base'));
|
|
assert.isTrue(el._methodObserver.calledOnce);
|
|
assert.isTrue(el._methodObserver.calledWith('base'));
|
|
});
|
|
|
|
test('listeners', function() {
|
|
el.dispatchEvent(new CustomEvent('click'));
|
|
assert.equal(el._tapListener.callCount, 1);
|
|
});
|
|
|
|
test('properties', function() {
|
|
assert.equal(el.prop, 'base');
|
|
assert.equal(el.computedPropDep, true);
|
|
assert.equal(el.computedProp, true);
|
|
});
|
|
|
|
test('attributes', function() {
|
|
const fixtureEl = fixture('my-element-attr');
|
|
assert.equal(fixtureEl.prop, 'attr');
|
|
assert.equal(fixtureEl._callAttributeChangedCallback, 1);
|
|
assert.isTrue(fixtureEl.hasAttribute('tabindex'));
|
|
});
|
|
|
|
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, PolymerElement);
|
|
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('template', function() {
|
|
assert.equal(el.prop, 'sub');
|
|
assert.equal(el.$.content.textContent, el.prop);
|
|
});
|
|
|
|
test('observers', function() {
|
|
assert.isTrue(el._propObserver.calledOnce);
|
|
assert.isTrue(el._propObserver.calledWith('sub'));
|
|
assert.isTrue(el._methodObserver.calledOnce);
|
|
assert.isTrue(el._methodObserver.calledWith('sub'));
|
|
// sub
|
|
assert.isTrue(el._prop2Observer.calledOnce);
|
|
assert.isTrue(el._prop2Observer.calledWith('prop2'));
|
|
assert.isTrue(el._subMethodObserver.calledOnce);
|
|
assert.isTrue(el._subMethodObserver.calledWith('sub', 'prop2'));
|
|
});
|
|
|
|
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.equal(el.computedPropDep, 'prop2');
|
|
assert.equal(el.computedProp, 'prop2', 'computedProp dependency cannot itself be a computed property');
|
|
});
|
|
|
|
test('attributes', function() {
|
|
const fixtureEl = fixture('sub-element-attr');
|
|
assert.equal(fixtureEl.prop, 'attr');
|
|
assert.equal(fixtureEl.prop2, 'attr');
|
|
assert.equal(fixtureEl._callAttributeChangedCallback, 2);
|
|
assert.isTrue(fixtureEl.hasAttribute('tabindex'));
|
|
assert.equal(fixtureEl.getAttribute('role'), 'button');
|
|
});
|
|
|
|
});
|
|
|
|
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, PolymerElement);
|
|
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('template', function() {
|
|
assert.equal(el.prop, 'mixin');
|
|
assert.equal(el.$.content.textContent, el.prop);
|
|
});
|
|
|
|
test('observers', function() {
|
|
assert.isTrue(el._propObserver.calledOnce);
|
|
assert.isTrue(el._propObserver.calledWith('mixin'));
|
|
assert.isTrue(el._methodObserver.calledOnce);
|
|
assert.isTrue(el._methodObserver.calledWith('mixin'));
|
|
// sub
|
|
assert.isTrue(el._prop2Observer.calledOnce);
|
|
assert.isTrue(el._prop2Observer.calledWith('prop2'));
|
|
assert.isTrue(el._subMethodObserver.calledOnce);
|
|
assert.isTrue(el._subMethodObserver.calledWith('mixin', 'prop2'));
|
|
// mixin
|
|
assert.isTrue(el._mixinObserver.calledOnce);
|
|
assert.isTrue(el._mixinObserver.calledWith('mixin'));
|
|
assert.isTrue(el._mixinMethodObserver.calledOnce);
|
|
assert.isTrue(el._mixinMethodObserver.calledWith('mixin', 'mixin', 'prop2'));
|
|
// submixin
|
|
assert.isTrue(el._prop3Observer.calledOnce);
|
|
assert.isTrue(el._prop3Observer.calledWith('prop3'));
|
|
assert.isTrue(el._subMixinMethodObserver.calledOnce);
|
|
assert.isTrue(el._subMixinMethodObserver.calledWith('mixin', 'mixin', 'prop2', 'prop3'));
|
|
|
|
});
|
|
|
|
test('listeners', function() {
|
|
el.dispatchEvent(new CustomEvent('click'));
|
|
assert.equal(el._tapListener.callCount, 4);
|
|
});
|
|
|
|
test('attributes', function() {
|
|
const fixtureEl = fixture('sub-mixin-element-attr');
|
|
assert.equal(fixtureEl.prop, 'attr');
|
|
assert.equal(fixtureEl.prop2, 'attr');
|
|
assert.equal(fixtureEl.prop3, 'attr');
|
|
assert.equal(fixtureEl._callAttributeChangedCallback, 3);
|
|
assert.isTrue(fixtureEl.hasAttribute('tabindex'));
|
|
assert.equal(fixtureEl.getAttribute('role'), 'button');
|
|
assert.equal(fixtureEl.getAttribute('mixinattr'), 'mixinattr');
|
|
assert.equal(fixtureEl.getAttribute('submixinattr'), 'submixinattr');
|
|
});
|
|
|
|
});
|
|
|
|
suite('subclass new template', function() {
|
|
let el;
|
|
|
|
setup(function() {
|
|
el = document.createElement('sub-new-template');
|
|
document.body.appendChild(el);
|
|
});
|
|
|
|
teardown(function() {
|
|
document.body.removeChild(el);
|
|
});
|
|
|
|
test('template', function() {
|
|
assert.equal(el.prop2, 'prop2');
|
|
assert.equal(el.$.subContent.textContent, el.prop2);
|
|
assert.notOk(el.$.content);
|
|
});
|
|
|
|
suite('use without dom-module', function() {
|
|
let el;
|
|
|
|
setup(function() {
|
|
el = document.createElement('no-dom-module');
|
|
document.body.appendChild(el);
|
|
});
|
|
|
|
teardown(function() {
|
|
document.body.removeChild(el);
|
|
});
|
|
|
|
test('template', function() {
|
|
assert.ok(el.$.child);
|
|
assert.equal(el.$.child.textContent, 'Content');
|
|
assert.equal(window.ShadyCSS.getComputedStyleValue(el, 'border-top-width'), '10px');
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('extend sub-class template', function() {
|
|
let el, baseEl;
|
|
setup(function() {
|
|
el = document.createElement('sub-extended-template');
|
|
document.body.appendChild(el);
|
|
baseEl = document.createElement('element-with-bind-listeners');
|
|
document.body.appendChild(baseEl);
|
|
});
|
|
teardown(function() {
|
|
document.body.removeChild(el);
|
|
document.body.removeChild(baseEl);
|
|
});
|
|
test('has original template', function() {
|
|
assert.equal(el.prop, 'base');
|
|
assert.equal(el.$.content.textContent, el.prop);
|
|
});
|
|
test('has new element', function() {
|
|
assert.ok(el.$.subContent);
|
|
assert.equal(el.$.subContent.getAttribute('imaginary-prop'), 'base');
|
|
});
|
|
test('base doesnt have new element', function() {
|
|
assert.notOk(baseEl.$.subContent);
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|