[iOS] Shift + Tab does not focus previous field
[WebKit-https.git] / LayoutTests / resources / ui-helper.js
1
2 window.UIHelper = class UIHelper {
3     static isIOS()
4     {
5         return navigator.userAgent.includes('iPhone') || navigator.userAgent.includes('iPad');
6     }
7
8     static isWebKit2()
9     {
10         return window.testRunner.isWebKit2;
11     }
12
13     static tapAt(x, y)
14     {
15         console.assert(this.isIOS());
16
17         if (!this.isWebKit2()) {
18             eventSender.addTouchPoint(x, y);
19             eventSender.touchStart();
20             eventSender.releaseTouchPoint(0);
21             eventSender.touchEnd();
22             return Promise.resolve();
23         }
24
25         return new Promise((resolve) => {
26             testRunner.runUIScript(`
27                 uiController.singleTapAtPoint(${x}, ${y}, function() {
28                     uiController.uiScriptComplete('Done');
29                 });`, resolve);
30         });
31     }
32
33     static activateAt(x, y)
34     {
35         if (!this.isWebKit2() || !this.isIOS()) {
36             eventSender.mouseMoveTo(x, y);
37             eventSender.mouseDown();
38             eventSender.mouseUp();
39             return Promise.resolve();
40         }
41
42         return new Promise((resolve) => {
43             testRunner.runUIScript(`
44                 uiController.singleTapAtPoint(${x}, ${y}, function() {
45                     uiController.uiScriptComplete('Done');
46                 });`, resolve);
47         });
48     }
49
50     static activateElement(element)
51     {
52         const x = element.offsetLeft + element.offsetWidth / 2;
53         const y = element.offsetTop + element.offsetHeight / 2;
54         return UIHelper.activateAt(x, y);
55     }
56
57     static keyDown(key, modifiers=[])
58     {
59         if (!this.isWebKit2() || !this.isIOS()) {
60             eventSender.keyDown(key);
61             return Promise.resolve();
62         }
63
64         return new Promise((resolve) => {
65             testRunner.runUIScript(`uiController.keyDown("${key}", ${JSON.stringify(modifiers)});`, resolve);
66         });
67     }
68
69     static ensurePresentationUpdate()
70     {
71         if (!this.isWebKit2()) {
72             testRunner.display();
73             return Promise.resolve();
74         }
75
76         return new Promise(resolve => {
77             testRunner.runUIScript(`
78                 uiController.doAfterPresentationUpdate(function() {
79                     uiController.uiScriptComplete('Done');
80                 });`, resolve);
81         });
82     }
83
84     static ensureVisibleContentRectUpdate()
85     {
86         if (!this.isWebKit2())
87             return Promise.resolve();
88
89         return new Promise(resolve => {
90             const visibleContentRectUpdateScript = "uiController.doAfterVisibleContentRectUpdate(() => uiController.uiScriptComplete())";
91             testRunner.runUIScript(visibleContentRectUpdateScript, resolve);
92         });
93     }
94
95     static activateAndWaitForInputSessionAt(x, y)
96     {
97         if (!this.isWebKit2() || !this.isIOS())
98             return this.activateAt(x, y);
99
100         return new Promise(resolve => {
101             testRunner.runUIScript(`
102                 (function() {
103                     uiController.didShowKeyboardCallback = function() {
104                         uiController.uiScriptComplete("Done");
105                     };
106                     uiController.singleTapAtPoint(${x}, ${y}, function() { });
107                 })()`, resolve);
108         });
109     }
110
111     static activateFormControl(element)
112     {
113         if (!this.isWebKit2() || !this.isIOS())
114             return this.activateElement(element);
115
116         const x = element.offsetLeft + element.offsetWidth / 2;
117         const y = element.offsetTop + element.offsetHeight / 2;
118
119         return new Promise(resolve => {
120             testRunner.runUIScript(`
121                 (function() {
122                     uiController.didStartFormControlInteractionCallback = function() {
123                         uiController.uiScriptComplete("Done");
124                     };
125                     uiController.singleTapAtPoint(${x}, ${y}, function() { });
126                 })()`, resolve);
127         });
128     }
129
130     static waitForKeyboardToHide()
131     {
132         if (!this.isWebKit2() || !this.isIOS())
133             return Promise.resolve();
134
135         return new Promise(resolve => {
136             testRunner.runUIScript(`
137                 (function() {
138                     if (uiController.isShowingKeyboard)
139                         uiController.didHideKeyboardCallback = () => uiController.uiScriptComplete();
140                     else
141                         uiController.uiScriptComplete();
142                 })()`, resolve);
143         });
144     }
145
146     static getUICaretRect()
147     {
148         if (!this.isWebKit2() || !this.isIOS())
149             return Promise.resolve();
150
151         return new Promise(resolve => {
152             testRunner.runUIScript(`(function() {
153                 uiController.doAfterNextStablePresentationUpdate(function() {
154                     uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionCaretRect));
155                 });
156             })()`, jsonString => {
157                 resolve(JSON.parse(jsonString));
158             });
159         });
160     }
161
162     static getUISelectionRects()
163     {
164         if (!this.isWebKit2() || !this.isIOS())
165             return Promise.resolve();
166
167         return new Promise(resolve => {
168             testRunner.runUIScript(`(function() {
169                 uiController.doAfterNextStablePresentationUpdate(function() {
170                     uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionRangeRects));
171                 });
172             })()`, jsonString => {
173                 resolve(JSON.parse(jsonString));
174             });
175         });
176     }
177
178     static getUICaretViewRect()
179     {
180         if (!this.isWebKit2() || !this.isIOS())
181             return Promise.resolve();
182
183         return new Promise(resolve => {
184             testRunner.runUIScript(`(function() {
185                 uiController.doAfterNextStablePresentationUpdate(function() {
186                     uiController.uiScriptComplete(JSON.stringify(uiController.selectionCaretViewRect));
187                 });
188             })()`, jsonString => {
189                 resolve(JSON.parse(jsonString));
190             });
191         });
192     }
193
194     static getUISelectionViewRects()
195     {
196         if (!this.isWebKit2() || !this.isIOS())
197             return Promise.resolve();
198
199         return new Promise(resolve => {
200             testRunner.runUIScript(`(function() {
201                 uiController.doAfterNextStablePresentationUpdate(function() {
202                     uiController.uiScriptComplete(JSON.stringify(uiController.selectionRangeViewRects));
203                 });
204             })()`, jsonString => {
205                 resolve(JSON.parse(jsonString));
206             });
207         });
208     }
209
210     static getSelectionStartGrabberViewRect()
211     {
212         if (!this.isWebKit2() || !this.isIOS())
213             return Promise.resolve();
214
215         return new Promise(resolve => {
216             testRunner.runUIScript(`(function() {
217                 uiController.doAfterNextStablePresentationUpdate(function() {
218                     uiController.uiScriptComplete(JSON.stringify(uiController.selectionStartGrabberViewRect));
219                 });
220             })()`, jsonString => {
221                 resolve(JSON.parse(jsonString));
222             });
223         });
224     }
225
226     static getSelectionEndGrabberViewRect()
227     {
228         if (!this.isWebKit2() || !this.isIOS())
229             return Promise.resolve();
230
231         return new Promise(resolve => {
232             testRunner.runUIScript(`(function() {
233                 uiController.doAfterNextStablePresentationUpdate(function() {
234                     uiController.uiScriptComplete(JSON.stringify(uiController.selectionEndGrabberViewRect));
235                 });
236             })()`, jsonString => {
237                 resolve(JSON.parse(jsonString));
238             });
239         });
240     }
241
242     static replaceTextAtRange(text, location, length) {
243         return new Promise(resolve => {
244             testRunner.runUIScript(`(() => {
245                 uiController.replaceTextAtRange("${text}", ${location}, ${length});
246                 uiController.uiScriptComplete('Done');
247             })()`, resolve);
248         });
249     }
250
251     static wait(promise)
252     {
253         testRunner.waitUntilDone();
254         if (window.finishJSTest)
255             window.jsTestIsAsync = true;
256
257         let finish = () => {
258             if (window.finishJSTest)
259                 finishJSTest();
260             else
261                 testRunner.notifyDone();
262         }
263
264         return promise.then(finish, finish);
265     }
266
267     static withUserGesture(callback)
268     {
269         internals.withUserGesture(callback);
270     }
271
272     static selectFormAccessoryPickerRow(rowIndex)
273     {
274         const selectRowScript = `(() => uiController.selectFormAccessoryPickerRow(${rowIndex}))()`;
275         return new Promise(resolve => testRunner.runUIScript(selectRowScript, resolve));
276     }
277
278     static selectFormPopoverTitle()
279     {
280         return new Promise(resolve => {
281             testRunner.runUIScript(`(() => {
282                 uiController.uiScriptComplete(uiController.selectFormPopoverTitle);
283             })()`, resolve);
284         });
285     }
286
287     static enterText(text)
288     {
289         const escapedText = text.replace(/`/g, "\\`");
290         const enterTextScript = `(() => uiController.enterText(\`${escapedText}\`))()`;
291         return new Promise(resolve => testRunner.runUIScript(enterTextScript, resolve));
292     }
293
294     static setTimePickerValue(hours, minutes)
295     {
296         const setValueScript = `(() => uiController.setTimePickerValue(${hours}, ${minutes}))()`;
297         return new Promise(resolve => testRunner.runUIScript(setValueScript, resolve));
298     }
299
300     static setShareSheetCompletesImmediatelyWithResolution(resolved)
301     {
302         const resolveShareSheet = `(() => uiController.setShareSheetCompletesImmediatelyWithResolution(${resolved}))()`;
303         return new Promise(resolve => testRunner.runUIScript(resolveShareSheet, resolve));
304     }
305
306     static textContentType()
307     {
308         return new Promise(resolve => {
309             testRunner.runUIScript(`(() => {
310                 uiController.uiScriptComplete(uiController.textContentType);
311             })()`, resolve);
312         });
313     }
314
315     static formInputLabel()
316     {
317         return new Promise(resolve => {
318             testRunner.runUIScript(`(() => {
319                 uiController.uiScriptComplete(uiController.formInputLabel);
320             })()`, resolve);
321         });
322     }
323
324     static isShowingDataListSuggestions()
325     {
326         return new Promise(resolve => {
327             testRunner.runUIScript(`(() => {
328                 uiController.uiScriptComplete(uiController.isShowingDataListSuggestions);
329             })()`, result => resolve(result === "true" ? true : false));
330         });
331     }
332
333     static zoomScale()
334     {
335         return new Promise(resolve => {
336             testRunner.runUIScript(`(() => {
337                 uiController.uiScriptComplete(uiController.zoomScale);
338             })()`, scaleAsString => resolve(parseFloat(scaleAsString)));
339         });
340     }
341
342     static typeCharacter(characterString)
343     {
344         if (!this.isWebKit2() || !this.isIOS()) {
345             eventSender.keyDown(characterString);
346             return;
347         }
348
349         const escapedString = characterString.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
350         const uiScript = `uiController.typeCharacterUsingHardwareKeyboard(\`${escapedString}\`, () => uiController.uiScriptComplete())`;
351         return new Promise(resolve => testRunner.runUIScript(uiScript, resolve));
352     }
353
354     static applyAutocorrection(newText, oldText)
355     {
356         if (!this.isWebKit2())
357             return;
358
359         const [escapedNewText, escapedOldText] = [newText.replace(/`/g, "\\`"), oldText.replace(/`/g, "\\`")];
360         const uiScript = `uiController.applyAutocorrection(\`${escapedNewText}\`, \`${escapedOldText}\`, () => uiController.uiScriptComplete())`;
361         return new Promise(resolve => testRunner.runUIScript(uiScript, resolve));
362     }
363
364     static inputViewBounds()
365     {
366         if (!this.isWebKit2() || !this.isIOS())
367             return Promise.resolve();
368
369         return new Promise(resolve => {
370             testRunner.runUIScript(`(() => {
371                 uiController.uiScriptComplete(JSON.stringify(uiController.inputViewBounds));
372             })()`, jsonString => {
373                 resolve(JSON.parse(jsonString));
374             });
375         });
376     }
377
378     static calendarType()
379     {
380         if (!this.isWebKit2())
381             return Promise.resolve();
382
383         return new Promise(resolve => {
384             testRunner.runUIScript(`(() => {
385                 uiController.doAfterNextStablePresentationUpdate(() => {
386                     uiController.uiScriptComplete(JSON.stringify(uiController.calendarType));
387                 })
388             })()`, jsonString => {
389                 resolve(JSON.parse(jsonString));
390             });
391         });
392     }
393
394     static setDefaultCalendarType(calendarIdentifier)
395     {
396         if (!this.isWebKit2())
397             return Promise.resolve();
398
399         return new Promise(resolve => testRunner.runUIScript(`uiController.setDefaultCalendarType('${calendarIdentifier}')`, resolve));
400
401     }
402
403     static setViewScale(scale)
404     {
405         if (!this.isWebKit2())
406             return Promise.resolve();
407
408         return new Promise(resolve => testRunner.runUIScript(`uiController.setViewScale(${scale})`, resolve));
409     }
410
411     static resignFirstResponder()
412     {
413         if (!this.isWebKit2())
414             return Promise.resolve();
415
416         return new Promise(resolve => testRunner.runUIScript(`uiController.resignFirstResponder()`, resolve));
417     }
418
419     static minimumZoomScale()
420     {
421         if (!this.isWebKit2())
422             return Promise.resolve();
423
424         return new Promise(resolve => {
425             testRunner.runUIScript(`(() => {
426                 uiController.uiScriptComplete(uiController.minimumZoomScale);
427             })()`, scaleAsString => resolve(parseFloat(scaleAsString)))
428         });
429     }
430
431     static drawSquareInEditableImage()
432     {
433         if (!this.isWebKit2())
434             return Promise.resolve();
435
436         return new Promise(resolve => testRunner.runUIScript(`uiController.drawSquareInEditableImage()`, resolve));
437     }
438
439     static numberOfStrokesInEditableImage()
440     {
441         if (!this.isWebKit2())
442             return Promise.resolve();
443
444         return new Promise(resolve => {
445             testRunner.runUIScript(`(() => {
446                 uiController.uiScriptComplete(uiController.numberOfStrokesInEditableImage);
447             })()`, numberAsString => resolve(parseInt(numberAsString, 10)))
448         });
449     }
450 }