| |
| window.UIHelper = class UIHelper { |
| static isIOSFamily() |
| { |
| return testRunner.isIOSFamily; |
| } |
| |
| static isWebKit2() |
| { |
| return testRunner.isWebKit2; |
| } |
| |
| static doubleClickAt(x, y) |
| { |
| eventSender.mouseMoveTo(x, y); |
| eventSender.mouseDown(); |
| eventSender.mouseUp(); |
| eventSender.mouseDown(); |
| eventSender.mouseUp(); |
| } |
| |
| static doubleClickAtThenDragTo(x1, y1, x2, y2) |
| { |
| eventSender.mouseMoveTo(x1, y1); |
| eventSender.mouseDown(); |
| eventSender.mouseUp(); |
| eventSender.mouseDown(); |
| eventSender.mouseMoveTo(x2, y2); |
| eventSender.mouseUp(); |
| } |
| |
| static async moveMouseAndWaitForFrame(x, y) |
| { |
| eventSender.mouseMoveTo(x, y); |
| await UIHelper.animationFrame(); |
| } |
| |
| static async mouseWheelScrollAt(x, y, beginX, beginY, deltaX, deltaY) |
| { |
| if (beginX === undefined) |
| beginX = 0; |
| if (beginY === undefined) |
| beginY = -1; |
| |
| if (deltaX === undefined) |
| deltaX = 0; |
| if (deltaY === undefined) |
| deltaY = -10; |
| |
| eventSender.monitorWheelEvents(); |
| eventSender.mouseMoveTo(x, y); |
| eventSender.mouseScrollByWithWheelAndMomentumPhases(beginX, beginY, "began", "none"); |
| eventSender.mouseScrollByWithWheelAndMomentumPhases(deltaX, deltaY, "changed", "none"); |
| eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none"); |
| return new Promise(resolve => { |
| eventSender.callAfterScrollingCompletes(() => { |
| requestAnimationFrame(resolve); |
| }); |
| }); |
| } |
| |
| static async mouseWheelMayBeginAt(x, y) |
| { |
| eventSender.mouseMoveTo(x, y); |
| eventSender.mouseScrollByWithWheelAndMomentumPhases(x, y, "maybegin", "none"); |
| await UIHelper.animationFrame(); |
| } |
| |
| static async mouseWheelCancelAt(x, y) |
| { |
| eventSender.mouseMoveTo(x, y); |
| eventSender.mouseScrollByWithWheelAndMomentumPhases(x, y, "cancelled", "none"); |
| await UIHelper.animationFrame(); |
| } |
| |
| static async waitForScrollCompletion() |
| { |
| return new Promise(resolve => { |
| eventSender.callAfterScrollingCompletes(() => { |
| requestAnimationFrame(resolve); |
| }); |
| }); |
| } |
| |
| static async animationFrame() |
| { |
| return new Promise(requestAnimationFrame); |
| } |
| |
| static async waitForCondition(conditionFunc) |
| { |
| while (!conditionFunc()) { |
| await UIHelper.animationFrame(); |
| } |
| } |
| |
| static sendEventStream(eventStream) |
| { |
| const eventStreamAsString = JSON.stringify(eventStream); |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| uiController.sendEventStream(\`${eventStreamAsString}\`, () => { |
| uiController.uiScriptComplete(); |
| }); |
| })(); |
| `, resolve); |
| }); |
| } |
| |
| static tapAt(x, y, modifiers=[]) |
| { |
| console.assert(this.isIOSFamily()); |
| |
| if (!this.isWebKit2()) { |
| console.assert(!modifiers || !modifiers.length); |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise((resolve) => { |
| testRunner.runUIScript(` |
| uiController.singleTapAtPointWithModifiers(${x}, ${y}, ${JSON.stringify(modifiers)}, function() { |
| uiController.uiScriptComplete(); |
| });`, resolve); |
| }); |
| } |
| |
| static doubleTapAt(x, y, delay = 0) |
| { |
| console.assert(this.isIOSFamily()); |
| |
| if (!this.isWebKit2()) { |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise((resolve) => { |
| testRunner.runUIScript(` |
| uiController.doubleTapAtPoint(${x}, ${y}, ${delay}, function() { |
| uiController.uiScriptComplete(); |
| });`, resolve); |
| }); |
| } |
| |
| static humanSpeedDoubleTapAt(x, y) |
| { |
| console.assert(this.isIOSFamily()); |
| |
| if (!this.isWebKit2()) { |
| // FIXME: Add a sleep in here. |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| return Promise.resolve(); |
| } |
| |
| return UIHelper.doubleTapAt(x, y, 0.12); |
| } |
| |
| static humanSpeedZoomByDoubleTappingAt(x, y) |
| { |
| console.assert(this.isIOSFamily()); |
| |
| if (!this.isWebKit2()) { |
| // FIXME: Add a sleep in here. |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise(async (resolve) => { |
| testRunner.runUIScript(` |
| uiController.didEndZoomingCallback = () => { |
| uiController.didEndZoomingCallback = null; |
| uiController.uiScriptComplete(uiController.zoomScale); |
| }; |
| uiController.doubleTapAtPoint(${x}, ${y}, 0.12, () => { });`, resolve); |
| }); |
| } |
| |
| static zoomByDoubleTappingAt(x, y) |
| { |
| console.assert(this.isIOSFamily()); |
| |
| if (!this.isWebKit2()) { |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| eventSender.addTouchPoint(x, y); |
| eventSender.touchStart(); |
| eventSender.releaseTouchPoint(0); |
| eventSender.touchEnd(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise((resolve) => { |
| testRunner.runUIScript(` |
| uiController.didEndZoomingCallback = () => { |
| uiController.didEndZoomingCallback = null; |
| uiController.uiScriptComplete(uiController.zoomScale); |
| }; |
| uiController.doubleTapAtPoint(${x}, ${y}, 0, () => { });`, resolve); |
| }); |
| } |
| |
| static activateAt(x, y) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) { |
| eventSender.mouseMoveTo(x, y); |
| eventSender.mouseDown(); |
| eventSender.mouseUp(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise((resolve) => { |
| testRunner.runUIScript(` |
| uiController.singleTapAtPoint(${x}, ${y}, function() { |
| uiController.uiScriptComplete(); |
| });`, resolve); |
| }); |
| } |
| |
| static activateElement(element) |
| { |
| const x = element.offsetLeft + element.offsetWidth / 2; |
| const y = element.offsetTop + element.offsetHeight / 2; |
| return UIHelper.activateAt(x, y); |
| } |
| |
| static async doubleActivateAt(x, y) |
| { |
| if (this.isIOSFamily()) |
| await UIHelper.doubleTapAt(x, y); |
| else |
| await UIHelper.doubleClickAt(x, y); |
| } |
| |
| static async doubleActivateAtSelectionStart() |
| { |
| const rects = window.getSelection().getRangeAt(0).getClientRects(); |
| const x = rects[0].left; |
| const y = rects[0].top; |
| if (this.isIOSFamily()) { |
| await UIHelper.activateAndWaitForInputSessionAt(x, y); |
| await UIHelper.doubleTapAt(x, y); |
| // This is only here to deal with async/sync copy/paste calls, so |
| // once <rdar://problem/16207002> is resolved, should be able to remove for faster tests. |
| await new Promise(resolve => testRunner.runUIScript("uiController.uiScriptComplete()", resolve)); |
| } else |
| await UIHelper.doubleClickAt(x, y); |
| } |
| |
| static async selectWordByDoubleTapOrClick(element, relativeX = 5, relativeY = 5) |
| { |
| const boundingRect = element.getBoundingClientRect(); |
| const x = boundingRect.x + relativeX; |
| const y = boundingRect.y + relativeY; |
| if (this.isIOSFamily()) { |
| await UIHelper.activateAndWaitForInputSessionAt(x, y); |
| await UIHelper.doubleTapAt(x, y); |
| // This is only here to deal with async/sync copy/paste calls, so |
| // once <rdar://problem/16207002> is resolved, should be able to remove for faster tests. |
| await new Promise(resolve => testRunner.runUIScript("uiController.uiScriptComplete()", resolve)); |
| } else { |
| await UIHelper.doubleClickAt(x, y); |
| } |
| } |
| |
| static keyDown(key, modifiers=[]) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) { |
| eventSender.keyDown(key, modifiers); |
| return Promise.resolve(); |
| } |
| |
| return new Promise((resolve) => { |
| testRunner.runUIScript(`uiController.keyDown("${key}", ${JSON.stringify(modifiers)});`, resolve); |
| }); |
| } |
| |
| static toggleCapsLock() |
| { |
| return new Promise((resolve) => { |
| testRunner.runUIScript(`uiController.toggleCapsLock(() => uiController.uiScriptComplete());`, resolve); |
| }); |
| } |
| |
| static keyboardIsAutomaticallyShifted() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`uiController.keyboardIsAutomaticallyShifted`, result => resolve(result === "true")); |
| }); |
| } |
| |
| static ensurePresentationUpdate() |
| { |
| if (!this.isWebKit2()) { |
| testRunner.display(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| uiController.doAfterPresentationUpdate(function() { |
| uiController.uiScriptComplete(); |
| });`, resolve); |
| }); |
| } |
| |
| static ensureStablePresentationUpdate() |
| { |
| if (!this.isWebKit2()) { |
| testRunner.display(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| uiController.doAfterNextStablePresentationUpdate(function() { |
| uiController.uiScriptComplete(); |
| });`, resolve); |
| }); |
| } |
| |
| static ensurePositionInformationUpdateForElement(element) |
| { |
| const boundingRect = element.getBoundingClientRect(); |
| const x = boundingRect.x + 5; |
| const y = boundingRect.y + 5; |
| |
| if (!this.isWebKit2()) { |
| testRunner.display(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| uiController.ensurePositionInformationIsUpToDateAt(${x}, ${y}, function () { |
| uiController.uiScriptComplete(); |
| });`, resolve); |
| }); |
| } |
| |
| static delayFor(ms) |
| { |
| return new Promise(resolve => setTimeout(resolve, ms)); |
| } |
| |
| static immediateScrollTo(x, y) |
| { |
| if (!this.isWebKit2()) { |
| window.scrollTo(x, y); |
| return Promise.resolve(); |
| } |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| uiController.immediateScrollToOffset(${x}, ${y});`, resolve); |
| }); |
| } |
| |
| static immediateUnstableScrollTo(x, y) |
| { |
| if (!this.isWebKit2()) { |
| window.scrollTo(x, y); |
| return Promise.resolve(); |
| } |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| uiController.stableStateOverride = false; |
| uiController.immediateScrollToOffset(${x}, ${y});`, resolve); |
| }); |
| } |
| |
| static immediateScrollElementAtContentPointToOffset(x, y, scrollX, scrollY, scrollUpdatesDisabled = false) |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| uiController.scrollUpdatesDisabled = ${scrollUpdatesDisabled}; |
| uiController.immediateScrollElementAtContentPointToOffset(${x}, ${y}, ${scrollX}, ${scrollY});`, resolve); |
| }); |
| } |
| |
| static ensureVisibleContentRectUpdate() |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| const visibleContentRectUpdateScript = "uiController.doAfterVisibleContentRectUpdate(() => uiController.uiScriptComplete())"; |
| testRunner.runUIScript(visibleContentRectUpdateScript, resolve); |
| }); |
| } |
| |
| static longPressAndGetContextMenuContentAt(x, y) |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| uiController.didShowContextMenuCallback = function() { |
| uiController.uiScriptComplete(JSON.stringify(uiController.contentsOfUserInterfaceItem('contextMenu'))); |
| }; |
| uiController.longPressAtPoint(${x}, ${y}, function() { }); |
| })();`, result => resolve(JSON.parse(result))); |
| }); |
| } |
| |
| static activateAndWaitForInputSessionAt(x, y) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return this.activateAt(x, y); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| function clearCallbacksAndScriptComplete() { |
| uiController.didShowKeyboardCallback = null; |
| uiController.willPresentPopoverCallback = null; |
| uiController.uiScriptComplete(); |
| } |
| uiController.didShowKeyboardCallback = clearCallbacksAndScriptComplete; |
| uiController.willPresentPopoverCallback = clearCallbacksAndScriptComplete; |
| uiController.singleTapAtPoint(${x}, ${y}, function() { }); |
| })()`, resolve); |
| }); |
| } |
| |
| static waitForInputSessionToDismiss() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| function clearCallbacksAndScriptComplete() { |
| uiController.didHideKeyboardCallback = null; |
| uiController.didDismissPopoverCallback = null; |
| uiController.uiScriptComplete(); |
| } |
| uiController.didHideKeyboardCallback = clearCallbacksAndScriptComplete; |
| uiController.didDismissPopoverCallback = clearCallbacksAndScriptComplete; |
| })()`, resolve); |
| }); |
| } |
| |
| static activateElementAndWaitForInputSession(element) |
| { |
| const x = element.offsetLeft + element.offsetWidth / 2; |
| const y = element.offsetTop + element.offsetHeight / 2; |
| return this.activateAndWaitForInputSessionAt(x, y); |
| } |
| |
| static activateFormControl(element) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return this.activateElement(element); |
| |
| const x = element.offsetLeft + element.offsetWidth / 2; |
| const y = element.offsetTop + element.offsetHeight / 2; |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| uiController.didStartFormControlInteractionCallback = function() { |
| uiController.uiScriptComplete(); |
| }; |
| uiController.singleTapAtPoint(${x}, ${y}, function() { }); |
| })()`, resolve); |
| }); |
| } |
| |
| static dismissFormAccessoryView() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| uiController.dismissFormAccessoryView(); |
| uiController.uiScriptComplete(); |
| })()`, resolve); |
| }); |
| } |
| |
| static isShowingKeyboard() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript("uiController.isShowingKeyboard", result => resolve(result === "true")); |
| }); |
| } |
| |
| static hasInputSession() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript("uiController.hasInputSession", result => resolve(result === "true")); |
| }); |
| } |
| |
| static isPresentingModally() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript("uiController.isPresentingModally", result => resolve(result === "true")); |
| }); |
| } |
| |
| static deactivateFormControl(element) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) { |
| element.blur(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise(async resolve => { |
| element.blur(); |
| while (await this.isPresentingModally()) |
| continue; |
| while (await this.isShowingKeyboard()) |
| continue; |
| resolve(); |
| }); |
| } |
| |
| static waitForPopoverToPresent() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| if (uiController.isShowingPopover) |
| uiController.uiScriptComplete(); |
| else |
| uiController.willPresentPopoverCallback = () => uiController.uiScriptComplete(); |
| })()`, resolve); |
| }); |
| } |
| |
| static waitForPopoverToDismiss() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| if (uiController.isShowingPopover) |
| uiController.didDismissPopoverCallback = () => uiController.uiScriptComplete(); |
| else |
| uiController.uiScriptComplete(); |
| })()`, resolve); |
| }); |
| } |
| |
| static waitForKeyboardToHide() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| if (uiController.isShowingKeyboard) |
| uiController.didHideKeyboardCallback = () => uiController.uiScriptComplete(); |
| else |
| uiController.uiScriptComplete(); |
| })()`, resolve); |
| }); |
| } |
| |
| static getUICaretRect() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(function() { |
| uiController.doAfterNextStablePresentationUpdate(function() { |
| uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionCaretRect)); |
| }); |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }); |
| }); |
| } |
| |
| static getUISelectionRects() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(function() { |
| uiController.doAfterNextStablePresentationUpdate(function() { |
| uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionRangeRects)); |
| }); |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }); |
| }); |
| } |
| |
| static getUICaretViewRect() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(function() { |
| uiController.doAfterNextStablePresentationUpdate(function() { |
| uiController.uiScriptComplete(JSON.stringify(uiController.selectionCaretViewRect)); |
| }); |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }); |
| }); |
| } |
| |
| static getUISelectionViewRects() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(function() { |
| uiController.doAfterNextStablePresentationUpdate(function() { |
| uiController.uiScriptComplete(JSON.stringify(uiController.selectionRangeViewRects)); |
| }); |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }); |
| }); |
| } |
| |
| static getSelectionStartGrabberViewRect() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(function() { |
| uiController.doAfterNextStablePresentationUpdate(function() { |
| uiController.uiScriptComplete(JSON.stringify(uiController.selectionStartGrabberViewRect)); |
| }); |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }); |
| }); |
| } |
| |
| static getSelectionEndGrabberViewRect() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(function() { |
| uiController.doAfterNextStablePresentationUpdate(function() { |
| uiController.uiScriptComplete(JSON.stringify(uiController.selectionEndGrabberViewRect)); |
| }); |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }); |
| }); |
| } |
| |
| static replaceTextAtRange(text, location, length) { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.replaceTextAtRange("${text}", ${location}, ${length}); |
| uiController.uiScriptComplete(); |
| })()`, resolve); |
| }); |
| } |
| |
| static wait(promise) |
| { |
| testRunner.waitUntilDone(); |
| if (window.finishJSTest) |
| window.jsTestIsAsync = true; |
| |
| let finish = () => { |
| if (window.finishJSTest) |
| finishJSTest(); |
| else |
| testRunner.notifyDone(); |
| } |
| |
| return promise.then(finish, finish); |
| } |
| |
| static withUserGesture(callback) |
| { |
| internals.withUserGesture(callback); |
| } |
| |
| static selectFormAccessoryPickerRow(rowIndex) |
| { |
| const selectRowScript = `uiController.selectFormAccessoryPickerRow(${rowIndex})`; |
| return new Promise(resolve => testRunner.runUIScript(selectRowScript, resolve)); |
| } |
| |
| static selectFormAccessoryHasCheckedItemAtRow(rowIndex) |
| { |
| return new Promise(resolve => testRunner.runUIScript(`uiController.selectFormAccessoryHasCheckedItemAtRow(${rowIndex})`, result => { |
| resolve(result === "true"); |
| })); |
| } |
| |
| static selectFormPopoverTitle() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(uiController.selectFormPopoverTitle); |
| })()`, resolve); |
| }); |
| } |
| |
| static enterText(text) |
| { |
| const escapedText = text.replace(/`/g, "\\`"); |
| const enterTextScript = `(() => uiController.enterText(\`${escapedText}\`))()`; |
| return new Promise(resolve => testRunner.runUIScript(enterTextScript, resolve)); |
| } |
| |
| static setTimePickerValue(hours, minutes) |
| { |
| const setValueScript = `(() => uiController.setTimePickerValue(${hours}, ${minutes}))()`; |
| return new Promise(resolve => testRunner.runUIScript(setValueScript, resolve)); |
| } |
| |
| static timerPickerValues() |
| { |
| if (!this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| const uiScript = "JSON.stringify([uiController.timePickerValueHour, uiController.timePickerValueMinute])"; |
| return new Promise(resolve => testRunner.runUIScript(uiScript, result => { |
| const [hour, minute] = JSON.parse(result) |
| resolve({ hour: hour, minute: minute }); |
| })); |
| } |
| |
| static setShareSheetCompletesImmediatelyWithResolution(resolved) |
| { |
| const resolveShareSheet = `(() => uiController.setShareSheetCompletesImmediatelyWithResolution(${resolved}))()`; |
| return new Promise(resolve => testRunner.runUIScript(resolveShareSheet, resolve)); |
| } |
| |
| static textContentType() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(uiController.textContentType); |
| })()`, resolve); |
| }); |
| } |
| |
| static formInputLabel() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(uiController.formInputLabel); |
| })()`, resolve); |
| }); |
| } |
| |
| static activateDataListSuggestion(index) { |
| const script = `uiController.activateDataListSuggestion(${index}, () => { |
| uiController.uiScriptComplete(""); |
| });`; |
| return new Promise(resolve => testRunner.runUIScript(script, resolve)); |
| } |
| |
| static isShowingDataListSuggestions() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(uiController.isShowingDataListSuggestions); |
| })()`, result => resolve(result === "true")); |
| }); |
| } |
| |
| static zoomScale() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(uiController.zoomScale); |
| })()`, scaleAsString => resolve(parseFloat(scaleAsString))); |
| }); |
| } |
| |
| static zoomToScale(scale) |
| { |
| const uiScript = `uiController.zoomToScale(${scale}, () => uiController.uiScriptComplete(uiController.zoomScale))`; |
| return new Promise(resolve => testRunner.runUIScript(uiScript, resolve)); |
| } |
| |
| static immediateZoomToScale(scale) |
| { |
| const uiScript = `uiController.immediateZoomToScale(${scale})`; |
| return new Promise(resolve => testRunner.runUIScript(uiScript, resolve)); |
| } |
| |
| static typeCharacter(characterString) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) { |
| eventSender.keyDown(characterString); |
| return; |
| } |
| |
| const escapedString = characterString.replace(/\\/g, "\\\\").replace(/`/g, "\\`"); |
| const uiScript = `uiController.typeCharacterUsingHardwareKeyboard(\`${escapedString}\`, () => uiController.uiScriptComplete())`; |
| return new Promise(resolve => testRunner.runUIScript(uiScript, resolve)); |
| } |
| |
| static applyAutocorrection(newText, oldText) |
| { |
| if (!this.isWebKit2()) |
| return; |
| |
| const [escapedNewText, escapedOldText] = [newText.replace(/`/g, "\\`"), oldText.replace(/`/g, "\\`")]; |
| const uiScript = `uiController.applyAutocorrection(\`${escapedNewText}\`, \`${escapedOldText}\`, () => uiController.uiScriptComplete())`; |
| return new Promise(resolve => testRunner.runUIScript(uiScript, resolve)); |
| } |
| |
| static inputViewBounds() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(JSON.stringify(uiController.inputViewBounds)); |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }); |
| }); |
| } |
| |
| static calendarType() |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.doAfterNextStablePresentationUpdate(() => { |
| uiController.uiScriptComplete(JSON.stringify(uiController.calendarType)); |
| }) |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }); |
| }); |
| } |
| |
| static setDefaultCalendarType(calendarIdentifier, localeIdentifier) |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => testRunner.runUIScript(`uiController.setDefaultCalendarType('${calendarIdentifier}', '${localeIdentifier}')`, resolve)); |
| |
| } |
| |
| static setViewScale(scale) |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => testRunner.runUIScript(`uiController.setViewScale(${scale})`, resolve)); |
| } |
| |
| static resignFirstResponder() |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => testRunner.runUIScript(`uiController.resignFirstResponder()`, resolve)); |
| } |
| |
| static minimumZoomScale() |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(uiController.minimumZoomScale); |
| })()`, scaleAsString => resolve(parseFloat(scaleAsString))) |
| }); |
| } |
| |
| static drawSquareInEditableImage() |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => testRunner.runUIScript(`uiController.drawSquareInEditableImage()`, resolve)); |
| } |
| |
| static stylusTapAt(x, y, modifiers=[]) |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise((resolve) => { |
| testRunner.runUIScript(` |
| uiController.stylusTapAtPointWithModifiers(${x}, ${y}, 2, 1, 0.5, ${JSON.stringify(modifiers)}, function() { |
| uiController.uiScriptComplete(); |
| });`, resolve); |
| }); |
| } |
| |
| static numberOfStrokesInEditableImage() |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(uiController.numberOfStrokesInEditableImage); |
| })()`, numberAsString => resolve(parseInt(numberAsString, 10))) |
| }); |
| } |
| |
| static attachmentInfo(attachmentIdentifier) |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.uiScriptComplete(JSON.stringify(uiController.attachmentInfo('${attachmentIdentifier}'))); |
| })()`, jsonString => { |
| resolve(JSON.parse(jsonString)); |
| }) |
| }); |
| } |
| |
| static setMinimumEffectiveWidth(effectiveWidth) |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => testRunner.runUIScript(`uiController.setMinimumEffectiveWidth(${effectiveWidth})`, resolve)); |
| } |
| |
| static setAllowsViewportShrinkToFit(allows) |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => testRunner.runUIScript(`uiController.setAllowsViewportShrinkToFit(${allows})`, resolve)); |
| } |
| |
| static setKeyboardInputModeIdentifier(identifier) |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| const escapedIdentifier = identifier.replace(/`/g, "\\`"); |
| return new Promise(resolve => testRunner.runUIScript(`uiController.setKeyboardInputModeIdentifier(\`${escapedIdentifier}\`)`, resolve)); |
| } |
| |
| static contentOffset() |
| { |
| if (!this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| const uiScript = "JSON.stringify([uiController.contentOffsetX, uiController.contentOffsetY])"; |
| return new Promise(resolve => testRunner.runUIScript(uiScript, result => { |
| const [offsetX, offsetY] = JSON.parse(result) |
| resolve({ x: offsetX, y: offsetY }); |
| })); |
| } |
| |
| static undoAndRedoLabels() |
| { |
| if (!this.isWebKit2()) |
| return Promise.resolve(); |
| |
| const script = "JSON.stringify([uiController.lastUndoLabel, uiController.firstRedoLabel])"; |
| return new Promise(resolve => testRunner.runUIScript(script, result => resolve(JSON.parse(result)))); |
| } |
| |
| static waitForMenuToShow() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| if (!uiController.isShowingMenu) |
| uiController.didShowMenuCallback = () => uiController.uiScriptComplete(); |
| else |
| uiController.uiScriptComplete(); |
| })()`, resolve); |
| }); |
| } |
| |
| static waitForMenuToHide() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| if (uiController.isShowingMenu) |
| uiController.didHideMenuCallback = () => uiController.uiScriptComplete(); |
| else |
| uiController.uiScriptComplete(); |
| })()`, resolve); |
| }); |
| } |
| |
| static isShowingMenu() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`uiController.isShowingMenu`, result => resolve(result === "true")); |
| }); |
| } |
| |
| static isDismissingMenu() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`uiController.isDismissingMenu`, result => resolve(result === "true")); |
| }); |
| } |
| |
| static menuRect() |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript("JSON.stringify(uiController.menuRect)", result => resolve(JSON.parse(result))); |
| }); |
| } |
| |
| static setHardwareKeyboardAttached(attached) |
| { |
| return new Promise(resolve => testRunner.runUIScript(`uiController.setHardwareKeyboardAttached(${attached ? "true" : "false"})`, resolve)); |
| } |
| |
| static rectForMenuAction(action) |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (() => { |
| const rect = uiController.rectForMenuAction("${action}"); |
| uiController.uiScriptComplete(rect ? JSON.stringify(rect) : ""); |
| })(); |
| `, stringResult => { |
| resolve(stringResult.length ? JSON.parse(stringResult) : null); |
| }); |
| }); |
| } |
| |
| static async chooseMenuAction(action) |
| { |
| const menuRect = await this.rectForMenuAction(action); |
| if (menuRect) |
| await this.activateAt(menuRect.left + menuRect.width / 2, menuRect.top + menuRect.height / 2); |
| } |
| |
| static waitForEvent(target, eventName) |
| { |
| return new Promise(resolve => target.addEventListener(eventName, resolve, { once: true })); |
| } |
| |
| static callFunctionAndWaitForEvent(functionToCall, target, eventName) |
| { |
| return new Promise((resolve) => { |
| target.addEventListener(eventName, resolve, { once: true }); |
| functionToCall(); |
| }); |
| } |
| |
| static callFunctionAndWaitForScrollToFinish(functionToCall, ...theArguments) |
| { |
| return UIHelper.callFunctionAndWaitForTargetScrollToFinish(window, functionToCall, theArguments) |
| } |
| |
| static callFunctionAndWaitForTargetScrollToFinish(scrollTarget, functionToCall, ...theArguments) |
| { |
| return new Promise((resolved) => { |
| function scrollDidFinish() { |
| scrollTarget.removeEventListener("scroll", handleScroll, true); |
| resolved(); |
| } |
| |
| let lastScrollTimerId = 0; // When the timer with this id fires then the page has finished scrolling. |
| function handleScroll() { |
| if (lastScrollTimerId) { |
| window.clearTimeout(lastScrollTimerId); |
| lastScrollTimerId = 0; |
| } |
| lastScrollTimerId = window.setTimeout(scrollDidFinish, 300); // Over 250ms to give some room for error. |
| } |
| scrollTarget.addEventListener("scroll", handleScroll, true); |
| |
| functionToCall.apply(this, theArguments); |
| }); |
| } |
| |
| static rotateDevice(orientationName, animatedResize = false) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.${animatedResize ? "simulateRotationLikeSafari" : "simulateRotation"}("${orientationName}", function() { |
| uiController.doAfterVisibleContentRectUpdate(() => uiController.uiScriptComplete()); |
| }); |
| })()`, resolve); |
| }); |
| } |
| |
| static getScrollingTree() |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| return uiController.scrollingTreeAsText; |
| })()`, resolve); |
| }); |
| } |
| |
| static dragFromPointToPoint(fromX, fromY, toX, toY, duration) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) { |
| eventSender.mouseMoveTo(fromX, fromY); |
| eventSender.mouseDown(); |
| eventSender.leapForward(duration * 1000); |
| eventSender.mouseMoveTo(toX, toY); |
| eventSender.mouseUp(); |
| return Promise.resolve(); |
| } |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(`(() => { |
| uiController.dragFromPointToPoint(${fromX}, ${fromY}, ${toX}, ${toY}, ${duration}, () => { |
| uiController.uiScriptComplete(); |
| }); |
| })();`, resolve); |
| }); |
| } |
| |
| static waitForDoubleTapDelay() |
| { |
| const uiScript = `uiController.doAfterDoubleTapDelay(() => uiController.uiScriptComplete(""))`; |
| return new Promise(resolve => testRunner.runUIScript(uiScript, resolve)); |
| } |
| |
| static async waitForSelectionToAppear() { |
| while (true) { |
| if ((await this.getUISelectionViewRects()).length > 0) |
| break; |
| } |
| } |
| |
| static async waitForSelectionToDisappear() { |
| while (true) { |
| if (!(await this.getUISelectionViewRects()).length) |
| break; |
| } |
| } |
| |
| static async copyText(text) { |
| const copyTextScript = `uiController.copyText(\`${text.replace(/`/g, "\\`")}\`)`; |
| return new Promise(resolve => testRunner.runUIScript(copyTextScript, resolve)); |
| } |
| |
| static async paste() { |
| return new Promise(resolve => testRunner.runUIScript(`uiController.paste()`, resolve)); |
| } |
| |
| static async setContinuousSpellCheckingEnabled(enabled) { |
| return new Promise(resolve => { |
| testRunner.runUIScript(`uiController.setContinuousSpellCheckingEnabled(${enabled})`, resolve); |
| }); |
| } |
| |
| static async longPressElement(element) |
| { |
| return this.longPressAtPoint(element.offsetLeft + element.offsetWidth / 2, element.offsetTop + element.offsetHeight / 2); |
| } |
| |
| static async longPressAtPoint(x, y) |
| { |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| uiController.longPressAtPoint(${x}, ${y}, function() { |
| uiController.uiScriptComplete(); |
| }); |
| })();`, resolve); |
| }); |
| } |
| |
| static async activateElementAfterInstallingTapGestureOnWindow(element) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return activateElement(element); |
| |
| const x = element.offsetLeft + element.offsetWidth / 2; |
| const y = element.offsetTop + element.offsetHeight / 2; |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| let progress = 0; |
| function incrementProgress() { |
| if (++progress == 2) |
| uiController.uiScriptComplete(); |
| } |
| uiController.installTapGestureOnWindow(incrementProgress); |
| uiController.singleTapAtPoint(${x}, ${y}, incrementProgress); |
| })();`, resolve); |
| }); |
| } |
| |
| static mayContainEditableElementsInRect(x, y, width, height) |
| { |
| if (!this.isWebKit2() || !this.isIOSFamily()) |
| return Promise.resolve(false); |
| |
| return new Promise(resolve => { |
| testRunner.runUIScript(` |
| (function() { |
| uiController.doAfterPresentationUpdate(function() { |
| uiController.uiScriptComplete(uiController.mayContainEditableElementsInRect(${x}, ${y}, ${width}, ${height})); |
| }) |
| })();`, result => resolve(result === "true")); |
| }); |
| } |
| } |
| |
| UIHelper.EventStreamBuilder = class { |
| constructor() |
| { |
| // FIXME: This could support additional customization options, such as interpolation, timestep, and different |
| // digitizer indices in the future. For now, just make it simpler to string together sequences of pan gestures. |
| this._reset(); |
| } |
| |
| _reset() { |
| this.events = []; |
| this.currentTimeOffset = 0; |
| this.currentX = 0; |
| this.currentY = 0; |
| } |
| |
| begin(x, y) { |
| console.assert(this.currentTimeOffset === 0); |
| this.events.push({ |
| interpolate : "linear", |
| timestep : 0.016, |
| coordinateSpace : "content", |
| startEvent : { |
| inputType : "hand", |
| timeOffset : this.currentTimeOffset, |
| touches : [{ inputType : "finger", phase : "began", id : 1, x : x, y : y, pressure : 0 }] |
| }, |
| endEvent : { |
| inputType : "hand", |
| timeOffset : this.currentTimeOffset, |
| touches : [{ inputType : "finger", phase : "began", id : 1, x : x, y : y, pressure : 0 }] |
| } |
| }); |
| this.currentX = x; |
| this.currentY = y; |
| return this; |
| } |
| |
| move(x, y, duration = 0) { |
| const previousTimeOffset = this.currentTimeOffset; |
| this.currentTimeOffset += duration; |
| this.events.push({ |
| interpolate : "linear", |
| timestep : 0.016, |
| coordinateSpace : "content", |
| startEvent : { |
| inputType : "hand", |
| timeOffset : previousTimeOffset, |
| touches : [{ inputType : "finger", phase : "moved", id : 1, x : this.currentX, y : this.currentY, pressure : 0 }] |
| }, |
| endEvent : { |
| inputType : "hand", |
| timeOffset : this.currentTimeOffset, |
| touches : [{ inputType : "finger", phase : "moved", id : 1, x : x, y : y, pressure : 0 }] |
| } |
| }); |
| this.currentX = x; |
| this.currentY = y; |
| return this; |
| } |
| |
| end() { |
| this.events.push({ |
| interpolate : "linear", |
| timestep : 0.016, |
| coordinateSpace : "content", |
| startEvent : { |
| inputType : "hand", |
| timeOffset : this.currentTimeOffset, |
| touches : [{ inputType : "finger", phase : "ended", id : 1, x : this.currentX, y : this.currentY, pressure : 0 }] |
| }, |
| endEvent : { |
| inputType : "hand", |
| timeOffset : this.currentTimeOffset, |
| touches : [{ inputType : "finger", phase : "ended", id : 1, x : this.currentX, y : this.currentY, pressure : 0 }] |
| } |
| }); |
| return this; |
| } |
| |
| takeResult() { |
| const events = this.events; |
| this._reset(); |
| return { "events": events }; |
| } |
| } |