Merge pull request #5266 from Polymer/5262-kschaaf-legacy-undefined

[2.x] Add legacy-data-mixin as 1.x->2.x migration aide. Fixes #5262.
This commit is contained in:
Kevin Schaaf
2018-11-01 23:20:04 -07:00
committed by GitHub
15 changed files with 3313 additions and 976 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ before_script:
update-types".' && false)
script:
- wct -l chrome
- wct -l firefox
- xvfb-run wct -l firefox
- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then travis_wait 30 ./util/travis-sauce-test.sh; fi
env:
global:
+19 -1
View File
@@ -1412,4 +1412,22 @@ Polymer_DisableUpgradeMixin.prototype.connectedCallback = function(){};
/**
* @override
*/
Polymer_DisableUpgradeMixin.prototype.disconnectedCallback = function(){};
Polymer_DisableUpgradeMixin.prototype.disconnectedCallback = function(){};
/**
* @interface
*/
function Polymer_LegacyDataMixin(){}
/**
* @param {string} property Property that should trigger the effect
* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
* @param {Object=} effect Effect metadata object
* @return {void}
*/
Polymer_LegacyDataMixin.prototype._addPropertyEffect = function(property, type, effect){};
/**
* @param {Object} templateInfo Template metadata to add effect to
* @param {string} prop Property that should trigger the effect
* @param {Object=} effect Effect metadata object
* @return {void}
*/
Polymer_LegacyDataMixin._addTemplatePropertyEffect = function(templateInfo, prop, effect){};
+7 -3
View File
@@ -354,17 +354,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
*
* @param {!PolymerInit} info Object containing Polymer metadata and functions
* to become class methods.
* @template T
* @param {function(T):T} mixin Optional mixin to apply to legacy base class
* @return {function(new:HTMLElement)} Generated class
* @memberof Polymer
*/
Polymer.Class = function(info) {
Polymer.Class = function(info, mixin) {
if (!info) {
console.warn('Polymer.Class requires `info` argument');
}
let klass = GenerateClassFromInfo(info, info.behaviors ?
const baseWithBehaviors = info.behaviors ?
// note: mixinBehaviors ensures `LegacyElementMixin`.
mixinBehaviors(info.behaviors, HTMLElement) :
Polymer.LegacyElementMixin(HTMLElement));
Polymer.LegacyElementMixin(HTMLElement);
const baseWithMixin = mixin ? mixin(baseWithBehaviors) : baseWithBehaviors;
const klass = GenerateClassFromInfo(info, baseWithMixin);
// decorate klass with registration info
klass.is = info.is;
return klass;
+171
View File
@@ -0,0 +1,171 @@
<!--
@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
-->
<link rel="import" href="class.html">
<link rel="import" href="../../polymer.html">
<link rel="import" href="../utils/mixin.html">
<link rel="import" href="../utils/templatize.html">
<script>
(function() {
'use strict';
const UndefinedArgumentError = class extends Error {
constructor(message, arg) {
super(message);
this.arg = arg;
this.name = this.constructor.name;
// Affordances for ensuring instanceof works after babel ES5 compilation
// TODO(kschaaf): Remove after polymer CLI updates to newer Babel that
// sets the constructor/prototype correctly for subclassed builtins
this.constructor = UndefinedArgumentError;
this.__proto__ = UndefinedArgumentError.prototype;
}
};
/**
* Wraps effect functions to catch `UndefinedArgumentError`s and warn.
*
* @param {Object=} effect Effect metadata object
* @param {Object=} fnName Name of user function, if known
* @return {?Object} Effect metadata object
*/
function wrapEffect(effect, fnName) {
if (effect && effect.fn) {
const fn = effect.fn;
effect.fn = function() {
try {
fn.apply(this, arguments);
} catch (e) {
if (e instanceof UndefinedArgumentError) {
console.warn(`Argument '${e.arg}'${fnName ?` for method '${fnName}'` : ''} was undefined. Ensure it has an undefined check.`);
} else {
throw e;
}
}
};
}
return effect;
}
/**
* Mixin to selectively add back Polymer 1.x's `undefined` rules
* governing when observers & computing functions run based
* on all arguments being defined (reference https://www.polymer-project.org/1.0/docs/devguide/observers#multi-property-observers).
*
* When loaded, all legacy elements (defined with `Polymer({...})`)
* will have the mixin applied. The mixin only restores legacy data handling
* if `_legacyUndefinedCheck: true` is set on the element's prototype.
*
* This mixin is intended for use to help migration from Polymer 1.x to
* 2.x+ by allowing legacy code to work while identifying observers and
* computing functions that need undefined checks to work without
* the mixin in Polymer 2.
*
* @mixinFunction
* @polymer
* @summary Mixin to selectively add back Polymer 1.x's `undefined` rules
* governing when observers & computing functions run.
*/
Polymer.LegacyDataMixin = Polymer.dedupingMixin(superClass => {
/**
* @polymer
* @mixinClass
* @implements {Polymer_LegacyDataMixin}
*/
class LegacyDataMixin extends superClass {
/**
* Overrides `Polyer.PropertyEffects` to add `undefined` argument
* checking to match Polymer 1.x style rules
*
* @param {!Array<!MethodArg>} args Array of argument metadata
* @param {string} path Property/path name that triggered the method effect
* @param {Object} props Bag of current property changes
* @return {Array<*>} Array of argument values
* @private
*/
_marshalArgs(args, path, props) {
const vals = super._marshalArgs(args, path, props);
// Per legacy data rules, single-property observers (whether in `properties`
// and in `observers`) are called regardless of whether their argument is
// undefined or not. Multi-property observers must have all arguments defined
if (this._legacyUndefinedCheck && vals.length > 1) {
for (let i=0; i<vals.length; i++) {
if (vals[i] === undefined) {
// Break out of effect's control flow; will be caught in
// wrapped property effect function below
const name = args[i].name;
throw new UndefinedArgumentError(`Argument '${name}' is undefined. Ensure it has an undefined check.`, name);
}
}
}
return vals;
}
/**
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
* catch `UndefinedArgumentError`s and warn.
*
* @param {string} property Property that should trigger the effect
* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
* @param {Object=} effect Effect metadata object
* @return {void}
* @protected
*/
_addPropertyEffect(property, type, effect) {
return super._addPropertyEffect(property, type,
wrapEffect(effect, effect && effect.info && effect.info.methodName));
}
/**
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
* catch `UndefinedArgumentError`s and warn.
*
* @param {Object} templateInfo Template metadata to add effect to
* @param {string} prop Property that should trigger the effect
* @param {Object=} effect Effect metadata object
* @return {void}
* @protected
*/
static _addTemplatePropertyEffect(templateInfo, prop, effect) {
return super._addTemplatePropertyEffect(templateInfo, prop, wrapEffect(effect));
}
}
return LegacyDataMixin;
});
// LegacyDataMixin is applied to base class _before_ metaprogramming, to
// ensure override of _addPropertyEffect et.al. are used by metaprogramming
// performed in _finalizeClass
const Class = Polymer.Class;
Polymer.Class = (info, mixin) => Class(info,
superClass => mixin ?
mixin(Polymer.LegacyDataMixin(superClass)) :
Polymer.LegacyDataMixin(superClass)
);
// Apply LegacyDataMixin to Templatizer instances as well, and defer
// runtime switch to the root's host (_methodHost)
Polymer.Templatize.mixin =
Polymer.dedupingMixin(superClass => class extends Polymer.LegacyDataMixin(superClass) {
get _legacyUndefinedCheck() {
return this._methodHost && this._methodHost._legacyUndefinedCheck;
}
});
console.info('LegacyDataMixin will be applied to all legacy elements.\n' +
'Set `_legacyUndefinedCheck: true` to enable.');
})();
</script>
+51 -51
View File
@@ -814,7 +814,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
let context = inst._methodHost || inst;
let fn = context[info.methodName];
if (fn) {
let args = marshalArgs(inst.__data, info.args, property, props);
let args = inst._marshalArgs(info.args, property, props);
return fn.apply(context, args);
} else if (!info.dynamicFn) {
console.warn('method `' + info.methodName + '` not defined');
@@ -970,56 +970,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return a;
}
/**
* Gather the argument values for a method specified in the provided array
* of argument metadata.
*
* The `path` and `value` arguments are used to fill in wildcard descriptor
* when the method is being called as a result of a path notification.
*
* @param {Object} data Instance data storage object to read properties from
* @param {!Array<!MethodArg>} args Array of argument metadata
* @param {string} path Property/path name that triggered the method effect
* @param {Object} props Bag of current property changes
* @return {Array<*>} Array of argument values
* @private
*/
function marshalArgs(data, args, path, props) {
let values = [];
for (let i=0, l=args.length; i<l; i++) {
let arg = args[i];
let name = arg.name;
let v;
if (arg.literal) {
v = arg.value;
} else {
if (arg.structured) {
v = Polymer.Path.get(data, name);
// when data is not stored e.g. `splices`
if (v === undefined) {
v = props[name];
}
} else {
v = data[name];
}
}
if (arg.wildcard) {
// Only send the actual path changed info if the change that
// caused the observer to run matched the wildcard
let baseChanged = (name.indexOf(path + '.') === 0);
let matches = (path.indexOf(name) === 0 && !baseChanged);
values[i] = {
path: matches ? path : name,
value: matches ? props[path] : v,
base: v
};
} else {
values[i] = v;
}
}
return values;
}
// data api
/**
@@ -2181,6 +2131,56 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
createMethodEffect(this, sig, TYPES.COMPUTE, runComputedEffect, property, dynamicFn);
}
/**
* Gather the argument values for a method specified in the provided array
* of argument metadata.
*
* The `path` and `value` arguments are used to fill in wildcard descriptor
* when the method is being called as a result of a path notification.
*
* @param {!Array<!MethodArg>} args Array of argument metadata
* @param {string} path Property/path name that triggered the method effect
* @param {Object} props Bag of current property changes
* @return {Array<*>} Array of argument values
* @private
*/
_marshalArgs(args, path, props) {
const data = this.__data;
let values = [];
for (let i=0, l=args.length; i<l; i++) {
let arg = args[i];
let name = arg.name;
let v;
if (arg.literal) {
v = arg.value;
} else {
if (arg.structured) {
v = Polymer.Path.get(data, name);
// when data is not stored e.g. `splices`
if (v === undefined) {
v = props[name];
}
} else {
v = data[name];
}
}
if (arg.wildcard) {
// Only send the actual path changed info if the change that
// caused the observer to run matched the wildcard
let baseChanged = (name.indexOf(path + '.') === 0);
let matches = (path.indexOf(name) === 0 && !baseChanged);
values[i] = {
path: matches ? path : name,
value: matches ? props[path] : v,
base: v
};
} else {
values[i] = v;
}
}
return values;
}
// -- static class methods ------------
/**
+4
View File
@@ -297,6 +297,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// Anonymous class created by the templatize
let base = options.mutableData ?
MutableTemplateInstanceBase : TemplateInstanceBase;
// Affordance for global mixins onto TemplatizeInstance
if (Polymer.Templatize.mixin) {
base = Polymer.Templatize.mixin(base);
}
/**
* @constructor
* @extends {base}
+2526 -912
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -31,7 +31,7 @@
"polymer-build": "^2.1.1",
"run-sequence": "^2.2.0",
"through2": "^2.0.0",
"web-component-tester": "^6.5.0"
"web-component-tester": "^6.9.0"
},
"scripts": {
"build": "gulp",
+2 -1
View File
@@ -79,7 +79,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
'unit/dir.html',
'unit/disable-upgrade.html',
'unit/shady-unscoped-style.html',
'unit/html-tag.html'
'unit/html-tag.html',
'unit/legacy-data.html'
// 'unit/multi-style.html'
];
+4 -1
View File
@@ -85,7 +85,10 @@ suite('globals', function() {
// weird safari + selenium globals
alert: true,
confirm: true,
prompt: true
prompt: true,
// weird FF globals
XULElement: true
};
test('check global leakage', function() {
+436
View File
@@ -0,0 +1,436 @@
<!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="../../lib/legacy/legacy-data-mixin.html">
</head>
<body>
<dom-module id="x-data">
<template>
<div id="child"
computed-single="[[computeSingle(inlineSingleDep)]]"
computed-multi="[[computeMulti(inlineMultiDep1, inlineMultiDep2)]]">
<dom-if if>
<template><div id="ifChild" computed-multi="[[computeMulti(inlineMultiIfDep1, inlineMultiIfDep2)]]"></div></template>
</dom-if>
</div>
</template>
<script>
HTMLImports.whenReady(() => {
Polymer({
is: 'x-data',
_legacyUndefinedCheck: true,
properties: {
singleProp: String,
multiProp1: String,
multiProp2: String,
computedSingleDep: String,
computedMultiDep1: String,
computedMultiDep2: String,
inlineSingleDep: String,
inlineMultiDep1: String,
inlineMultiDep2: String,
inlineMultiIfDep1: String,
inlineMultiIfDep2: String,
computedSingle: {
computed: 'computeSingle(computedSingleDep)'
},
computedMulti: {
computed: 'computeMulti(computedMultiDep1, computedMultiDep2)'
}
},
observers: [
'staticObserver("staticObserver")',
'singlePropObserver(singleProp)',
'multiPropObserver(multiProp1, multiProp2)',
'throws(throwProp)'
],
created() {
this.singlePropObserver = sinon.spy();
this.multiPropObserver = sinon.spy();
this.staticObserver = sinon.spy();
this.computeSingle = sinon.spy((inlineSingleDep) => `[${inlineSingleDep}]`);
this.computeMulti = sinon.spy((inlineMultiDep1, inlineMultiDep2) => `[${inlineMultiDep1},${inlineMultiDep2}]`);
},
throws() {
throw new Error('real error');
}
});
});
</script>
</dom-module>
<test-fixture id="declarative-none">
<template>
<x-data></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-single">
<template>
<x-data single-prop="a"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-multi-one">
<template>
<x-data multi-prop1="b"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-multi-all">
<template>
<x-data multi-prop1="b" multi-prop2="c"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-single-computed">
<template>
<x-data computed-single-dep="a"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-multi-one-computed">
<template>
<x-data computed-multi-dep1="b"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-multi-all-computed">
<template>
<x-data computed-multi-dep1="b" computed-multi-dep2="c"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-single-computed-inline">
<template>
<x-data inline-single-dep="a"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-multi-one-computed-inline">
<template>
<x-data inline-multi-dep1="b"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-multi-all-computed-inline">
<template>
<x-data inline-multi-dep1="b" inline-multi-dep2="c"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-multi-if-one-computed-inline">
<template>
<x-data inline-multi-if-dep1="b"></x-data>
</template>
</test-fixture>
<test-fixture id="declarative-multi-if-all-computed-inline">
<template>
<x-data inline-multi-if-dep1="b" inline-multi-if-dep2="c"></x-data>
</template>
</test-fixture>
<script>
(function() {
let el;
function assertEffects(callCounts) {
assert.equal(el.staticObserver.callCount, 1, 'staticObserver call count wrong');
assert.equal(el.singlePropObserver.callCount,
callCounts.singlePropObserver || 0, 'singlePropObserver call count wrong');
assert.equal(el.multiPropObserver.callCount,
callCounts.multiPropObserver || 0, 'multiPropObserver call count wrong');
assert.equal(el.computeSingle.callCount,
callCounts.computeSingle || 0, 'computeSingle call count wrong');
assert.equal(el.computeMulti.callCount,
callCounts.computeMulti || 0, 'computeMulti call count wrong');
assert.equal(console.warn.callCount, callCounts.warn || 0,
'console.warn call count wrong');
}
suite('imperative', () => {
setup(() => sinon.spy(console, 'warn'));
function setupElement(check, props) {
el = document.createElement('x-data');
el._legacyUndefinedCheck = check;
Object.assign(el, props);
document.body.appendChild(el);
Polymer.flush();
}
teardown(() => {
console.warn.restore();
el.parentNode.removeChild(el);
});
const singleProp = 'singleProp';
const multiProp1 = 'multiProp1';
const multiProp2 = 'multiProp2';
const computedSingleDep = 'computedSingleDep';
const computedMultiDep1 = 'computedMultiDep1';
const computedMultiDep2 = 'computedMultiDep2';
const inlineSingleDep = 'inlineSingleDep';
const inlineMultiDep1 = 'inlineMultiDep1';
const inlineMultiDep2 = 'inlineMultiDep2';
const inlineMultiIfDep1 = 'inlineMultiIfDep1';
const inlineMultiIfDep2 = 'inlineMultiIfDep2';
suite('check disabled', () => {
test('no arguments defined', () => {
setupElement(false, {});
assertEffects({});
});
test('singlePropObserver argument defined', () => {
setupElement(false, {singleProp});
assertEffects({singlePropObserver: 1});
});
test('one multiPropObserver arguments defined', () => {
setupElement(false, {multiProp1});
assertEffects({multiPropObserver: 1});
});
test('all multiPropObserver defined', () => {
setupElement(false, {multiProp1, multiProp2});
assertEffects({multiPropObserver: 1});
});
test('singlePropObserver argument undefined', () => {
setupElement(false, {singleProp});
assertEffects({singlePropObserver: 1});
el.singleProp = undefined;
assertEffects({singlePropObserver: 2});
});
test('one multiPropObserver arguments undefined', () => {
setupElement(false, {multiProp1, multiProp2});
assertEffects({multiPropObserver: 1});
el.multiProp1 = undefined;
assertEffects({multiPropObserver: 2});
});
test('all multiPropObserver undefined', () => {
setupElement(false, {multiProp1, multiProp2});
assertEffects({multiPropObserver: 1});
el.multiProp1 = undefined;
assertEffects({multiPropObserver: 2});
el.multiProp2 = undefined;
assertEffects({multiPropObserver: 3});
});
test('computeSingle argument defined', () => {
setupElement(false, {computedSingleDep});
assertEffects({computeSingle: 1});
assert.equal(el.computedSingle, '[computedSingleDep]');
});
test('one computeMulti argument defined', () => {
setupElement(false, {computedMultiDep1});
assertEffects({computeMulti: 1});
assert.equal(el.computedMulti, '[computedMultiDep1,undefined]');
});
test('all computeMulti argument defined', () => {
setupElement(false, {computedMultiDep1, computedMultiDep2});
assertEffects({computeMulti: 1});
assert.equal(el.computedMulti, '[computedMultiDep1,computedMultiDep2]');
});
test('inline computeSingle argument defined', () => {
setupElement(false, {inlineSingleDep});
assertEffects({computeSingle: 1});
assert.equal(el.$.child.computedSingle, '[inlineSingleDep]');
});
test('one inline computeMulti argument defined', () => {
setupElement(false, {inlineMultiDep1});
assertEffects({computeMulti: 1});
assert.equal(el.$.child.computedMulti, '[inlineMultiDep1,undefined]');
});
test('all inline computeMulti argument defined', () => {
setupElement(false, {inlineMultiDep1, inlineMultiDep2});
assertEffects({computeMulti: 1});
assert.equal(el.$.child.computedMulti, '[inlineMultiDep1,inlineMultiDep2]');
});
test('one inline computeMulti argument defined in dom-if', () => {
setupElement(false, {inlineMultiIfDep1});
assertEffects({computeMulti: 1});
assert.equal(el.$$('#ifChild').computedMulti, '[inlineMultiIfDep1,undefined]');
});
test('all inline computeMulti argument defined in dom-if', () => {
setupElement(false, {inlineMultiIfDep1, inlineMultiIfDep2});
assertEffects({computeMulti: 1});
assert.equal(el.$$('#ifChild').computedMulti, '[inlineMultiIfDep1,inlineMultiIfDep2]');
});
});
suite('warn', () => {
test('no arguments defined', () => {
setupElement(true, {});
assertEffects({});
});
test('singlePropObserver argument defined', () => {
setupElement(true, {singleProp});
assertEffects({singlePropObserver: 1});
});
test('one multiPropObserver arguments defined', () => {
setupElement(true, {multiProp1});
assertEffects({multiPropObserver: 0, warn: 1});
});
test('all multiPropObserver defined', () => {
setupElement(true, {multiProp1, multiProp2});
assertEffects({multiPropObserver: 1});
});
test('singlePropObserver argument undefined', () => {
setupElement(true, {singleProp});
assertEffects({singlePropObserver: 1});
el.singleProp = undefined;
assertEffects({singlePropObserver: 2});
});
test('one multiPropObserver arguments undefined', () => {
setupElement(true, {multiProp1, multiProp2});
assertEffects({multiPropObserver: 1});
el.multiProp1 = undefined;
assertEffects({multiPropObserver: 1, warn: 1});
});
test('all multiPropObserver undefined', () => {
setupElement(true, {multiProp1, multiProp2});
assertEffects({multiPropObserver: 1});
el.multiProp1 = undefined;
assertEffects({multiPropObserver: 1, warn: 1});
el.multiProp2 = undefined;
assertEffects({multiPropObserver: 1, warn: 2});
});
test('computeSingle argument defined', () => {
setupElement(true, {computedSingleDep});
assertEffects({computeSingle: 1});
assert.equal(el.computedSingle, '[computedSingleDep]');
});
test('one computeMulti argument defined', () => {
setupElement(true, {computedMultiDep1});
assertEffects({warn: 1});
assert.equal(el.computedMulti, undefined);
});
test('all computeMulti argument defined', () => {
setupElement(true, {computedMultiDep1, computedMultiDep2});
assertEffects({computeMulti: 1});
assert.equal(el.computedMulti, '[computedMultiDep1,computedMultiDep2]');
});
test('inline computeSingle argument defined', () => {
setupElement(true, {inlineSingleDep});
assertEffects({computeSingle: 1});
assert.equal(el.$.child.computedSingle, '[inlineSingleDep]');
});
test('one inline computeMulti argument defined', () => {
setupElement(true, {inlineMultiDep1});
assertEffects({warn: 1});
assert.equal(el.$.child.computedMulti, undefined);
});
test('all inline computeMulti argument defined', () => {
setupElement(true, {inlineMultiDep1, inlineMultiDep2});
assertEffects({computeMulti: 1});
assert.equal(el.$.child.computedMulti, '[inlineMultiDep1,inlineMultiDep2]');
});
test('one inline computeMulti argument defined in dom-if', () => {
setupElement(true, {inlineMultiIfDep1});
assertEffects({warn: 1});
assert.equal(el.$$('#ifChild').computedMulti, undefined);
});
test('all inline computeMulti argument defined in dom-if', () => {
setupElement(true, {inlineMultiIfDep1, inlineMultiIfDep2});
assertEffects({computeMulti: 1});
assert.equal(el.$$('#ifChild').computedMulti, '[inlineMultiIfDep1,inlineMultiIfDep2]');
});
});
});
suite('declarative', () => {
setup(() => sinon.spy(console, 'warn'));
teardown(() => console.warn.restore());
suite('warn', () => {
test('no arguments defined', () => {
el = fixture('declarative-none');
assertEffects({});
});
test('singlePropObserver argument defined', () => {
el = fixture('declarative-single');
assertEffects({singlePropObserver: 1});
});
test('one multiPropObserver arguments defined', () => {
el = fixture('declarative-multi-one');
assertEffects({multiPropObserver: 0, warn: 1});
});
test('all multiPropObserver defined', () => {
el = fixture('declarative-multi-all');
assertEffects({multiPropObserver: 1});
});
test('computeSingle argument defined', () => {
el = fixture('declarative-single-computed');
assertEffects({computeSingle: 1});
assert.equal(el.computedSingle, '[a]');
});
test('one computeMulti arguments defined', () => {
el = fixture('declarative-multi-one-computed');
assertEffects({computeMulti: 0, warn: 1});
assert.equal(el.computedMulti, undefined);
});
test('all computeMulti defined', () => {
el = fixture('declarative-multi-all-computed');
assert.equal(el.computedMulti, '[b,c]');
});
test('inline computeSingle argument defined', () => {
el = fixture('declarative-single-computed-inline');
assertEffects({computeSingle: 1});
assert.equal(el.$.child.computedSingle, '[a]');
});
test('inline one computeMulti arguments defined', () => {
el = fixture('declarative-multi-one-computed-inline');
assertEffects({computeMulti: 0, warn: 1});
assert.equal(el.$.child.computedMulti, undefined);
});
test('inline all computeMulti defined', () => {
el = fixture('declarative-multi-all-computed-inline');
assertEffects({computeMulti: 1});
assert.equal(el.$.child.computedMulti, '[b,c]');
});
test('one inline computeMulti argument defined in dom-if', () => {
el = fixture('declarative-multi-if-one-computed-inline');
Polymer.flush();
assertEffects({computeMulti: 0, warn: 1});
assert.equal(el.$$('#ifChild').computedMulti, undefined);
});
test('all inline computeMulti argument defined in dom-if', () => {
el = fixture('declarative-multi-if-all-computed-inline');
Polymer.flush();
assertEffects({computeMulti: 1});
assert.equal(el.$$('#ifChild').computedMulti, '[b,c]');
});
});
});
suite('other', () => {
test('real errors still throw', () => {
const el = document.createElement('x-data');
document.body.appendChild(el);
assert.throws(() => {
el.throwProp = true;
}, /real error/);
document.body.removeChild(el);
});
});
})();
</script>
</body>
</html>
+1 -1
View File
@@ -91,5 +91,5 @@ declare namespace Polymer {
*
* @returns Generated class
*/
function Class(info: PolymerInit): {new(): HTMLElement};
function Class<T>(info: PolymerInit, mixin: (p0: T) => T): {new(): HTMLElement};
}
+89
View File
@@ -0,0 +1,89 @@
/**
* DO NOT EDIT
*
* This file was automatically generated by
* https://github.com/Polymer/gen-typescript-declarations
*
* To modify these typings, edit the source file(s):
* lib/legacy/legacy-data-mixin.html
*/
/// <reference path="class.d.ts" />
/// <reference path="../../polymer.d.ts" />
/// <reference path="../utils/mixin.d.ts" />
/// <reference path="../utils/templatize.d.ts" />
declare class UndefinedArgumentError extends Error {
constructor(message: any, arg: any);
}
declare namespace Polymer {
/**
* Mixin to selectively add back Polymer 1.x's `undefined` rules
* governing when observers & computing functions run based
* on all arguments being defined (reference https://www.polymer-project.org/1.0/docs/devguide/observers#multi-property-observers).
*
* When loaded, all legacy elements (defined with `Polymer({...})`)
* will have the mixin applied. The mixin only restores legacy data handling
* if `_legacyUndefinedCheck: true` is set on the element's prototype.
*
* This mixin is intended for use to help migration from Polymer 1.x to
* 2.x+ by allowing legacy code to work while identifying observers and
* computing functions that need undefined checks to work without
* the mixin in Polymer 2.
*/
function LegacyDataMixin<T extends new (...args: any[]) => {}>(base: T): T & LegacyDataMixinConstructor;
interface LegacyDataMixinConstructor {
new(...args: any[]): LegacyDataMixin;
/**
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
* catch `UndefinedArgumentError`s and warn.
*
* @param templateInfo Template metadata to add effect to
* @param prop Property that should trigger the effect
* @param effect Effect metadata object
*/
_addTemplatePropertyEffect(templateInfo: object|null, prop: string, effect?: object|null): void;
}
interface LegacyDataMixin {
readonly _legacyUndefinedCheck: any;
/**
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
* catch `UndefinedArgumentError`s and warn.
*
* @param property Property that should trigger the effect
* @param type Effect type, from this.PROPERTY_EFFECT_TYPES
* @param effect Effect metadata object
*/
_addPropertyEffect(property: string, type: string, effect?: object|null): void;
}
}
declare class LegacyDataMixin extends superClass {
/**
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
* catch `UndefinedArgumentError`s and warn.
*
* @param templateInfo Template metadata to add effect to
* @param prop Property that should trigger the effect
* @param effect Effect metadata object
*/
static _addTemplatePropertyEffect(templateInfo: object|null, prop: string, effect?: object|null): void;
/**
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
* catch `UndefinedArgumentError`s and warn.
*
* @param property Property that should trigger the effect
* @param type Effect type, from this.PROPERTY_EFFECT_TYPES
* @param effect Effect metadata object
*/
_addPropertyEffect(property: string, type: string, effect?: object|null): void;
}
+1 -1
View File
@@ -9,4 +9,4 @@
# subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
#
set -x
wct -s 'windows 10/microsoftedge@14' -s 'windows 10/microsoftedge@15' -s 'windows 10/microsoftedge@16' -s 'windows 8.1/internet explorer@11' -s 'os x 10.11/safari@9' -s 'macos 10.12/safari@10' -s 'macos 10.13/safari@11' -s 'Linux/chrome@41'
wct -s 'windows 10/microsoftedge@15' -s 'windows 10/microsoftedge@17' -s 'windows 8.1/internet explorer@11' -s 'os x 10.11/safari@9' -s 'macos 10.13/safari@11' -s 'macos 10.13/safari@12' -s 'Linux/chrome@41' -s 'windows/firefox@62'
-3
View File
@@ -10,9 +10,6 @@
"headless",
"disable-gpu",
"no-sandbox"
],
"firefox": [
"-headless"
]
}
}