summaryrefslogtreecommitdiff
path: root/toolkit/content/tests/chrome/xul_selectcontrol.js
blob: d6518c15039efd1aca41bee609b7f043fe8dcff4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
// This script is used to test elements that implement
// nsIDOMXULSelectControlElement. This currently is the following elements:
//   listbox, menulist, radiogroup, richlistbox, tabs
//
// flag behaviours that differ for certain elements
//   allow-other-value - alternate values for the value property may be used
//                       besides those in the list
//   other-value-clears-selection - alternative values for the value property
//                                  clears the selected item
//   selection-required - an item must be selected in the list, unless there
//                        aren't any to select
//   activate-disabled-menuitem - disabled menuitems can be highlighted
//   select-keynav-wraps - key navigation over a selectable list wraps
//   select-extended-keynav - home, end, page up and page down keys work to
//                            navigate over a selectable list
//   keynav-leftright - key navigation is left/right rather than up/down
// The win:, mac: and gtk: or other prefixes may be used for platform specific behaviour
var behaviours = {
  menu: "win:activate-disabled-menuitem activate-disabled-menuitem-mousemove select-keynav-wraps select-extended-keynav",
  menulist: "allow-other-value other-value-clears-selection",
  listbox: "select-extended-keynav",
  richlistbox: "select-extended-keynav",
  radiogroup: "select-keynav-wraps dont-select-disabled allow-other-value",
  tabs: "select-extended-keynav mac:select-keynav-wraps allow-other-value selection-required keynav-leftright"
};

function behaviourContains(tag, behaviour)
{
  var platform = "none:";
  if (navigator.platform.indexOf("Mac") >= 0)
    platform = "mac:";
  else if (navigator.platform.indexOf("Win") >= 0)
    platform = "win:";
  else if (navigator.platform.indexOf("X") >= 0)
    platform = "gtk:";

  var re = new RegExp("\\s" + platform + behaviour + "\\s|\\s" + behaviour + "\\s");
  return re.test(" " + behaviours[tag] + " ");
}

function test_nsIDOMXULSelectControlElement(element, childtag, testprefix)
{
  var testid = (testprefix) ? testprefix + " " : "";
  testid += element.localName + " nsIDOMXULSelectControlElement ";

  // editable menulists use the label as the value instead
  var firstvalue = "first", secondvalue = "second", fourthvalue = "fourth";
  if (element.localName == "menulist" && element.editable) {
    firstvalue = "First Item";
    secondvalue = "Second Item"
    fourthvalue = "Fourth Item";
  }

  // 'initial' - check if the initial state of the element is correct
  test_nsIDOMXULSelectControlElement_States(element, testid + "initial", 0, null, -1, "");

  test_nsIDOMXULSelectControlElement_init(element, testid);

  // 'appendItem' - check if appendItem works to add a new item
  var firstitem = element.appendItem("First Item", "first");
  is(firstitem.localName, childtag,
                testid + "appendItem - first item is " + childtag);
  test_nsIDOMXULSelectControlElement_States(element, testid + "appendItem", 1, null, -1, "");

  is(firstitem.control, element, testid + "control");

  // 'selectedIndex' - check if an item may be selected
  element.selectedIndex = 0;
  test_nsIDOMXULSelectControlElement_States(element, testid + "selectedIndex", 1, firstitem, 0, firstvalue);

  // 'appendItem 2' - check if a second item may be added
  var seconditem = element.appendItem("Second Item", "second");
  test_nsIDOMXULSelectControlElement_States(element, testid + "appendItem 2", 2, firstitem, 0, firstvalue);

  // 'selectedItem' - check if the second item may be selected
  element.selectedItem = seconditem;
  test_nsIDOMXULSelectControlElement_States(element, testid + "selectedItem", 2, seconditem, 1, secondvalue);

  // 'selectedIndex 2' - check if selectedIndex may be set to -1 to deselect items
  var selectionRequired = behaviourContains(element.localName, "selection-required");
  element.selectedIndex = -1;
  test_nsIDOMXULSelectControlElement_States(element, testid + "selectedIndex 2", 2,
        selectionRequired ? seconditem : null, selectionRequired ? 1 : -1,
        selectionRequired ? secondvalue : "");

  // 'selectedItem 2' - check if the selectedItem property may be set to null
  element.selectedIndex = 1;
  element.selectedItem = null;
  test_nsIDOMXULSelectControlElement_States(element, testid + "selectedItem 2", 2,
        selectionRequired ? seconditem : null, selectionRequired ? 1 : -1,
        selectionRequired ? secondvalue : "");

  // 'getIndexOfItem' - check if getIndexOfItem returns the right index
  is(element.getIndexOfItem(firstitem), 0, testid + "getIndexOfItem - first item at index 0");
  is(element.getIndexOfItem(seconditem), 1, testid + "getIndexOfItem - second item at index 1");

  var otheritem = element.ownerDocument.createElement(childtag);
  is(element.getIndexOfItem(otheritem), -1, testid + "getIndexOfItem - other item not found");

  // 'getItemAtIndex' - check if getItemAtIndex returns the right item
  is(element.getItemAtIndex(0), firstitem, testid + "getItemAtIndex - index 0 is first item");
  is(element.getItemAtIndex(1), seconditem, testid + "getItemAtIndex - index 0 is second item");
  is(element.getItemAtIndex(-1), null, testid + "getItemAtIndex - index -1 is null");
  is(element.getItemAtIndex(2), null, testid + "getItemAtIndex - index 2 is null");

  // check if setting the value changes the selection
  element.value = firstvalue;
  test_nsIDOMXULSelectControlElement_States(element, testid + "set value 1", 2, firstitem, 0, firstvalue);
  element.value = secondvalue;
  test_nsIDOMXULSelectControlElement_States(element, testid + "set value 2", 2, seconditem, 1, secondvalue);
  // setting the value attribute to one not in the list doesn't change the selection.
  // The value is only changed for elements which support having a value other than the
  // selection.
  element.value = "other";
  var allowOtherValue = behaviourContains(element.localName, "allow-other-value");
  var otherValueClearsSelection = behaviourContains(element.localName, "other-value-clears-selection");
  test_nsIDOMXULSelectControlElement_States(element, testid + "set value other", 2,
                                            otherValueClearsSelection ? null : seconditem,
                                            otherValueClearsSelection ? -1 : 1,
                                            allowOtherValue ? "other" : secondvalue);
  if (allowOtherValue)
    element.value = "";

  // 'removeItemAt' - check if removeItemAt removes the right item
  if (selectionRequired)
    element.value = secondvalue;
  else
    element.selectedIndex = -1;

  var removeditem = element.removeItemAt(0);
  is(removeditem, firstitem, testid + "removeItemAt return value");
  test_nsIDOMXULSelectControlElement_States(element, testid + "removeItemAt", 1,
        selectionRequired ? seconditem : null, selectionRequired ? 0 : -1,
        selectionRequired ? secondvalue : "");

  is(removeditem.control, undefined, testid + "control not set");

  var thirditem = element.appendItem("Third Item", "third");
  var fourthitem = element.appendItem("Fourth Item", fourthvalue);
  var fifthitem = element.appendItem("Fifth Item", "fifth");

  // 'removeItemAt 2' - check if removeItemAt removes the selected item and
  //                    adjusts the selection to the next item
  element.selectedItem = thirditem;
  is(element.removeItemAt(1), thirditem, testid + "removeItemAt 2 return value");

  // radio buttons don't handle removing quite right due to XBL issues,
  // so disable testing some of these remove tests for now - bug 367400
  var isnotradio = (element.localName != "radiogroup");
  // XXXndeakin disable these tests for all widgets for now. They require bug 331513.
  isnotradio = false;
  if (isnotradio)
    test_nsIDOMXULSelectControlElement_States(element, testid + "removeItemAt 2", 3, fourthitem, 1, fourthvalue);

  // 'removeItemAt 3' - check if removeItemAt adjusts the selection
  //                    if an earlier item is removed
  element.selectedItem = fourthitem;
  element.removeItemAt(0);
  test_nsIDOMXULSelectControlElement_States(element, testid + "removeItemAt 3", 2, fourthitem, 0, fourthvalue);

  // 'removeItemAt 4' - check if removeItemAt adjusts the selection if the
  //                    last item is selected and removed
  element.selectedItem = fifthitem;
  element.removeItemAt(1);
  if (isnotradio)
    test_nsIDOMXULSelectControlElement_States(element, testid + "removeItemAt 4", 1, fourthitem, 0, fourthvalue);

  // 'removeItemAt 5' - check that removeItemAt doesn't fail when removing invalid items
  is(element.removeItemAt(-1), null, testid + "removeItemAt 5 return value");
  if (isnotradio)
    test_nsIDOMXULSelectControlElement_States(element, testid + "removeItemAt 5", 1, fourthitem, 0, fourthvalue);

  // 'removeItemAt 6' - check that removeItemAt doesn't fail when removing invalid items
  is(element.removeItemAt(1), null, testid + "removeItemAt 6 return value");
  is("item removed", "item removed", testid + "removeItemAt 6");
  if (isnotradio)
    test_nsIDOMXULSelectControlElement_States(element, testid + "removeItemAt 6", 1, fourthitem, 0, fourthvalue);

  // 'insertItemAt' - check if insertItemAt inserts items at the right locations
  element.selectedIndex = 0;
  test_nsIDOMXULSelectControlElement_insertItemAt(element, 0, 0, testid, 5);
  test_nsIDOMXULSelectControlElement_insertItemAt(element, 2, 2, testid, 6);
  test_nsIDOMXULSelectControlElement_insertItemAt(element, -1, 3, testid, 7);
  test_nsIDOMXULSelectControlElement_insertItemAt(element, 6, 4, testid, 8);

  element.selectedIndex = 0;
  fourthitem.disabled = true;
  element.selectedIndex = 1;
  test_nsIDOMXULSelectControlElement_States(element, testid + "selectedIndex disabled", 5, fourthitem, 1, fourthvalue);

  element.selectedIndex = 0;
  element.selectedItem = fourthitem;
  test_nsIDOMXULSelectControlElement_States(element, testid + "selectedIndex disabled", 5, fourthitem, 1, fourthvalue);

  // 'removeall' - check if all items are removed
  while (element.itemCount)
    element.removeItemAt(0);
  if (isnotradio)
    test_nsIDOMXULSelectControlElement_States(element, testid + "remove all", 0, null, -1,
                                              allowOtherValue ? "number8" : "");
}

function test_nsIDOMXULSelectControlElement_init(element, testprefix)
{
  // editable menulists use the label as the value
  var isEditable = (element.localName == "menulist" && element.editable);

  var id = element.id;
  element = document.getElementById(id + "-initwithvalue");
  if (element) {
    var seconditem = element.getItemAtIndex(1);
    test_nsIDOMXULSelectControlElement_States(element, testprefix + " value initialization",
                                              3, seconditem, 1,
                                              isEditable ? seconditem.label : seconditem.value);
  }

  element = document.getElementById(id + "-initwithselected");
  if (element) {
    var thirditem = element.getItemAtIndex(2);
    test_nsIDOMXULSelectControlElement_States(element, testprefix + " selected initialization",
                                              3, thirditem, 2,
                                              isEditable ? thirditem.label : thirditem.value);
  }
}

function test_nsIDOMXULSelectControlElement_States(element, testid,
                                                   expectedcount, expecteditem,
                                                   expectedindex, expectedvalue)
{
  // need an itemCount property here
  var count = element.itemCount;
  is(count, expectedcount, testid + " item count");
  is(element.selectedItem, expecteditem, testid + " selectedItem");
  is(element.selectedIndex, expectedindex, testid + " selectedIndex");
  is(element.value, expectedvalue, testid + " value");
  if (element.selectedItem) {
    is(element.selectedItem.selected, true,
                  testid + " selectedItem marked as selected");
  }
}

function test_nsIDOMXULSelectControlElement_insertItemAt(element, index, expectedindex, testid, number)
{
  var expectedCount = element.itemCount;
  var expectedSelItem = element.selectedItem;
  var expectedSelIndex = element.selectedIndex;
  var expectedSelValue = element.value;

  var newitem = element.insertItemAt(index, "Item " + number, "number" + number);
  is(element.getIndexOfItem(newitem), expectedindex,
                testid + "insertItemAt " + expectedindex + " - get inserted item");
  expectedCount++;
  if (expectedSelIndex >= expectedindex)
    expectedSelIndex++;

  test_nsIDOMXULSelectControlElement_States(element, testid + "insertItemAt " + index,
                                           expectedCount, expectedSelItem,
                                           expectedSelIndex, expectedSelValue);
  return newitem;
}

/** test_nsIDOMXULSelectControlElement_UI
  *
  * Test the UI aspects of an element which implements nsIDOMXULSelectControlElement
  *
  * Parameters:
  *   element - element to test
  */
function test_nsIDOMXULSelectControlElement_UI(element, testprefix)
{
  var testid = (testprefix) ? testprefix + " " : "";
  testid += element.localName + " nsIDOMXULSelectControlElement UI ";

  while (element.itemCount)
    element.removeItemAt(0);

  var firstitem = element.appendItem("First Item", "first");
  var seconditem = element.appendItem("Second Item", "second");

  // 'mouse select' - check if clicking an item selects it
  synthesizeMouseExpectEvent(firstitem, 2, 2, {}, element, "select", testid + "mouse select");
  test_nsIDOMXULSelectControlElement_States(element, testid + "mouse select", 2, firstitem, 0, "first");

  synthesizeMouseExpectEvent(seconditem, 2, 2, {}, element, "select", testid + "mouse select 2");
  test_nsIDOMXULSelectControlElement_States(element, testid + "mouse select 2", 2, seconditem, 1, "second");

  // make sure the element is focused so keyboard navigation will apply
  element.selectedIndex = 1;
  element.focus();

  var navLeftRight = behaviourContains(element.localName, "keynav-leftright");
  var backKey = navLeftRight ? "VK_LEFT" : "VK_UP";
  var forwardKey = navLeftRight ? "VK_RIGHT" : "VK_DOWN";

  // 'key select' - check if keypresses move between items
  synthesizeKeyExpectEvent(backKey, {}, element, "select", testid + "key up");
  test_nsIDOMXULSelectControlElement_States(element, testid + "key up", 2, firstitem, 0, "first");

  var keyWrap = behaviourContains(element.localName, "select-keynav-wraps");

  var expectedItem = keyWrap ? seconditem : firstitem;
  var expectedIndex = keyWrap ? 1 : 0;
  var expectedValue = keyWrap ? "second" : "first";
  synthesizeKeyExpectEvent(backKey, {}, keyWrap ? element : null, "select", testid + "key up 2");
  test_nsIDOMXULSelectControlElement_States(element, testid + "key up 2", 2,
    expectedItem, expectedIndex, expectedValue);

  element.selectedIndex = 0;
  synthesizeKeyExpectEvent(forwardKey, {}, element, "select", testid + "key down");
  test_nsIDOMXULSelectControlElement_States(element, testid + "key down", 2, seconditem, 1, "second");

  expectedItem = keyWrap ? firstitem : seconditem;
  expectedIndex = keyWrap ? 0 : 1;
  expectedValue = keyWrap ? "first" : "second";
  synthesizeKeyExpectEvent(forwardKey, {}, keyWrap ? element : null, "select", testid + "key down 2");
  test_nsIDOMXULSelectControlElement_States(element, testid + "key down 2", 2,
    expectedItem, expectedIndex, expectedValue);

  var thirditem = element.appendItem("Third Item", "third");
  var fourthitem = element.appendItem("Fourth Item", "fourth");
  if (behaviourContains(element.localName, "select-extended-keynav")) {
    var fifthitem = element.appendItem("Fifth Item", "fifth");
    var sixthitem = element.appendItem("Sixth Item", "sixth");

    synthesizeKeyExpectEvent("VK_END", {}, element, "select", testid + "key end");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key end", 6, sixthitem, 5, "sixth");

    synthesizeKeyExpectEvent("VK_HOME", {}, element, "select", testid + "key home");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key home", 6, firstitem, 0, "first");

    synthesizeKeyExpectEvent("VK_PAGE_DOWN", {}, element, "select", testid + "key page down");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key page down", 6, fourthitem, 3, "fourth");
    synthesizeKeyExpectEvent("VK_PAGE_DOWN", {}, element, "select", testid + "key page down to end");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key page down to end", 6, sixthitem, 5, "sixth");

    synthesizeKeyExpectEvent("VK_PAGE_UP", {}, element, "select", testid + "key page up");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key page up", 6, thirditem, 2, "third");
    synthesizeKeyExpectEvent("VK_PAGE_UP", {}, element, "select", testid + "key page up to start");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key page up to start", 6, firstitem, 0, "first");

    element.removeItemAt(5);
    element.removeItemAt(4);
  }

  // now test whether a disabled item works.
  element.selectedIndex = 0;
  seconditem.disabled = true;

  var dontSelectDisabled = (behaviourContains(element.localName, "dont-select-disabled"));

  // 'mouse select' - check if clicking an item selects it
  synthesizeMouseExpectEvent(seconditem, 2, 2, {}, element,
                             dontSelectDisabled ? "!select" : "select",
                             testid + "mouse select disabled");
  test_nsIDOMXULSelectControlElement_States(element, testid + "mouse select disabled", 4,
    dontSelectDisabled ? firstitem: seconditem, dontSelectDisabled ? 0 : 1,
    dontSelectDisabled ? "first" : "second");

  if (dontSelectDisabled) {
    // test whether disabling an item won't allow it to be selected
    synthesizeKeyExpectEvent(forwardKey, {}, element, "select", testid + "key down disabled");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key down disabled", 4, thirditem, 2, "third");

    synthesizeKeyExpectEvent(backKey, {}, element, "select", testid + "key up disabled");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key up disabled", 4, firstitem, 0, "first");

    element.selectedIndex = 2;
    firstitem.disabled = true;

    synthesizeKeyExpectEvent(backKey, {}, keyWrap ? element : null, "select", testid + "key up disabled 2");
    expectedItem = keyWrap ? fourthitem : thirditem;
    expectedIndex = keyWrap ? 3 : 2;
    expectedValue = keyWrap ? "fourth" : "third";
    test_nsIDOMXULSelectControlElement_States(element, testid + "key up disabled 2", 4,
      expectedItem, expectedIndex, expectedValue);
  }
  else {
    // in this case, disabled items should behave the same as non-disabled items.
    element.selectedIndex = 0;
    synthesizeKeyExpectEvent(forwardKey, {}, element, "select", testid + "key down disabled");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key down disabled", 4, seconditem, 1, "second");
    synthesizeKeyExpectEvent(forwardKey, {}, element, "select", testid + "key down disabled again");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key down disabled again", 4, thirditem, 2, "third");

    synthesizeKeyExpectEvent(backKey, {}, element, "select", testid + "key up disabled");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key up disabled", 4, seconditem, 1, "second");
    synthesizeKeyExpectEvent(backKey, {}, element, "select", testid + "key up disabled again");
    test_nsIDOMXULSelectControlElement_States(element, testid + "key up disabled again", 4, firstitem, 0, "first");
  }
}