OLD | NEW |
---|---|
1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
2 <title>Custom Elements: defineElement</title> | 2 <title>Custom Elements: defineElement</title> |
3 <link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#cus tomelementsregistry"> | 3 <link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#cus tomelementsregistry"> |
4 <meta name="author" title="Dominic Cooney" href="mailto:dominicc@chromium.org"> | 4 <meta name="author" title="Dominic Cooney" href="mailto:dominicc@chromium.org"> |
5 <script src="../../resources/testharness.js"></script> | 5 <script src="../../resources/testharness.js"></script> |
6 <script src="../../resources/testharness-helpers.js"></script> | 6 <script src="../../resources/testharness-helpers.js"></script> |
7 <script src="../../resources/testharnessreport.js"></script> | 7 <script src="../../resources/testharnessreport.js"></script> |
8 <script src="resources/custom-elements-helpers.js"></script> | 8 <script src="resources/custom-elements-helpers.js"></script> |
9 <body> | 9 <body> |
10 <script> | 10 <script> |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
57 test_with_window((w) => { | 57 test_with_window((w) => { |
58 class X extends w.HTMLElement {} | 58 class X extends w.HTMLElement {} |
59 class Y extends w.HTMLElement {} | 59 class Y extends w.HTMLElement {} |
60 w.customElements.define('a-a', X); | 60 w.customElements.define('a-a', X); |
61 assert_throws('NotSupportedError', () => { | 61 assert_throws('NotSupportedError', () => { |
62 w.customElements.define('a-a', Y); | 62 w.customElements.define('a-a', Y); |
63 }, 'defining an element with a name that is already defined should throw ' + | 63 }, 'defining an element with a name that is already defined should throw ' + |
64 'a NotSupportedError'); | 64 'a NotSupportedError'); |
65 }, 'Duplicate name'); | 65 }, 'Duplicate name'); |
66 | 66 |
67 // TODO(dominicc): Update this (perhaps by removing this comment) when | 67 |
dominicc (has gone to gerrit)
2016/06/23 04:13:10
Delete this blank line; put just one blank line be
| |
68 // https://github.com/whatwg/html/pull/1333 lands/issue | |
69 // https://github.com/whatwg/html/issues/1329 is closed. | |
70 test_with_window((w) => { | 68 test_with_window((w) => { |
71 class Y extends w.HTMLElement {} | 69 class Y extends w.HTMLElement {} |
72 let X = (function () {}).bind({}); | 70 let X = (function () {}).bind({}); |
73 Object.defineProperty(X, 'prototype', { | 71 Object.defineProperty(X, 'prototype', { |
74 get() { | 72 get() { |
75 assert_throws('NotSupportedError', () => { | 73 assert_throws('NotSupportedError', () => { |
76 w.customElements.define('a-a', Y); | 74 w.customElements.define('a-a', Y); |
77 }, 'defining an element with a name that is being defined should ' + | 75 }, 'defining an element with a name that is being defined should ' + |
78 'throw a NotSupportedError'); | 76 'throw a NotSupportedError'); |
79 return new Object(); | 77 return new Object(); |
80 } | 78 } |
81 }); | 79 }); |
82 // TODO(dominicc): When callback retrieval is implemented, change this | |
83 // to pass a valid constructor and recursively call define when retrieving | |
84 // callbacks instead; then it is possible to assert the first definition | |
85 // worked: | |
86 // let element = Reflect.construct(HTMLElement, [], X); | |
87 // assert_equals(element.localName, 'a-a'); | |
88 w.customElements.define('a-a', X); | 80 w.customElements.define('a-a', X); |
81 assert_true(w.customElements.get('a-a') === X, 'asserting that the first defin ition worked'); | |
dominicc (has gone to gerrit)
2016/06/23 04:13:10
Don't put "asserting" in the message because that'
| |
82 assert_false(w.customElements.get('a-a') === Y, 'asserting that the first defi nition worked'); | |
dominicc (has gone to gerrit)
2016/06/23 04:13:10
You can probably omit this assertion; it is not ve
| |
89 }, 'Duplicate name defined recursively'); | 83 }, 'Duplicate name defined recursively'); |
90 | 84 |
91 test_with_window((w) => { | 85 test_with_window((w) => { |
92 class X extends w.HTMLElement {} | 86 class X extends w.HTMLElement {} |
93 w.customElements.define('a-a', X); | 87 w.customElements.define('a-a', X); |
94 assert_throws('NotSupportedError', () => { | 88 assert_throws('NotSupportedError', () => { |
95 w.customElements.define('a-b', X); | 89 w.customElements.define('a-b', X); |
96 }, 'defining an element with a constructor that is already in the ' + | 90 }, 'defining an element with a constructor that is already in the ' + |
97 'registry should throw a NotSupportedError'); | 91 'registry should throw a NotSupportedError'); |
98 }, 'Reused constructor'); | 92 }, 'Reused constructor'); |
99 | 93 |
100 // TODO(dominicc): Update this (perhaps by removing this comment) when | |
101 // https://github.com/whatwg/html/pull/1333 lands/issue | |
102 // https://github.com/whatwg/html/issues/1329 is closed. | |
103 test_with_window((w) => { | 94 test_with_window((w) => { |
104 let X = (function () {}).bind({}); | 95 let X = (function () {}).bind({}); |
105 Object.defineProperty(X, 'prototype', { | 96 Object.defineProperty(X, 'prototype', { |
106 get() { | 97 get() { |
107 assert_throws('NotSupportedError', () => { | 98 assert_throws('NotSupportedError', () => { |
108 w.customElements.define('second-name', X); | 99 w.customElements.define('second-name', X); |
109 }, 'defining an element with a constructor that is being defined ' + | 100 }, 'defining an element with a constructor that is being defined ' + |
110 'should throw a NotSupportedError'); | 101 'should throw a NotSupportedError'); |
111 return new Object(); | 102 return new Object(); |
112 } | 103 } |
113 }); | 104 }); |
114 // TODO(dominicc): When callback retrieval is implemented, change this | |
115 // to pass a valid constructor and recursively call define when retrieving | |
116 // callbacks instead; then it is possible to assert the first definition | |
117 // worked: | |
118 // let element = Reflect.construct(HTMLElement, [], X); | |
119 // assert_equals(element.localName, 'a-a'); | |
120 w.customElements.define('first-name', X); | 105 w.customElements.define('first-name', X); |
106 assert_true(w.customElements.get('first-name') === X, 'asserting that the firs t definition worked'); | |
107 assert_false(w.customElements.get('second-name') === X, 'asserting that the fi rst definition worked'); | |
121 }, 'Reused constructor recursively'); | 108 }, 'Reused constructor recursively'); |
122 | 109 |
123 test_with_window((w) => { | 110 test_with_window((w) => { |
124 function F() {} | 111 function F() {} |
125 F.prototype = 42; | 112 F.prototype = 42; |
126 assert_throws(TypeError.prototype, () => { | 113 assert_throws(TypeError.prototype, () => { |
127 w.customElements.define('a-a', F); | 114 w.customElements.define('a-a', F); |
128 }, 'defining an element with a constructor with a prototype that is not an ' + | 115 }, 'defining an element with a constructor with a prototype that is not an ' + |
129 'object should throw a TypeError'); | 116 'object should throw a TypeError'); |
130 }, 'Retrieved prototype is a non-object'); | 117 }, 'Retrieved prototype is a non-object'); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
185 'to the defined prototype'); | 172 'to the defined prototype'); |
186 invocations.push(this); | 173 invocations.push(this); |
187 } | 174 } |
188 } | 175 } |
189 w.customElements.define('a-a', C); | 176 w.customElements.define('a-a', C); |
190 assert_array_equals([a], invocations, | 177 assert_array_equals([a], invocations, |
191 'the constructor should have been invoked for the in-' + | 178 'the constructor should have been invoked for the in-' + |
192 'document element'); | 179 'document element'); |
193 }, 'Upgrade: sets prototype of existing elements'); | 180 }, 'Upgrade: sets prototype of existing elements'); |
194 | 181 |
182 | |
195 test_with_window((w) => { | 183 test_with_window((w) => { |
196 let doc = w.document; | 184 let doc = w.document; |
197 var shadow = doc.body.attachShadow({mode: 'open'}); | 185 var shadow = doc.body.attachShadow({mode: 'open'}); |
198 let a = doc.createElement('a-a'); | 186 let a = doc.createElement('a-a'); |
199 shadow.appendChild(a); | 187 shadow.appendChild(a); |
200 let invocations = []; | 188 let invocations = []; |
201 class C extends w.HTMLElement { | 189 class C extends w.HTMLElement { |
202 constructor() { | 190 constructor() { |
203 super(); | 191 super(); |
204 invocations.push(this); | 192 invocations.push(this); |
205 } | 193 } |
206 } | 194 } |
207 w.customElements.define('a-a', C); | 195 w.customElements.define('a-a', C); |
208 assert_array_equals([a], invocations, | 196 assert_array_equals([a], invocations, |
209 'the constructor should have been invoked once for the ' + | 197 'the constructor should have been invoked once for the ' + |
210 'elements in the shadow tree'); | 198 'elements in the shadow tree'); |
211 }, 'Upgrade: shadow tree'); | 199 }, 'Upgrade: shadow tree'); |
200 | |
201 test_with_window((w) => { | |
202 let invocations = []; | |
dominicc (has gone to gerrit)
2016/06/23 04:13:10
There's only one thing going into invocations, so
| |
203 class Y extends w.HTMLElement {} | |
204 let X = (function () {}).bind({}); | |
205 Object.defineProperty(X, 'prototype', { | |
206 get() { throw 42; } | |
207 }); | |
208 try { | |
209 w.customElements.define('a-a', X); | |
210 } catch(e) { | |
211 invocations.push(e); | |
212 } | |
213 assert_equals(invocations[0], 42, 'rethrows Get(constructor, "prototype") exce ption'); | |
214 w.customElements.define('a-a', Y); | |
215 assert_true(w.customElements.get('a-a') === Y, 'algorithm terminates when the first set of steps threw an exception'); | |
dominicc (has gone to gerrit)
2016/06/23 04:13:10
"the first set of steps" is a bit vague. This desc
| |
216 }, 'If an exception is thrown, rethrow that exception and terminate the algorith m'); | |
217 | |
218 test_with_window((w) => { | |
dominicc (has gone to gerrit)
2016/06/23 04:13:10
Tests from here vvv
| |
219 function F() {} | |
220 F.prototype = new Object(); | |
221 Object.defineProperty(F.prototype, 'connectedCallback', { | |
222 get() { return new Object(); } | |
223 }); | |
224 assert_throws(TypeError.prototype, () => { | |
225 w.customElements.define('a-a', F); | |
226 }, 'defining an element with a constructor with a connectedCallback that is ' + | |
227 'not undefined and not callable should throw a TypeError'); | |
228 }, 'Retrieved connectedCallback is not undefined and not callable'); | |
229 | |
230 test_with_window((w) => { | |
231 function F() {} | |
232 F.prototype = new Object(); | |
233 Object.defineProperty(F.prototype, 'disconnectedCallback', { | |
234 get() { return new Object(); } | |
235 }); | |
236 assert_throws(TypeError.prototype, () => { | |
237 w.customElements.define('a-a', F); | |
238 }, 'defining an element with a constructor with a disconnectedCallback that is ' + | |
239 'not undefined and not callable should throw a TypeError'); | |
240 }, 'Retrieved disconnectedCallback is not undefined and not callable'); | |
241 | |
242 test_with_window((w) => { | |
243 function F() {} | |
244 F.prototype = new Object(); | |
245 Object.defineProperty(F.prototype, 'attributeChangedCallback', { | |
246 get() {return new Object(); } | |
247 }); | |
248 assert_throws(TypeError.prototype, () => { | |
249 w.customElements.define('a-a', F); | |
250 }, 'defining an element with a constructor with a attribiteChangedCallback tha t is ' + | |
251 'not undefined and not callable should throw a TypeError'); | |
252 }, 'Retrieved attrbuteChangedCallback is not undefined and not callable'); | |
dominicc (has gone to gerrit)
2016/06/23 04:13:10
... to here ^^^
are perfect. Good work.
| |
253 | |
254 // should be TypeError | |
255 test_with_window((w) => { | |
256 let invalid_name = 'annotation-xml'; | |
257 assert_throws('SYNTAX_ERR', () => { | |
dominicc (has gone to gerrit)
2016/06/23 04:13:10
The implementation has a bug you found--awesome. W
| |
258 w.customElements.define(invalid_name, HTMLElement); | |
259 }, 'defining a constructor that is an interface object whose interface is HT MLElement' + | |
260 'should throw TypeError not SyntaxError'); | |
dominicc (has gone to gerrit)
2016/06/23 04:13:09
Let's put a comment mentioning the exact step.
Th
| |
261 }, 'Invalid constructor'); | |
262 | |
263 // should be TypeError | |
264 test_with_window((w) => { | |
265 let invalid_name = 'annotation-xml'; | |
266 assert_throws('SYNTAX_ERR', () => { | |
267 w.customElements.define(invalid_name, HTMLButtonElement); | |
268 }, 'defining a constructor that is an interface object who has HTMLElement i n its set of inhertied interfaces' + | |
269 'should throw TypeError not SyntaxError'); | |
270 }, 'Invalid constructor'); | |
271 | |
272 | |
273 test_with_window((w) => { | |
274 let invalid_name = 'annotation-xml'; | |
275 assert_throws('SYNTAX_ERR', () => { | |
276 w.customElements.define(invalid_name, class extends HTMLElement {}); | |
277 }, 'defining author-defined custom element constructor' + | |
278 'should pass this step without throwing TypeError'); | |
279 }, 'Invalid constructor'); | |
280 | |
281 | |
282 test_with_window((w) => { | |
283 let element = w.document.createElement('a-a'); | |
284 w.document.body.appendChild(element); | |
285 w.customElements.define('a-a', Set); | |
286 assert_true(w.customElements.get('a-a') === Set, 'definition is added to Custo mElementRegistry'); | |
287 assert_equals(element.matches(':defined'), false, 'fails during upgrade'); | |
288 assert_equals(element.matches(':not(:defined)'), true, 'fails during upgrade') ; | |
289 }, 'Invalid constructor'); | |
290 | |
291 | |
212 </script> | 292 </script> |
OLD | NEW |