]> git.donarmstrong.com Git - dactyl.git/blob - common/tests/functional/shared-modules/tabview.js
Initial import of 1.0~b6
[dactyl.git] / common / tests / functional / shared-modules / tabview.js
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is MozMill Test code.
15  *
16  * The Initial Developer of the Original Code is Mozilla Foundation.
17  * Portions created by the Initial Developer are Copyright (C) 2010
18  * the Initial Developer. All Rights Reserved.
19  *
20  * Contributor(s):
21  *   Henrik Skupin <hskupin@mozilla.com>
22  *
23  * Alternatively, the contents of this file may be used under the terms of
24  * either the GNU General Public License Version 2 or later (the "GPL"), or
25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26  * in which case the provisions of the GPL or the LGPL are applicable instead
27  * of those above. If you wish to allow use of your version of this file only
28  * under the terms of either the GPL or the LGPL, and not to allow others to
29  * use your version of this file under the terms of the MPL, indicate your
30  * decision by deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL or the LGPL. If you do not delete
32  * the provisions above, a recipient may use your version of this file under
33  * the terms of any one of the MPL, the GPL or the LGPL.
34  *
35  * ***** END LICENSE BLOCK ***** */
36
37 // Include required modules
38 var domUtils = require("dom-utils");
39 var tabs = require("tabs");
40 var utils = require("utils");
41
42 const TIMEOUT = 5000;
43
44 /**
45  * Constructor
46  */
47 function tabView(aController) {
48   this._controller = aController;
49   this._tabView = null;
50   this._tabViewDoc = this._controller.window.document;
51   this._tabViewObject = this._controller.window.TabView;
52 }
53
54 /**
55  * Tab View class
56  */
57 tabView.prototype = {
58
59   ///////////////////////////////
60   // Global section
61   ///////////////////////////////
62
63   /**
64    * Returns the MozMill controller
65    *
66    * @returns Mozmill controller
67    * @type {MozMillController}
68    */
69   get controller() {
70     return this._controller;
71   },
72
73   /**
74    * Check if the Tab View is open
75    *
76    * @returns True if the Tab View is open
77    * @type {boolean}
78    */
79   get isOpen() {
80     var deck = this.getElement({type: "deck"});
81     return deck.getNode().getAttribute("selectedIndex") == "1";
82   },
83
84   /**
85    * Open the Tab View
86    */
87   open : function tabView_open() {
88     var menuitem = new elementslib.Elem(this._controller.menus['view-menu'].menu_tabview);
89     this._controller.click(menuitem);
90     this.waitForOpened();
91
92     this._tabView = this.getElement({type: "tabView"});
93     this._tabViewDoc = this._tabView.getNode().webNavigation.document;
94   },
95
96   /**
97    * Reset the Tab View settings for the current window
98    */
99   reset : function tabView_reset() {
100     // Make sure to close TabView before resetting its ui
101     if (this.isOpen) {
102       this.close();
103     }
104
105     var self = this;
106     this._tabViewObject._initFrame(function () {
107       var contentWindow = self._tabViewObject._window;
108       contentWindow.UI.reset();
109     });
110
111     // Make sure all tabs will be shown
112     Array.forEach(this._controller.window.gBrowser.tabs, function (tab) {
113       this._controller.window.gBrowser.showTab(tab);
114     }, this);
115   },
116
117   /**
118    * Wait until the Tab View has been opened
119    */
120   waitForOpened : function tabView_waitForOpened() {
121     // Add event listener to wait until the tabview has been opened
122     var self = { opened: false };
123     function checkOpened() { self.opened = true; }
124     this._controller.window.addEventListener("tabviewshown", checkOpened, false);
125
126     try {
127       mozmill.utils.waitFor(function () {
128         return self.opened == true;
129       }, "TabView is not open.");
130
131       this._groupItemsObject = this._tabViewObject._window.GroupItems;
132       this._tabItemsObject = this._tabViewObject._window.TabItems;
133     }
134     finally {
135       this._controller.window.removeEventListener("tabviewshown", checkOpened, false);
136     }
137   },
138
139   /**
140    * Close the Tab View
141    */
142   close : function tabView_close() {
143     var menuitem = new elementslib.Elem(this._controller.menus['view-menu'].menu_tabview);
144     this._controller.click(menuitem);
145     this.waitForClosed();
146
147     this._tabView = null;
148     this._tabViewDoc = this._controller.window.document;
149   },
150
151   /**
152    * Wait until the Tab View has been closed
153    */
154   waitForClosed : function tabView_waitForClosed() {
155     // Add event listener to wait until the tabview has been closed
156     var self = { closed: false };
157     function checkClosed() { self.closed = true; }
158     this._controller.window.addEventListener("tabviewhidden", checkClosed, false);
159
160     try {
161       mozmill.utils.waitFor(function () {
162         return self.closed == true;
163       }, "TabView is still open.");
164     } finally {
165       this._controller.window.removeEventListener("tabviewhidden", checkClosed, false);
166     }
167
168     this._groupItemsObject = null;
169     this._tabItemsObject = null;
170   },
171
172
173   ///////////////////////////////
174   // Groups section
175   ///////////////////////////////
176
177   /**
178    * Returns the tab groups which match the filter criteria
179    *
180    * @param {object} aSpec
181    *        Information about the filter to apply
182    *        Elements: filter - Type of filter to apply
183    *                           (active, title)
184    *                           [optional - default: ""]
185    *                  value  - Value of the element
186    *                           [optional - default: ""]
187    *
188    * @returns List of groups
189    * @type {array of ElemBase}
190    */
191   getGroups : function tabView_getGroups(aSpec) {
192     var spec = aSpec || {};
193
194     return this.getElements({
195       type: "groups",
196       subtype: spec.filter,
197       value: spec.value
198     });
199   },
200
201   /**
202    * Retrieve the group's title box
203    *
204    * @param {object} aSpec
205    *        Information on which group to operate on
206    *        Elements: group - Group element
207    *
208    * @returns Group title box
209    * @type {ElemBase}
210    */
211   getGroupTitleBox : function tabView_getGroupTitleBox(aSpec) {
212     var spec = aSpec || {};
213     var group = spec.group;
214
215     if (!group) {
216       throw new Error(arguments.callee.name + ": Group not specified.");
217     }
218
219     return this.getElement({
220       type: "group_titleBox",
221       parent: group
222     });
223   },
224
225   /**
226    * Close the specified tab group
227    *
228    * @param {object} aSpec
229    *        Information on which group to operate on
230    *        Elements: group - Group
231    */
232   closeGroup : function tabView_closeGroup(aSpec) {
233     var spec = aSpec || {};
234     var group = spec.group;
235
236     if (!group) {
237       throw new Error(arguments.callee.name + ": Group not specified.");
238     }
239
240     var button = this.getElement({
241       type: "group_closeButton",
242       parent: group
243     });
244     this._controller.click(button);
245
246     this.waitForGroupClosed({group: group});
247   },
248
249   /**
250    * Wait until the specified tab group has been closed
251    *
252    * @param {object} aSpec
253    *        Information on which group to operate on
254    *        Elements: group - Group
255    */
256   waitForGroupClosed : function tabView_waitForGroupClosed(aSpec) {
257     var spec = aSpec || {};
258     var group = spec.group;
259     var groupObj = null;
260
261     var self = { closed: false };
262     function checkClosed() { self.closed = true; }
263
264     if (!group) {
265       throw new Error(arguments.callee.name + ": Group not specified.");
266     }
267
268     this._groupItemsObject.groupItems.forEach(function (node) {
269       if (node.container == group.getNode()) {
270         groupObj = node;
271       }
272     });
273
274     if (!groupObj) {
275       throw new Error(arguments.callee.name + ": Group not found.");
276     }
277
278     try {
279       groupObj.addSubscriber(groupObj, "groupHidden", checkClosed);
280       mozmill.utils.waitFor(function () {
281         return self.closed;
282       }, "Tab Group has not been closed.");
283     }
284     finally {
285       groupObj.removeSubscriber(groupObj, "groupHidden");
286     }
287   },
288
289   /**
290    * Undo the closing of the specified tab group
291    *
292    * @param {object} aSpec
293    *        Information on which group to operate on
294    *        Elements: group - Group
295    */
296   undoCloseGroup : function tabView_undoCloseGroup(aSpec) {
297     var spec = aSpec || {};
298     var group = spec.group;
299
300     if (!group) {
301       throw new Error(arguments.callee.name + ": Group not specified.");
302     }
303
304     var undo = this.getElement({
305       type: "group_undoButton",
306       parent: group
307     });
308     this._controller.waitThenClick(undo);
309
310     this.waitForGroupUndo({group: group});
311   },
312
313   /**
314    * Wait until the specified tab group has been reopened
315    *
316    * @param {object} aSpec
317    *        Information on which group to operate on
318    *        Elements: group - Group
319    */
320   waitForGroupUndo : function tabView_waitForGroupUndo(aSpec) {
321     var spec = aSpec || {};
322     var group = spec.group;
323     var groupObj = null;
324
325     var self = { reopened: false };
326     function checkClosed() { self.reopened = true; }
327
328     if (!group) {
329       throw new Error(arguments.callee.name + ": Group not specified.");
330     }
331
332     var groupObj = null;
333     this._groupItemsObject.groupItems.forEach(function(node) {
334       if (node.container == group.getNode()) {
335         groupObj = node;
336       }
337     });
338
339     if (!groupObj) {
340       throw new Error(arguments.callee.name + ": Group not found.");
341     }
342
343     try {
344       groupObj.addSubscriber(groupObj, "groupShown", checkClosed);
345       mozmill.utils.waitFor(function () {
346         return self.reopened;
347       }, "Tab Group has not been reopened.");
348     }
349     finally {
350       groupObj.removeSubscriber(groupObj, "groupShown");
351     }
352   },
353
354
355   ///////////////////////////////
356   // Tabs section
357   ///////////////////////////////
358
359   /**
360    * Returns the tabs which match the filter criteria
361    *
362    * @param {object} aSpec
363    *        Information about the filter to apply
364    *        Elements: filter - Type of filter to apply
365    *                           (active, title)
366    *                           [optional - default: ""]
367    *                  value  - Value of the element
368    *                           [optional - default: ""]
369    *
370    * @returns List of tabs
371    * @type {array of ElemBase}
372    */
373   getTabs : function tabView_getTabs(aSpec) {
374     var spec = aSpec || {};
375
376     return this.getElements({
377       type: "tabs",
378       subtype: spec.filter,
379       value: spec.value
380     });
381   },
382
383   /**
384    * Close a tab
385    *
386    * @param {object} aSpec
387    *        Information about the element to operate on
388    *        Elements: tab - Tab to close
389    */
390   closeTab : function tabView_closeTab(aSpec) {
391     var spec = aSpec || {};
392     var tab = spec.tab;
393
394     if (!tab) {
395       throw new Error(arguments.callee.name + ": Tab not specified.");
396     }
397
398     var button = this.getElement({
399       type: "tab_closeButton",
400       value: tab}
401     );
402     this._controller.click(button);
403   },
404
405   /**
406    * Retrieve the tab's title box
407    *
408    * @param {object} aSpec
409    *        Information on which tab to operate on
410    *        Elements: tab - Tab
411    *
412    * @returns Tab title box
413    * @type {ElemBase}
414    */
415   getTabTitleBox : function tabView_getTabTitleBox(aSpec) {
416     var spec = aSpec || {};
417     var tab = spec.tab;
418
419     if (!tab) {
420       throw new Error(arguments.callee.name + ": Tab not specified.");
421     }
422
423     return this.getElement({
424       type: "tab_titleBox",
425       parent: spec.tab
426     });
427   },
428
429   /**
430    * Open a new tab in the specified group
431    *
432    * @param {object} aSpec
433    *        Information about the element to operate on
434    *        Elements: group - Group to create a new tab in
435    */
436   openTab : function tabView_openTab(aSpec) {
437     var spec = aSpec || {};
438     var group = spec.group;
439
440     if (!group) {
441       throw new Error(arguments.callee.name + ": Group not specified.");
442     }
443
444     var button = this.getElement({
445       type: "group_newTabButton",
446       parent: group
447     });
448
449     this._controller.click(button);
450     this.waitForClosed();
451   },
452
453
454   ///////////////////////////////
455   // UI Elements section
456   ///////////////////////////////
457
458   /**
459    * Retrieve an UI element based on the given specification
460    *
461    * @param {object} aSpec
462    *        Information of the UI elements which should be retrieved
463    *        Elements: type     - Identifier of the element
464    *                  subtype  - Attribute of the element to filter
465    *                             [optional - default: ""]
466    *                  value    - Value of the attribute to filter
467    *                             [optional - default: ""]
468    *                  parent   - Parent of the to find element
469    *                             [optional - default: document]
470    *
471    * @returns Element which has been found
472    * @type {ElemBase}
473    */
474   getElement : function tabView_getElement(aSpec) {
475     var elements = this.getElements(aSpec);
476
477     return (elements.length > 0) ? elements[0] : undefined;
478   },
479
480   /**
481    * Retrieve list of UI elements based on the given specification
482    *
483    * @param {object} aSpec
484    *        Information of the UI elements which should be retrieved
485    *        Elements: type     - Identifier of the element
486    *                  subtype  - Attribute of the element to filter
487    *                             [optional - default: ""]
488    *                  value    - Value of the attribute to filter
489    *                             [optional - default: ""]
490    *                  parent   - Parent of the to find element
491    *                             [optional - default: document]
492    *
493    * @returns Elements which have been found
494    * @type {array of ElemBase}
495    */
496   getElements : function tabView_getElement(aSpec) {
497     var spec = aSpec || { };
498     var type = spec.type;
499     var subtype = spec.subtype;
500     var value = spec.value;
501     var parent = spec.parent;
502
503     var root = parent ? parent.getNode() : this._tabViewDoc;
504     var nodeCollector = new domUtils.nodeCollector(root);
505
506     switch(type) {
507       // Top level elements
508       case "tabView":
509         nodeCollector.root = this._controller.window.document;
510         nodeCollector.queryNodes("#tab-view");
511         break;
512       case "contentArea":
513         nodeCollector.queryNodes("#content");
514         break;
515       case "deck":
516         nodeCollector.root = this._controller.window.document;
517         nodeCollector.queryNodes("#tab-view-deck");
518         break;
519       case "exitButton":
520         nodeCollector.queryNodes("#exit-button");
521         break;
522
523       // Group elements
524       case "group_appTabs":
525         nodeCollector.queryNodes(".appTabIcon");
526         break;
527       case "group_closeButton":
528         nodeCollector.queryNodes(".close");
529         break;
530       case "group_newTabButton":
531         nodeCollector.queryNodes(".newTabButton");
532         break;
533       case "group_resizer":
534         nodeCollector.queryNodes(".iq-resizable-handle");
535         break;
536       case "group_stackExpander":
537         nodeCollector.queryNodes(".stackExpander");
538         break;
539       case "group_titleBox":
540         nodeCollector.queryNodes(".name");
541         break;
542       case "group_undoButton":
543         // Bug 596504 - No reference to the undo button
544         nodeCollector.root = this._tabViewDoc;
545         nodeCollector.queryNodes(".undo").filter(function (node) {
546           var groups = this._groupItemsObject.groupItems;
547           for (var i = 0; i < groups.length; i++) {
548             var group = groups[i];
549             if (group.container == parent.getNode() &&
550                 group.$undoContainer.length == 1) {
551               return true;
552             }
553           }
554           return false;
555         }, this);
556         break;
557       case "groups":
558         nodeCollector.queryNodes(".groupItem").filter(function (node) {
559           switch(subtype) {
560             case "active":
561               return node.className.indexOf("activeGroup") != -1;
562             case "title":
563               // If no title is given the default name is used
564               if (!value) {
565                 value = utils.getProperty("chrome://browser/locale/tabview.properties",
566                                           "tabview.groupItem.defaultName");
567               }
568               var title = node.querySelector(".name");
569               return (value == title.value);
570             default:
571               return true;
572           }
573         }, this);
574         break;
575
576       // Search elements
577       case "search_box":
578         nodeCollector.queryNodes("#searchbox");
579         break;
580       case "search_button":
581         nodeCollector.queryNodes("#searchbutton");
582         break;
583
584       // Tab elements
585       case "tab_closeButton":
586         nodeCollector.queryNodes(".tab .close");
587         break;
588       case "tab_favicon":
589         nodeCollector.queryNodes(".tab .favicon");
590         break;
591       case "tab_titleBox":
592         nodeCollector.queryNodes(".tab .tab-title");
593         break;
594       case "tabs":
595         nodeCollector.queryNodes(".tab").filter(function (node) {
596           switch (subtype) {
597             case "active":
598               return (node.className.indexOf("focus") != -1);
599             case "group":
600               var group = value ? value.getNode() : null;
601               if (group) {
602                 var tabs = this._tabItemsObject.getItems();
603                 for (var i = 0; i < tabs.length; i++) {
604                   var tab = tabs[i];
605                   if (tab.parent && tab.parent.container == group) {
606                     return true;
607                   }
608                 }
609                 return false;
610               }
611               else {
612                 return (node.className.indexOf("tabInGroupItem") == -1);
613               }
614             default:
615               return true;
616           }
617         }, this);
618         break;
619       default:
620         throw new Error(arguments.callee.name + ": Unknown element type - " +
621                         aSpec.type);
622     }
623
624     return nodeCollector.elements;
625   }
626 }
627
628 // Export of classes
629 exports.tabView = tabView;