mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
198 lines
7.4 KiB
Java
198 lines
7.4 KiB
Java
/*
|
|
* Copyright (c) Microsoft Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.microsoft.playwright;
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.io.OutputStreamWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.concurrent.Semaphore;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
import static com.microsoft.playwright.Utils.mapOf;
|
|
import static java.util.Arrays.asList;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
public class TestTap extends TestBase {
|
|
|
|
@Override
|
|
BrowserContext createContext() {
|
|
return browser.newContext(new Browser.NewContextOptions().withHasTouch(true));
|
|
}
|
|
|
|
private JSHandle trackEvents(ElementHandle target) {
|
|
return target.evaluateHandle("target => {\n" +
|
|
" const events = [];\n" +
|
|
" for (const event of [\n" +
|
|
" 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'click',\n" +
|
|
" 'pointercancel', 'pointerdown', 'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover', 'pointerup',\n" +
|
|
" 'touchstart', 'touchend', 'touchmove', 'touchcancel'])\n" +
|
|
" target.addEventListener(event, () => events.push(event), false);\n" +
|
|
" return events;\n" +
|
|
"}");
|
|
}
|
|
|
|
@Test
|
|
void shouldSendAllOfTheCorrectEvents() {
|
|
page.setContent("<div id='a' style='background: lightblue; width: 50px; height: 50px'>a</div>\n" +
|
|
"<div id='b' style='background: pink; width: 50px; height: 50px'>b</div>");
|
|
page.tap("#a");
|
|
JSHandle eventsHandle = trackEvents(page.querySelector("#b"));
|
|
page.tap("#b");
|
|
// webkit doesnt send pointerenter or pointerleave or mouseout
|
|
assertEquals(asList("pointerover", "pointerenter",
|
|
"pointerdown", "touchstart",
|
|
"pointerup", "pointerout",
|
|
"pointerleave", "touchend",
|
|
"mouseover", "mouseenter",
|
|
"mousemove", "mousedown",
|
|
"mouseup", "click"),
|
|
eventsHandle.jsonValue());
|
|
}
|
|
|
|
@Test
|
|
void shouldNotSendMouseEventsTouchstartIsCanceled() {
|
|
page.setContent("<div style='width: 50px; height: 50px; background: red'>");
|
|
page.evaluate("() => {\n" +
|
|
" // touchstart is not cancelable unless passive is false\n" +
|
|
" document.addEventListener('touchstart', t => t.preventDefault(), {passive: false});\n" +
|
|
" }");
|
|
JSHandle eventsHandle = trackEvents(page.querySelector("div"));
|
|
page.tap("div");
|
|
assertEquals(asList("pointerover", "pointerenter",
|
|
"pointerdown", "touchstart",
|
|
"pointerup", "pointerout",
|
|
"pointerleave", "touchend"),
|
|
eventsHandle.jsonValue());
|
|
}
|
|
|
|
@Test
|
|
void shouldNotSendMouseEventsWhenTouchendIsCanceled() {
|
|
page.setContent("<div style='width: 50px; height: 50px; background: red'>");
|
|
page.evaluate("() => document.addEventListener('touchend', t => t.preventDefault())");
|
|
JSHandle eventsHandle = trackEvents(page.querySelector("div"));
|
|
page.tap("div");
|
|
assertEquals(asList("pointerover", "pointerenter",
|
|
"pointerdown", "touchstart",
|
|
"pointerup", "pointerout",
|
|
"pointerleave", "touchend"),
|
|
eventsHandle.jsonValue());
|
|
}
|
|
|
|
@Test
|
|
void shouldWaitForANavigationCausedByATap() throws InterruptedException {
|
|
page.navigate(server.EMPTY_PAGE);
|
|
page.setContent("<a href='/intercept-this.html'>link</a>;");
|
|
Semaphore responseWritten = new Semaphore(0);
|
|
List<String> events = Collections.synchronizedList(new ArrayList<>());
|
|
server.setRoute("/intercept-this.html", exchange -> {
|
|
// make sure the tap doesnt resolve too early
|
|
try {
|
|
Thread.sleep(100);
|
|
} catch (InterruptedException e) {
|
|
events.add("interrupted");
|
|
}
|
|
exchange.getResponseHeaders().add("Content-Type", "application/octet-stream");
|
|
exchange.sendResponseHeaders(200, 0);
|
|
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
|
writer.write("foo");
|
|
}
|
|
events.add("sent response");
|
|
responseWritten.release();
|
|
});
|
|
page.tap("a");
|
|
events.add("tap finished");
|
|
responseWritten.acquire();
|
|
assertEquals(asList("sent response", "tap finished"), events);
|
|
}
|
|
|
|
@Test
|
|
void shouldWorkWithModifiers() {
|
|
page.setContent("hello world");
|
|
page.evaluate("() => {\n" +
|
|
" window.touchPromise = new Promise(resolve => {\n" +
|
|
" document.addEventListener('touchstart', event => {\n" +
|
|
" resolve(event.altKey);\n" +
|
|
" }, {passive: false});\n" +
|
|
" });\n" +
|
|
"}");
|
|
page.tap("body", new Page.TapOptions().withModifiers(Keyboard.Modifier.ALT));
|
|
Object altKey = page.evaluate("() => window.touchPromise");
|
|
assertEquals(true, altKey);
|
|
}
|
|
|
|
@Test
|
|
void shouldSendWellFormedTouchPoints() {
|
|
page.evaluate("() => {\n" +
|
|
" window.touchStartPromise = new Promise(resolve => {\n" +
|
|
" document.addEventListener('touchstart', event => {\n" +
|
|
" resolve([...event.touches].map(t => ({\n" +
|
|
" identifier: t.identifier,\n" +
|
|
" clientX: t.clientX,\n" +
|
|
" clientY: t.clientY,\n" +
|
|
" pageX: t.pageX,\n" +
|
|
" pageY: t.pageY,\n" +
|
|
" radiusX: 'radiusX' in t ? t.radiusX : t['webkitRadiusX'],\n" +
|
|
" radiusY: 'radiusY' in t ? t.radiusY : t['webkitRadiusY'],\n" +
|
|
" rotationAngle: 'rotationAngle' in t ? t.rotationAngle : t['webkitRotationAngle'],\n" +
|
|
" force: 'force' in t ? t.force : t['webkitForce'],\n" +
|
|
" })));\n" +
|
|
" }, false);\n" +
|
|
" })\n" +
|
|
" }");
|
|
page.evaluate("() => {\n" +
|
|
" window.touchEndPromise = new Promise(resolve => {\n" +
|
|
" document.addEventListener('touchend', event => {\n" +
|
|
" resolve([...event.touches].map(t => ({\n" +
|
|
" identifier: t.identifier,\n" +
|
|
" clientX: t.clientX,\n" +
|
|
" clientY: t.clientY,\n" +
|
|
" pageX: t.pageX,\n" +
|
|
" pageY: t.pageY,\n" +
|
|
" radiusX: 'radiusX' in t ? t.radiusX : t['webkitRadiusX'],\n" +
|
|
" radiusY: 'radiusY' in t ? t.radiusY : t['webkitRadiusY'],\n" +
|
|
" rotationAngle: 'rotationAngle' in t ? t.rotationAngle : t['webkitRotationAngle'],\n" +
|
|
" force: 'force' in t ? t.force : t['webkitForce'],\n" +
|
|
" })));\n" +
|
|
" }, false);\n" +
|
|
" })\n" +
|
|
" }");
|
|
|
|
page.touchscreen().tap(40, 60);
|
|
Object touchStart = page.evaluate("() => window.touchStartPromise");
|
|
assertEquals(asList(mapOf(
|
|
"clientX", 40,
|
|
"clientY", 60,
|
|
"force", 1,
|
|
"identifier", 0,
|
|
"pageX", 40,
|
|
"pageY", 60,
|
|
"radiusX", 1,
|
|
"radiusY", 1,
|
|
"rotationAngle", 0
|
|
)), touchStart);
|
|
Object touchEnd = page.evaluate("() => window.touchEndPromise");
|
|
assertEquals(Collections.emptyList(), touchEnd);
|
|
}
|
|
|
|
void shouldWaitUntilAnElementIsVisibleToTapIt() {
|
|
// Ignored in sync api.
|
|
}
|
|
}
|