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

1,127 lines (1,038 loc) 39.8 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"> <link rel="import" href="path-effects-elements.html"> <body> <script> (function() { // Safari 9 has a horrible bug related to array enumeration after defining // a non-enumerable property on it (we do for `.splices`); for now we work // around chai's deepEqual implementation that enumerates array keys using // `for in` in the specific case the bug causes an assert failure function arraysEqual(a, b) { for (var i=0; i<a.length; i++) { if (a[i] !== b[i]) { throw new Error('expected ' + a + ' to equal ' + b); } } if (a.length != b.length) { throw new Error('expected ' + a + ' to equal ' + b); } } suite('basic path bindings', function() { var el; setup(function() { el = document.createElement('x-stuff'); document.body.appendChild(el); }); teardown(function() { document.body.removeChild(el); }); function verifyObserverOutput(nestingLevel, nested, done) { assert.equal(el.computed, '[' + nested.obj.value + ']'); assert.equal(el.nestedChanged.callCount, nestingLevel == 0 ? 1 : 0); if (nestingLevel == 0) { assert.equal(el.nestedChanged.firstCall.args[0], nested); } assert.equal(el.nestedSubpathChanged.callCount, 1); assert.equal(el.nestedObjChanged.callCount, nestingLevel <= 1 ? 1 : 0); if (nestingLevel <= 1) { assert.equal(el.nestedObjChanged.firstCall.args[0], nested.obj); } assert.equal(el.nestedObjSubpathChanged.callCount, 1); assert.equal(el.nestedObjSubpathChanged.firstCall.args[0].base, nested.obj); assert.equal(el.nestedObjValueChanged.callCount, 1); assert.equal(el.nestedObjValueChanged.firstCall.args[0], nested.obj.value); assert.equal(el.nestedSubpathChanged.callCount, 1); assert.equal(el.nestedSubpathChanged.firstCall.args[0].base, nested); assert.equal(el.$.compose.computed, '[' + nested.obj.value + ']'); assert.equal(el.$.compose.objSubpathChanged.callCount, 1); assert.equal(el.$.compose.objSubpathChanged.firstCall.args[0].base, nested.obj); assert.equal(el.$.compose.objValueChanged.callCount, 1); assert.equal(el.$.compose.objValueChanged.firstCall.args[0], nested.obj.value); assert.equal(el.$.forward.computed, '[' + nested.obj.value + ']'); assert.equal(el.$.forward.objSubpathChanged.callCount, 1); assert.equal(el.$.forward.objSubpathChanged.firstCall.args[0].base, nested.obj); assert.equal(el.$.forward.objValueChanged.callCount, 1); assert.equal(el.$.forward.objValueChanged.firstCall.args[0], nested.obj.value); assert.equal(el.$.basic.computed, '[' + nested.obj.value + ']'); assert.equal(el.$.basic.notifyingValue, 42); assert.equal(el.$.basic.notifyingValue, 42); assert.equal(el.$.compose.$.basic1.computed, '[' + nested.obj.value + ']'); assert.equal(el.$.compose.$.basic1.notifyingValue, 42); assert.equal(el.$.compose.$.basic2.computed, '[' + nested.obj.value + ']'); assert.equal(el.$.compose.$.basic2.notifyingValue, 42); assert.equal(el.$.forward.$.compose.$.basic1.computed, '[' + nested.obj.value + ']'); assert.equal(el.$.forward.$.compose.$.basic1.notifyingValue, 42); assert.equal(el.$.forward.$.compose.$.basic2.computed, '[' + nested.obj.value + ']'); assert.equal(el.$.forward.$.compose.$.basic2.notifyingValue, 42); assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); assert.equal(el.$.pe.obj, nested.obj); switch (nestingLevel) { case 0: assert.equal(el.nestedSubpathChanged.firstCall.args[0].path, 'nested'); assert.equal(el.nestedSubpathChanged.firstCall.args[0].value, nested); assert.equal(el.nestedObjSubpathChanged.firstCall.args[0].path, 'nested.obj'); assert.equal(el.nestedObjSubpathChanged.firstCall.args[0].value, nested.obj); assert.equal(el.$.compose.objSubpathChanged.firstCall.args[0].path, 'obj'); assert.equal(el.$.compose.objSubpathChanged.firstCall.args[0].value, nested.obj); assert.equal(el.$.forward.objSubpathChanged.firstCall.args[0].path, 'obj'); assert.equal(el.$.forward.objSubpathChanged.firstCall.args[0].value, nested.obj); assert.equal(el.$.forward.$.compose.objSubpathChanged.firstCall.args[0].path, 'obj'); assert.equal(el.$.forward.$.compose.objSubpathChanged.firstCall.args[0].value, nested.obj); // x-pe uses async PropertiesChanged setTimeout(() => { assert.equal(el.$.pe._propertiesChanged.callCount, 1); assert.equal(el.$.pe._propertiesChanged.firstCall.args[1].obj, nested.obj); done(); }); break; case 1: assert.equal(el.nestedSubpathChanged.firstCall.args[0].path, 'nested.obj'); assert.equal(el.nestedSubpathChanged.firstCall.args[0].value, nested.obj); assert.equal(el.nestedObjSubpathChanged.firstCall.args[0].path, 'nested.obj'); assert.equal(el.nestedObjSubpathChanged.firstCall.args[0].value, nested.obj); assert.equal(el.$.compose.objSubpathChanged.firstCall.args[0].path, 'obj'); assert.equal(el.$.compose.objSubpathChanged.firstCall.args[0].value, nested.obj); assert.equal(el.$.forward.objSubpathChanged.firstCall.args[0].path, 'obj'); assert.equal(el.$.forward.objSubpathChanged.firstCall.args[0].value, nested.obj); assert.equal(el.$.forward.$.compose.objSubpathChanged.firstCall.args[0].path, 'obj'); assert.equal(el.$.forward.$.compose.objSubpathChanged.firstCall.args[0].value, nested.obj); // x-pe uses async PropertiesChanged setTimeout(() => { assert.equal(el.$.pe._propertiesChanged.callCount, 1); assert.equal(el.$.pe._propertiesChanged.firstCall.args[1].obj, nested.obj); done(); }); break; case 2: assert.equal(el.nestedSubpathChanged.firstCall.args[0].path, 'nested.obj.value'); assert.equal(el.nestedSubpathChanged.firstCall.args[0].value, 42); assert.equal(el.nestedObjSubpathChanged.firstCall.args[0].path, 'nested.obj.value'); assert.equal(el.nestedObjSubpathChanged.firstCall.args[0].value, 42); assert.equal(el.$.compose.objSubpathChanged.firstCall.args[0].path, 'obj.value'); assert.equal(el.$.compose.objSubpathChanged.firstCall.args[0].value, 42); assert.equal(el.$.forward.objSubpathChanged.firstCall.args[0].path, 'obj.value'); assert.equal(el.$.forward.objSubpathChanged.firstCall.args[0].value, 42); assert.equal(el.$.forward.$.compose.objSubpathChanged.firstCall.args[0].path, 'obj.value'); assert.equal(el.$.forward.$.compose.objSubpathChanged.firstCall.args[0].value, 42); // x-pe uses async PropertiesChanged setTimeout(() => { // Note the PropertiesMixin element only sees a deep set to 'nested.obj.value' // because it overrides _shouldPropertyChange to allow object re-sets through assert.equal(el.$.pe._propertiesChanged.callCount, 1); assert.equal(el.$.pe._propertiesChanged.firstCall.args[1].obj, nested.obj); done(); }); break; } } test('downward data flow', function(done) { // Do the thing el.nested = { obj: { value: 42 } }; // Verify verifyObserverOutput(0, el.nested, done); }); test('notification from basic element property change', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.$.basic.notifyingValue = 42; // Verify verifyObserverOutput(2, el.nested, done); }); test('notification from composed element property change', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.$.compose.$.basic1.notifyingValue = 42; // Verify verifyObserverOutput(2, el.nested, done); }); test('notification from forward\'s composed element property change', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.$.forward.$.compose.$.basic1.notifyingValue = 42; // Verify verifyObserverOutput(2, el.nested, done); }); test('notification from set in top element', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.set('nested.obj.value', 42); // Verify verifyObserverOutput(2, el.nested, done); }); test('notification from set in composed element', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.$.compose.set('obj.value', 42); // Verify verifyObserverOutput(2, el.nested, done); }); test('notification from set in forward element', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.$.forward.set('obj.value', 42); // Verify verifyObserverOutput(2, el.nested, done); }); test('notification from set in forward\'s composed element', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.$.forward.$.compose.set('obj.value', 42); // Verify verifyObserverOutput(2, el.nested, done); }); test('notification from object change in compose element', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.$.compose.obj = { value: 42 }; // Verify verifyObserverOutput(1, el.nested, done); }); test('notification from object change in forward element', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing el.$.forward.obj = { value: 42 }; // Verify verifyObserverOutput(1, el.nested, done); }); test('notification from object change in forward\'s compose element', function(done) { // Setup el.nested = { obj: { value: 41 } }; el.resetObservers(); // Do the thing // Do the thing el.$.forward.$.compose.obj = { value: 42 }; // Verify verifyObserverOutput(1, el.nested, done); }); test('cached paths get invalidated by object sets', function() { el.nested = {obj: { value: 'initial' }}; assert.equal(el.$.forward.$.compose.$.basic1.notifyingValue, 'initial'); el.$.forward.$.compose.$.basic1.notifyingValue = 'correct'; assert.equal(el.$.forward.$.compose.$.basic1.notifyingValue, 'correct'); el.nested = {obj: { value: 'wrong' }}; assert.equal(el.$.forward.$.compose.$.basic1.notifyingValue, 'wrong'); el.set('nested.obj.value', 'correct'); assert.equal(el.$.forward.$.compose.$.basic1.notifyingValue, 'correct'); }); test('similarly named properties', function() { var nested = { obj: { value: 41, value2: 99 } }; el.nested = nested; assert.equal(el.$.compose.$.basic1.notifyingValue, 41); assert.equal(el.$.compose.$.basic1.othervalue, 99); el.set('nested.obj.value', 42); assert.equal(el.$.compose.$.basic1.notifyingValue, 42); assert.equal(el.$.compose.$.basic1.othervalue, 99); el.set('nested.obj.value2', 98); assert.equal(el.$.compose.$.basic1.notifyingValue, 42); assert.equal(el.$.compose.$.basic1.othervalue, 98); }); }); suite('path effects', function() { var el; setup(function() { el = document.createElement('x-stuff'); document.body.appendChild(el); }); teardown(function() { document.body.removeChild(el); }); test('observer with multiple args, object last', function() { // Setup var nested = { obj: { value: 42 } }; // Do the thing el.setProperties({ a: 'a', b: 'b', nested: nested }); // Verify assert.equal(el.multipleChanged.callCount, 1); assert.equal(el.multipleChanged.firstCall.args[0], 'a'); assert.equal(el.multipleChanged.firstCall.args[1], 'b'); assert.equal(el.multipleChanged.firstCall.args[2].path, 'nested.obj'); assert.equal(el.multipleChanged.firstCall.args[2].value, nested.obj); assert.equal(el.multipleChanged.firstCall.args[2].base, nested.obj); }); test('observer with multiple args, object not last', function() { // Setup var nested = { obj: { value: 42 } }; // Do the thing el.setProperties({ a: 'a', nested: nested, b: 'b' }); // Verify assert.equal(el.multipleChanged.callCount, 1); assert.equal(el.multipleChanged.firstCall.args[0], 'a'); assert.equal(el.multipleChanged.firstCall.args[1], 'b'); assert.equal(el.multipleChanged.firstCall.args[2].path, 'nested.obj'); assert.equal(el.multipleChanged.firstCall.args[2].value, nested.obj); assert.equal(el.multipleChanged.firstCall.args[2].base, nested.obj); }); test('observer with multiple args, object first, then last', function() { // Setup var nested = { obj: { value: 42 } }; el.nested = nested; el.resetObservers(); // Do the thing el.setProperties({ a: 'a', b: 'b' }); // Verify assert.equal(el.multipleChanged.callCount, 1); assert.equal(el.multipleChanged.firstCall.args[0], 'a'); assert.equal(el.multipleChanged.firstCall.args[1], 'b'); assert.equal(el.multipleChanged.firstCall.args[2].path, 'nested.obj'); assert.equal(el.multipleChanged.firstCall.args[2].value, nested.obj); assert.equal(el.multipleChanged.firstCall.args[2].base, nested.obj); // Do another thing el.set('nested.obj.value', 43); // Verify assert.equal(el.multipleChanged.callCount, 2); assert.equal(el.multipleChanged.secondCall.args[0], 'a'); assert.equal(el.multipleChanged.secondCall.args[1], 'b'); assert.equal(el.multipleChanged.secondCall.args[2].path, 'nested.obj.value'); assert.equal(el.multipleChanged.secondCall.args[2].value, 43); assert.equal(el.multipleChanged.secondCall.args[2].base, nested.obj); }); test('observer & computed with multiple path args', function() { // Setup var nested = { b: 33, obj: { c: 66 } }; var a = 1; // Do the thing el.setProperties({ a: a, nested: nested }); // Verify assert.equal(el.multiplePathsChanged.callCount, 1); assert.equal(el.multiplePathsChanged.firstCall.args[0], 1); assert.equal(el.multiplePathsChanged.firstCall.args[1], 33); assert.equal(el.multiplePathsChanged.firstCall.args[2], 66); assert.equal(el.computedFromPaths, 100); assert.equal(el.$.boundChild.computedFromPaths, 100); el.set('a', a + 10); assert.equal(el.$.boundChild.computedFromPaths, 110); el.set('nested.b', nested.b + 10); assert.equal(el.$.boundChild.computedFromPaths, 120); el.set('nested.obj.c', nested.obj.c + 10); assert.equal(el.$.boundChild.computedFromPaths, 130); }); test('array.splices notified', function() { el.arrayNoColl = []; assert.equal(el.arrayNoCollChanged.callCount, 1); el.push('arrayNoColl', 1, 2, 3); assert.equal(el.arrayNoCollChanged.callCount, 2); el.push('arrayNoColl', 4, 5, 6); assert.equal(el.arrayNoCollChanged.callCount, 3); }); test('ensure splices sent into userland dont get nulled', function(){ el.array = []; el.push('array', 1, 2, 3); assert.notEqual(el.arraySplices.indexSplices, null); }); test('array.splices notified, multiple args, prop first', function() { var array = []; el.setProperties({ array: array, prop: 'first' }); assert.equal(el.arrayOrPropChanged.callCount, 1); el.push('array', 1, 2, 3); assert.equal(el.arrayOrPropChanged.callCount, 2); el.push('array', 4, 5, 6); assert.equal(el.arrayOrPropChanged.callCount, 3); }); test('array.splices notified, multiple args, prop last', function() { el.array = []; el.push('array', 1, 2, 3); el.prop = 'last'; // Twice for array (.splices & .length), once for prop assert.equal(el.arrayOrPropChanged.callCount, 3); }); test('array.length notified', function() { el.data = []; assert.equal(el.$.boundChild.arrayLength, el.data.length); el.push('data', 1, 2, 3); assert.equal(el.$.boundChild.arrayLength, el.data.length); el.pop('data'); el.pop('data'); assert.equal(el.$.boundChild.arrayLength, el.data.length); el.unshift('data', 4, 5); el.unshift('data', 6, 7, 8); assert.equal(el.$.boundChild.arrayLength, el.data.length); el.splice('data', 9, 10, 11, 12, 13); assert.equal(el.$.boundChild.arrayLength, el.data.length); el.shift('data'); el.shift('data'); el.shift('data'); assert.equal(el.$.boundChild.arrayLength, el.data.length); }); test('splice correctly deletes full array if only 1 argument is passed in', function() { el.data = [1,2,3]; assert.equal(el.data.length, 3); el.splice('data', 0); assert.equal(el.data.length, 0); }); }); suite('path API', function() { var el; setup(function() { el = document.createElement('x-stuff'); document.body.appendChild(el); }); teardown(function() { document.body.removeChild(el); }); test('get', function() { el.simple = 11; el.nested = { again: { again: { wayOverThere: 99 }, there: 55 }, here: 42 }; assert.equal(el.get('simple'), 11); assert.equal(el.get('nested'), el.nested); assert.equal(el.get('nested.here'), 42); assert.equal(el.get(['nested', 'again']), el.nested.again); assert.equal(el.get('nested.again.there'), 55); assert.equal(el.get('nested.again.again'), el.nested.again.again); assert.equal(el.get(['nested', 'again.again', 'wayOverThere']), 99); }); test('set', function() { el.set('simple', 11); el.set('nested', {}); el.set('nested.here', 42); el.set(['nested', 'again'], {}); el.set('nested.again.there', 55); el.set('nested.again.again', {}); el.set(['nested', 'again.again', 'wayOverThere'], 99); el.set(['again.thereToo'], 111, el.nested.again); assert.equal(el.simple, 11); assert.equal(el.get('simple'), 11); assert.equal(el.get('nested'), el.nested); assert.equal(el.nested.here, 42); assert.equal(el.get('nested.here'), 42); assert.equal(el.get('nested.again'), el.nested.again); assert.equal(el.nested.again.there, 55); assert.equal(el.get('nested.again.there'), 55); assert.equal(el.get('nested.again.again'), el.nested.again.again); assert.equal(el.nested.again.again.wayOverThere, 99); assert.equal(el.get('nested.again.again.wayOverThere'), 99); assert.equal(el.nested.again.again.thereToo, 111); assert.equal(el.get('nested.again.again.thereToo'), 111); }); test('notifyPath basic', function() { el.a = {b: {c: 0}}; el.resetObservers(); el.a.b.c = 55; el.notifyPath('a.b.c'); assert.isTrue(el.aChanged.calledOnce); assert.equal(el.aChanged.firstCall.args[0].path, 'a.b.c'); assert.equal(el.aChanged.firstCall.args[0].value, 55); assert.equal(el.get('a.b.c'), 55); }); test('notifyPath with array path argument', function() { el.a = {b: {c: 0}}; el.resetObservers(); el.a.b.c = 56; el.notifyPath(['a', 'b.c']); assert.isTrue(el.aChanged.calledOnce); assert.equal(el.aChanged.firstCall.args[0].path, 'a.b.c'); assert.equal(el.aChanged.firstCall.args[0].value, 56); assert.equal(el.get('a.b.c'), 56); }); test('notifyPath with value argument', function() { el.a = {b: {c: 0}}; el.resetObservers(); el.a.b.c = 57; el.notifyPath('a.b.c', 57); assert.isTrue(el.aChanged.calledOnce); assert.equal(el.aChanged.firstCall.args[0].path, 'a.b.c'); assert.equal(el.aChanged.firstCall.args[0].value, 57); assert.equal(el.get('a.b.c'), 57); }); test('notifyPath with value argument and array path argument', function() { el.a = {b: {c: 0}}; el.resetObservers(); el.a.b.c = 57; el.notifyPath(['a', 'b.c'], 57); assert.isTrue(el.aChanged.calledOnce); assert.equal(el.aChanged.firstCall.args[0].path, 'a.b.c'); assert.equal(el.aChanged.firstCall.args[0].value, 57); assert.equal(el.get('a.b.c'), 57); }); test('notifyPath a non-extistant does nothing', function() { el.notifyPath('does.not.exist', true); }); test('get array', function() { el.arrayChanged = function() {}; el.array = [1, 2, 3]; el.array.array = [5, 6, 7]; el.array.prop = 'prop'; assert.equal(el.get('array'), el.array); assert.equal(el.get('array.0'), 1); assert.equal(el.get('array.1'), 2); assert.equal(el.get('array.2'), 3); assert.equal(el.get('array.array'), el.array.array); assert.equal(el.get('array.array.0'), 5); assert.equal(el.get('array.array.1'), 6); assert.equal(el.get('array.array.2'), 7); assert.equal(el.get('array.prop'), 'prop'); el.unshift('array', 0); el.splice('array', 2, 0, 1.5); el.unshift('array.array', 4); el.splice('array.array', 2, 0, 5.5); assert.equal(el.get('array.0'), 0); assert.equal(el.get('array.1'), 1); assert.equal(el.get('array.2'), 1.5); assert.equal(el.get('array.3'), 2); assert.equal(el.get('array.4'), 3); assert.equal(el.get('array.array.0'), 4); assert.equal(el.get('array.array.1'), 5); assert.equal(el.get('array.array.2'), 5.5); assert.equal(el.get('array.array.3'), 6); assert.equal(el.get('array.array.4'), 7); }); test('set array', function() { el.arrayChanged = function() {}; el.set('array', [1, 2, 3]); el.set('array.array', [5, 6, 7]); el.set('array.prop', 'prop'); assert.equal(el.get('array'), el.array); assert.equal(el.get('array.0'), 1); assert.equal(el.get('array.1'), 2); assert.equal(el.get('array.2'), 3); assert.equal(el.get('array.array'), el.array.array); assert.equal(el.get('array.array.0'), 5); assert.equal(el.get('array.array.1'), 6); assert.equal(el.get('array.array.2'), 7); assert.equal(el.get('array.prop'), 'prop'); el.set(['array', 0], 81); el.set(['array', 1], 82); el.set(['array', 2], 83); el.set(['array.array', 0], 91); el.set(['array.array', 1], 92); el.set(['array.array', 2], 93); el.set('array.prop', 'foo'); assert.equal(el.get('array.0'), 81); assert.equal(el.get('array.1'), 82); assert.equal(el.get('array.2'), 83); assert.equal(el.get('array.array.0'), 91); assert.equal(el.get('array.array.1'), 92); assert.equal(el.get('array.array.2'), 93); assert.equal(el.get('array.prop'), 'foo'); // el.set(['array', '#0'], 31); // el.set(['array', '#1'], 32); // el.set(['array', '#2'], 33); // el.set(['array.array', '#0'], 41); // el.set(['array.array', '#1'], 42); // el.set(['array.array', '#2'], 43); }); var nop = function() {}; test('push array', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; // var key = el.array.length; el.arrayChanged = function(change) { assert.strictEqual(change.indexSplices.length, 1); assert.strictEqual(change.indexSplices[0].index, 3); assert.strictEqual(change.indexSplices[0].addedCount, 2); assert.strictEqual(change.indexSplices[0].removed.length, 0); }; var ret = el.push('array', 'new1', 'new2'); assert.strictEqual(ret, 5); assert.strictEqual(el.array.length, 5); assert.strictEqual(el.array[0], 'orig1'); assert.strictEqual(el.array[1], 'orig2'); assert.strictEqual(el.array[2], 'orig3'); assert.strictEqual(el.array[3], 'new1'); assert.strictEqual(el.array[4], 'new2'); }); test('pop array', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; // var key = el.array.length-1; el.arrayChanged = function(change) { assert.strictEqual(change.indexSplices.length, 1); assert.strictEqual(change.indexSplices[0].index, 2); assert.strictEqual(change.indexSplices[0].addedCount, 0); assert.strictEqual(change.indexSplices[0].removed.length, 1); assert.strictEqual(change.indexSplices[0].removed[0], 'orig3'); }; var ret = el.pop('array'); assert.strictEqual(ret, 'orig3'); assert.strictEqual(el.array.length, 2); assert.strictEqual(el.array[0], 'orig1'); assert.strictEqual(el.array[1], 'orig2'); }); test('unshift array', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; // var key = el.array.length; el.arrayChanged = function(change) { assert.strictEqual(change.indexSplices.length, 1); assert.strictEqual(change.indexSplices[0].index, 0); assert.strictEqual(change.indexSplices[0].addedCount, 2); assert.strictEqual(change.indexSplices[0].removed.length, 0); }; var ret = el.unshift('array', 'new1', 'new2'); assert.strictEqual(ret, 5); assert.strictEqual(el.array.length, 5); assert.strictEqual(el.array[0], 'new1'); assert.strictEqual(el.array[1], 'new2'); assert.strictEqual(el.array[2], 'orig1'); assert.strictEqual(el.array[3], 'orig2'); assert.strictEqual(el.array[4], 'orig3'); }); test('shift array', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; el.arrayChanged = function(change) { assert.strictEqual(change.indexSplices.length, 1); assert.strictEqual(change.indexSplices[0].index, 0); assert.strictEqual(change.indexSplices[0].addedCount, 0); assert.strictEqual(change.indexSplices[0].removed.length, 1); assert.strictEqual(change.indexSplices[0].removed[0], 'orig1'); }; var ret = el.shift('array'); assert.strictEqual(ret, 'orig1'); assert.strictEqual(el.array.length, 2); assert.strictEqual(el.array[0], 'orig2'); assert.strictEqual(el.array[1], 'orig3'); }); test('splice array', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; // var key = el.array.length; el.arrayChanged = function(change) { assert.strictEqual(change.indexSplices.length, 1); assert.strictEqual(change.indexSplices[0].index, 1); assert.strictEqual(change.indexSplices[0].addedCount, 2); assert.strictEqual(change.indexSplices[0].removed.length, 1); assert.strictEqual(change.indexSplices[0].removed[0], 'orig2'); }; var ret = el.splice('array', 1, 1, 'new1', 'new2'); assert.deepEqual(ret, ['orig2']); assert.strictEqual(el.array.length, 4); assert.strictEqual(el.array[0], 'orig1'); assert.strictEqual(el.array[1], 'new1'); assert.strictEqual(el.array[2], 'new2'); assert.strictEqual(el.array[3], 'orig3'); }); test('corner: no-op push array', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; el.arrayChanged = function() { throw new Error("should not notify"); }; var ret = el.push('array'); assert.deepEqual(ret, 3); assert.strictEqual(el.array.length, 3); assert.strictEqual(el.array[0], 'orig1'); assert.strictEqual(el.array[1], 'orig2'); assert.strictEqual(el.array[2], 'orig3'); }); test('corner: no-op pop array', function() { el.arrayChanged = nop; el.array = []; el.arrayChanged = function() { throw new Error("should not notify"); }; var ret = el.pop('array'); assert.strictEqual(ret, undefined); assert.strictEqual(el.array.length, 0); }); test('corner: no-op unshift array', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; el.arrayChanged = function() { throw new Error("should not notify"); }; var ret = el.unshift('array'); assert.deepEqual(ret, 3); assert.strictEqual(el.array.length, 3); assert.strictEqual(el.array[0], 'orig1'); assert.strictEqual(el.array[1], 'orig2'); assert.strictEqual(el.array[2], 'orig3'); }); test('corner: no-op shift array', function() { el.arrayChanged = nop; el.array = []; el.arrayChanged = function() { throw new Error("should not notify"); }; var ret = el.shift('array'); assert.strictEqual(ret, undefined); assert.strictEqual(ret, undefined); assert.strictEqual(el.array.length, 0); }); test('corner: no-op splice array', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; el.arrayChanged = function() { throw new Error("should not notify"); }; var ret = el.splice('array'); assert.deepEqual(ret, []); assert.strictEqual(el.array.length, 3); assert.strictEqual(el.array[0], 'orig1'); assert.strictEqual(el.array[1], 'orig2'); assert.strictEqual(el.array[2], 'orig3'); }); test('corner: splice array: string args', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; // var key = el.array.length; el.arrayChanged = function(change) { assert.strictEqual(change.indexSplices.length, 1); assert.strictEqual(change.indexSplices[0].index, 1); assert.strictEqual(change.indexSplices[0].addedCount, 2); assert.strictEqual(change.indexSplices[0].removed.length, 1); assert.strictEqual(change.indexSplices[0].removed[0], 'orig2'); }; var ret = el.splice('array', '1', '1', 'new1', 'new2'); assert.deepEqual(ret, ['orig2']); assert.strictEqual(el.array.length, 4); assert.strictEqual(el.array[0], 'orig1'); assert.strictEqual(el.array[1], 'new1'); assert.strictEqual(el.array[2], 'new2'); assert.strictEqual(el.array[3], 'orig3'); }); test('corner: splice array: negative start', function() { el.arrayChanged = nop; el.array = ['orig1', 'orig2', 'orig3']; // var key = el.array.length; el.arrayChanged = function(change) { assert.strictEqual(change.indexSplices.length, 1); assert.strictEqual(change.indexSplices[0].index, 1); assert.strictEqual(change.indexSplices[0].addedCount, 2); assert.strictEqual(change.indexSplices[0].removed.length, 1); assert.strictEqual(change.indexSplices[0].removed[0], 'orig2'); }; var ret = el.splice('array', '-2', '1', 'new1', 'new2'); assert.deepEqual(ret, ['orig2']); assert.strictEqual(el.array.length, 4); assert.strictEqual(el.array[0], 'orig1'); assert.strictEqual(el.array[1], 'new1'); assert.strictEqual(el.array[2], 'new2'); assert.strictEqual(el.array[3], 'orig3'); }); test('link two objects', function() { el.x = el.y = {}; el.linkPaths('y', 'x'); el.xChanged = sinon.spy(); el.yChanged = sinon.spy(); el.set('x.foo', 1); assert.equal(el.xChanged.callCount, 1); assert.equal(el.yChanged.callCount, 1); el.unlinkPaths('y'); el.set('x.foo', 2); assert.equal(el.xChanged.callCount, 2); assert.equal(el.yChanged.callCount, 1); }); test('link three objects', function() { el.x = el.y = el.a = {}; el.linkPaths('y', 'x'); el.linkPaths('a', 'x'); el.xChanged = sinon.spy(); el.yChanged = sinon.spy(); el.aChanged = sinon.spy(); el.set('x.foo', 1); assert.equal(el.xChanged.callCount, 1); assert.equal(el.yChanged.callCount, 1); assert.equal(el.aChanged.callCount, 1); el.set('a.foo', 2); assert.equal(el.xChanged.callCount, 2); assert.equal(el.yChanged.callCount, 2); assert.equal(el.aChanged.callCount, 2); el.unlinkPaths('y'); el.set('a.foo', 3); assert.equal(el.xChanged.callCount, 3); assert.equal(el.yChanged.callCount, 2); assert.equal(el.aChanged.callCount, 3); }); test('multiple linked dependencies to computed property', function() { let linkedObj = {prop: 'Linked'}; el.computeFromLinkedPaths.reset(); el.setProperties({ linked1: linkedObj, linked2: linkedObj, a: 'A' }); assert.equal(el.computeFromLinkedPaths.callCount, 1); assert.equal(el.computedFromLinkedPaths, 'ALinkedLinked'); el.linkPaths('linked1', 'linked2'); el.set('linked1.prop', 'Linked+'); assert.equal(el.computeFromLinkedPaths.callCount, 2); assert.equal(el.computedFromLinkedPaths, 'ALinked+Linked+'); el.linked3 = el.linked2; el.linkPaths('linked2', 'linked3'); el.set('linked3.prop', 'Linked++'); assert.equal(el.computeFromLinkedPaths.callCount, 3); assert.equal(el.computedFromLinkedPaths, 'ALinked++Linked++'); el.set('linked2.prop', 'Linked+++'); assert.equal(el.computeFromLinkedPaths.callCount, 4); assert.equal(el.computedFromLinkedPaths, 'ALinked+++Linked+++'); el.set('linked1.prop', 'Linked++++'); assert.equal(el.computeFromLinkedPaths.callCount, 5); assert.equal(el.computedFromLinkedPaths, 'ALinked++++Linked++++'); el.linked4 = el.linked1; el.linkPaths('linked4', 'linked1'); el.set('linked4.prop', 'Linked+++++'); assert.equal(el.computeFromLinkedPaths.callCount, 6); assert.equal(el.computedFromLinkedPaths, 'ALinked+++++Linked+++++'); el.set('linked3.prop', 'Linked++++++'); assert.equal(el.computeFromLinkedPaths.callCount, 7); assert.equal(el.computedFromLinkedPaths, 'ALinked++++++Linked++++++'); el.set('linked2.prop', 'Linked+++++++'); assert.equal(el.computeFromLinkedPaths.callCount, 8); assert.equal(el.computedFromLinkedPaths, 'ALinked+++++++Linked+++++++'); el.set('linked1.prop', 'Linked++++++++'); assert.equal(el.computeFromLinkedPaths.callCount, 9); assert.equal(el.computedFromLinkedPaths, 'ALinked++++++++Linked++++++++'); el.linked5 = el.linked3; el.linkPaths('linked5', 'linked3'); el.set('linked4.prop', 'Linked+++++++++'); assert.equal(el.computeFromLinkedPaths.callCount, 10); assert.equal(el.computedFromLinkedPaths, 'ALinked+++++++++Linked+++++++++'); el.set('linked3.prop', 'Linked++++++++++'); assert.equal(el.computeFromLinkedPaths.callCount, 11); assert.equal(el.computedFromLinkedPaths, 'ALinked++++++++++Linked++++++++++'); el.set('linked2.prop', 'Linked+++++++++++'); assert.equal(el.computeFromLinkedPaths.callCount, 12); assert.equal(el.computedFromLinkedPaths, 'ALinked+++++++++++Linked+++++++++++'); el.set('linked1.prop', 'Linked++++++++++++'); assert.equal(el.computeFromLinkedPaths.callCount, 13); assert.equal(el.computedFromLinkedPaths, 'ALinked++++++++++++Linked++++++++++++'); el.unlinkPaths('linked4'); el.set('linked4.prop', 'Linked+++++++++++++'); assert.equal(el.computeFromLinkedPaths.callCount, 13); assert.equal(el.computedFromLinkedPaths, 'ALinked++++++++++++Linked++++++++++++'); }); test('link two arrays', function() { el.x = el.y = []; el.linkPaths('y', 'x'); el.xChanged = sinon.spy(); el.yChanged = sinon.spy(); el.push('x', {}); // 2 changes for arrays (splices & length) assert.equal(el.xChanged.callCount, 2); assert.equal(el.yChanged.callCount, 2); el.unlinkPaths('y'); el.push('x', {}); assert.equal(el.xChanged.callCount, 4); assert.equal(el.yChanged.callCount, 2); }); test('link three arrays', function() { el.x = el.y = el.a = []; el.linkPaths('y', 'x'); el.linkPaths('a', 'x'); el.xChanged = sinon.spy(); el.yChanged = sinon.spy(); el.aChanged = sinon.spy(); el.push('x', {}); // 2 changes for arrays (splices & length) assert.equal(el.xChanged.callCount, 2); assert.equal(el.yChanged.callCount, 2); assert.equal(el.aChanged.callCount, 2); el.unlinkPaths('y'); el.push('x', {}); assert.equal(el.xChanged.callCount, 4); assert.equal(el.yChanged.callCount, 2); assert.equal(el.aChanged.callCount, 4); }); test('get from path in deep observer', function() { el.arrayChanged = nop; var array = [1, 2, 3]; // Initialize array el.arrayChangedDeep = function(info) { assert.strictEqual(el.get(info.path), array); }; el.array = array; // Change index 0 el.arrayChangedDeep = function(info) { assert.strictEqual(el.get(info.path), 99); }; el.set('array.0', 99); // Unshift value el.arrayChangedDeep = function(info) { if (info.path == 'array.splices') { assert.strictEqual(el.get(info.path), array.splices); } else { assert.strictEqual(el.get(info.path), 4); } }; el.unshift('array', 0); // Change index 0 el.arrayChangedDeep = function(info) { assert.strictEqual(el.get(info.path), -1); }; el.set('array.0', -1); // Verify array contents arraysEqual(el.array, [-1, 99, 2, 3]); }); }); suite('corner cases', function() { test('malformed observer has nice message on failure', function() { var thrown = false; var verifyError = function(e) { assert.equal(e.message, "Malformed observer expression 'foo(missingParenthesis'"); thrown = true; }; try { Polymer({ is: 'x-broken', observers: ['foo(missingParenthesis'] }).finalize(); } catch (e) { verifyError(e); } assert.equal(thrown, true, "No exception thrown when parsing malformed observer"); }); test('reentry during path processing', function() { let host = document.createElement('x-reentry-host'); document.body.appendChild(host); assert.equal(host.objChanged.callCount, 1); assert.equal(host.obj.prop, 0); host.set('obj.prop', host.obj.prop+1); assert.equal(host.objChanged.callCount, 2); assert.equal(host.objChanged.secondCall.args[0].path, 'obj.prop'); assert.equal(host.objChanged.secondCall.args[0].value, 1); }); test('element without notify effects still notifies paths (1.x guarantee)', function() { let host = document.createElement('x-path-host'); let listener = sinon.spy(); host.addEventListener('obj-changed', listener); document.body.appendChild(host); assert.equal(host.objChanged.callCount, 1); assert.equal(host.obj.prop, 0); assert.equal(listener.callCount, 0); host.$.client.set('obj.prop', host.obj.prop+1); assert.equal(host.objChanged.callCount, 2); assert.equal(host.objChanged.secondCall.args[0].path, 'obj.prop'); assert.equal(host.objChanged.secondCall.args[0].value, 1); assert.equal(listener.callCount, 1); assert.equal(listener.firstCall.args[0].detail.path, 'obj.prop'); assert.equal(listener.firstCall.args[0].detail.value, 1); }); }); })(); </script> </body> </html>