accessible/tests/mochitest/events/test_mutation.html

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 <html>
     3 <head>
     4   <title>Accessible mutation events testing</title>
     6   <link rel="stylesheet" type="text/css"
     7         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
     9   <style>
    10     div.displayNone a { display:none; }
    11     div.visibilityHidden a { visibility:hidden; }
    12 </style>
    14   <script type="application/javascript"
    15           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
    16   <script type="application/javascript"
    17           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
    19   <script type="application/javascript"
    20           src="../common.js"></script>
    21   <script type="application/javascript"
    22           src="../events.js"></script>
    24   <script type="application/javascript">
    25     /**
    26      * Invokers.
    27      */
    28     var kNoEvents = 0;
    30     var kShowEvent = 1;
    31     var kHideEvent = 2;
    32     var kReorderEvent = 4;
    33     var kShowEvents = kShowEvent | kReorderEvent;
    34     var kHideEvents = kHideEvent | kReorderEvent;
    35     var kHideAndShowEvents = kHideEvents | kShowEvent;
    37     /**
    38      * Base class to test mutation a11y events.
    39      *
    40      * @param aNodeOrID          [in] node invoker's action is executed for
    41      * @param aEventTypes        [in] events to register (see constants above)
    42      * @param aDoNotExpectEvents [in] boolean indicates if events are expected
    43      */
    44     function mutateA11yTree(aNodeOrID, aEventTypes, aDoNotExpectEvents)
    45     {
    46       // Interface
    47       this.DOMNode = getNode(aNodeOrID);
    48       this.doNotExpectEvents = aDoNotExpectEvents;
    49       this.eventSeq = [];
    50       this.unexpectedEventSeq = [];
    52       /**
    53        * Change default target (aNodeOrID) registered for the given event type.
    54        */
    55       this.setTarget = function mutateA11yTree_setTarget(aEventType, aTarget)
    56       {
    57         var type = this.getA11yEventType(aEventType);
    58         for (var idx = 0; idx < this.getEventSeq().length; idx++) {
    59           if (this.getEventSeq()[idx].type == type) {
    60             this.getEventSeq()[idx].target = aTarget;
    61             return idx;
    62           }
    63         }
    64         return -1;
    65       }
    67       /**
    68        * Replace the default target currently registered for a given event type
    69        * with the nodes in the targets array.
    70        */
    71       this.setTargets = function mutateA11yTree_setTargets(aEventType, aTargets) {
    72         var targetIdx = this.setTarget(aEventType, aTargets[0]);
    74         var type = this.getA11yEventType(aEventType);
    75         for (var i = 1; i < aTargets.length; i++) {
    76           var checker = new invokerChecker(type, aTargets[i]);
    77           this.getEventSeq().splice(++targetIdx, 0, checker);
    78         }
    79       }
    81       // Implementation
    82       this.getA11yEventType = function mutateA11yTree_getA11yEventType(aEventType)
    83       {
    84         if (aEventType == kReorderEvent)
    85           return nsIAccessibleEvent.EVENT_REORDER;
    87         if (aEventType == kHideEvent)
    88           return nsIAccessibleEvent.EVENT_HIDE;
    90         if (aEventType == kShowEvent)
    91           return nsIAccessibleEvent.EVENT_SHOW;
    92       }
    94       this.getEventSeq = function mutateA11yTree_getEventSeq()
    95       {
    96         return this.doNotExpectEvents ? this.unexpectedEventSeq : this.eventSeq;
    97       }
    99       if (aEventTypes & kHideEvent) {
   100         var checker = new invokerChecker(this.getA11yEventType(kHideEvent),
   101                                          this.DOMNode);
   102         this.getEventSeq().push(checker);
   103       }
   105       if (aEventTypes & kShowEvent) {
   106         var checker = new invokerChecker(this.getA11yEventType(kShowEvent),
   107                                          this.DOMNode);
   108         this.getEventSeq().push(checker);
   109       }
   111       if (aEventTypes & kReorderEvent) {
   112         var checker = new invokerChecker(this.getA11yEventType(kReorderEvent),
   113                                          this.DOMNode.parentNode);
   114         this.getEventSeq().push(checker);
   115       }
   116     }
   118     /**
   119      * Change CSS style for the given node.
   120      */
   121     function changeStyle(aNodeOrID, aProp, aValue, aEventTypes)
   122     {
   123       this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false);
   125       this.invoke = function changeStyle_invoke()
   126       {
   127         this.DOMNode.style[aProp] = aValue;
   128       }
   130       this.getID = function changeStyle_getID()
   131       {
   132         return aNodeOrID + " change style " + aProp + " on value " + aValue;
   133       }
   134     }
   136     /**
   137      * Change class name for the given node.
   138      */
   139     function changeClass(aParentNodeOrID, aNodeOrID, aClassName, aEventTypes)
   140     {
   141       this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false);
   143       this.invoke = function changeClass_invoke()
   144       {
   145         this.parentDOMNode.className = aClassName;
   146       }
   148       this.getID = function changeClass_getID()
   149       {
   150         return aNodeOrID + " change class " + aClassName;
   151       }
   153       this.parentDOMNode = getNode(aParentNodeOrID);
   154     }
   156     /**
   157      * Clone the node and append it to its parent.
   158      */
   159     function cloneAndAppendToDOM(aNodeOrID, aEventTypes,
   160                                  aTargetsFunc, aReorderTargetFunc)
   161     {
   162       var eventTypes = aEventTypes || kShowEvents;
   163       var doNotExpectEvents = (aEventTypes == kNoEvents);
   165       this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes,
   166                                           doNotExpectEvents);
   168       this.invoke = function cloneAndAppendToDOM_invoke()
   169       {
   170         var newElm = this.DOMNode.cloneNode(true);
   171         newElm.removeAttribute('id');
   173         var targets = aTargetsFunc ?
   174           aTargetsFunc.call(null, newElm) : [newElm];
   175         this.setTargets(kShowEvent, targets);
   177         if (aReorderTargetFunc) {
   178           var reorderTarget = aReorderTargetFunc.call(null, this.DOMNode);
   179           this.setTarget(kReorderEvent, reorderTarget);
   180         }
   182         this.DOMNode.parentNode.appendChild(newElm);
   183       }
   185       this.getID = function cloneAndAppendToDOM_getID()
   186       {
   187         return aNodeOrID + " clone and append to DOM.";
   188       }
   189     }
   191     /**
   192      * Removes the node from DOM.
   193      */
   194     function removeFromDOM(aNodeOrID, aEventTypes,
   195                            aTargetsFunc, aReorderTargetFunc)
   196     {
   197       var eventTypes = aEventTypes || kHideEvents;
   198       var doNotExpectEvents = (aEventTypes == kNoEvents);
   200       this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes,
   201                                           doNotExpectEvents);
   203       this.invoke = function removeFromDOM_invoke()
   204       {
   205         this.DOMNode.parentNode.removeChild(this.DOMNode);
   206       }
   208       this.getID = function removeFromDOM_getID()
   209       {
   210         return prettyName(aNodeOrID) + " remove from DOM.";
   211       }
   213       if (aTargetsFunc && (eventTypes & kHideEvent))
   214         this.setTargets(kHideEvent, aTargetsFunc.call(null, this.DOMNode));
   216       if (aReorderTargetFunc && (eventTypes & kReorderEvent))
   217         this.setTarget(kReorderEvent,
   218                        aReorderTargetFunc.call(null, this.DOMNode));
   219     }
   221     /**
   222      * Clone the node and replace the original node by cloned one.
   223      */
   224     function cloneAndReplaceInDOM(aNodeOrID)
   225     {
   226       this.__proto__ = new mutateA11yTree(aNodeOrID, kHideAndShowEvents,
   227                                           false);
   229       this.invoke = function cloneAndReplaceInDOM_invoke()
   230       {
   231         this.DOMNode.parentNode.replaceChild(this.newElm, this.DOMNode);
   232       }
   234       this.getID = function cloneAndReplaceInDOM_getID()
   235       {
   236         return aNodeOrID + " clone and replace in DOM.";
   237       }
   239       this.newElm = this.DOMNode.cloneNode(true);
   240       this.newElm.removeAttribute('id');
   241       this.setTarget(kShowEvent, this.newElm);
   242     }
   244     /**
   245      * Trigger content insertion (flush layout), removal and insertion of
   246      * the same element for the same parent.
   247      */
   248     function test1(aContainerID)
   249     {
   250       this.divNode = document.createElement("div");
   251       this.divNode.setAttribute("id", "div-test1");
   252       this.containerNode = getNode(aContainerID);
   254       this.eventSeq = [
   255         new invokerChecker(EVENT_SHOW, this.divNode),
   256         new invokerChecker(EVENT_REORDER, this.containerNode)
   257       ];
   259       this.invoke = function test1_invoke()
   260       {
   261         this.containerNode.appendChild(this.divNode);
   262         getComputedStyle(this.divNode, "").color;
   263         this.containerNode.removeChild(this.divNode);
   264         this.containerNode.appendChild(this.divNode);
   265       }
   267       this.getID = function test1_getID()
   268       {
   269         return "fuzzy test #1: content insertion (flush layout), removal and" +
   270           "reinsertion";
   271       }
   272     }
   274     /**
   275      * Trigger content insertion (flush layout), removal and insertion of
   276      * the same element for the different parents.
   277      */
   278     function test2(aContainerID, aTmpContainerID)
   279     {
   280       this.divNode = document.createElement("div");
   281       this.divNode.setAttribute("id", "div-test2");
   282       this.containerNode = getNode(aContainerID);
   283       this.tmpContainerNode = getNode(aTmpContainerID);
   284       this.container = getAccessible(this.containerNode);
   285       this.tmpContainer = getAccessible(this.tmpContainerNode);
   287       this.eventSeq = [
   288         new invokerChecker(EVENT_SHOW, this.divNode),
   289         new invokerChecker(EVENT_REORDER, this.containerNode)
   290       ];
   292       this.unexpectedEventSeq = [
   293         new invokerChecker(EVENT_REORDER, this.tmpContainerNode)
   294       ];
   296       this.invoke = function test2_invoke()
   297       {
   298         this.tmpContainerNode.appendChild(this.divNode);
   299         getComputedStyle(this.divNode, "").color;
   300         this.tmpContainerNode.removeChild(this.divNode);
   301         this.containerNode.appendChild(this.divNode);
   302       }
   304       this.getID = function test2_getID()
   305       {
   306         return "fuzzy test #2: content insertion (flush layout), removal and" +
   307           "reinsertion under another container";
   308       }
   309     }
   311     /**
   312      * Content insertion (flush layout) and then removal (nothing was changed).
   313      */
   314     function test3(aContainerID)
   315     {
   316       this.divNode = document.createElement("div");
   317       this.divNode.setAttribute("id", "div-test3");
   318       this.containerNode = getNode(aContainerID);
   320       this.unexpectedEventSeq = [
   321         new invokerChecker(EVENT_SHOW, this.divNode),
   322         new invokerChecker(EVENT_HIDE, this.divNode),
   323         new invokerChecker(EVENT_REORDER, this.containerNode)
   324       ];
   326       this.invoke = function test3_invoke()
   327       {
   328         this.containerNode.appendChild(this.divNode);
   329         getComputedStyle(this.divNode, "").color;
   330         this.containerNode.removeChild(this.divNode);
   331       }
   333       this.getID = function test3_getID()
   334       {
   335         return "fuzzy test #3: content insertion (flush layout) and removal";
   336       }
   337     }
   339     /**
   340      * Target getters.
   341      */
   342     function getFirstChild(aNode)
   343     {
   344       return [aNode.firstChild];
   345     }
   347     function getNEnsureFirstChild(aNode)
   348     {
   349       var node = aNode.firstChild;
   350       getAccessible(node);
   351       return [node];
   352     }
   354     function getNEnsureChildren(aNode)
   355     {
   356       var children = [];
   357       var node = aNode.firstChild;
   358       do {
   359         children.push(node);
   360         getAccessible(node);
   361         node = node.nextSibling;
   362       } while (node);
   364       return children;
   365     }
   367     function getParent(aNode)
   368     {
   369       return aNode.parentNode;
   370     }
   372     /**
   373      * Do tests.
   374      */
   375     var gQueue = null;
   376     //gA11yEventDumpToConsole = true; // debug stuff
   378     function doTests()
   379     {
   380       gQueue = new eventQueue();
   382       // Show/hide events by changing of display style of accessible DOM node
   383       // from 'inline' to 'none', 'none' to 'inline'.
   384       var id = "link1";
   385       getAccessible(id); // ensure accessible is created
   386       gQueue.push(new changeStyle(id, "display", "none", kHideEvents));
   387       gQueue.push(new changeStyle(id, "display", "inline", kShowEvents));
   389       // Show/hide events by changing of visibility style of accessible DOM node
   390       // from 'visible' to 'hidden', 'hidden' to 'visible'.
   391       var id = "link2";
   392       getAccessible(id);
   393       gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents));
   394       gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
   396       // Show/hide events by changing of display style of accessible DOM node
   397       // from 'inline' to 'block', 'block' to 'inline'.
   398       var id = "link3";
   399       getAccessible(id); // ensure accessible is created
   400       gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents));
   401       gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents));
   403       // Show/hide events by changing of visibility style of accessible DOM node
   404       // from 'collapse' to 'visible', 'visible' to 'collapse'.
   405       var id = "link4";
   406       gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
   407       gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents));
   409       // Show/hide events by adding new accessible DOM node and removing old one.
   410       var id = "link5";
   411       gQueue.push(new cloneAndAppendToDOM(id));
   412       gQueue.push(new removeFromDOM(id));
   414       // No show/hide events by adding new not accessible DOM node and removing
   415       // old one, no reorder event for their parent.
   416       var id = "child1";
   417       gQueue.push(new cloneAndAppendToDOM(id, kNoEvents));
   418       gQueue.push(new removeFromDOM(id, kNoEvents));
   420       // Show/hide events by adding new accessible DOM node and removing
   421       // old one, there is reorder event for their parent.
   422       var id = "child2";
   423       gQueue.push(new cloneAndAppendToDOM(id));
   424       gQueue.push(new removeFromDOM(id));
   426       // Show/hide events by adding new DOM node containing accessible DOM and
   427       // removing old one, there is reorder event for their parent.
   428       var id = "child3";
   429       gQueue.push(new cloneAndAppendToDOM(id, kShowEvents, getFirstChild,
   430                                           getParent));
   432       // Hide event for accessible child of unaccessible removed DOM node and
   433       // reorder event for its parent.
   434       gQueue.push(new removeFromDOM(id, kHideEvents,
   435                                     getNEnsureFirstChild, getParent));
   437       // Hide events for accessible children of unaccessible removed DOM node
   438       // and reorder event for its parent.
   439       gQueue.push(new removeFromDOM("child4", kHideEvents,
   440                                     getNEnsureChildren, getParent));
   442       // Show/hide events by creating new accessible DOM node and replacing
   443       // old one.
   444       getAccessible("link6"); // ensure accessible is created
   445       gQueue.push(new cloneAndReplaceInDOM("link6"));
   447       // Show/hide events by changing class name on the parent node.
   448       gQueue.push(new changeClass("container2", "link7", "", kShowEvents));
   449       gQueue.push(new changeClass("container2", "link7", "displayNone",
   450                                   kHideEvents));
   452       gQueue.push(new changeClass("container3", "link8", "", kShowEvents));
   453       gQueue.push(new changeClass("container3", "link8", "visibilityHidden",
   454                                   kHideEvents));
   456       gQueue.push(new test1("testContainer"));
   457       gQueue.push(new test2("testContainer", "testContainer2"));
   458       gQueue.push(new test2("testContainer", "testNestedContainer"));
   459       gQueue.push(new test3("testContainer"));
   461       gQueue.invoke(); // Will call SimpleTest.finish();
   462     }
   464     SimpleTest.waitForExplicitFinish();
   465     addA11yLoadEvent(doTests);
   466   </script>
   467 </head>
   469 <body>
   471   <a target="_blank"
   472      href="https://bugzilla.mozilla.org/show_bug.cgi?id=469985"
   473      title=" turn the test from bug 354745 into mochitest">
   474     Mozilla Bug 469985</a>
   475   <a target="_blank"
   476      href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662"
   477      title="no reorder event when html:link display property is changed from 'none' to 'inline'">
   478     Mozilla Bug 472662</a>
   479   <a target="_blank"
   480      title="Rework accessible tree update code"
   481      href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
   482     Mozilla Bug 570275</a>
   483   <a target="_blank"
   484      title="Develop a way to handle visibility style"
   485      href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125">
   486     Mozilla Bug 606125</a>
   487   <a target="_blank"
   488      title="Update accessible tree on content insertion after layout"
   489      href="https://bugzilla.mozilla.org/show_bug.cgi?id=498015">
   490     Mozilla Bug 498015</a>
   492   <p id="display"></p>
   493   <div id="content" style="display: none"></div>
   494   <pre id="test">
   495   </pre>
   496   <div id="eventdump"></div>
   498   <div id="testContainer">
   499     <a id="link1" href="http://www.google.com">Link #1</a>
   500     <a id="link2" href="http://www.google.com">Link #2</a>
   501     <a id="link3" href="http://www.google.com">Link #3</a>
   502     <a id="link4" href="http://www.google.com" style="visibility:collapse">Link #4</a>
   503     <a id="link5" href="http://www.google.com">Link #5</a>
   505     <div id="container" role="list">
   506       <span id="child1"></span>
   507       <span id="child2" role="listitem"></span>
   508       <span id="child3"><span role="listitem"></span></span>
   509       <span id="child4"><span id="child4_1" role="listitem"></span><span id="child4_2" role="listitem"></span></span>
   510     </div>
   512     <a id="link6" href="http://www.google.com">Link #6</a>
   514     <div id="container2" class="displayNone"><a id="link7">Link #7</a></div>
   515     <div id="container3" class="visibilityHidden"><a id="link8">Link #8</a></div>
   516     <div id="testNestedContainer"></div>
   517   </div>
   518   <div id="testContainer2"></div>
   519 </body>
   520 </html>

mercurial