Bug 1528888 [wpt PR 15454] - Add CEReactions tests for some HTML elements, a=testonly
authorkaixinjxq <xiuqix.jiang@intel.com>
Wed, 06 Mar 2019 12:36:00 +0000
changeset 464667 21ae0e9fd9073645a2b247ac613ea09ea11806bf
parent 464666 5020df9a98bd75e0c6c6f4a04c0844c6c8dad1e1
child 464668 55dcd4b52946f3674e3c93b70b9dafe9f0f3f5ee
push id35717
push useraciure@mozilla.com
push dateSun, 17 Mar 2019 09:45:26 +0000
treeherdermozilla-central@e0861be8d6c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1528888, 15454
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1528888 [wpt PR 15454] - Add CEReactions tests for some HTML elements, a=testonly Automatic update from web-platform-tests Add CEReactions tests for some HTML elements - Covers elements of HTMLAreaElement/HTMLEmbedElement/HTMLFieldSetElement/HTMLImageElement - Wrap `testReflectAttrWithContentValuesAndDepAttr` function in HTMLButtonElement.html as a common function - Wrap `testReflectAttrWithDepAttr` function in HTMLButtonElement.html as a common function - Test formNoValidate of HTMLButtonElement use testReflectBooleanAttributeWithDependentAttributes function -- wpt-commits: f7086960db87211ada9cd1bcca3abc0334997d3d wpt-pr: 15454
testing/web-platform/tests/custom-elements/reactions/HTMLAreaElement.html
testing/web-platform/tests/custom-elements/reactions/HTMLButtonElement.html
testing/web-platform/tests/custom-elements/reactions/HTMLEmbedElement.html
testing/web-platform/tests/custom-elements/reactions/HTMLFieldSetElement.html
testing/web-platform/tests/custom-elements/reactions/HTMLImageElement.html
testing/web-platform/tests/custom-elements/reactions/resources/reactions.js
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/HTMLAreaElement.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<title>Custom Elements: CEReactions on HTMLAreaElement interface</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<meta name="assert" content="alt, coords, shape, target, download, ping, rel,
+  referrerPolicy of HTMLAreaElement interface must have CEReactions">
+<meta name="help" content="https://html.spec.whatwg.org/#the-area-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/custom-elements-helpers.js"></script>
+<script src="./resources/reactions.js"></script>
+
+<map name="yellow" id="map">
+</map>
+<img usemap="#yellow" src="/images/yellow.png" alt="yellow pic">
+
+<script>
+
+function getParentElement() {
+  let map = document.getElementById('map');
+  return map;
+}
+
+function setAttributes(instance) {
+  instance.setAttribute('href', '/images/yellow.png');
+}
+
+testReflectAttributeWithDependentAttributes(
+  'alt', 'alt', 'yellow pic',
+  'yellow pic2', 'alt on HTMLAreaElement', 'area',
+  getParentElement, instance => setAttributes(instance), HTMLAreaElement
+);
+testReflectAttributeWithParentNode(
+  'coords', 'coords', '1, 1, 5, 5',
+  '2, 2, 6, 6', 'coords on HTMLAreaElement', 'area',
+  getParentElement, HTMLAreaElement
+);
+testReflectAttributeWithDependentAttributes(
+  'shape', 'shape', 'rectangle',
+  'default', 'shape on HTMLAreaElement', 'area',
+  getParentElement, instance => instance.setAttribute('coords', '1, 1, 5, 5'),
+  HTMLAreaElement
+);
+testReflectAttributeWithDependentAttributes(
+  'target', 'target', '_blank',
+  '_top', 'target on HTMLAreaElement', 'area',
+  getParentElement, instance => setAttributes(instance), HTMLAreaElement
+);
+testReflectAttributeWithDependentAttributes(
+  'download', 'download', 'pic1',
+  'pic2', 'download on HTMLAreaElement', 'area',
+  getParentElement, instance => setAttributes(instance), HTMLAreaElement
+);
+testReflectAttributeWithDependentAttributes(
+  'ping', 'ping', 'location.href',
+  `${location.protocol}\/\/${location.host}`, 'ping on HTMLAreaElement', 'area',
+  getParentElement, instance => setAttributes(instance), HTMLAreaElement
+);
+testReflectAttributeWithDependentAttributes(
+  'rel', 'rel', 'help',
+  'noreferrer', 'rel on HTMLAreaElement', 'area',
+  getParentElement, instance => setAttributes(instance), HTMLAreaElement
+);
+testReflectAttributeWithDependentAttributes(
+  'referrerPolicy', 'referrerpolicy', 'same-origin',
+  'origin', 'referrerPolicy on HTMLAreaElement', 'area',
+  getParentElement, instance => setAttributes(instance), HTMLAreaElement
+);
+
+</script>
--- a/testing/web-platform/tests/custom-elements/reactions/HTMLButtonElement.html
+++ b/testing/web-platform/tests/custom-elements/reactions/HTMLButtonElement.html
@@ -1,75 +1,80 @@
 <!DOCTYPE html>
 <title>Custom Elements: CEReactions on HTMLButtonElement interface</title>
 <meta name="author" title="Zhang Xiaoyu" href="xiaoyux.zhang@intel.com">
-<meta name="assert" content=" autofocus, disabled, formAction, formEnctype, formMethod, formNoValidate, formTarget, name, type, value of HTMLButtonElement interface must have CEReactions">
+<meta name="assert" content=" autofocus, disabled, formAction, formEnctype,
+  formMethod, formNoValidate, formTarget, name, type, value
+  of HTMLButtonElement interface must have CEReactions">
 <meta name="help" content="https://html.spec.whatwg.org/#htmlbuttonelement">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="../resources/custom-elements-helpers.js"></script>
 <script src="./resources/reactions.js"></script>
 <body>
 <script>
 
 function getParentElement(parentElementName) {
   let parentElement = document.createElement(parentElementName);
   document.body.appendChild(parentElement);
   return parentElement;
 }
 
-testReflectBooleanAttribute('autofocus', 'autofocus', 'autofocus on HTMLButtonElement', 'button', HTMLButtonElement);
-testReflectBooleanAttribute('disabled', 'disabled', 'disabled on HTMLButtonElement', 'button', HTMLButtonElement);
-testReflectAttribute('name', 'name', 'intel', 'intel1', 'name on HTMLButtonElement', 'button', HTMLButtonElement);
-testReflectAttribute('value', 'value', 'HTML', 'CSS', 'value on HTMLButtonElement', 'button', HTMLButtonElement);
-testReflectAttributeWithParentNode('type', 'type', 'submit', 'reset', 'type on HTMLButtonElement', 'button', () => getParentElement('form'), HTMLButtonElement);
-testReflectAttrWithDepAttr('formAction', 'formaction', 'type', 'intel.asp', 'intel1.asp', 'submit', 'formAction on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
-testReflectAttrWithDepAttr('formEnctype', 'formenctype', 'type', 'text/plain', 'multipart/form-data', 'submit', 'formEnctype on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
-testReflectAttrWithDepAttr('formMethod', 'formmethod', 'type', 'get', 'post', 'submit', 'formMethod on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
-testReflectAttrWithContentValuesAndDepAttr('formNoValidate', 'formnovalidate', 'type', true, '', false, null, 'submit', 'formNoValidate on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
-testReflectAttrWithDepAttr('formTarget', 'formtarget', 'type', '_blank', '_self', 'submit', 'formTarget on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
-
-//In parent node, sub node's observeAttribute which depends another attribute can enqueue by changing attribute value
-//Test reflect attribute with content values and dependent attribute 
-function testReflectAttrWithContentValuesAndDepAttr(jsAtName, coAtName, deAtName, jsAtValue1, coAtValue1, jsAtValue2, coAtValue2, deAtValue, name, elementName, pElementName, interfaceName) {
-    var parentElement = document.createElement(pElementName);
-    document.body.appendChild(parentElement);
-
-    test(() => {
-        var element = define_build_in_custom_element([coAtName], interfaceName, elementName);
-        var instance = document.createElement(elementName, { is: element.name });
-
-        assert_array_equals(element.takeLog().types(), ['constructed']);
-        parentElement.appendChild(instance);
-        assert_array_equals(element.takeLog().types(), ['connected']);
-        instance.setAttribute(deAtName, deAtValue);
-        instance[jsAtName] = jsAtValue1;
-        var logEntries = element.takeLog();
-        assert_array_equals(logEntries.types(), ['attributeChanged']);
-        assert_attribute_log_entry(logEntries.last(), { name: coAtName, oldValue: null, newValue: coAtValue1, namespace: null });
-
-    }, name + ' must enqueue an attributeChanged reaction when adding a new attribute');
-
-    test(() => {
-        var element = define_build_in_custom_element([coAtName], interfaceName, elementName);
-        var instance = document.createElement(elementName, { is: element.name });
-        parentElement.appendChild(instance);
-        instance.setAttribute(deAtName, deAtValue);
-        instance[jsAtName] = jsAtValue1;
-
-        assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged']);
-        instance[jsAtName] = jsAtValue2;
-        var logEntries = element.takeLog();
-        assert_array_equals(logEntries.types(), ['attributeChanged']);
-        assert_attribute_log_entry(logEntries.last(), { name: coAtName, oldValue: coAtValue1, newValue: coAtValue2, namespace: null });
-
-    }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
-
-    parentElement.parentNode.removeChild(parentElement);
+function setAttributes(instance) {
+  instance.setAttribute('type', 'submit');
 }
 
-//Package reflect attribute with dependent attribute
-function testReflectAttrWithDepAttr(jsAtName, coAtName, deAtName, jsAtValue1, jsAtValue2, deAtValue, name, elementName, pElementName, interfaceName) {
-    testReflectAttrWithContentValuesAndDepAttr(jsAtName, coAtName, deAtName, jsAtValue1, jsAtValue1, jsAtValue2, jsAtValue2, deAtValue, name, elementName, pElementName, interfaceName);
-}
+testReflectBooleanAttribute(
+  'autofocus', 'autofocus', 'autofocus on HTMLButtonElement',
+  'button', HTMLButtonElement
+);
+testReflectBooleanAttribute(
+  'disabled', 'disabled','disabled on HTMLButtonElement',
+  'button', HTMLButtonElement
+);
+testReflectAttribute(
+  'name', 'name', 'intel',
+  'intel1', 'name on HTMLButtonElement', 'button',
+  HTMLButtonElement
+);
+testReflectAttribute(
+  'value', 'value', 'HTML',
+  'CSS', 'value on HTMLButtonElement', 'button',
+  HTMLButtonElement
+);
+testReflectAttributeWithParentNode(
+  'type', 'type', 'submit',
+  'reset',  'type on HTMLButtonElement', 'button',
+  () => getParentElement('form'), HTMLButtonElement
+);
+testReflectAttributeWithDependentAttributes(
+  'formAction', 'formaction', 'intel.asp',
+  'intel1.asp', 'formAction on HTMLButtonElement', 'button',
+  () => getParentElement('form'), instance => setAttributes(instance),
+  HTMLButtonElement
+);
+testReflectAttributeWithDependentAttributes(
+  'formEnctype', 'formenctype', 'text/plain', 'multipart/form-data',
+  'formEnctype on HTMLButtonElement', 'button', () => getParentElement('form'),
+  instance => setAttributes(instance),
+  HTMLButtonElement
+);
+testReflectAttributeWithDependentAttributes(
+  'formMethod', 'formmethod', 'get',
+  'post', 'formMethod on HTMLButtonElement', 'button',
+  () => getParentElement('form'), instance => setAttributes(instance),
+  HTMLButtonElement
+);
+testReflectBooleanAttributeWithDependentAttributes(
+  'formNoValidate', 'formnovalidate', 'formNoValidate on HTMLButtonElement',
+  'button', () => getParentElement('form'),
+  instance => setAttributes(instance),
+  HTMLButtonElement
+);
+testReflectAttributeWithDependentAttributes(
+  'formTarget', 'formtarget', '_blank',
+  '_self', 'formTarget on HTMLButtonElement', 'button',
+  () => getParentElement('form'), instance => setAttributes(instance),
+  HTMLButtonElement
+);
 
 </script>
 </body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/HTMLEmbedElement.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Custom Elements: CEReactions on HTMLEmbedElement interface</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<meta name="assert" content="src, type, width, height of
+  HTMLEmbedElement interface must have CEReactions">
+<meta name="help" content="https://html.spec.whatwg.org/#the-embed-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/custom-elements-helpers.js"></script>
+<script src="./resources/reactions.js"></script>
+
+<script>
+
+testReflectAttribute(
+  'src', 'src', '/media/movie_5.mp4',
+  '/media/sound_5.mp3', 'src on HTMLEmbedElement', 'embed',
+  HTMLEmbedElement
+);
+testReflectAttribute(
+  'type', 'type', 'video/webm',
+  'video/mp4', 'type on HTMLEmbedElement', 'embed',
+  HTMLEmbedElement
+);
+testReflectAttribute(
+  'width', 'width', '100',
+  '120', 'width on HTMLEmbedElement', 'embed',
+  HTMLEmbedElement
+);
+testReflectAttribute(
+  'height', 'height', '100',
+  '120', 'height on HTMLEmbedElement', 'embed',
+  HTMLEmbedElement
+);
+
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/HTMLFieldSetElement.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Custom Elements: CEReactions on HTMLFieldSetElement interface</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<meta name="assert" content="disabled, name of
+  HTMLFieldSetElement interface must have CEReactions">
+<meta name="help" content="https://html.spec.whatwg.org/#the-fieldset-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/custom-elements-helpers.js"></script>
+<script src="./resources/reactions.js"></script>
+
+<body>
+<script>
+
+function getParentElement() {
+  let form = document.createElement("form");
+  document.body.appendChild(form);
+  return form;
+}
+
+testReflectBooleanAttributeWithParentNode(
+  'disabled', 'disabled', 'disabled on HTMLFieldSetElement',
+  'fieldset', getParentElement, HTMLFieldSetElement
+);
+testReflectAttributeWithParentNode(
+  'name', 'name', 'fieldset1',
+  'fieldset2', 'name on HTMLFieldSetElement', 'fieldset',
+  getParentElement, HTMLFieldSetElement
+);
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/HTMLImageElement.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<title>Custom Elements: CEReactions on HTMLImageElement interface</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<meta name="assert" content="alt, src, srcset, sizes, crossOrigin, useMap,
+  isMap, width, height, referrerPolicy, decoding of
+  HTMLImageElement interface must have CEReactions">
+<meta name="help" content="https://html.spec.whatwg.org/#the-img-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/custom-elements-helpers.js"></script>
+<script src="./resources/reactions.js"></script>
+
+<map name="yellow"></map>
+<map name="green"></map>
+<a href="/" id="a">
+</a>
+<body>
+<script>
+
+function getParentElement() {
+  return document.body;
+}
+
+function setAttributes(instance) {
+  instance.setAttribute('src', '/images/green-1x1.png');
+}
+
+testReflectAttributeWithDependentAttributes(
+  'alt', 'alt', 'image1',
+  'image2', 'alt on HTMLImageElement', 'img',
+  getParentElement, instance => setAttributes(instance), HTMLImageElement
+);
+testReflectAttributeWithParentNode(
+  'src', 'src', '/images/green-1x1.png',
+  '/images/green-2x2.png', 'src on HTMLImageElement', 'img',
+  getParentElement, HTMLImageElement
+);
+testReflectAttributeWithDependentAttributes(
+  'srcset', 'srcset', '/images/green.png',
+  '/images/green-2x2.png', 'srcset on HTMLImageElement', 'img',
+  getParentElement, instance => setAttributes(instance), HTMLImageElement
+);
+testReflectAttributeWithDependentAttributes(
+  'sizes', 'sizes', '(max-width: 32px) 28px',
+  '(max-width: 48px) 44px', 'sizes on HTMLImageElement', 'img',
+  getParentElement, instance => {
+    instance.setAttribute('src', '/images/green-1x1.png');
+    instance.setAttribute('srcset', '/images/green-2x2.png 1x');
+  }, HTMLImageElement
+);
+testReflectAttributeWithDependentAttributes(
+  'crossOrigin', 'crossorigin', 'use-credentials',
+  'anonymous', 'crossOrigin on HTMLImageElement', 'img',
+  getParentElement, instance => setAttributes(instance), HTMLImageElement
+);
+testReflectAttributeWithDependentAttributes(
+  'useMap', 'usemap', '#yellow',
+  '#green', 'useMap on HTMLImageElement', 'img',
+  getParentElement, instance => setAttributes(instance), HTMLImageElement
+);
+testReflectBooleanAttributeWithDependentAttributes(
+  'isMap', 'ismap', 'isMap on HTMLImageElement',
+  'img', () => { return document.getElementById('a') },
+  instance => setAttributes(instance),
+  HTMLImageElement
+);
+testReflectAttributeWithDependentAttributes(
+  'width', 'width', '1',
+  '2', 'width on HTMLImageElement', 'img',
+  getParentElement, instance => setAttributes(instance), HTMLImageElement
+);
+testReflectAttributeWithDependentAttributes(
+  'height', 'height', '1',
+  '2', 'height on HTMLImageElement', 'img',
+  getParentElement, instance => setAttributes(instance), HTMLImageElement
+);
+testReflectAttributeWithDependentAttributes(
+  'referrerPolicy', 'referrerpolicy', 'same-origin',
+  'origin', 'referrerPolicy on HTMLImageElement', 'img',
+  getParentElement, instance => setAttributes(instance), HTMLImageElement
+);
+testReflectAttributeWithDependentAttributes(
+  'decoding', 'decoding', 'async',
+  'sync', 'decoding on HTMLImageElement', 'img',
+  getParentElement, instance => setAttributes(instance), HTMLImageElement
+);
+
+</script>
+</body>
--- a/testing/web-platform/tests/custom-elements/reactions/resources/reactions.js
+++ b/testing/web-platform/tests/custom-elements/reactions/resources/reactions.js
@@ -163,16 +163,58 @@ function testReflectAttributeWithContent
 function testReflectAttribute(jsAttributeName, contentAttributeName, validValue1, validValue2, name, elementName, interfaceName) {
     testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, validValue1, validValue1, validValue2, validValue2, name, elementName, interfaceName);
 }
 
 function testReflectBooleanAttribute(jsAttributeName, contentAttributeName, name, elementName, interfaceName) {
     testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, true, '', false, null, name, elementName, interfaceName);
 }
 
+function testReflectAttributeWithContentValuesAndDependentAttributes(jsAttributeName, contentAttributeName, validValue1, contentValue1, validValue2, contentValue2, name, elementName, getParentElement, setAttributes, interfaceName) {
+    let parentElement = getParentElement();
+
+    test(() => {
+        let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName);
+        let instance = document.createElement(elementName, { is: element.name });
+
+        assert_array_equals(element.takeLog().types(), ['constructed']);
+        parentElement.appendChild(instance);
+        assert_array_equals(element.takeLog().types(), ['connected']);
+        setAttributes(instance);
+        instance[jsAttributeName] = validValue1;
+        let logEntries = element.takeLog();
+        assert_array_equals(logEntries.types(), ['attributeChanged']);
+        assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: null, newValue: contentValue1, namespace: null });
+
+    }, name + ' must enqueue an attributeChanged reaction when adding a new attribute');
+
+    test(() => {
+        let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName);
+        let instance = document.createElement(elementName, { is: element.name });
+        parentElement.appendChild(instance);
+        setAttributes(instance);
+        instance[jsAttributeName] = validValue1;
+
+        assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged']);
+        instance[jsAttributeName] = validValue2;
+        let logEntries = element.takeLog();
+        assert_array_equals(logEntries.types(), ['attributeChanged']);
+        assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: contentValue1, newValue: contentValue2, namespace: null });
+
+    }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
+}
+
+function testReflectAttributeWithDependentAttributes(jsAttributeName, contentAttributeName, validValue1, validValue2, name, elementName, getParentElement, setAttributes, interfaceName) {
+    testReflectAttributeWithContentValuesAndDependentAttributes(jsAttributeName, contentAttributeName, validValue1, validValue1, validValue2, validValue2, name, elementName, getParentElement, setAttributes, interfaceName);
+}
+
+function testReflectBooleanAttributeWithDependentAttributes(jsAttributeName, contentAttributeName, name, elementName, getParentElement, setAttributes, interfaceName) {
+    testReflectAttributeWithContentValuesAndDependentAttributes(jsAttributeName, contentAttributeName, true, '', false, null, name, elementName, getParentElement, setAttributes, interfaceName);
+}
+
 function testReflectAttributeWithContentValuesAndParentNode(jsAttributeName, contentAttributeName, validValue1, contentValue1, validValue2, contentValue2, name, elementName, getParentElement, interfaceName) {
     let parentElement = getParentElement();
 
     test(() => {
         let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName);
         let instance = document.createElement(elementName, { is: element.name });
 
         assert_array_equals(element.takeLog().types(), ['constructed']);
@@ -198,16 +240,20 @@ function testReflectAttributeWithContent
         assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: contentValue1, newValue: contentValue2, namespace: null });
     }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
 }
 
 function testReflectAttributeWithParentNode(jsAttributeName, contentAttributeName, validValue1, validValue2, name, elementName, getParentElement, interfaceName) {
     testReflectAttributeWithContentValuesAndParentNode(jsAttributeName, contentAttributeName, validValue1, validValue1, validValue2, validValue2, name, elementName, getParentElement, interfaceName);
 }
 
+function testReflectBooleanAttributeWithParentNode(jsAttributeName, contentAttributeName, name, elementName, getParentElement, interfaceName) {
+    testReflectAttributeWithContentValuesAndParentNode(jsAttributeName, contentAttributeName, true, '', false, null, name, elementName, getParentElement, interfaceName);
+}
+
 function testAttributeAdder(testFunction, name) {
     test(function () {
         var element = define_new_custom_element(['id']);
         var instance = document.createElement(element.name);
         assert_array_equals(element.takeLog().types(), ['constructed']);
         testFunction(instance, 'id', 'foo');
         var logEntries = element.takeLog();
         assert_array_equals(logEntries.types(), ['attributeChanged']);