|
7 | 7 | <link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#dfn-attribute-changed-callback">
|
8 | 8 | <script src="/resources/testharness.js"></script>
|
9 | 9 | <script src="/resources/testharnessreport.js"></script>
|
| 10 | +<script src="resources/custom-elements-helpers.js"></script> |
10 | 11 | </head>
|
11 | 12 | <body>
|
12 | 13 | <div id="log"></div>
|
13 | 14 | <script>
|
14 | 15 |
|
15 |
| -var argumentList = []; |
16 |
| -class MyCustomElement extends HTMLElement { |
17 |
| - attributeChangedCallback(name, oldValue, newValue, namespace) { |
18 |
| - argumentList.push({arguments: arguments, value: this.getAttributeNS(namespace, name)}); |
19 |
| - } |
20 |
| -} |
21 |
| -MyCustomElement.observedAttributes = ['title', 'id', 'r']; |
22 |
| -customElements.define('my-custom-element', MyCustomElement); |
| 16 | +var customElement = define_new_custom_element(['title', 'id', 'r']); |
23 | 17 |
|
24 | 18 | test(function () {
|
25 |
| - var instance = document.createElement('my-custom-element'); |
26 |
| - argumentList = []; |
| 19 | + const instance = document.createElement(customElement.name); |
| 20 | + assert_array_equals(customElement.takeLog().types(), ['constructed']); |
27 | 21 |
|
28 | 22 | instance.setAttribute('title', 'foo');
|
29 | 23 | assert_equals(instance.getAttribute('title'), 'foo');
|
30 |
| - assert_equals(argumentList.length, 1); |
31 |
| - assert_equals(argumentList[0].value, 'foo'); |
32 |
| - assert_array_equals(argumentList[0].arguments, ['title', null, 'foo', null]); |
| 24 | + var logEntries = customElement.takeLog(); |
| 25 | + assert_array_equals(logEntries.types(), ['attributeChanged']); |
| 26 | + assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: null, newValue: 'foo', namespace: null}); |
33 | 27 |
|
34 | 28 | instance.removeAttribute('title');
|
35 | 29 | assert_equals(instance.getAttribute('title'), null);
|
36 |
| - assert_equals(argumentList.length, 2); |
37 |
| - assert_equals(argumentList[1].value, null); |
38 |
| - assert_array_equals(argumentList[1].arguments, ['title', 'foo', null, null]); |
39 |
| - |
| 30 | + var logEntries = customElement.takeLog(); |
| 31 | + assert_array_equals(logEntries.types(), ['attributeChanged']); |
| 32 | + assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'foo', newValue: null, namespace: null}); |
40 | 33 | }, 'setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback');
|
41 | 34 |
|
42 | 35 | test(function () {
|
43 |
| - var instance = document.createElement('my-custom-element'); |
44 |
| - argumentList = []; |
| 36 | + var instance = document.createElement(customElement.name); |
| 37 | + assert_array_equals(customElement.takeLog().types(), ['constructed']); |
45 | 38 |
|
46 | 39 | instance.setAttributeNS('http://www.w3.org/2000/svg', 'title', 'hello');
|
47 | 40 | assert_equals(instance.getAttribute('title'), 'hello');
|
48 |
| - assert_equals(argumentList.length, 1); |
49 |
| - assert_equals(argumentList[0].value, 'hello'); |
50 |
| - assert_array_equals(argumentList[0].arguments, ['title', null, 'hello', 'http://www.w3.org/2000/svg']); |
| 41 | + var logEntries = customElement.takeLog(); |
| 42 | + assert_array_equals(logEntries.types(), ['attributeChanged']); |
| 43 | + assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: null, newValue: 'hello', namespace: 'http://www.w3.org/2000/svg'}); |
51 | 44 |
|
52 | 45 | instance.removeAttributeNS('http://www.w3.org/2000/svg', 'title');
|
53 | 46 | assert_equals(instance.getAttribute('title'), null);
|
54 |
| - assert_equals(argumentList.length, 2); |
55 |
| - assert_equals(argumentList[1].value, null); |
56 |
| - assert_array_equals(argumentList[1].arguments, ['title', 'hello', null, 'http://www.w3.org/2000/svg']); |
57 |
| - |
| 47 | + var logEntries = customElement.takeLog(); |
| 48 | + assert_array_equals(logEntries.types(), ['attributeChanged']); |
| 49 | + assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: null, namespace: 'http://www.w3.org/2000/svg'}); |
58 | 50 | }, 'setAttributeNS and removeAttributeNS must enqueue and invoke attributeChangedCallback');
|
59 | 51 |
|
60 | 52 | test(function () {
|
61 |
| - var instance = document.createElement('my-custom-element'); |
62 |
| - argumentList = []; |
| 53 | + var instance = document.createElement(customElement.name); |
| 54 | + assert_array_equals(customElement.takeLog().types(), ['constructed']); |
63 | 55 |
|
64 | 56 | var attr = document.createAttribute('id');
|
65 | 57 | attr.value = 'bar';
|
66 | 58 | instance.setAttributeNode(attr);
|
67 | 59 |
|
68 | 60 | assert_equals(instance.getAttribute('id'), 'bar');
|
69 |
| - assert_equals(argumentList.length, 1); |
70 |
| - assert_equals(argumentList[0].value, 'bar'); |
71 |
| - assert_array_equals(argumentList[0].arguments, ['id', null, 'bar', null]); |
| 61 | + var logEntries = customElement.takeLog(); |
| 62 | + assert_array_equals(logEntries.types(), ['attributeChanged']); |
| 63 | + assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'bar', namespace: null}); |
72 | 64 |
|
73 | 65 | instance.removeAttributeNode(attr);
|
74 | 66 | assert_equals(instance.getAttribute('id'), null);
|
75 |
| - assert_equals(argumentList.length, 2); |
76 |
| - assert_equals(argumentList[1].value, null); |
77 |
| - assert_array_equals(argumentList[1].arguments, ['id', 'bar', null, null]); |
78 |
| - |
79 |
| -}, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback'); |
| 67 | + var logEntries = customElement.takeLog(); |
| 68 | + assert_array_equals(logEntries.types(), ['attributeChanged']); |
| 69 | + assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: 'bar', newValue: null, namespace: null}); |
| 70 | +}, 'setAttributeNode and removeAttributeNode must enqueue and invoke attributeChangedCallback for an HTML attribute'); |
80 | 71 |
|
81 | 72 | test(function () {
|
82 |
| - var instance = document.createElement('my-custom-element'); |
83 |
| - argumentList = []; |
| 73 | + const instance = document.createElement(customElement.name); |
| 74 | + assert_array_equals(customElement.takeLog().types(), ['constructed']); |
84 | 75 |
|
85 |
| - var attr = document.createAttributeNS('http://www.w3.org/2000/svg', 'r'); |
| 76 | + const attr = document.createAttributeNS('http://www.w3.org/2000/svg', 'r'); |
86 | 77 | attr.value = '100';
|
87 | 78 | instance.setAttributeNode(attr);
|
88 | 79 |
|
89 | 80 | assert_equals(instance.getAttribute('r'), '100');
|
90 |
| - assert_equals(argumentList.length, 1); |
91 |
| - assert_equals(argumentList[0].value, '100'); |
92 |
| - assert_array_equals(argumentList[0].arguments, ['r', null, '100', 'http://www.w3.org/2000/svg']); |
| 81 | + var logEntries = customElement.takeLog(); |
| 82 | + assert_array_equals(logEntries.types(), ['attributeChanged']); |
| 83 | + assert_attribute_log_entry(logEntries.last(), {name: 'r', oldValue: null, newValue: '100', namespace: 'http://www.w3.org/2000/svg'}); |
93 | 84 |
|
94 | 85 | instance.removeAttributeNode(attr);
|
95 | 86 | assert_equals(instance.getAttribute('r'), null);
|
96 |
| - assert_equals(argumentList.length, 2); |
97 |
| - assert_equals(argumentList[1].value, null); |
98 |
| - assert_array_equals(argumentList[1].arguments, ['r', '100', null, 'http://www.w3.org/2000/svg']); |
99 |
| -}, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback'); |
| 87 | + var logEntries = customElement.takeLog(); |
| 88 | + assert_array_equals(logEntries.types(), ['attributeChanged']); |
| 89 | + assert_attribute_log_entry(logEntries.last(), {name: 'r', oldValue: '100', newValue: null, namespace: 'http://www.w3.org/2000/svg'}); |
| 90 | +}, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback for an SVG attribute'); |
100 | 91 |
|
101 | 92 | test(function () {
|
102 |
| - var callsToOld = []; |
103 |
| - var callsToNew = []; |
| 93 | + const callsToOld = []; |
| 94 | + const callsToNew = []; |
104 | 95 | class CustomElement extends HTMLElement { }
|
105 |
| - CustomElement.prototype.attributeChangedCallback = function () { |
106 |
| - callsToOld.push(Array.from(arguments)); |
| 96 | + CustomElement.prototype.attributeChangedCallback = function (...args) { |
| 97 | + callsToOld.push(create_attribute_changed_callback_log(this, ...args)); |
107 | 98 | }
|
108 | 99 | CustomElement.observedAttributes = ['title'];
|
109 | 100 | customElements.define('element-with-mutated-attribute-changed-callback', CustomElement);
|
110 |
| - CustomElement.prototype.attributeChangedCallback = function () { |
111 |
| - callsToNew.push(Array.from(arguments)); |
| 101 | + CustomElement.prototype.attributeChangedCallback = function (...args) { |
| 102 | + callsToNew.push(create_attribute_changed_callback_log(this, ...args)); |
112 | 103 | }
|
113 | 104 |
|
114 |
| - var instance = document.createElement('element-with-mutated-attribute-changed-callback'); |
| 105 | + const instance = document.createElement('element-with-mutated-attribute-changed-callback'); |
115 | 106 | instance.setAttribute('title', 'hi');
|
116 | 107 | assert_equals(instance.getAttribute('title'), 'hi');
|
117 | 108 | assert_array_equals(callsToNew, []);
|
118 | 109 | assert_equals(callsToOld.length, 1);
|
119 |
| - assert_array_equals(callsToOld[0], ['title', null, 'hi', null]); |
| 110 | + assert_attribute_log_entry(callsToOld[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null}); |
120 | 111 | }, 'Mutating attributeChangedCallback after calling customElements.define must not affect the callback being invoked');
|
121 | 112 |
|
122 | 113 | test(function () {
|
123 |
| - var calls = []; |
| 114 | + const calls = []; |
124 | 115 | class CustomElement extends HTMLElement {
|
125 |
| - attributeChangedCallback() { |
126 |
| - calls.push(Array.from(arguments)); |
| 116 | + attributeChangedCallback(...args) { |
| 117 | + calls.push(create_attribute_changed_callback_log(this, ...args)); |
127 | 118 | }
|
128 | 119 | }
|
129 | 120 | CustomElement.observedAttributes = ['title'];
|
130 | 121 | customElements.define('element-not-observing-id-attribute', CustomElement);
|
131 | 122 |
|
132 |
| - var instance = document.createElement('element-not-observing-id-attribute'); |
| 123 | + const instance = document.createElement('element-not-observing-id-attribute'); |
| 124 | + assert_equals(calls.length, 0); |
133 | 125 | instance.setAttribute('title', 'hi');
|
134 | 126 | assert_equals(calls.length, 1);
|
135 |
| - assert_array_equals(calls[0], ['title', null, 'hi', null]); |
| 127 | + assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null}); |
136 | 128 | instance.setAttribute('id', 'some');
|
137 | 129 | assert_equals(calls.length, 1);
|
138 |
| -}, 'attributedChangedCallback must not be invoked when the observed attributes does not contain the attribute.'); |
| 130 | + assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null}); |
| 131 | +}, 'attributedChangedCallback must not be invoked when the observed attributes does not contain the attribute'); |
139 | 132 |
|
140 | 133 | test(function () {
|
141 |
| - var calls = []; |
| 134 | + const calls = []; |
142 | 135 | class CustomElement extends HTMLElement { }
|
143 |
| - CustomElement.prototype.attributeChangedCallback = function () { |
144 |
| - calls.push(Array.from(arguments)); |
| 136 | + CustomElement.prototype.attributeChangedCallback = function (...args) { |
| 137 | + calls.push(create_attribute_changed_callback_log(this, ...args)); |
145 | 138 | }
|
146 | 139 | CustomElement.observedAttributes = ['title', 'lang'];
|
147 | 140 | customElements.define('element-with-mutated-observed-attributes', CustomElement);
|
148 | 141 | CustomElement.observedAttributes = ['title', 'id'];
|
149 | 142 |
|
150 |
| - var instance = document.createElement('element-with-mutated-observed-attributes'); |
| 143 | + const instance = document.createElement('element-with-mutated-observed-attributes'); |
151 | 144 | instance.setAttribute('title', 'hi');
|
152 | 145 | assert_equals(calls.length, 1);
|
153 |
| - assert_array_equals(calls[0], ['title', null, 'hi', null]); |
| 146 | + assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null}); |
154 | 147 |
|
155 | 148 | instance.setAttribute('id', 'some');
|
156 | 149 | assert_equals(calls.length, 1);
|
157 | 150 |
|
158 | 151 | instance.setAttribute('lang', 'en');
|
159 | 152 | assert_equals(calls.length, 2);
|
160 |
| - assert_array_equals(calls[0], ['title', null, 'hi', null]); |
161 |
| - assert_array_equals(calls[1], ['lang', null, 'en', null]); |
| 153 | + assert_attribute_log_entry(calls[1], {name: 'lang', oldValue: null, newValue: 'en', namespace: null}); |
162 | 154 | }, 'Mutating observedAttributes after calling customElements.define must not affect the set of attributes for which attributedChangedCallback is invoked');
|
163 | 155 |
|
164 | 156 | test(function () {
|
165 | 157 | var calls = [];
|
166 | 158 | class CustomElement extends HTMLElement { }
|
167 |
| - CustomElement.prototype.attributeChangedCallback = function () { |
168 |
| - calls.push(Array.from(arguments)); |
| 159 | + CustomElement.prototype.attributeChangedCallback = function (...args) { |
| 160 | + calls.push(create_attribute_changed_callback_log(this, ...args)); |
169 | 161 | }
|
170 | 162 | CustomElement.observedAttributes = { [Symbol.iterator]: function *() { yield 'lang'; yield 'style'; } };
|
171 | 163 | customElements.define('element-with-generator-observed-attributes', CustomElement);
|
172 | 164 |
|
173 | 165 | var instance = document.createElement('element-with-generator-observed-attributes');
|
174 | 166 | instance.setAttribute('lang', 'en');
|
175 | 167 | assert_equals(calls.length, 1);
|
176 |
| - assert_array_equals(calls[0], ['lang', null, 'en', null]); |
| 168 | + assert_attribute_log_entry(calls[0], {name: 'lang', oldValue: null, newValue: 'en', namespace: null}); |
177 | 169 |
|
178 | 170 | instance.setAttribute('lang', 'ja');
|
179 | 171 | assert_equals(calls.length, 2);
|
180 |
| - assert_array_equals(calls[1], ['lang', 'en', 'ja', null]); |
| 172 | + assert_attribute_log_entry(calls[1], {name: 'lang', oldValue: 'en', newValue: 'ja', namespace: null}); |
181 | 173 |
|
182 | 174 | instance.setAttribute('title', 'hello');
|
183 | 175 | assert_equals(calls.length, 2);
|
184 | 176 |
|
185 | 177 | instance.setAttribute('style', 'font-size: 2rem');
|
186 | 178 | assert_equals(calls.length, 3);
|
187 |
| - assert_array_equals(calls[2], ['style', null, 'font-size: 2rem', null]); |
| 179 | + assert_attribute_log_entry(calls[2], {name: 'style', oldValue: null, newValue: 'font-size: 2rem', namespace: null}); |
188 | 180 | }, 'attributedChangedCallback must be enqueued for attributes specified in a non-Array iterable observedAttributes');
|
189 | 181 |
|
| 182 | +test(function () { |
| 183 | + var calls = []; |
| 184 | + class CustomElement extends HTMLElement { } |
| 185 | + CustomElement.prototype.attributeChangedCallback = function (...args) { |
| 186 | + calls.push(create_attribute_changed_callback_log(this, ...args)); |
| 187 | + } |
| 188 | + CustomElement.observedAttributes = ['style']; |
| 189 | + customElements.define('element-with-style-attribute-observation', CustomElement); |
| 190 | + |
| 191 | + var instance = document.createElement('element-with-style-attribute-observation'); |
| 192 | + assert_equals(calls.length, 0); |
| 193 | + |
| 194 | + instance.style.fontSize = '10px'; |
| 195 | + assert_equals(calls.length, 1); |
| 196 | + assert_attribute_log_entry(calls[0], {name: 'style', oldValue: null, newValue: 'font-size: 10px;', namespace: null}); |
| 197 | + |
| 198 | + instance.style.fontSize = '20px'; |
| 199 | + assert_equals(calls.length, 2); |
| 200 | + assert_attribute_log_entry(calls[1], {name: 'style', oldValue: 'font-size: 10px;', newValue: 'font-size: 20px;', namespace: null}); |
| 201 | + |
| 202 | +}, 'attributedChangedCallback must be enqueued for style attribute change by mutating inline style declaration'); |
| 203 | + |
| 204 | +test(function () { |
| 205 | + var calls = []; |
| 206 | + class CustomElement extends HTMLElement { } |
| 207 | + CustomElement.prototype.attributeChangedCallback = function (...args) { |
| 208 | + calls.push(create_attribute_changed_callback_log(this, ...args)); |
| 209 | + } |
| 210 | + CustomElement.observedAttributes = ['title']; |
| 211 | + customElements.define('element-with-no-style-attribute-observation', CustomElement); |
| 212 | + |
| 213 | + var instance = document.createElement('element-with-no-style-attribute-observation'); |
| 214 | + assert_equals(calls.length, 0); |
| 215 | + instance.style.fontSize = '10px'; |
| 216 | + assert_equals(calls.length, 0); |
| 217 | + instance.title = 'hello'; |
| 218 | + assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hello', namespace: null}); |
| 219 | +}, 'attributedChangedCallback must not be enqueued when mutating inline style declaration if the style attribute is not observed'); |
| 220 | + |
190 | 221 | </script>
|
191 | 222 | </body>
|
192 | 223 | </html>
|
0 commit comments