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

793 lines (643 loc) 19.2 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="../../polymer.html"> <body> <dom-module id="behavior-registered"> <template> <div id="content"></div> </template> </dom-module> <dom-module id="template-from-base"> <template> <div id="from-base">should not be used</div> </template> </dom-module> <script> HTMLImports.whenReady(function() { Polymer.setLegacyOptimizations(Boolean(window.location.search.match('legacyOptimizations'))); window.BehaviorA = { properties: { label: { type: String, observer: '_labelChanged' }, hasOptionsA: { readOnly: true, notify: true }, overridableProperty: { value: false }, overridablePropertyB: { value: false }, hasBehaviorA: { value: true }, computeADependency: { value: true }, computeA: { computed: '_computeProp(computeADependency)' } }, observers: [], _simpleProperty: 'A', hostAttributes: { behavior: 'A', element: 'A', user: 'A' }, listeners: { change: '_changeHandler' }, ready: function() { this.__readyA = true; }, _labelChanged: function(label) { this.__label = label; }, _changeHandler: function(e) { this.__change = e.detail.value; }, _computeProp: function(a) { return a; } }; window.BehaviorB = { properties: { disabled: { type: Boolean, value: false, observer: '_disabledChanged' }, hasOptionsB: { readOnly: true, notify: true }, hasBehaviorB: { value: true }, overridablePropertyB: { value: true }, computeADependencyDependency: { value: 'hi' }, computeADependency: { computed: '_computeProp(computeADependencyDependency)' } }, hostAttributes: { behavior: 'B', element: 'B', user: 'B' }, _simpleProperty: 'B', _disabledChanged: function(disabled) { this.__disabled = disabled; }, ready: function() { this.__readyB = true; }, }; window.BehaviorC = { properties: { hasBehaviorC: { value: true } }, _simpleProperty: 'C' }; window.BehaviorD = { properties: { hasBehaviorD: { value: true } }, _simpleProperty: 'D' }; Polymer({ is: 'single-behavior', behaviors: [ window.BehaviorA ], properties: {}, observers: [], hostAttributes: {}, listeners: {} }); Polymer({ is: 'multi-behaviors', behaviors: [ window.BehaviorA, window.BehaviorB ], hostAttributes: { element: 'element' }, properties: { foo: { type: String, reflectToAttribute: true, readOnly: true, observer: '_fooChanged' }, overridableProperty: { value: true } }, _fooChanged: function(foo) { this.__foo = foo; }, }); Polymer({ is: 'nested-behaviors', behaviors: [ [window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA], [window.BehaviorD] ] }); window.registerBehavior1 ={ beforeRegister: function() { this._createPropertyObserver('beforeProp', 'beforePropChanged1'); this.beforeRegisterCount++; this.beforeRegisterBehaviors = this.behaviors; }, registered: function() { this._createPropertyObserver('prop', 'propChanged1'); this._createMethodObserver('propChanged2(prop)'); this.registeredCount++; this.registeredProps = [this.prop1, this.prop2, this.prop3, this.prop4]; this.registeredBehaviors = this.behaviors; }, prop1: true, ready: function() { this._ensureAttribute('attr', true); }, beforePropChanged1: function() { this.beforePropChangedCalled = true; }, propChanged1: function() { this.propChanged1Called = true; }, propChanged2: function() { this.propChanged2Called = true; } }; window.registerBehavior2 ={ prop2: true, beforeRegister: function() { this.beforeRegisterCount++; }, registered: function() { this.registeredCount++; } }; window.registerBehavior3 ={ prop3: true, beforeRegister: function() { this.beforeRegisterCount++; }, registered: function() { this.registeredCount++; } }; Polymer({ behaviors: [ window.registerBehavior1, window.registerBehavior2, window.registerBehavior3 ], prop4: true, beforeRegister: function() { this.beforeRegisterCount++; }, registered: function() { this.registeredCount++; }, beforeRegisterCount: 0, registeredCount: 0, is: 'behavior-registered' }); window.templateBehavior1 = { _template: Polymer.html`<div id="from-behavior1"></div>` }; window.templateBehavior2 = { _template: Polymer.html`<div id="from-behavior2"></div>` }; window.templateBehaviorFromRegistered = { registered: function() { this._template = Polymer.html`<div id="behavior-from-registered"></div>`; } }; Polymer({ is: 'template-from-registered', registered: function() { this._template = Polymer.html`<div id="from-registered"></div>`; } }); Polymer({ is: 'template-from-base', behaviors: [ window.templateBehavior1 ] }); Polymer({ is: 'template-from-behavior', behaviors: [ window.templateBehavior1 ] }); Polymer({ is: 'template-from-behavior-overridden', behaviors: [ window.templateBehavior1, window.templateBehavior2 ] }); Polymer({ is: 'template-from-behavior-registered', behaviors: [ window.templateBehaviorFromRegistered ] }); window.ModifyObserversBehavior = { __barChangedCalled: 0, beforeRegister: function() { const observers = this.constructor.generatedFrom.observers; this.constructor.generatedFrom.observers = observers.concat([ '_barChanged(bar)' ]); }, _barChanged: function() { this.__barChangedCalled++; } }; Polymer({ is: 'modify-observers-via-behavior', __zonkChangedCalled: 0, observers: [ '_zonkChanged(zonk)' ], behaviors: [ window.ModifyObserversBehavior ], _zonkChanged: function() { this.__zonkChangedCalled++; } }); Polymer({ is: 'behavior-properties', behaviors: [window.BehaviorA] }); Polymer({ is: 'no-accessors-behavior', behaviors: [{ _noAccessors: true, properties: { nug: String }, foo: function() {}, bar: true }], _noAccessors: true, zot: 'zot' }); Polymer({ is: 'override-default-value', behaviors: [ { properties: { foo: { value: true }, bar: { value: true} } }, { properties: { foo: { value: true }, bar: String, zot: {value: true} } }, ], properties: { foo: String, zot: String } }); Polymer({ is: 'property-observer-readonly', behaviors: [ { observers: ['_changed(bar)'], _changed() {} } ], properties: { bar: {readOnly: true} } }); }); </script> <test-fixture id="single"> <template> <single-behavior></single-behavior> </template> </test-fixture> <test-fixture id="multi"> <template> <multi-behaviors user="user"></multi-behaviors> </template> </test-fixture> <test-fixture id="nested"> <template> <nested-behaviors></nested-behaviors> </template> </test-fixture> <test-fixture id="registered"> <template> <behavior-registered></behavior-registered> </template> </test-fixture> <test-fixture id="from-registered"> <template> <template-from-registered></template-from-registered> </template> </test-fixture> <test-fixture id="from-base"> <template> <template-from-base></template-from-base> </template> </test-fixture> <test-fixture id="from-behavior"> <template> <template-from-behavior></template-from-behavior> </template> </test-fixture> <test-fixture id="from-behavior-overridden"> <template> <template-from-behavior-overridden></template-from-behavior-overridden> </template> </test-fixture> <test-fixture id="from-behavior-registered"> <template> <template-from-behavior-registered></template-from-behavior-registered> </template> </test-fixture> <test-fixture id="modify-observers-via-behavior"> <template> <modify-observers-via-behavior></modify-observers-via-behavior> </template> </test-fixture> <test-fixture id="behavior-properties"> <template> <behavior-properties></behavior-properties> </template> </test-fixture> <test-fixture id="no-accessors-behavior"> <template> <no-accessors-behavior></no-accessors-behavior> </template> </test-fixture> <test-fixture id="override-default-value"> <template> <override-default-value></override-default-value> </template> </test-fixture> <test-fixture id="property-observer-readonly"> <template> <property-observer-readonly></property-observer-readonly> </template> </test-fixture> <script> suite('single behavior element', function() { var el; setup(function() { el = fixture('single'); }); test('is on prototype', function() { assert.equal(el.is, 'single-behavior'); }); test('instance behaviors, properties, observers, hostAttributes, listeners', function() { assert.isOk(el.behaviors); assert.isOk(el.properties); assert.isOk(el.observers); assert.isOk(el.hostAttributes); assert.isOk(el.listeners); }); test('ready from behavior', function() { assert.equal(el.__readyA, true); }); test('properties from behavior', function() { el.label = 'foo'; assert.equal(el.__label, 'foo'); }); test('listener from behavior', function() { el.fire('change', {value: 'bar'}); assert.equal(el.__change, 'bar'); }); test('property info from behavior', function() { assert.equal(el._hasNotifyEffect('hasOptionsA'), true); assert.equal(el._hasReadOnlyEffect('hasOptionsA'), true); assert.equal(typeof el._setHasOptionsA, 'function'); }); test('compute property from behavior', function() { assert.equal(el.computeA, true); }); test('special properties not copied from behavior to element', function() { const el = fixture('behavior-properties'); assert.notOk(el.properties); assert.notOk(el.observers); assert.notOk(el.hostAttributes); assert.notOk(el.listeners); }); test('properties on objects marked with `_noAccessors` are copied to class', function() { const el = fixture('no-accessors-behavior'); assert.ok(el.foo); assert.isTrue(el.bar); assert.equal(el.zot, 'zot'); el.setAttribute('nug', 'nug'); assert.equal(el.nug, 'nug'); }); test('behavior default values can be overridden', function() { const el = fixture('override-default-value'); assert.notOk(el.foo); assert.notOk(el.bar); assert.notOk(el.zot); }); test('readOnly not applied when property was previously observed', function() { const el = fixture('property-observer-readonly'); el.bar = 5; assert.equal(el.bar, 5); }); }); suite('behavior.registered', function() { test('can install dynamic properties', function() { var el = fixture('registered'); assert.ok(el.$.content); el.prop = 42; assert.isTrue(el.propChanged1Called); assert.isTrue(el.propChanged2Called); assert.isTrue(el.hasAttribute('attr')); }); test('called once for each behavior with access to element prototype', function() { var el = fixture('registered'); assert.equal(el.registeredCount, 4); assert.equal(el.registeredBehaviors.length, 3); assert.equal(el.registeredBehaviors, el.behaviors); assert.deepEqual(el.registeredProps, [true, true, true, true]); }); }); suite('behavior.beforeRegister', function() { test('can install dynamic properties', function() { var el = fixture('registered'); assert.ok(el.$.content); el.beforeProp = 43; assert.isTrue(el.beforePropChangedCalled); }); test('called once for each behavior with access to element prototype', function() { var el = fixture('registered'); assert.equal(el.beforeRegisterCount, 4); assert.equal(el.beforeRegisterBehaviors.length, 3); assert.equal(el.beforeRegisterBehaviors, el.behaviors); }); test('modify element observers', function() { var el = fixture('modify-observers-via-behavior'); el.bar = 1; assert.equal(el.__barChangedCalled, 1); el.zonk = 1; assert.equal(el.__zonkChangedCalled, 1); }); }); suite('multi-behaviors element', function() { var el; setup(function() { el = fixture('multi'); }); test('ready from behaviors', function() { assert.equal(el.__readyA, true); assert.equal(el.__readyB, true); }); test('properties from behaviors', function() { el.label = 'foo'; assert.equal(el.__label, 'foo'); el.disabled = true; assert.equal(el.__disabled, true); }); test('properties from itself', function() { assert.isDefined(el._setFoo, 'readOnly setter not available'); el._setFoo('bar'); assert.equal(el.__foo, 'bar', 'observer not getting called'); assert.equal(el.getAttribute('foo'), 'bar', 'not getting reflected'); }); test('listener from behaviors', function() { el.fire('change', {value: 'bar'}); assert.equal(el.__change, 'bar'); }); test('property info from behavior A', function() { assert.equal(el._hasNotifyEffect('hasOptionsA'), true); assert.equal(el._hasReadOnlyEffect('hasOptionsA'), true); assert.equal(typeof el._setHasOptionsA, 'function'); }); test('property info from behavior B', function() { assert.equal(el._hasReadOnlyEffect('hasOptionsB'), true); assert.equal(el._hasNotifyEffect('hasOptionsB'), true); assert.equal(typeof el._setHasOptionsB, 'function'); }); test('computed property dependency can become a computed property', function() { assert.equal(el.computeA, 'hi'); }); test('multi-behavior overrides ordering', function() { assert(el.overridableProperty, 'Behavior property was not overridden by prototype'); assert(el.overridablePropertyB, 'Behavior config-property was not overridden by sub-behavior'); }); test('hostAttributes ordering', function() { assert.equal(el.attributes.behavior.value, 'B', 'Behavior hostAttribute not overridden by younger behavior'); assert.equal(el.attributes.element.value, 'element', 'Behavior hostAttribute overridden by behavior'); assert.equal(el.attributes.user.value, 'user', 'Behavior hostAttribute overrode user attribute'); }); test('behavior is null generates warning', function() { sinon.spy(console, 'warn'); Polymer({ is: 'behavior-null', behaviors: [ null ] }); document.createElement('behavior-null'); assert.equal(console.warn.callCount, 1, 'Null behaviour should generate warning'); console.warn.restore(); }); test('behavior array is unique', function() { Polymer({ is: 'behavior-unique', behaviors: [window.BehaviorA, window.BehaviorA] }); assert.equal(document.createElement('behavior-unique').behaviors.length, 1); }); test('duplicate behaviors keep first behavior', function() { Polymer({ is: 'behavior-unique-last-behavior', behaviors: [window.BehaviorA, window.BehaviorB, window.BehaviorC, window.BehaviorA, window.BehaviorB] }); var behaviors = document.createElement('behavior-unique-last-behavior').behaviors; assert.deepEqual(behaviors, [window.BehaviorC, window.BehaviorA, window.BehaviorB]); }); }); suite('nested-behaviors element', function() { var el; setup(function() { el = fixture('nested'); }); test('nested-behavior dedups', function() { assert.equal(el.behaviors.length, 4); }); test('nested-behavior overrides ordering', function() { assert.ok(el.hasBehaviorA, "missing BehaviorA"); assert.ok(el.hasBehaviorB, "missing BehaviorB"); assert.ok(el.hasBehaviorC, "missing BehaviorC"); assert.ok(el.hasBehaviorD, "missing BehaviorD"); assert.equal(el._simpleProperty, 'D', 'Behavior simple property was not overridden by sub-behavior'); }); }); suite('templates from behaviors', function() { test('template from registered callback', function() { var el = fixture('from-registered'); assert.ok(el.shadowRoot.querySelector('#from-registered')); }); test('template from base', function() { var el = fixture('from-base'); assert.notOk(el.shadowRoot.querySelector('#from-base')); assert.ok(el.shadowRoot.querySelector('#from-behavior1')); }); test('template from behavior', function() { var el = fixture('from-behavior'); assert.ok(el.shadowRoot.querySelector('#from-behavior1')); }); test('template from overriding behavior', function() { var el = fixture('from-behavior-overridden'); assert.notOk(el.shadowRoot.querySelector('#from-behavior1')); assert.ok(el.shadowRoot.querySelector('#from-behavior2')); }); test('template from behavior registered callback', function() { var el = fixture('from-behavior-registered'); assert.ok(el.shadowRoot.querySelector('#behavior-from-registered')); }); }); </script> </body> </html>