summaryrefslogtreecommitdiffstats
path: root/toolkit/content/tests/chrome/test_autocomplete_placehold_last_complete.xhtml
blob: 33fca83c324338057603a791c53388e5af0e7bbf (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
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>

<window title="Autocomplete Widget Test"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        onload="runTest();">

  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
  <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
  <script type="application/javascript"
          src="chrome://global/content/globalOverlay.js"/>

<html:input id="autocomplete"
            is="autocomplete-input"
            completedefaultindex="true"
            timeout="0"
            autocompletesearch="simple"/>

<script class="testbody" type="application/javascript">
<![CDATA[

function autoCompleteSimpleResult(aString, searchId) {
  this.searchString = aString;
  this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
  this.matchCount = 1;
  if (aString.startsWith('ret')) {
    this._param = autoCompleteSimpleResult.retireCompletion;
  } else {
    this._param = "Result";
  }
  this._searchId = searchId;
}
autoCompleteSimpleResult.retireCompletion = "Retire";
autoCompleteSimpleResult.prototype = {
  _param: "",
  searchString: null,
  searchResult: Ci.nsIAutoCompleteResult.RESULT_FAILURE,
  defaultIndex: 0,
  errorDescription: null,
  matchCount: 0,
  getValueAt() { return this._param; },
  getCommentAt() { return null; },
  getStyleAt() { return null; },
  getImageAt() { return null; },
  getLabelAt() { return null; },
  removeValueAt() {}
};

var searchCounter = 0;

// A basic autocomplete implementation that returns one result.
let autoCompleteSimple = {
  classID: Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca"),
  contractID: "@mozilla.org/autocomplete/search;1?name=simple",
  searchAsync: false,
  pendingSearch: null,

  QueryInterface: ChromeUtils.generateQI([
    "nsIFactory",
    "nsIAutoCompleteSearch"
  ]),
  createInstance(iid) {
    return this.QueryInterface(iid);
  },

  registerFactory() {
    let registrar =
      Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
    registrar.registerFactory(this.classID, "Test Simple Autocomplete",
                              this.contractID, this);
  },
  unregisterFactory() {
    let registrar =
      Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
    registrar.unregisterFactory(this.classID, this);
  },

  startSearch(aString, aParam, aResult, aListener) {
    let result = new autoCompleteSimpleResult(aString);

    if (this.searchAsync) {
      // Simulate an async search by using a timeout before invoking the
      // |onSearchResult| callback.
      // Store the searchTimeout such that it can be canceled if stopSearch is called.
      this.pendingSearch = setTimeout(() => {
        this.pendingSearch = null;

        aListener.onSearchResult(this, result);

         // Move to the next step in the async test.
        asyncTest.next();
      }, 0);
    } else {
      aListener.onSearchResult(this, result);
    }
  },
  stopSearch() {
    clearTimeout(this.pendingSearch);
  }
};

SimpleTest.waitForExplicitFinish();

let gACTimer;
let gAutoComplete;
let asyncTest;

let searchCompleteTimeoutId = null;

function finishTest() {
  // Unregister the factory so that we don't get in the way of other tests
  autoCompleteSimple.unregisterFactory();
  SimpleTest.finish();
}

function runTest() {
  autoCompleteSimple.registerFactory();
  gAutoComplete = $("autocomplete");
  gAutoComplete.focus();

  // Return the search results synchronous, which also makes the completion
  // happen synchronous.
  autoCompleteSimple.searchAsync = false;

  sendString("r");
  is(gAutoComplete.value, "result", "Value should be autocompleted immediately");

  sendString("e");
  is(gAutoComplete.value, "result", "Value should be autocompleted immediately");

  synthesizeKey("KEY_Delete");
  is(gAutoComplete.value, "re", "Deletion should not complete value");

  synthesizeKey("KEY_Backspace");
  is(gAutoComplete.value, "r", "Backspace should not complete value");

  synthesizeKey("KEY_ArrowLeft");
  is(gAutoComplete.value, "r", "Value should stay same when navigating with cursor");

  runAsyncTest();
}

function* asyncTestGenerator() {
  sendString("re");
  is(gAutoComplete.value, "re", "Value should not be autocompleted immediately");

  // Calling |yield undefined| makes this generator function wait until
  // |asyncTest.next();| is called. This happens from within the
  // |autoCompleteSimple.startSearch()| function once the simulated async
  // search has finished.
  // Therefore, the effect of the |yield undefined;| here (and the ones) below
  // is to wait until the async search result comes back.
  yield undefined;

  is(gAutoComplete.value, "result", "Value should be autocompleted");

  // Test if typing the `s` character completes directly based on the last
  // completion
  sendString("s");
  is(gAutoComplete.value, "result", "Value should be completed immediately");

  yield undefined;

  is(gAutoComplete.value, "result", "Value should be autocompleted to same value");
  synthesizeKey("KEY_Delete");
  is(gAutoComplete.value, "res", "Deletion should not complete value");

  // No |yield undefined| needed here as no completion is triggered by the deletion.

  is(gAutoComplete.value, "res", "Still no complete value after deletion");

  synthesizeKey("KEY_Backspace");
  is(gAutoComplete.value, "re", "Backspace should not complete value");

  yield undefined;

  is(gAutoComplete.value, "re", "Value after search due to backspace should stay the same"); (3)

  // Typing a character that is not like the previous match. In this case, the
  // completion cannot happen directly and therefore the value will be completed
  // only after the search has finished.
  sendString("t");
  is(gAutoComplete.value, "ret", "Value should not be autocompleted immediately");

  yield undefined;

  is(gAutoComplete.value, "retire", "Value should be autocompleted");

  sendString("i");
  is(gAutoComplete.value, "retire", "Value should be autocompleted immediately");

  yield undefined;

  is(gAutoComplete.value, "retire", "Value should be autocompleted to the same value");

  // Setup the scene to test how the completion behaves once the placeholder
  // completion and the result from the search do not agree with each other.
  gAutoComplete.value = 'r';
  // Need to type two characters as the input was reset and the autocomplete
  // controller things, ther user hit the backspace button, in which case
  // no completion is performed. But as a completion is desired, another
  // character `t` is typed afterwards.
  sendString("e");
  yield undefined;
  sendString("t");
  is(gAutoComplete.value, "ret", "Value should not be autocompleted");

  yield undefined;

  is(gAutoComplete.value, "retire", "Value should be autocompleted");

  // The placeholder string is now set to "retire". Changing the completion
  // string to "retirement" and see what the completion will turn out like.
  autoCompleteSimpleResult.retireCompletion = "Retirement";
  sendString("i");
  is(gAutoComplete.value, "retire", "Value should be autocompleted based on placeholder");

  yield undefined;

  is(gAutoComplete.value, "retirement", "Value should be autocompleted based on search result");

  // Change the search result to `Retire` again and see if the new result is
  // complited.
  autoCompleteSimpleResult.retireCompletion = "Retire";
  sendString("r");
  is(gAutoComplete.value, "retirement", "Value should be autocompleted based on placeholder");

  yield undefined;

  is(gAutoComplete.value, "retire", "Value should be autocompleted based on search result");

  // Complete the value
  gAutoComplete.value = 're';
  sendString("t");
  yield undefined;
  sendString("i");
  is(gAutoComplete.value, "reti", "Value should not be autocompleted");

  yield undefined;

  is(gAutoComplete.value, "retire", "Value should be autocompleted");

  // Remove the selected text "re" (1) and the "et" (2). Afterwards, add it again (3).
  // This should not cause the completion to kick in.
  synthesizeKey("KEY_Delete"); // (1)

  is(gAutoComplete.value, "reti", "Value should not complete after deletion");

  gAutoComplete.selectionStart = 1;
  gAutoComplete.selectionEnd = 3;
  synthesizeKey("KEY_Delete"); // (2)

  is(gAutoComplete.value, "ri", "Value should stay unchanged after removing character in the middle");

  yield undefined;

  sendString("e"); // (3.1)
  is(gAutoComplete.value, "rei", "Inserting a character in the middle should not complete the value");

  yield undefined;

  sendString("t"); // (3.2)
  is(gAutoComplete.value, "reti", "Inserting a character in the middle should not complete the value");

  yield undefined;

  // Adding a new character at the end should not cause the completion to happen again
  // as the completion failed before.
  gAutoComplete.selectionStart = 4;
  gAutoComplete.selectionEnd = 4;
  sendString("r");
  is(gAutoComplete.value, "retir", "Value should not be autocompleted immediately");

  yield undefined;

  is(gAutoComplete.value, "retire", "Value should be autocompleted");

  finishTest();
  yield undefined;
}

function runAsyncTest() {
  gAutoComplete.value = '';
  autoCompleteSimple.searchAsync = true;

  asyncTest = asyncTestGenerator();
  asyncTest.next();
}
]]>
</script>

<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display">
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>

</window>