summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/custom-elements/reactions
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/custom-elements/reactions')
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/Attr.html23
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/ChildNode.html35
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/DOMTokenList.html217
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/Document.html47
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/Element.html61
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/NamedNodeMap.html39
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/Node.html49
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/ParentNode.html27
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/Range.html49
-rw-r--r--testing/web-platform/tests/custom-elements/reactions/resources/reactions.js217
10 files changed, 764 insertions, 0 deletions
diff --git a/testing/web-platform/tests/custom-elements/reactions/Attr.html b/testing/web-platform/tests/custom-elements/reactions/Attr.html
new file mode 100644
index 000000000..c9fa37f96
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/Attr.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on Attr interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="value of Attr interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#node">
+<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>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+testAttributeMutator(function (element, name, value) {
+ element.attributes[name].value = value;
+}, 'value on Attr');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/ChildNode.html b/testing/web-platform/tests/custom-elements/reactions/ChildNode.html
new file mode 100644
index 000000000..756f17229
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/ChildNode.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on ChildNode interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="before, after, after, replaceWith, and remove of ChildNode interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#parentnode">
+<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>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+testNodeConnector(function (newContainer, customElement) {
+ newContainer.firstChild.before(customElement);
+}, 'before on ChildNode');
+
+testNodeConnector(function (newContainer, customElement) {
+ newContainer.firstChild.after(customElement);
+}, 'after on ChildNode');
+
+testNodeConnector(function (newContainer, customElement) {
+ newContainer.firstChild.replaceWith(customElement);
+}, 'replaceWith on ChildNode');
+
+testNodeDisconnector(function (customElement) {
+ customElement.remove();
+}, 'replaceWith on ChildNode');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/DOMTokenList.html b/testing/web-platform/tests/custom-elements/reactions/DOMTokenList.html
new file mode 100644
index 000000000..38006d3e7
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/DOMTokenList.html
@@ -0,0 +1,217 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on DOMTokenList interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="add, remove, toggle, replace, and the stringifier of DOMTokenList interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#node">
+<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>
+</head>
+<body>
+<script>
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList.add('foo');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: null, newValue: 'foo', namespace: null});
+}, 'add on DOMTokenList must enqueue an attributeChanged reaction when adding an attribute');
+
+test(function () {
+ var element = define_new_custom_element(['style']);
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList.add('foo');
+ assert_array_equals(element.takeLog().types(), []);
+}, 'add on DOMTokenList must not enqueue an attributeChanged reaction when adding an unobserved attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList.add('world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'hello world', namespace: null});
+}, 'add on DOMTokenList must enqueue an attributeChanged reaction when adding a value to an existing attribute');
+
+test(function () {
+ var element = define_new_custom_element(['contenteditable']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList.add('world');
+ assert_array_equals(element.takeLog().types(), []);
+}, 'add on DOMTokenList must not enqueue an attributeChanged reaction when adding a value to an unobserved attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList.add('hello', 'world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: null, newValue: 'hello world', namespace: null});
+}, 'add on DOMTokenList must enqueue exactly one attributeChanged reaction when adding multiple values to an attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello world');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList.remove('world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello world', newValue: 'hello', namespace: null});
+}, 'remove on DOMTokenList must enqueue an attributeChanged reaction when removing a value from an attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello foo world bar');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList.remove('hello', 'world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello foo world bar', newValue: 'foo bar', namespace: null});
+}, 'remove on DOMTokenList must enqueue exactly one attributeChanged reaction when removing multiple values to an attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello world');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList.remove('foo');
+ assert_array_equals(element.takeLog().types(), []);
+}, 'remove on DOMTokenList must not enqueue an attributeChanged reaction when removing a non-existent value from an attribute');
+
+test(function () {
+ var element = define_new_custom_element(['title']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello world');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList.remove('world');
+ assert_array_equals(element.takeLog().types(), []);
+}, 'remove on DOMTokenList must not enqueue an attributeChanged reaction when removing a value from an unobserved attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList.toggle('world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'hello world', namespace: null});
+}, 'toggle on DOMTokenList must enqueue an attributeChanged reaction when adding a value to an attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello world');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList.toggle('world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello world', newValue: 'hello', namespace: null});
+}, 'toggle on DOMTokenList must enqueue an attributeChanged reaction when removing a value from an attribute');
+
+test(function () {
+ var element = define_new_custom_element(['lang']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello world');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList.toggle('world');
+ assert_array_equals(element.takeLog().types(), []);
+}, 'remove on DOMTokenList must not enqueue an attributeChanged reaction when removing a value from an unobserved attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList.replace('hello', 'world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'world', namespace: null});
+}, 'replace on DOMTokenList must enqueue an attributeChanged reaction when replacing a value in an attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello world');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList.replace('foo', 'bar');
+ assert_array_equals(element.takeLog().types(), []);
+}, 'replace on DOMTokenList must not enqueue an attributeChanged reaction when the token to replace does not exist in the attribute');
+
+test(function () {
+ var element = define_new_custom_element(['title']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList.replace('hello', 'world');
+ assert_array_equals(element.takeLog().types(), []);
+}, 'replace on DOMTokenList must not enqueue an attributeChanged reaction when replacing a value in an unobserved attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList = 'hello';
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: null, newValue: 'hello', namespace: null});
+}, 'the stringifier of DOMTokenList must enqueue an attributeChanged reaction when adding an observed attribute');
+
+test(function () {
+ var element = define_new_custom_element(['id']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList = 'hello';
+ var logEntries = element.takeLog();
+ assert_array_equals(element.takeLog().types(), []);
+}, 'the stringifier of DOMTokenList must not enqueue an attributeChanged reaction when adding an unobserved attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList = 'world';
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'world', namespace: null});
+}, 'the stringifier of DOMTokenList must enqueue an attributeChanged reaction when mutating the value of an observed attribute');
+
+test(function () {
+ var element = define_new_custom_element([]);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance.classList = 'world';
+ assert_array_equals(element.takeLog().types(), []);
+}, 'the stringifier of DOMTokenList must not enqueue an attributeChanged reaction when mutating the value of an unobserved attribute');
+
+test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('class', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance.classList = 'hello';
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'hello', namespace: null});
+}, 'the stringifier of DOMTokenList must enqueue an attributeChanged reaction when the setter is called with the original value of the attribute');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/Document.html b/testing/web-platform/tests/custom-elements/reactions/Document.html
new file mode 100644
index 000000000..304ec9861
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/Document.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on ParentNode interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="importNode and adoptNode of Document interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#document">
+<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>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+test(function () {
+ var element = define_new_custom_element();
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+
+ var newDoc = document.implementation.createHTMLDocument();
+ newDoc.importNode(instance);
+
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['constructed']);
+ assert_equals(logEntries.last().oldDocument, document);
+ assert_equals(logEntries.last().newDocument, newDoc);
+}, 'importNode on Document must construct a new custom element when importing a custom element');
+
+test(function () {
+ var element = define_new_custom_element();
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+
+ var newDoc = document.implementation.createHTMLDocument();
+ newDoc.adoptNode(instance);
+
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['adopted']);
+ assert_equals(logEntries.last().oldDocument, document);
+ assert_equals(logEntries.last().newDocument, newDoc);
+}, 'adoptNode on Document must enqueue an adopted reaction when importing a custom element');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/Element.html b/testing/web-platform/tests/custom-elements/reactions/Element.html
new file mode 100644
index 000000000..ed627f44e
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/Element.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on Node interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="id, className, slot, setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, setAttributeNode, setAttributeNodeNS, removeAttributeNode, and insertAdjacentElement of Element interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#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>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+testReflectAttribute('id', 'id', 'foo', 'bar', 'id on Element');
+testReflectAttribute('className', 'class', 'foo', 'bar', 'className on Element');
+testReflectAttribute('slot', 'slot', 'foo', 'bar', 'slot on Element');
+
+testAttributeAdder(function (element, name, value) {
+ element.setAttribute(name, value);
+}, 'setAttribute on Element');
+
+testAttributeAdder(function (element, name, value) {
+ element.setAttributeNS(null, name, value);
+}, 'setAttributeNS on Element');
+
+testAttributeRemover(function (element, name) {
+ element.removeAttribute(name);
+}, 'removeAttribute on Element');
+
+testAttributeRemover(function (element, name) {
+ element.removeAttributeNS(null, name);
+}, 'removeAttributeNS on Element');
+
+testAttributeAdder(function (element, name, value) {
+ var attr = document.createAttribute(name);
+ attr.value = value;
+ element.setAttributeNode(attr);
+}, 'setAttributeNode on Element');
+
+testAttributeAdder(function (element, name, value) {
+ var attr = document.createAttribute(name);
+ attr.value = value;
+ element.setAttributeNodeNS(attr);
+}, 'setAttributeNodeNS on Element');
+
+testAttributeRemover(function (element, name) {
+ var attr = element.getAttributeNode(name);
+ if (attr)
+ element.removeAttributeNode(element.getAttributeNode(name));
+}, 'removeAttributeNode on Element');
+
+testNodeConnector(function (newContainer, element) {
+ newContainer.insertAdjacentElement('afterBegin', element);
+});
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/NamedNodeMap.html b/testing/web-platform/tests/custom-elements/reactions/NamedNodeMap.html
new file mode 100644
index 000000000..0ae83e9ab
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/NamedNodeMap.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on NamedNodeMap interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="setNamedItem, setNamedItemNS, removeNameditem, and removeNamedItemNS of NamedNodeMap interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#node">
+<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>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+testAttributeAdder(function (element, name, value) {
+ var attr = element.ownerDocument.createAttribute(name);
+ attr.value = value;
+ element.attributes.setNamedItem(attr);
+}, 'setNamedItem on NamedNodeMap');
+
+testAttributeAdder(function (element, name, value) {
+ var attr = element.ownerDocument.createAttribute(name);
+ attr.value = value;
+ element.attributes.setNamedItemNS(attr);
+}, 'setNamedItemNS on NamedNodeMap');
+
+testAttributeRemover(function (element, name) {
+ element.attributes.removeNamedItem(name);
+}, 'removeNamedItem on NamedNodeMap');
+
+testAttributeRemover(function (element, name) {
+ element.attributes.removeNamedItemNS(null, name);
+}, 'removeNamedItemNS on NamedNodeMap');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/Node.html b/testing/web-platform/tests/custom-elements/reactions/Node.html
new file mode 100644
index 000000000..94da3d020
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/Node.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on Node interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="nodeValue, textContent, normalize, cloneNode, insertBefore, appendChild, replaceChild, and removeChild of Node interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#node">
+<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>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+testAttributeMutator(function (element, name, value) {
+ element.getAttributeNode(name).nodeValue = value;
+}, 'nodeValue on Node');
+
+testAttributeMutator(function (element, name, value) {
+ element.getAttributeNode(name).textContent = value;
+}, 'textContent on Node');
+
+// FIXME: Add a test for normalize()
+
+testCloner(function (customElement) {
+ return customElement.cloneNode(false);
+}, 'cloneNode on Node');
+
+testNodeConnector(function (newContainer, customElement) {
+ newContainer.insertBefore(customElement, newContainer.firstChild);
+}, 'insertBefore on ChildNode');
+
+testNodeConnector(function (newContainer, customElement) {
+ newContainer.appendChild(customElement);
+}, 'appendChild on ChildNode');
+
+testNodeConnector(function (newContainer, customElement) {
+ newContainer.replaceChild(customElement, newContainer.firstChild);
+}, 'replaceChild on ChildNode');
+
+testNodeDisconnector(function (customElement) {
+ customElement.parentNode.removeChild(customElement);
+}, 'removeChild on ChildNode');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/ParentNode.html b/testing/web-platform/tests/custom-elements/reactions/ParentNode.html
new file mode 100644
index 000000000..b143b5a98
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/ParentNode.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on ParentNode interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="prepend and append of ParentNode interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#parentnode">
+<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>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+testNodeConnector(function (newContainer, customElement) {
+ newContainer.prepend(customElement);
+}, 'prepend on ParentNode');
+
+testNodeConnector(function (newContainer, customElement) {
+ newContainer.append(customElement);
+}, 'append on ParentNode');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/Range.html b/testing/web-platform/tests/custom-elements/reactions/Range.html
new file mode 100644
index 000000000..82382d520
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/Range.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: CEReactions on Range interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="deleteContents, extractContents, cloneContents, insertNode, and surroundContents of Range interface must have CEReactions">
+<meta name="help" content="https://dom.spec.whatwg.org/#node">
+<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>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+testNodeDisconnector(function (customElement) {
+ var range = document.createRange();
+ range.selectNode(customElement);
+ range.deleteContents();
+}, 'deleteContents on Range');
+
+testNodeDisconnector(function (customElement) {
+ var range = document.createRange();
+ range.selectNode(customElement);
+ range.extractContents();
+}, 'extractContents on Range');
+
+testCloner(function (customElement) {
+ var range = document.createRange();
+ range.selectNode(customElement);
+ range.cloneContents();
+}, 'cloneContents on Range')
+
+testNodeConnector(function (container, customElement) {
+ var range = document.createRange();
+ range.selectNodeContents(container);
+ range.insertNode(customElement);
+}, 'insertNode on Range');
+
+testNodeConnector(function (container, customElement) {
+ var range = document.createRange();
+ range.selectNodeContents(container);
+ range.surroundContents(customElement);
+}, 'insertNode on Range');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/reactions/resources/reactions.js b/testing/web-platform/tests/custom-elements/reactions/resources/reactions.js
new file mode 100644
index 000000000..c260dc4f3
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/reactions/resources/reactions.js
@@ -0,0 +1,217 @@
+
+let testNumber = 1;
+
+function testNodeConnector(testFunction, name) {
+ let container = document.createElement('div');
+ container.appendChild(document.createElement('div'));
+ document.body.appendChild(container);
+
+ test(function () {
+ var element = define_new_custom_element();
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ testFunction(container, instance);
+ assert_array_equals(element.takeLog().types(), ['connected']);
+ }, name + ' must enqueue a connected reaction');
+
+ test(function () {
+ var element = define_new_custom_element();
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ var newDoc = document.implementation.createHTMLDocument();
+ testFunction(container, instance);
+ assert_array_equals(element.takeLog().types(), ['connected']);
+ testFunction(newDoc.documentElement, instance);
+ assert_array_equals(element.takeLog().types(), ['disconnected', 'adopted', 'connected']);
+ }, name + ' must enqueue a disconnected reaction, an adopted reaction, and a connected reaction when the custom element was in another document');
+
+ container.parentNode.removeChild(container);
+}
+
+function testNodeDisconnector(testFunction, name) {
+ let container = document.createElement('div');
+ container.appendChild(document.createElement('div'));
+ document.body.appendChild(container);
+
+ test(function () {
+ var element = define_new_custom_element();
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ container.appendChild(instance);
+ assert_array_equals(element.takeLog().types(), ['connected']);
+ testFunction(instance);
+ assert_array_equals(element.takeLog().types(), ['disconnected']);
+ }, name + ' must enqueue a disconnected reaction');
+
+ container.parentNode.removeChild(container);
+}
+
+function testCloner(testFunction, name) {
+ let container = document.createElement('div');
+ container.appendChild(document.createElement('div'));
+ document.body.appendChild(container);
+
+ test(function () {
+ var element = define_new_custom_element(['id']);
+ var instance = document.createElement(element.name);
+ container.appendChild(instance);
+
+ instance.setAttribute('id', 'foo');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged']);
+ var newInstance = testFunction(instance);
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'foo', namespace: null});
+ }, name + ' must enqueue an attributeChanged reaction when cloning an element with an observed attribute');
+
+ test(function () {
+ var element = define_new_custom_element(['id']);
+ var instance = document.createElement(element.name);
+ container.appendChild(instance);
+
+ instance.setAttribute('lang', 'en');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
+ var newInstance = testFunction(instance);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ }, name + ' must not enqueue an attributeChanged reaction when cloning an element with an unobserved attribute');
+
+ test(function () {
+ var element = define_new_custom_element(['title', 'class']);
+ var instance = document.createElement(element.name);
+ container.appendChild(instance);
+
+ instance.setAttribute('lang', 'en');
+ instance.className = 'foo';
+ instance.setAttribute('title', 'hello world');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged', 'attributeChanged']);
+ var newInstance = testFunction(instance);
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged', 'attributeChanged']);
+ assert_attribute_log_entry(logEntries[1], {name: 'class', oldValue: null, newValue: 'foo', namespace: null});
+ assert_attribute_log_entry(logEntries[2], {name: 'title', oldValue: null, newValue: 'hello world', namespace: null});
+ }, name + ' must enqueue an attributeChanged reaction when cloning an element only for observed attributes');
+}
+
+function testReflectAttribute(jsAttributeName, contentAttributeName, validValue1, validValue2, name) {
+ test(function () {
+ var element = define_new_custom_element([contentAttributeName]);
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ instance[jsAttributeName] = validValue1;
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: contentAttributeName, oldValue: null, newValue: validValue1, namespace: null});
+ }, name + ' must enqueue an attributeChanged reaction when adding ' + contentAttributeName + ' content attribute');
+
+ test(function () {
+ var element = define_new_custom_element([contentAttributeName]);
+ var instance = document.createElement(element.name);
+ instance[jsAttributeName] = validValue1;
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ instance[jsAttributeName] = validValue2;
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: contentAttributeName, oldValue: validValue1, newValue: validValue2, namespace: null});
+ }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
+}
+
+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']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'foo', namespace: null});
+ }, name + ' must enqueue an attributeChanged reaction when adding an attribute');
+
+ test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ testFunction(instance, 'data-lang', 'en');
+ assert_array_equals(element.takeLog().types(), []);
+ }, name + ' must not enqueue an attributeChanged reaction when adding an unobserved attribute');
+
+ test(function () {
+ var element = define_new_custom_element(['title']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('title', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ testFunction(instance, 'title', 'world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: 'world', namespace: null});
+ }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
+
+ test(function () {
+ var element = define_new_custom_element([]);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('data-lang', 'zh');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ testFunction(instance, 'data-lang', 'en');
+ assert_array_equals(element.takeLog().types(), []);
+ }, name + ' must enqueue an attributeChanged reaction when replacing an existing unobserved attribute');
+}
+
+function testAttributeMutator(testFunction, name) {
+ test(function () {
+ var element = define_new_custom_element(['title']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('title', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ testFunction(instance, 'title', 'world');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: 'world', namespace: null});
+ }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
+
+ test(function () {
+ var element = define_new_custom_element(['class']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('data-lang', 'zh');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ testFunction(instance, 'data-lang', 'en');
+ assert_array_equals(element.takeLog().types(), []);
+ }, name + ' must not enqueue an attributeChanged reaction when replacing an existing unobserved attribute');
+}
+
+function testAttributeRemover(testFunction, name) {
+ test(function () {
+ var element = define_new_custom_element(['title']);
+ var instance = document.createElement(element.name);
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ testFunction(instance, 'title');
+ assert_array_equals(element.takeLog().types(), []);
+ }, name + ' must not enqueue an attributeChanged reaction when removing an attribute that does not exist');
+
+ test(function () {
+ var element = define_new_custom_element([]);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('data-lang', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ testFunction(instance, 'data-lang');
+ assert_array_equals(element.takeLog().types(), []);
+ }, name + ' must not enqueue an attributeChanged reaction when removing an unobserved attribute');
+
+ test(function () {
+ var element = define_new_custom_element(['title']);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('title', 'hello');
+ assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
+ testFunction(instance, 'title');
+ var logEntries = element.takeLog();
+ assert_array_equals(logEntries.types(), ['attributeChanged']);
+ assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: null, namespace: null});
+ }, name + ' must enqueue an attributeChanged reaction when removing an existing attribute');
+
+ test(function () {
+ var element = define_new_custom_element([]);
+ var instance = document.createElement(element.name);
+ instance.setAttribute('data-lang', 'ja');
+ assert_array_equals(element.takeLog().types(), ['constructed']);
+ testFunction(instance, 'data-lang');
+ assert_array_equals(element.takeLog().types(), []);
+ }, name + ' must not enqueue an attributeChanged reaction when removing an existing unobserved attribute');
+}