dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 <!DOCTYPE HTML>
     2 <html>
     3 <!--
     4 https://bugzilla.mozilla.org/show_bug.cgi?id=783129
     5 -->
     6 <head>
     7   <title>Test for document.registerElement lifecycle callback</title>
     8   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
    10 </head>
    11 <body>
    12 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
    13 <div id="container">
    14   <x-hello id="hello"></x-hello>
    15   <button id="extbutton" is="x-button"></button>
    16 </div>
    17 <script>
    19 var container = document.getElementById("container");
    21 // Tests callbacks after registering element type that is already in the document.
    22 // create element in document -> register -> remove from document
    23 function testRegisterUnresolved() {
    24   var helloElem = document.getElementById("hello");
    26   var createdCallbackCalled = false;
    27   var attachedCallbackCalled = false;
    28   var detachedCallbackCalled = false;
    30   var p = Object.create(HTMLElement.prototype);
    31   p.createdCallback = function() {
    32     is(helloElem.__proto__, p, "Prototype should be adjusted just prior to invoking the created callback.");
    33     is(createdCallbackCalled, false, "Created callback should only be called once in this tests.");
    34     is(this, helloElem, "The 'this' value should be the custom element.");
    35     createdCallbackCalled = true;
    36   };
    38   p.attachedCallback = function() {
    39     is(createdCallbackCalled, true, "Created callback should be called before attached");
    40     is(attachedCallbackCalled, false, "attached callback should only be called once in this test.");
    41     is(this, helloElem, "The 'this' value should be the custom element.");
    42     attachedCallbackCalled = true;
    43   };
    45   p.detachedCallback = function() {
    46     is(attachedCallbackCalled, true, "attached callback should be called before detached");
    47     is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
    48     detachedCallbackCalled = true;
    49     is(this, helloElem, "The 'this' value should be the custom element.");
    50     runNextTest();
    51   };
    53   p.attributeChangedCallback = function(name, oldValue, newValue) {
    54     ok(false, "attributeChanged callback should never be called in this test.");
    55   };
    57   document.registerElement("x-hello", { prototype: p });
    58   is(createdCallbackCalled, true, "created callback should be called when control returns to script from user agent code");
    60   // Remove element from document to trigger detached callback.
    61   container.removeChild(helloElem);
    62 }
    64 // Tests callbacks after registering an extended element type that is already in the document.
    65 // create element in document -> register -> remove from document
    66 function testRegisterUnresolvedExtended() {
    67   var buttonElem = document.getElementById("extbutton");
    69   var createdCallbackCalled = false;
    70   var attachedCallbackCalled = false;
    71   var detachedCallbackCalled = false;
    73   var p = Object.create(HTMLButtonElement.prototype);
    74   p.createdCallback = function() {
    75     is(buttonElem.__proto__, p, "Prototype should be adjusted just prior to invoking the created callback.");
    76     is(createdCallbackCalled, false, "Created callback should only be called once in this tests.");
    77     is(this, buttonElem, "The 'this' value should be the custom element.");
    78     createdCallbackCalled = true;
    79   };
    81   p.attachedCallback = function() {
    82     is(createdCallbackCalled, true, "Created callback should be called before attached");
    83     is(attachedCallbackCalled, false, "attached callback should only be called once in this test.");
    84     is(this, buttonElem, "The 'this' value should be the custom element.");
    85     attachedCallbackCalled = true;
    86   };
    88   p.detachedCallback = function() {
    89     is(attachedCallbackCalled, true, "attached callback should be called before detached");
    90     is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
    91     detachedCallbackCalled = true;
    92     is(this, buttonElem, "The 'this' value should be the custom element.");
    93     runNextTest();
    94   };
    96   p.attributeChangedCallback = function(name, oldValue, newValue) {
    97     ok(false, "attributeChanged callback should never be called in this test.");
    98   };
   100   document.registerElement("x-button", { prototype: p, extends: "button" });
   101   is(createdCallbackCalled, true, "created callback should be called when control returns to script from user agent code");
   103   // Remove element from document to trigger detached callback.
   104   container.removeChild(buttonElem);
   105 }
   107 function testInnerHTML() {
   108   var createdCallbackCalled = false;
   110   var p = Object.create(HTMLElement.prototype);
   111   p.createdCallback = function() {
   112     is(createdCallbackCalled, false, "created callback should only be called once in this test.");
   113     createdCallbackCalled = true;
   114   };
   116   document.registerElement("x-inner-html", { prototype: p });
   117   var div = document.createElement(div);
   118   div.innerHTML = '<x-inner-html></x-inner-html>';
   119   is(createdCallbackCalled, true, "created callback should be called after setting innerHTML.");
   120   runNextTest();
   121 }
   123 function testInnerHTMLExtended() {
   124   var createdCallbackCalled = false;
   126   var p = Object.create(HTMLButtonElement.prototype);
   127   p.createdCallback = function() {
   128     is(createdCallbackCalled, false, "created callback should only be called once in this test.");
   129     createdCallbackCalled = true;
   130   };
   132   document.registerElement("x-inner-html-extended", { prototype: p, extends: "button" });
   133   var div = document.createElement(div);
   134   div.innerHTML = '<button is="x-inner-html-extended"></button>';
   135   is(createdCallbackCalled, true, "created callback should be called after setting innerHTML.");
   136   runNextTest();
   137 }
   139 function testInnerHTMLUpgrade() {
   140   var createdCallbackCalled = false;
   142   var div = document.createElement(div);
   143   div.innerHTML = '<x-inner-html-upgrade></x-inner-html-upgrade>';
   145   var p = Object.create(HTMLElement.prototype);
   146   p.createdCallback = function() {
   147     is(createdCallbackCalled, false, "created callback should only be called once in this test.");
   148     createdCallbackCalled = true;
   149   };
   151   document.registerElement("x-inner-html-upgrade", { prototype: p });
   152   is(createdCallbackCalled, true, "created callback should be called after registering.");
   153   runNextTest();
   154 }
   156 function testInnerHTMLExtendedUpgrade() {
   157   var createdCallbackCalled = false;
   159   var div = document.createElement(div);
   160   div.innerHTML = '<button is="x-inner-html-extended-upgrade"></button>';
   162   var p = Object.create(HTMLButtonElement.prototype);
   163   p.createdCallback = function() {
   164     is(createdCallbackCalled, false, "created callback should only be called once in this test.");
   165     createdCallbackCalled = true;
   166   };
   168   document.registerElement("x-inner-html-extended-upgrade", { prototype: p, extends: "button" });
   169   is(createdCallbackCalled, true, "created callback should be called after registering.");
   170   runNextTest();
   171 }
   173 // Test callback when creating element after registering an element type.
   174 // register -> create element -> insert into document -> remove from document
   175 function testRegisterResolved() {
   176   var createdCallbackCalled = false;
   177   var attachedCallbackCalled = false;
   178   var detachedCallbackCalled = false;
   180   var createdCallbackThis;
   182   var p = Object.create(HTMLElement.prototype);
   183   p.createdCallback = function() {
   184     is(createdCallbackCalled, false, "Created callback should only be called once in this test.");
   185     createdCallbackThis = this;
   186     createdCallbackCalled = true;
   187   };
   189   p.attachedCallback = function() {
   190     is(createdCallbackCalled, true, "created callback should be called before attached callback.");
   191     is(attachedCallbackCalled, false, "attached callback should only be called on in this test.");
   192     is(this, createdElement, "The 'this' value should be the custom element.");
   193     attachedCallbackCalled = true;
   194   };
   196   p.detachedCallback = function() {
   197     is(attachedCallbackCalled, true, "attached callback should be called before detached");
   198     is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
   199     is(this, createdElement, "The 'this' value should be the custom element.");
   200     detachedCallbackCalled = true;
   201     runNextTest();
   202   };
   204   p.attributeChangedCallback = function() {
   205     ok(false, "attributeChanged callback should never be called in this test.");
   206   };
   208   document.registerElement("x-resolved", { prototype: p });
   209   is(createdCallbackCalled, false, "Created callback should not be called when custom element instance has not been created.");
   211   var createdElement = document.createElement("x-resolved");
   212   is(createdCallbackThis, createdElement, "The 'this' value in the created callback should be the custom element.");
   213   is(createdElement.__proto__, p, "Prototype of custom element should be the registered prototype.");
   215   // Insert element into document to trigger attached callback.
   216   container.appendChild(createdElement);
   218   // Remove element from document to trigger detached callback.
   219   container.removeChild(createdElement);
   220 }
   222 // Callbacks should always be the same ones when registered.
   223 function testChangingCallback() {
   224   var p = Object.create(HTMLElement.prototype);
   225   var callbackCalled = false;
   226   p.attributeChangedCallback = function(name, oldValue, newValue) {
   227     is(callbackCalled, false, "Callback should only be called once in this test.");
   228     callbackCalled = true;
   229     runNextTest();
   230   };
   232   document.registerElement("x-test-callback", { prototype: p });
   234   p.attributeChangedCallback = function(name, oldValue, newValue) {
   235     ok(false, "Only callbacks at registration should be called.");
   236   };
   238   var elem = document.createElement("x-test-callback");
   239   elem.setAttribute("foo", "bar");
   240 }
   242 function testAttributeChanged() {
   243   var createdCallbackCalled = false;
   245   var createdElement;
   246   var createdCallbackThis;
   248   var p = Object.create(HTMLElement.prototype);
   249   p.createdCallback = function() {
   250     is(createdCallbackCalled, false, "Created callback should only be called once in this test.");
   251     createdCallbackThis = this;
   252     createdCallbackCalled = true;
   253   };
   255   // Sequence of callback arguments that we expect from attribute changed callback
   256   // after changing attributes values in a specific order.
   257   var expectedCallbackArguments = [
   258     // [oldValue, newValue]
   259     [null, "newvalue"], // Setting the attribute value to "newvalue"
   260     ["newvalue", "nextvalue"], // Changing the attribute value from "newvalue" to "nextvalue"
   261     ["nextvalue", ""], // Changing the attribute value from "nextvalue" to empty string
   262     ["", null], // Removing the attribute.
   263   ];
   265   p.attributeChangedCallback = function(name, oldValue, newValue) {
   266     is(createdCallbackCalled, true, "created callback should be called before attribute changed.");
   267     is(this, createdElement, "The 'this' value should be the custom element.");
   268     ok(expectedCallbackArguments.length > 0, "Attribute changed callback should not be called more than expected.");
   270     is(name, "changeme", "name arugment in attribute changed callback should be the name of the changed attribute.");
   272     var expectedArgs = expectedCallbackArguments.shift();
   273     is(oldValue, expectedArgs[0], "The old value argument should match the expected value.");
   274     is(newValue, expectedArgs[1], "The new value argument should match the expected value.");
   276     if (expectedCallbackArguments.length === 0) {
   277       // Done with the attribute changed callback test.
   278       runNextTest();
   279     }
   280   };
   282   document.registerElement("x-attrchange", { prototype: p });
   284   var createdElement = document.createElement("x-attrchange");
   285   is(createdCallbackThis, createdElement, "The 'this' value in the created callback should be the custom element.");
   286   createdElement.setAttribute("changeme", "newvalue");
   287   createdElement.setAttribute("changeme", "nextvalue");
   288   createdElement.setAttribute("changeme", "");
   289   createdElement.removeAttribute("changeme");
   290 }
   292 function testAttributeChangedExtended() {
   293   var p = Object.create(HTMLButtonElement.prototype);
   294   var callbackCalled = false;
   295   p.attributeChangedCallback = function(name, oldValue, newValue) {
   296     is(callbackCalled, false, "Callback should only be called once in this test.");
   297     callbackCalled = true;
   298     runNextTest();
   299   };
   301   document.registerElement("x-extended-attribute-change", { prototype: p, extends: "button" });
   303   var elem = document.createElement("button", "x-extended-attribute-change");
   304   elem.setAttribute("foo", "bar");
   305 }
   307 // Creates a custom element that is an upgrade candidate (no registration)
   308 // and mutate the element in ways that would call callbacks for registered
   309 // elements.
   310 function testUpgradeCandidate() {
   311   var createdElement = document.createElement("x-upgrade-candidate");
   312   container.appendChild(createdElement);
   313   createdElement.setAttribute("foo", "bar");
   314   container.removeChild(createdElement);
   315   ok(true, "Nothing bad should happen when trying to mutating upgrade candidates.");
   316   runNextTest();
   317 }
   319 function testNotInDocEnterLeave() {
   320   var p = Object.create(HTMLElement.prototype);
   322   p.attached = function() {
   323     ok(false, "attached should not be called when not entering the document.");
   324   };
   326   p.detached = function() {
   327     ok(false, "leaveView should not be called when not leaving the document.");
   328   };
   330   var createdElement = document.createElement("x-destined-for-fragment");
   332   document.registerElement("x-destined-for-fragment", { prototype: p });
   334   var fragment = new DocumentFragment();
   335   fragment.appendChild(createdElement);
   336   fragment.removeChild(createdElement);
   338   var divNotInDoc = document.createElement("div");
   339   divNotInDoc.appendChild(createdElement);
   340   divNotInDoc.removeChild(createdElement);
   342   runNextTest();
   343 }
   345 function testEnterLeaveView() {
   346   var attachedCallbackCalled = false;
   347   var detachedCallbackCalled = false;
   349   var p = Object.create(HTMLElement.prototype);
   350   p.attachedCallback = function() {
   351     is(attachedCallbackCalled, false, "attached callback should only be called on in this test.");
   352     attachedCallbackCalled = true;
   353   };
   355   p.detachedCallback = function() {
   356     is(attachedCallbackCalled, true, "attached callback should be called before detached");
   357     is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
   358     detachedCallbackCalled = true;
   359     runNextTest();
   360   };
   362   var div = document.createElement("div");
   363   document.registerElement("x-element-in-div", { prototype: p });
   364   var customElement = document.createElement("x-element-in-div");
   365   div.appendChild(customElement);
   366   is(attachedCallbackCalled, false, "Appending a custom element to a node that is not in the document should not call the attached callback.");
   368   container.appendChild(div);
   369   container.removeChild(div);
   370 }
   372 var testFunctions = [
   373   testRegisterUnresolved,
   374   testRegisterUnresolvedExtended,
   375   testInnerHTML,
   376   testInnerHTMLExtended,
   377   testInnerHTMLUpgrade,
   378   testInnerHTMLExtendedUpgrade,
   379   testRegisterResolved,
   380   testAttributeChanged,
   381   testAttributeChangedExtended,
   382   testUpgradeCandidate,
   383   testChangingCallback,
   384   testNotInDocEnterLeave,
   385   testEnterLeaveView,
   386   SimpleTest.finish
   387 ];
   389 function runNextTest() {
   390   if (testFunctions.length > 0) {
   391     var nextTestFunction = testFunctions.shift();
   392     nextTestFunction();
   393   }
   394 }
   396 SimpleTest.waitForExplicitFinish();
   398 runNextTest();
   400 </script>
   401 </body>
   402 </html>

mercurial