OLD | NEW |
(Empty) | |
| 1 <!DOCTYPE html> |
| 2 <html> |
| 3 <head> |
| 4 <title>Custom Elements: CustomElementRegistry interface</title> |
| 5 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> |
| 6 <meta name="assert" content="CustomElementRegistry interface must exist"> |
| 7 <script src="/resources/testharness.js"></script> |
| 8 <script src="/resources/testharnessreport.js"></script> |
| 9 </head> |
| 10 <body> |
| 11 <div id="log"></div> |
| 12 <script> |
| 13 |
| 14 test(function () { |
| 15 assert_true('define' in CustomElementRegistry.prototype, '"define" exists on
CustomElementRegistry.prototype'); |
| 16 assert_true('define' in customElements, '"define" exists on window.customEle
ments'); |
| 17 }, 'CustomElementRegistry interface must have define as a method'); |
| 18 |
| 19 test(function () { |
| 20 assert_throws({'name': 'TypeError'}, function () { customElements.define('ba
dname', 1); }, |
| 21 'customElements.define must throw a TypeError when the element interface
is a number'); |
| 22 assert_throws({'name': 'TypeError'}, function () { customElements.define('ba
dname', '123'); }, |
| 23 'customElements.define must throw a TypeError when the element interface
is a string'); |
| 24 assert_throws({'name': 'TypeError'}, function () { customElements.define('ba
dname', {}); }, |
| 25 'customElements.define must throw a TypeError when the element interface
is an object'); |
| 26 assert_throws({'name': 'TypeError'}, function () { customElements.define('ba
dname', []); }, |
| 27 'customElements.define must throw a TypeError when the element interface
is an array'); |
| 28 }, 'customElements.define must throw when the element interface is not a constru
ctor'); |
| 29 |
| 30 test(function () { |
| 31 customElements.define('custom-html-element', HTMLElement); |
| 32 }, 'customElements.define must not throw the constructor is HTMLElement'); |
| 33 |
| 34 test(function () { |
| 35 class MyCustomElement extends HTMLElement {}; |
| 36 |
| 37 assert_throws({'name': 'SyntaxError'}, function () { customElements.define(n
ull, MyCustomElement); }, |
| 38 'customElements.define must throw a SyntaxError if the tag name is null'
); |
| 39 assert_throws({'name': 'SyntaxError'}, function () { customElements.define('
', MyCustomElement); }, |
| 40 'customElements.define must throw a SyntaxError if the tag name is empty
'); |
| 41 assert_throws({'name': 'SyntaxError'}, function () { customElements.define('
abc', MyCustomElement); }, |
| 42 'customElements.define must throw a SyntaxError if the tag name does not
contain "-"'); |
| 43 assert_throws({'name': 'SyntaxError'}, function () { customElements.define('
a-Bc', MyCustomElement); }, |
| 44 'customElements.define must throw a SyntaxError if the tag name contains
an upper case letter'); |
| 45 |
| 46 var builtinTagNames = [ |
| 47 'annotation-xml', |
| 48 'color-profile', |
| 49 'font-face', |
| 50 'font-face-src', |
| 51 'font-face-uri', |
| 52 'font-face-format', |
| 53 'font-face-name', |
| 54 'missing-glyph' |
| 55 ]; |
| 56 |
| 57 for (var tagName of builtinTagNames) { |
| 58 assert_throws({'name': 'SyntaxError'}, function () { customElements.defi
ne(tagName, MyCustomElement); }, |
| 59 'customElements.define must throw a SyntaxError if the tag name is "
' + tagName + '"'); |
| 60 } |
| 61 |
| 62 }, 'customElements.define must throw with an invalid name'); |
| 63 |
| 64 test(function () { |
| 65 class SomeCustomElement extends HTMLElement {}; |
| 66 |
| 67 var calls = []; |
| 68 var OtherCustomElement = new Proxy(class extends HTMLElement {}, { |
| 69 get: function (target, name) { |
| 70 calls.push(name); |
| 71 return target[name]; |
| 72 } |
| 73 }) |
| 74 |
| 75 customElements.define('some-custom-element', SomeCustomElement); |
| 76 assert_throws({'name': 'NotSupportedError'}, function () { customElements.de
fine('some-custom-element', OtherCustomElement); }, |
| 77 'customElements.define must throw a NotSupportedError if the specified t
ag name is already used'); |
| 78 assert_array_equals(calls, [], 'customElements.define must validate the cust
om element name before getting the prototype of the constructor'); |
| 79 |
| 80 }, 'customElements.define must throw when there is already a custom element of t
he same name'); |
| 81 |
| 82 test(function () { |
| 83 class AnotherCustomElement extends HTMLElement {}; |
| 84 |
| 85 customElements.define('another-custom-element', AnotherCustomElement); |
| 86 assert_throws({'name': 'NotSupportedError'}, function () { customElements.de
fine('some-other-element', AnotherCustomElement); }, |
| 87 'customElements.define must throw a NotSupportedError if the specified c
lass already defines an element'); |
| 88 |
| 89 }, 'customElements.define must throw a NotSupportedError when there is already a
custom element with the same class'); |
| 90 |
| 91 test(function () { |
| 92 var outerCalls = []; |
| 93 var OuterCustomElement = new Proxy(class extends HTMLElement { }, { |
| 94 get: function (target, name) { |
| 95 outerCalls.push(name); |
| 96 customElements.define('inner-custom-element', InnerCustomElement); |
| 97 return target[name]; |
| 98 } |
| 99 }); |
| 100 var innerCalls = []; |
| 101 var InnerCustomElement = new Proxy(class extends HTMLElement { }, { |
| 102 get: function (target, name) { |
| 103 outerCalls.push(name); |
| 104 return target[name]; |
| 105 } |
| 106 }); |
| 107 |
| 108 assert_throws({'name': 'NotSupportedError'}, function () { customElements.de
fine('outer-custom-element', OuterCustomElement); }, |
| 109 'customElements.define must throw a NotSupportedError if the specified c
lass already defines an element'); |
| 110 assert_array_equals(outerCalls, ['prototype'], 'customElements.define must g
et "prototype"'); |
| 111 assert_array_equals(innerCalls, [], |
| 112 'customElements.define must throw a NotSupportedError when element defin
ition is running flag is set' |
| 113 + ' before getting the prototype of the constructor'); |
| 114 |
| 115 }, 'customElements.define must throw a NotSupportedError when element definition
is running flag is set'); |
| 116 |
| 117 test(function () { |
| 118 var calls = []; |
| 119 var ElementWithBadInnerConstructor = new Proxy(class extends HTMLElement { }
, { |
| 120 get: function (target, name) { |
| 121 calls.push(name); |
| 122 customElements.define('inner-custom-element', 1); |
| 123 return target[name]; |
| 124 } |
| 125 }); |
| 126 |
| 127 assert_throws({'name': 'TypeError'}, function () { |
| 128 customElements.define('element-with-bad-inner-constructor', ElementWithB
adInnerConstructor); |
| 129 }, 'customElements.define must throw a NotSupportedError if IsConstructor(co
nstructor) is false'); |
| 130 |
| 131 assert_array_equals(calls, ['prototype'], 'customElements.define must get "p
rototype"'); |
| 132 }, 'customElements.define must check IsConstructor on the constructor before che
cking the element definition is running flag'); |
| 133 |
| 134 test(function () { |
| 135 var calls = []; |
| 136 var ElementWithBadInnerName = new Proxy(class extends HTMLElement { }, { |
| 137 get: function (target, name) { |
| 138 calls.push(name); |
| 139 customElements.define('badname', class extends HTMLElement {}); |
| 140 return target[name]; |
| 141 } |
| 142 }); |
| 143 |
| 144 assert_throws({'name': 'SyntaxError'}, function () { |
| 145 customElements.define('element-with-bad-inner-name', ElementWithBadInner
Name); |
| 146 }, 'customElements.define must throw a SyntaxError if the specified name is
not a valid custom element name'); |
| 147 |
| 148 assert_array_equals(calls, ['prototype'], 'customElements.define must get "p
rototype"'); |
| 149 }, 'customElements.define must validate the custom element name before checking
the element definition is running flag'); |
| 150 |
| 151 test(function () { |
| 152 var unresolvedElement = document.createElement('constructor-calls-define'); |
| 153 document.body.appendChild(unresolvedElement); |
| 154 var elementUpgradedDuringUpgrade = document.createElement('defined-during-up
grade'); |
| 155 document.body.appendChild(elementUpgradedDuringUpgrade); |
| 156 |
| 157 var DefinedDuringUpgrade = class extends HTMLElement { }; |
| 158 |
| 159 class ConstructorCallsDefine extends HTMLElement { |
| 160 constructor() { |
| 161 customElements.define('defined-during-upgrade', DefinedDuringUpgrade
); |
| 162 assert_false(unresolvedElement instanceof ConstructorCallsDefine); |
| 163 assert_true(elementUpgradedDuringUpgrade instanceof DefinedDuringUpg
rade); |
| 164 super(); |
| 165 assert_true(unresolvedElement instanceof ConstructorCallsDefine); |
| 166 assert_true(elementUpgradedDuringUpgrade instanceof DefinedDuringUpg
rade); |
| 167 } |
| 168 } |
| 169 |
| 170 assert_false(unresolvedElement instanceof ConstructorCallsDefine); |
| 171 assert_false(elementUpgradedDuringUpgrade instanceof DefinedDuringUpgrade); |
| 172 |
| 173 customElements.define('constructor-calls-define', ConstructorCallsDefine); |
| 174 }, 'customElements.define unset the element definition is running flag before up
grading custom elements'); |
| 175 |
| 176 (function () { |
| 177 var testCase = async_test('customElements.define must not throw' |
| 178 +' when defining another custom element in a different global object dur
ing Get(constructor, "prototype")', {timeout: 100}); |
| 179 |
| 180 var iframe = document.createElement('iframe'); |
| 181 iframe.onload = function () { |
| 182 testCase.step(function () { |
| 183 var InnerCustomElement = class extends iframe.contentWindow.HTMLElem
ent {}; |
| 184 var calls = []; |
| 185 var proxy = new Proxy(class extends HTMLElement { }, { |
| 186 get: function (target, name) { |
| 187 calls.push(name); |
| 188 iframe.contentWindow.customElements.define('another-custom-e
lement', InnerCustomElement); |
| 189 return target[name]; |
| 190 } |
| 191 }) |
| 192 customElements.define('element-with-inner-element-define', proxy); |
| 193 assert_array_equals(calls, ['prototype'], 'customElements.define mus
t get "prototype"'); |
| 194 assert_true(iframe.contentDocument.createElement('another-custom-ele
ment') instanceof InnerCustomElement); |
| 195 }); |
| 196 document.body.removeChild(iframe); |
| 197 testCase.done(); |
| 198 } |
| 199 |
| 200 document.body.appendChild(iframe); |
| 201 })(); |
| 202 |
| 203 test(function () { |
| 204 var calls = []; |
| 205 var ElementWithBadInnerName = new Proxy(class extends HTMLElement { }, { |
| 206 get: function (target, name) { |
| 207 calls.push(name); |
| 208 customElements.define('badname', class extends HTMLElement {}); |
| 209 return target[name]; |
| 210 } |
| 211 }); |
| 212 |
| 213 assert_throws({'name': 'SyntaxError'}, function () { |
| 214 customElements.define('element-with-bad-inner-name', ElementWithBadInner
Name); |
| 215 }, 'customElements.define must throw a SyntaxError if the specified name is
not a valid custom element name'); |
| 216 |
| 217 assert_array_equals(calls, ['prototype'], 'customElements.define must get "p
rototype"'); |
| 218 }, ''); |
| 219 |
| 220 test(function () { |
| 221 var calls = []; |
| 222 var proxy = new Proxy(class extends HTMLElement { }, { |
| 223 get: function (target, name) { |
| 224 calls.push(name); |
| 225 return target[name]; |
| 226 } |
| 227 }); |
| 228 customElements.define('proxy-element', proxy); |
| 229 assert_array_equals(calls, ['prototype']); |
| 230 }, 'customElements.define must get "prototype" property of the constructor'); |
| 231 |
| 232 test(function () { |
| 233 var proxy = new Proxy(class extends HTMLElement { }, { |
| 234 get: function (target, name) { |
| 235 throw {name: 'expectedError'}; |
| 236 } |
| 237 }); |
| 238 assert_throws({'name': 'expectedError'}, function () { customElements.define
('element-with-string-prototype', proxy); }); |
| 239 }, 'customElements.define must rethrow an exception thrown while getting "protot
ype" property of the constructor'); |
| 240 |
| 241 test(function () { |
| 242 var returnedValue; |
| 243 var proxy = new Proxy(class extends HTMLElement { }, { |
| 244 get: function (target, name) { return returnedValue; } |
| 245 }); |
| 246 |
| 247 returnedValue = null; |
| 248 assert_throws({'name': 'TypeError'}, function () { customElements.define('el
ement-with-string-prototype', proxy); }, |
| 249 'customElements.define must throw when "prototype" property of the const
ructor is null'); |
| 250 returnedValue = undefined; |
| 251 assert_throws({'name': 'TypeError'}, function () { customElements.define('el
ement-with-string-prototype', proxy); }, |
| 252 'customElements.define must throw when "prototype" property of the const
ructor is undefined'); |
| 253 returnedValue = 'hello'; |
| 254 assert_throws({'name': 'TypeError'}, function () { customElements.define('el
ement-with-string-prototype', proxy); }, |
| 255 'customElements.define must throw when "prototype" property of the const
ructor is a string'); |
| 256 returnedValue = 1; |
| 257 assert_throws({'name': 'TypeError'}, function () { customElements.define('el
ement-with-string-prototype', proxy); }, |
| 258 'customElements.define must throw when "prototype" property of the const
ructor is a number'); |
| 259 |
| 260 }, 'customElements.define must throw when "prototype" property of the constructo
r is not an object'); |
| 261 |
| 262 test(function () { |
| 263 var constructor = function () {} |
| 264 var calls = []; |
| 265 constructor.prototype = new Proxy(constructor.prototype, { |
| 266 get: function (target, name) { |
| 267 calls.push(name); |
| 268 return target[name]; |
| 269 } |
| 270 }); |
| 271 customElements.define('element-with-proxy-prototype', constructor); |
| 272 assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback', 'ad
optedCallback', 'attributeChangedCallback']); |
| 273 }, 'customElements.define must get callbacks of the constructor prototype'); |
| 274 |
| 275 test(function () { |
| 276 var constructor = function () {} |
| 277 var calls = []; |
| 278 constructor.prototype = new Proxy(constructor.prototype, { |
| 279 get: function (target, name) { |
| 280 calls.push(name); |
| 281 if (name == 'disconnectedCallback') |
| 282 throw {name: 'expectedError'}; |
| 283 return target[name]; |
| 284 } |
| 285 }); |
| 286 assert_throws({'name': 'expectedError'}, function () { customElements.define
('element-with-throwing-callback', constructor); }); |
| 287 assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback'], |
| 288 'customElements.define must not get callbacks after one of the get throw
s'); |
| 289 }, 'customElements.define must rethrow an exception thrown while getting callbac
ks on the constructor prototype'); |
| 290 |
| 291 test(function () { |
| 292 var constructor = function () {} |
| 293 var calls = []; |
| 294 constructor.prototype = new Proxy(constructor.prototype, { |
| 295 get: function (target, name) { |
| 296 calls.push(name); |
| 297 if (name == 'adoptedCallback') |
| 298 return 1; |
| 299 return target[name]; |
| 300 } |
| 301 }); |
| 302 assert_throws({'name': 'TypeError'}, function () { customElements.define('el
ement-with-throwing-callback', constructor); }); |
| 303 assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback', 'ad
optedCallback'], |
| 304 'customElements.define must not get callbacks after one of the conversio
n throws'); |
| 305 }, 'customElements.define must rethrow an exception thrown while converting a ca
llback value to Function callback type'); |
| 306 |
| 307 test(function () { |
| 308 var constructor = function () {} |
| 309 constructor.prototype.attributeChangedCallback = function () { }; |
| 310 var prototypeCalls = []; |
| 311 var callOrder = 0; |
| 312 constructor.prototype = new Proxy(constructor.prototype, { |
| 313 get: function (target, name) { |
| 314 if (name == 'prototype' || name == 'observedAttributes') |
| 315 throw 'Unexpected access to observedAttributes'; |
| 316 prototypeCalls.push(callOrder++); |
| 317 prototypeCalls.push(name); |
| 318 return target[name]; |
| 319 } |
| 320 }); |
| 321 var constructorCalls = []; |
| 322 var proxy = new Proxy(constructor, { |
| 323 get: function (target, name) { |
| 324 constructorCalls.push(callOrder++); |
| 325 constructorCalls.push(name); |
| 326 return target[name]; |
| 327 } |
| 328 }); |
| 329 customElements.define('element-with-attribute-changed-callback', proxy); |
| 330 assert_array_equals(prototypeCalls, [1, 'connectedCallback', 2, 'disconnecte
dCallback', 3, 'adoptedCallback', 4, 'attributeChangedCallback']); |
| 331 assert_array_equals(constructorCalls, [0, 'prototype', 5, 'observedAttribute
s']); |
| 332 }, 'customElements.define must get "observedAttributes" property on the construc
tor prototype when "attributeChangedCallback" is present'); |
| 333 |
| 334 test(function () { |
| 335 var constructor = function () {} |
| 336 constructor.prototype.attributeChangedCallback = function () { }; |
| 337 var calls = []; |
| 338 var proxy = new Proxy(constructor, { |
| 339 get: function (target, name) { |
| 340 calls.push(name); |
| 341 if (name == 'observedAttributes') |
| 342 throw {name: 'expectedError'}; |
| 343 return target[name]; |
| 344 } |
| 345 }); |
| 346 assert_throws({'name': 'expectedError'}, function () { customElements.define
('element-with-throwing-observed-attributes', proxy); }); |
| 347 assert_array_equals(calls, ['prototype', 'observedAttributes'], |
| 348 'customElements.define must get "prototype" and "observedAttributes" on
the constructor'); |
| 349 }, 'customElements.define must rethrow an exception thrown while getting observe
dAttributes on the constructor prototype'); |
| 350 |
| 351 test(function () { |
| 352 var constructor = function () {} |
| 353 constructor.prototype.attributeChangedCallback = function () { }; |
| 354 var calls = []; |
| 355 var proxy = new Proxy(constructor, { |
| 356 get: function (target, name) { |
| 357 calls.push(name); |
| 358 if (name == 'observedAttributes') |
| 359 return 1; |
| 360 return target[name]; |
| 361 } |
| 362 }); |
| 363 assert_throws({'name': 'TypeError'}, function () { customElements.define('el
ement-with-invalid-observed-attributes', proxy); }); |
| 364 assert_array_equals(calls, ['prototype', 'observedAttributes'], |
| 365 'customElements.define must get "prototype" and "observedAttributes" on
the constructor'); |
| 366 }, 'customElements.define must rethrow an exception thrown while converting the
value of observedAttributes to sequence<DOMString>'); |
| 367 |
| 368 test(function () { |
| 369 var constructor = function () {} |
| 370 constructor.prototype.attributeChangedCallback = function () { }; |
| 371 constructor.observedAttributes = {[Symbol.iterator]: function *() { |
| 372 yield 'foo'; |
| 373 throw {name: 'SomeError'}; |
| 374 }}; |
| 375 assert_throws({'name': 'SomeError'}, function () { customElements.define('el
ement-with-generator-observed-attributes', constructor); }); |
| 376 }, 'customElements.define must rethrow an exception thrown while iterating over
observedAttributes to sequence<DOMString>'); |
| 377 |
| 378 test(function () { |
| 379 var constructor = function () {} |
| 380 constructor.prototype.attributeChangedCallback = function () { }; |
| 381 constructor.observedAttributes = {[Symbol.iterator]: 1}; |
| 382 assert_throws({'name': 'TypeError'}, function () { customElements.define('el
ement-with-observed-attributes-with-uncallable-iterator', constructor); }); |
| 383 }, 'customElements.define must rethrow an exception thrown while retrieving Symb
ol.iterator on observedAttributes'); |
| 384 |
| 385 test(function () { |
| 386 var constructor = function () {} |
| 387 constructor.observedAttributes = 1; |
| 388 customElements.define('element-without-callback-with-invalid-observed-attrib
utes', constructor); |
| 389 }, 'customElements.define must not throw even if "observedAttributes" fails to c
onvert if "attributeChangedCallback" is not defined'); |
| 390 |
| 391 test(function () { |
| 392 class MyCustomElement extends HTMLElement {}; |
| 393 customElements.define('my-custom-element', MyCustomElement); |
| 394 |
| 395 var instance = new MyCustomElement; |
| 396 assert_true(instance instanceof MyCustomElement, |
| 397 'An instance of a custom HTML element be an instance of the associated i
nterface'); |
| 398 |
| 399 assert_true(instance instanceof HTMLElement, |
| 400 'An instance of a custom HTML element must inherit from HTMLElement'); |
| 401 |
| 402 assert_equals(instance.localName, 'my-custom-element', |
| 403 'An instance of a custom element must use the associated tag name'); |
| 404 |
| 405 assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml', |
| 406 'A custom element HTML must use HTML namespace'); |
| 407 |
| 408 }, 'customElements.define must define an instantiatable custom element'); |
| 409 |
| 410 test(function () { |
| 411 var disconnectedElement = document.createElement('some-custom'); |
| 412 var connectedElementBeforeShadowHost = document.createElement('some-custom')
; |
| 413 var connectedElementAfterShadowHost = document.createElement('some-custom'); |
| 414 var elementInShadowTree = document.createElement('some-custom'); |
| 415 var childElementOfShadowHost = document.createElement('some-custom'); |
| 416 var customShadowHost = document.createElement('some-custom'); |
| 417 var elementInNestedShadowTree = document.createElement('some-custom'); |
| 418 |
| 419 var container = document.createElement('div'); |
| 420 var shadowHost = document.createElement('div'); |
| 421 var shadowRoot = shadowHost.attachShadow({mode: 'closed'}); |
| 422 container.appendChild(connectedElementBeforeShadowHost); |
| 423 container.appendChild(shadowHost); |
| 424 container.appendChild(connectedElementAfterShadowHost); |
| 425 shadowHost.appendChild(childElementOfShadowHost); |
| 426 shadowRoot.appendChild(elementInShadowTree); |
| 427 shadowRoot.appendChild(customShadowHost); |
| 428 |
| 429 var innerShadowRoot = customShadowHost.attachShadow({mode: 'closed'}); |
| 430 innerShadowRoot.appendChild(elementInNestedShadowTree); |
| 431 |
| 432 var calls = []; |
| 433 class SomeCustomElement extends HTMLElement { |
| 434 constructor() { |
| 435 super(); |
| 436 calls.push(this); |
| 437 } |
| 438 }; |
| 439 |
| 440 document.body.appendChild(container); |
| 441 customElements.define('some-custom', SomeCustomElement); |
| 442 assert_array_equals(calls, [connectedElementBeforeShadowHost, elementInShado
wTree, customShadowHost, elementInNestedShadowTree, childElementOfShadowHost, co
nnectedElementAfterShadowHost]); |
| 443 }, 'customElements.define must upgrade elements in the shadow-including tree ord
er'); |
| 444 |
| 445 test(function () { |
| 446 assert_true('get' in CustomElementRegistry.prototype, '"get" exists on Custo
mElementRegistry.prototype'); |
| 447 assert_true('get' in customElements, '"get" exists on window.customElements'
); |
| 448 }, 'CustomElementRegistry interface must have get as a method'); |
| 449 |
| 450 test(function () { |
| 451 assert_equals(customElements.get('a-b'), undefined); |
| 452 }, 'customElements.get must return undefined when the registry does not contain
an entry with the given name'); |
| 453 |
| 454 test(function () { |
| 455 assert_equals(customElements.get('html'), undefined); |
| 456 assert_equals(customElements.get('span'), undefined); |
| 457 assert_equals(customElements.get('div'), undefined); |
| 458 assert_equals(customElements.get('g'), undefined); |
| 459 assert_equals(customElements.get('ab'), undefined); |
| 460 }, 'customElements.get must return undefined when the registry does not contain
an entry with the given name even if the name was not a valid custom element nam
e'); |
| 461 |
| 462 test(function () { |
| 463 assert_equals(customElements.get('existing-custom-element'), undefined); |
| 464 class ExistingCustomElement extends HTMLElement {}; |
| 465 customElements.define('existing-custom-element', ExistingCustomElement); |
| 466 assert_equals(customElements.get('existing-custom-element'), ExistingCustomE
lement); |
| 467 }, 'customElements.get return the constructor of the entry with the given name w
hen there is a matching entry.'); |
| 468 |
| 469 test(function () { |
| 470 assert_true(customElements.whenDefined('some-name') instanceof Promise); |
| 471 }, 'customElements.whenDefined must return a promise for a valid custom element
name'); |
| 472 |
| 473 test(function () { |
| 474 assert_equals(customElements.whenDefined('some-name'), customElements.whenDe
fined('some-name')); |
| 475 }, 'customElements.whenDefined must return the same promise each time invoked fo
r a valid custom element name which has not been defined'); |
| 476 |
| 477 promise_test(function () { |
| 478 var resolved = false; |
| 479 var rejected = false; |
| 480 customElements.whenDefined('a-b').then(function () { resolved = true; }, fun
ction () { rejected = true; }); |
| 481 return Promise.resolve().then(function () { |
| 482 assert_false(resolved, 'The promise returned by "whenDefined" must not b
e resolved until a custom element is defined'); |
| 483 assert_false(rejected, 'The promise returned by "whenDefined" must not b
e rejected until a custom element is defined'); |
| 484 }); |
| 485 }, 'customElements.whenDefined must return an unresolved promise when the regist
ry does not contain the entry with the given name') |
| 486 |
| 487 promise_test(function () { |
| 488 var promise = customElements.whenDefined('badname'); |
| 489 promise.then(function (value) { promise.resolved = value; }, function (value
) { promise.rejected = value; }); |
| 490 |
| 491 assert_false('resolved' in promise, 'The promise returned by "whenDefined" m
ust not be resolved until the end of the next microtask'); |
| 492 assert_false('rejected' in promise, 'The promise returned by "whenDefined" m
ust not be rejected until the end of the next microtask'); |
| 493 |
| 494 return Promise.resolve().then(function () { |
| 495 assert_false('resolved' in promise, 'The promise returned by "whenDefine
d" must be resolved when a custom element is defined'); |
| 496 assert_true('rejected' in promise, 'The promise returned by "whenDefined
" must not be rejected when a custom element is defined'); |
| 497 }); |
| 498 }, 'customElements.whenDefined must return a rejected promise when the given nam
e is not a valid custom element name'); |
| 499 |
| 500 promise_test(function () { |
| 501 customElements.define('preexisting-custom-element', class extends HTMLElemen
t { }); |
| 502 |
| 503 var promise = customElements.whenDefined('preexisting-custom-element'); |
| 504 promise.then(function (value) { promise.resolved = value; }, function (value
) { promise.rejected = value; }); |
| 505 |
| 506 assert_false('resolved' in promise, 'The promise returned by "whenDefined" m
ust not be resolved until the end of the next microtask'); |
| 507 assert_false('rejected' in promise, 'The promise returned by "whenDefined" m
ust not be rejected until the end of the next microtask'); |
| 508 |
| 509 return Promise.resolve().then(function () { |
| 510 assert_true('resolved' in promise, 'The promise returned by "whenDefined
" must be resolved when a custom element is defined'); |
| 511 assert_equals(promise.resolved, undefined, |
| 512 'The promise returned by "whenDefined" must be resolved with "undefi
ned" when a custom element is defined'); |
| 513 assert_false('rejected' in promise, 'The promise returned by "whenDefine
d" must not be rejected when a custom element is defined'); |
| 514 }); |
| 515 }, 'customElements.whenDefined must return a resolved promise when the registry
contains the entry with the given name'); |
| 516 |
| 517 promise_test(function () { |
| 518 class AnotherExistingCustomElement extends HTMLElement {}; |
| 519 customElements.define('another-existing-custom-element', AnotherExistingCust
omElement); |
| 520 |
| 521 var promise1 = customElements.whenDefined('another-existing-custom-element')
; |
| 522 var promise2 = customElements.whenDefined('another-existing-custom-element')
; |
| 523 promise1.then(function (value) { promise1.resolved = value; }, function (val
ue) { promise1.rejected = value; }); |
| 524 promise2.then(function (value) { promise2.resolved = value; }, function (val
ue) { promise2.rejected = value; }); |
| 525 |
| 526 assert_not_equals(promise1, promise2); |
| 527 assert_false('resolved' in promise1, 'The promise returned by "whenDefined"
must not be resolved until the end of the next microtask'); |
| 528 assert_false('resolved' in promise2, 'The promise returned by "whenDefined"
must not be resolved until the end of the next microtask'); |
| 529 assert_false('rejected' in promise1, 'The promise returned by "whenDefined"
must not be rejected until the end of the next microtask'); |
| 530 assert_false('rejected' in promise2, 'The promise returned by "whenDefined"
must not be rejected until the end of the next microtask'); |
| 531 |
| 532 return Promise.resolve().then(function () { |
| 533 assert_true('resolved' in promise1, 'The promise returned by "whenDefine
d" must be resolved when a custom element is defined'); |
| 534 assert_equals(promise1.resolved, undefined, 'The promise returned by "wh
enDefined" must be resolved with "undefined" when a custom element is defined'); |
| 535 assert_false('rejected' in promise1, 'The promise returned by "whenDefin
ed" must not be rejected when a custom element is defined'); |
| 536 |
| 537 assert_true('resolved' in promise2, 'The promise returned by "whenDefine
d" must be resolved when a custom element is defined'); |
| 538 assert_equals(promise2.resolved, undefined, 'The promise returned by "wh
enDefined" must be resolved with "undefined" when a custom element is defined'); |
| 539 assert_false('rejected' in promise2, 'The promise returned by "whenDefin
ed" must not be rejected when a custom element is defined'); |
| 540 }); |
| 541 }, 'customElements.whenDefined must return a new resolved promise each time invo
ked when the registry contains the entry with the given name'); |
| 542 |
| 543 promise_test(function () { |
| 544 var promise = customElements.whenDefined('element-defined-after-whendefined'
); |
| 545 promise.then(function (value) { promise.resolved = value; }, function (value
) { promise.rejected = value; }); |
| 546 |
| 547 assert_false('resolved' in promise, 'The promise returned by "whenDefined" m
ust not be resolved until the end of the next microtask'); |
| 548 assert_false('rejected' in promise, 'The promise returned by "whenDefined" m
ust not be rejected until the end of the next microtask'); |
| 549 |
| 550 var promiseAfterDefine; |
| 551 return Promise.resolve().then(function () { |
| 552 assert_false('resolved' in promise, 'The promise returned by "whenDefine
d" must not be resolved until the element is defined'); |
| 553 assert_false('rejected' in promise, 'The promise returned by "whenDefine
d" must not be rejected until the element is defined'); |
| 554 assert_equals(customElements.whenDefined('element-defined-after-whendefi
ned'), promise, |
| 555 '"whenDefined" must return the same unresolved promise before the cu
stom element is defined'); |
| 556 customElements.define('element-defined-after-whendefined', class extends
HTMLElement { }); |
| 557 assert_false('resolved' in promise, 'The promise returned by "whenDefine
d" must not be resolved until the end of the next microtask'); |
| 558 assert_false('rejected' in promise, 'The promise returned by "whenDefine
d" must not be rejected until the end of the next microtask'); |
| 559 |
| 560 promiseAfterDefine = customElements.whenDefined('element-defined-after-w
hendefined'); |
| 561 promiseAfterDefine.then(function (value) { promiseAfterDefine.resolved =
value; }, function (value) { promiseAfterDefine.rejected = value; }); |
| 562 assert_not_equals(promiseAfterDefine, promise, '"whenDefined" must retur
n a resolved promise once the custom element is defined'); |
| 563 assert_false('resolved' in promiseAfterDefine, 'The promise returned by
"whenDefined" must not be resolved until the end of the next microtask'); |
| 564 assert_false('rejected' in promiseAfterDefine, 'The promise returned by
"whenDefined" must not be rejected until the end of the next microtask'); |
| 565 }).then(function () { |
| 566 assert_true('resolved' in promise, 'The promise returned by "whenDefined
" must be resolved when a custom element is defined'); |
| 567 assert_equals(promise.resolved, undefined, |
| 568 'The promise returned by "whenDefined" must be resolved with "undefi
ned" when a custom element is defined'); |
| 569 assert_false('rejected' in promise, 'The promise returned by "whenDefine
d" must not be rejected when a custom element is defined'); |
| 570 |
| 571 assert_true('resolved' in promiseAfterDefine, 'The promise returned by "
whenDefined" must be resolved when a custom element is defined'); |
| 572 assert_equals(promiseAfterDefine.resolved, undefined, |
| 573 'The promise returned by "whenDefined" must be resolved with "undefi
ned" when a custom element is defined'); |
| 574 assert_false('rejected' in promiseAfterDefine, 'The promise returned by
"whenDefined" must not be rejected when a custom element is defined'); |
| 575 }); |
| 576 }, 'A promise returned by customElements.whenDefined must be resolved by "define
"'); |
| 577 |
| 578 </script> |
| 579 </body> |
| 580 </html> |
OLD | NEW |