summaryrefslogtreecommitdiffstats
path: root/toolkit/components/satchel/test/test_form_autocomplete_with_list.html
blob: b903af5246fc4504559c8b5fefa0eaf03f4fdaf8 (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
391
392
393
394
395
396
397
398
399
400
401
402
<!DOCTYPE HTML>
<html>
<head>
  <title>Test for Form History Autocomplete</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/EventUtils.js"></script>
  <script type="text/javascript" src="satchel_common.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Form History test: form field autocomplete
<p id="display"></p>

<!-- we presumably can't hide the content for this test. -->
<div id="content">
  <datalist id="suggest">
    <option value="Google" label="PASS1">FAIL</option>
    <option value="Reddit">PASS2</option>
    <option value="final"></option>
  </datalist>

  <!-- normal, basic form -->
  <form id="form1">
    <input list="suggest" type="text" name="field1">
    <button type="submit">Submit</button>
  </form>

  <!-- form with autocomplete=off on input -->
  <form id="form3">
    <input list="suggest" type="text" name="field2" autocomplete="off">
    <button type="submit">Submit</button>
  </form>

  <!-- form with autocomplete=off on form -->
  <form id="form4" autocomplete="off">
    <input list="suggest" type="text" name="field2">
    <button type="submit">Submit</button>
  </form>

  <!-- yet another normal, basic form, with 2 history entries -->
  <form id="form5">
    <input list="suggest" type="text" name="field3">
    <button type="submit">Submit</button>
  </form>

  <form id="show_datalist_for_text_inputs_only">
    <input list="suggest" type="button" popup="false" />
    <input list="suggest" type="checkbox" popup="false" />
    <input list="suggest" type="color" popup="false" />
    <input list="suggest" type="date" popup="false" />
    <input list="suggest" type="datetime-local" popup="false" />
    <input list="suggest" type="email" popup="true" />
    <input list="suggest" type="file" popup="false" />
    <input list="suggest" type="image" popup="false" />
    <input list="suggest" type="month" popup="true" />
    <input list="suggest" type="number" popup="false" />
    <input list="suggest" type="password" popup="true" />
    <input list="suggest" type="radio" popup="false" />
    <input list="suggest" type="range" popup="false" />
    <input list="suggest" type="reset" popup="false" />
    <input list="suggest" type="search" popup="true" />
    <input list="suggest" type="submit" popup="false" />
    <input list="suggest" type="tel" popup="true" />
    <input list="suggest" type="text" popup="true" />
    <input list="suggest" type="time" popup="false" />
    <input list="suggest" type="url" popup="true" />
    <input list="suggest" type="week" popup="true" />
  </form>
</div>

<script>

preventSubmitOnForms();

add_setup(async () => {
  await updateFormHistory([
    { op: "remove" },
    { op: "add", fieldname: "field1", value: "historyvalue" },
    { op: "add", fieldname: "field2", value: "othervalue" },
    { op: "add", fieldname: "field3", value: "history1" },
    { op: "add", fieldname: "field3", value: "history2" },
  ]);
});

add_task(async function no_changes_when_opening_popup(){
  const { input } = await openPopupOn("#form1 > input");
  assertValueAfterKeys(input, [], "");
});

add_task(async function use_1st_entry() {
  const { input } = await openPopupOn("#form1 > input");
  assertAutocompleteItems("historyvalue", "PASS1", "PASS2", "final");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_Enter"],
    "historyvalue");
});

add_task(async function use_2nd_entry(){
  const { input } = await openPopupOn("#form1 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "Google");
});

add_task(async function use_3rd_entry(){
  const { input } = await openPopupOn("#form1 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "Reddit");
});

add_task(async function use_4th_entry(){
  const { input } = await openPopupOn("#form1 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "final");
});

add_task(async function delete_1st_entry(){
  const { input } = await openPopupOn("#form1 > input");
  assertValueAfterKeys(input, "KEY_ArrowDown", "");
  deleteSelectedAutocompleteItem();
  await notifyMenuChanged(3);
  is(await countEntries("field1", "historyvalue"), 0, "item is absent");
});

add_task(async function can_use_next_item_after_deletion(){
  const { input } = await openPopupOn("#form1 > input");
  assertAutocompleteItems("PASS1", "PASS2", "final");
  assertValueAfterKeys(input, ["KEY_ArrowDown", "KEY_Enter"], "Google");
});

add_task(async function autocomplete_on_datalist_with_cached_results(){
  const { input } = await openPopupOn("#form1 > input");
  sendString("PAS");
  await notifyMenuChanged(2);
  sendString("S1");
  await notifyMenuChanged(1);
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_Enter"],
    "Google");
});

add_task(async function fills_with_autocomplete_off_on_form(){
  const { input } = await openPopupOn("#form4 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_Enter"],
    "Google");
  assertAutocompleteItems("PASS1", "PASS2", "final");
});

add_task(async function use_1st_entry_with_autocomplete_off_on_form(){
  const { input } = await openPopupOn("#form4 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_Enter"],
    "Google");
});

add_task(async function use_2nd_entry_with_autocomplete_off_on_form(){
  const { input } = await openPopupOn("#form4 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "Reddit");
});

add_task(async function use_3rd_entry_with_autocomplete_off_on_form(){
  const { input } = await openPopupOn("#form4 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "final");
});

add_task(async function fills_with_autocomplete_off_on_input(){
  const { input } = await openPopupOn("#form3 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_Enter"],
    "Google");
  assertAutocompleteItems("PASS1", "PASS2", "final");
});

add_task(async function use_1st_entry_with_autocomplete_off_on_input(){
  const { input } = await openPopupOn("#form3 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_Enter"],
    "Google");
});

add_task(async function use_2nd_entry_with_autocomplete_off_on_input(){
  const { input } = await openPopupOn("#form3 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "Reddit");
});

add_task(async function use_3rd_entry_with_autocomplete_off_on_input(){
  const { input } = await openPopupOn("#form3 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "final");
});

add_task(async function remove_item_from_datalist(){
  // When there is an update of the list, the selection is lost so we need to
  // go down like if we were at the beginning of the list again.
  //
  // Removing the second element while on the first then going down and
  // push enter. Value should be one from the third suggesion.
  const { input } = await openPopupOn("#form3 > input");
  synthesizeKey("KEY_ArrowDown");
  const datalist = document.getElementById("suggest");
  const toRemove = datalist.children[1];
  datalist.removeChild(toRemove);

  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "final");

  // Restore the element.
  datalist.insertBefore(toRemove, datalist.children[1]);
});

add_task(async function add_item_to_datalist(){
  const { input } = await openPopupOn("#form3 > input");
  const datalist = document.getElementById("suggest");

  // Adding an attribute after the first one while on the first then going
  // down and push enter. Value should be the on from the new suggestion.
  synthesizeKey("KEY_ArrowDown");
  datalist.insertBefore(new Option("New value"), datalist.children[1]);
  await notifyMenuChanged(4);
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "New value");
});

add_task(async function change_datalist_option_value(){
  const datalist = document.getElementById("suggest");

  // Remove the element.
  datalist.removeChild(datalist.children[1]);
  await notifyMenuChanged(0);

  // Change the first element value attribute.
  const prevValue = datalist.children[0].value;
  datalist.children[0].value = "foo";

  const { input } = await openPopupOn("#form3 > input");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_Enter"],
    "foo");

  datalist.children[0].value = prevValue;
  await notifyMenuChanged(0);
});

add_task(async function change_datalist_option_text_content(){
  const datalist = document.getElementById("suggest");
  const prevValue = datalist.children[0].getAttribute("value");
  datalist.children[0].removeAttribute("value");
  datalist.children[0].textContent = "foobar";
  const { input } = await openPopupOn("#form3 > input");

  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_Enter"],
    "foobar");

  datalist.children[0].setAttribute("value", prevValue);
  await notifyMenuChanged(0);
});

add_task(async function filters_with_1st_letter(){
  const { input } = await openPopupOn("#form3 > input");
  sendString("f");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "final");
});

add_task(async function filters_with_letter_in_the_middle(){
  const { input } = await openPopupOn("#form3 > input");
  sendString("in");
  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_ArrowDown", "KEY_Enter"],
    "final");
});

add_task(async function input_events(){
  const { input } = await openPopupOn("#form3 > input");

  let beforeInputFired = false;
  input.addEventListener("beforeinput", (event) => {
    beforeInputFired = true;
    ok(event instanceof InputEvent, "beforeinput event has InputEvent interface");
    ok(event.bubbles, "beforeinput event should bubble");
    is(
      event.cancelable,
      SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input"),
      "beforeinput event for insertReplacementText should be cancelable if not suppressed");
    is(event.inputType, "insertReplacementText", "inputType of beforeinput event should be insertReplacementText");
    is(event.data, "Google", "data of beforeinput event should be Google");
    is(event.dataTransfer, null, "dataTransfer of beforeinput event should be null");
    is(event.getTargetRanges().length, 0, "getTargetRanges() of beforeinput event should be empty array");
  }, { once: true });

  let inputFired = false;
  input.addEventListener("input", function(event) {
    inputFired = true;
    ok(event instanceof InputEvent,
        "input event should be dispatched with InputEvent interface");
    ok(event.bubbles, "input event should bubble");
    ok(!event.cancelable, "input event should be cancelable");
    is(event.inputType, "insertReplacementText", "inputType of input event should be insertReplacementText");
    is(event.data, "Google", "data of input event should be Google");
    is(event.dataTransfer, null, "dataTransfer of input event should be null");
    is(event.getTargetRanges().length, 0, "getTargetRanges() of input event should be empty array");
  }, { once: true });

  assertValueAfterKeys(input, ["KEY_ArrowDown", "KEY_Enter"], "Google");
  ok(beforeInputFired, "beforeinput event should've been fired");
  ok(inputFired, "input event should've been fired");
});

add_task(async function cancelled_beforeinput_event(){
  const { input } = await openPopupOn("#form3 > input");
  await SpecialPowers.pushPrefEnv({
    set: [["dom.input_event.allow_to_cancel_set_user_input", true]],
  });
  input.addEventListener("beforeinput", e => e.preventDefault(), { once: true });
  let inputFired = false;
  input.addEventListener("input", () => inputFired = true, { once: true });

  assertValueAfterKeys(input, "KEY_ArrowDown", "");
  assertValueAfterKeys(input, "KEY_Enter", "");
  ok(!inputFired, "no input event when beforeinput is canceled");

  input.blur();
  await SpecialPowers.pushPrefEnv({
    clear: [["dom.input_event.allow_to_cancel_set_user_input"]],
  });
});

add_task(async function attempt_to_delete_datalist_entries(){
  const { input } = await openPopupOn("#form5 > input");
  assertAutocompleteItems("history1", "history2", "PASS1", "PASS2", "final");

  assertValueAfterKeys(
    input,
    ["KEY_ArrowDown", "KEY_ArrowDown", "KEY_ArrowDown"],
    "");

  deleteSelectedAutocompleteItem();
  synthesizeKey("KEY_ArrowUp");
  deleteSelectedAutocompleteItem();
  await notifyMenuChanged(4);
  assertAutocompleteItems("history1", "PASS1", "PASS2", "final");
  // Delete the second entry, that is the 1st entry of datalist.
  // This has no effect.
  deleteSelectedAutocompleteItem();

  // Delete the first entry, that is the 1nd entry of history.
  synthesizeKey("KEY_ArrowUp");
  deleteSelectedAutocompleteItem();
  await notifyMenuChanged(3);

  assertAutocompleteItems("PASS1", "PASS2", "final");
  assertValueAfterKeys(input, "KEY_Enter", "Google");
});

for (const input of document
  .querySelectorAll("form#show_datalist_for_text_inputs_only input")) {
  const expectPopup = input.getAttribute("popup") == "true";
  add_named_task(
    `datalist_is_${expectPopup ? "" : "not_"}shown_for_${input.type}_input`,
    async () => {
      input.focus();
      is(document.activeElement, input, "Input is focused.");
      await (expectPopup ? popupByArrowDown() : noPopupByArrowDown());
    }
  );
}
</script>
</body>
</html>