1
0
mirror of synced 2026-08-05 15:06:54 +00:00

test: add http server, first click test

This commit is contained in:
Yury Semikhatsky
2020-09-29 15:17:52 -07:00
parent 3fdecdca55
commit 2651edb3e5
17 changed files with 730 additions and 27 deletions
@@ -125,13 +125,10 @@ public class FrameImpl extends ChannelOwner implements Frame {
}
public <T> T evalTyped(String expression) {
JsonElement json = evaluate(expression, null, false);
System.out.println("json = " + new Gson().toJson(json));
SerializedValue value = new Gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
return deserialize(value);
return (T) evaluate(expression, null, false);
}
JsonElement evaluate(String expression, Object arg, boolean forceExpression) {
private Object evaluate(String expression, Object arg, boolean forceExpression) {
JsonObject params = new JsonObject();
params.addProperty("expression", expression);
params.addProperty("world", "main");
@@ -139,7 +136,10 @@ public class FrameImpl extends ChannelOwner implements Frame {
forceExpression = true;
params.addProperty("isFunction", !forceExpression);
params.add("arg", new Gson().toJsonTree(serializeArgument(arg)));
return sendMessage("evaluateExpression", params);
JsonElement json = sendMessage("evaluateExpression", params);
// System.out.println("json = " + new Gson().toJson(json));
SerializedValue value = new Gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
return deserialize(value);
}
@Override
@@ -92,10 +92,6 @@ public class PageImpl extends ChannelOwner implements Page {
return mainFrame.evalTyped(expression);
}
public JsonElement evaluate(String expression) {
return evaluate(expression, null);
}
@Override
public void close(CloseOptions options) {
@@ -177,8 +173,8 @@ public class PageImpl extends ChannelOwner implements Page {
}
@Override
public JsonElement evaluate(String expression, Object arg) {
return evaluate(expression, arg, false);
public Object evaluate(String expression, Object arg) {
return mainFrame.evaluate(expression, arg);
}
@Override
@@ -406,8 +402,4 @@ public class PageImpl extends ChannelOwner implements Page {
public List<Worker> workers() {
return null;
}
public JsonElement evaluate(String expression, Object arg, boolean forceExpression) {
return mainFrame.evaluate(expression, arg, forceExpression);
}
}
@@ -0,0 +1,297 @@
/**
* Copyright (c) Microsoft Corporation.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.spi.FileTypeDetector;
import java.util.*;
import static java.util.Collections.singletonList;
public class Server implements HttpHandler {
private static final int port = 8907;
private static final int httpsPort = 8908;
private final HttpServer server;
public final String PREFIX;
public final String CROSS_PROCESS_PREFIX;
public final int PORT;
public final String EMPTY_PAGE;
private final File resourcesDir;
Server(int port) throws IOException {
PORT = port;
PREFIX = "http://localhost:" + PORT;
CROSS_PROCESS_PREFIX = "http://127.0.0.1:" + PORT;
EMPTY_PAGE = PREFIX + "/empty.html";
server = HttpServer.create(new InetSocketAddress("localhost", port), 0);
server.createContext("/", this);
server.setExecutor(null); // creates a default executor
File cwd = FileSystems.getDefault().getPath(".").toFile();
resourcesDir = new File(cwd, "src/test/resources");
server.start();
}
void stop() {
server.stop(0);
}
@Override
public void handle(HttpExchange exchange) throws IOException {
File file = new File(resourcesDir, exchange.getRequestURI().getPath().substring(1));
exchange.getResponseHeaders().put("Content-Type", singletonList(mimeType(file)));
try (FileInputStream input = new FileInputStream(file)) {
exchange.sendResponseHeaders(200, 0);
copy(input, exchange.getResponseBody());
} catch (IOException e) {
exchange.sendResponseHeaders(404, 0);
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("File not found: " + file.getCanonicalPath());
}
}
exchange.getResponseBody().close();
}
private static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[8192];
int read;
while ((read = in.read(buffer, 0, 8192)) != -1) {
out.write(buffer, 0, read);
}
}
private static String mimeType(File file) {
String name = file.getName();
int lastDotPos = name.lastIndexOf('.');
String extension = lastDotPos == -1 ? name : name.substring(lastDotPos + 1);
String mimeType = extensionToMime.get(extension);
if (mimeType == null) {
mimeType = "application/octet-stream";
}
return mimeType;
}
private static Map<String, String> extensionToMime = new HashMap<>();
static {
extensionToMime.put("ai", "application/postscript");
extensionToMime.put("apng", "image/apng");
extensionToMime.put("appcache", "text/cache-manifest");
extensionToMime.put("au", "audio/basic");
extensionToMime.put("bmp", "image/bmp");
extensionToMime.put("cer", "application/pkix-cert");
extensionToMime.put("cgm", "image/cgm");
extensionToMime.put("coffee", "text/coffeescript");
extensionToMime.put("conf", "text/plain");
extensionToMime.put("crl", "application/pkix-crl");
extensionToMime.put("css", "text/css");
extensionToMime.put("csv", "text/csv");
extensionToMime.put("def", "text/plain");
extensionToMime.put("doc", "application/msword");
extensionToMime.put("dot", "application/msword");
extensionToMime.put("drle", "image/dicom-rle");
extensionToMime.put("dtd", "application/xml-dtd");
extensionToMime.put("ear", "application/java-archive");
extensionToMime.put("emf", "image/emf");
extensionToMime.put("eps", "application/postscript");
extensionToMime.put("exr", "image/aces");
extensionToMime.put("fits", "image/fits");
extensionToMime.put("g3", "image/g3fax");
extensionToMime.put("gbr", "application/rpki-ghostbusters");
extensionToMime.put("gif", "image/gif");
extensionToMime.put("glb", "model/gltf-binary");
extensionToMime.put("gltf", "model/gltf+json");
extensionToMime.put("gz", "application/gzip");
extensionToMime.put("h261", "video/h261");
extensionToMime.put("h263", "video/h263");
extensionToMime.put("h264", "video/h264");
extensionToMime.put("heic", "image/heic");
extensionToMime.put("heics", "image/heic-sequence");
extensionToMime.put("heif", "image/heif");
extensionToMime.put("heifs", "image/heif-sequence");
extensionToMime.put("htm", "text/html");
extensionToMime.put("html", "text/html");
extensionToMime.put("ics", "text/calendar");
extensionToMime.put("ief", "image/ief");
extensionToMime.put("ifb", "text/calendar");
extensionToMime.put("iges", "model/iges");
extensionToMime.put("igs", "model/iges");
extensionToMime.put("in", "text/plain");
extensionToMime.put("ini", "text/plain");
extensionToMime.put("jade", "text/jade");
extensionToMime.put("jar", "application/java-archive");
extensionToMime.put("jls", "image/jls");
extensionToMime.put("jp2", "image/jp2");
extensionToMime.put("jpe", "image/jpeg");
extensionToMime.put("jpeg", "image/jpeg");
extensionToMime.put("jpf", "image/jpx");
extensionToMime.put("jpg", "image/jpeg");
extensionToMime.put("jpg2", "image/jp2");
extensionToMime.put("jpgm", "video/jpm");
extensionToMime.put("jpgv", "video/jpeg");
extensionToMime.put("jpm", "image/jpm");
extensionToMime.put("jpx", "image/jpx");
extensionToMime.put("js", "application/javascript");
extensionToMime.put("json", "application/json");
extensionToMime.put("json5", "application/json5");
extensionToMime.put("jsx", "text/jsx");
extensionToMime.put("jxr", "image/jxr");
extensionToMime.put("kar", "audio/midi");
extensionToMime.put("ktx", "image/ktx");
extensionToMime.put("less", "text/less");
extensionToMime.put("list", "text/plain");
extensionToMime.put("litcoffee", "text/coffeescript");
extensionToMime.put("log", "text/plain");
extensionToMime.put("m1v", "video/mpeg");
extensionToMime.put("m21", "application/mp21");
extensionToMime.put("m2a", "audio/mpeg");
extensionToMime.put("m2v", "video/mpeg");
extensionToMime.put("m3a", "audio/mpeg");
extensionToMime.put("m4a", "audio/mp4");
extensionToMime.put("m4p", "application/mp4");
extensionToMime.put("man", "text/troff");
extensionToMime.put("manifest", "text/cache-manifest");
extensionToMime.put("markdown", "text/markdown");
extensionToMime.put("mathml", "application/mathml+xml");
extensionToMime.put("md", "text/markdown");
extensionToMime.put("mdx", "text/mdx");
extensionToMime.put("me", "text/troff");
extensionToMime.put("mesh", "model/mesh");
extensionToMime.put("mft", "application/rpki-manifest");
extensionToMime.put("mid", "audio/midi");
extensionToMime.put("midi", "audio/midi");
extensionToMime.put("mj2", "video/mj2");
extensionToMime.put("mjp2", "video/mj2");
extensionToMime.put("mjs", "application/javascript");
extensionToMime.put("mml", "text/mathml");
extensionToMime.put("mov", "video/quicktime");
extensionToMime.put("mp2", "audio/mpeg");
extensionToMime.put("mp21", "application/mp21");
extensionToMime.put("mp2a", "audio/mpeg");
extensionToMime.put("mp3", "audio/mpeg");
extensionToMime.put("mp4", "video/mp4");
extensionToMime.put("mp4a", "audio/mp4");
extensionToMime.put("mp4s", "application/mp4");
extensionToMime.put("mp4v", "video/mp4");
extensionToMime.put("mpe", "video/mpeg");
extensionToMime.put("mpeg", "video/mpeg");
extensionToMime.put("mpg", "video/mpeg");
extensionToMime.put("mpg4", "video/mp4");
extensionToMime.put("mpga", "audio/mpeg");
extensionToMime.put("mrc", "application/marc");
extensionToMime.put("ms", "text/troff");
extensionToMime.put("msh", "model/mesh");
extensionToMime.put("n3", "text/n3");
extensionToMime.put("oga", "audio/ogg");
extensionToMime.put("ogg", "audio/ogg");
extensionToMime.put("ogv", "video/ogg");
extensionToMime.put("ogx", "application/ogg");
extensionToMime.put("otf", "font/otf");
extensionToMime.put("p10", "application/pkcs10");
extensionToMime.put("p7c", "application/pkcs7-mime");
extensionToMime.put("p7m", "application/pkcs7-mime");
extensionToMime.put("p7s", "application/pkcs7-signature");
extensionToMime.put("p8", "application/pkcs8");
extensionToMime.put("pdf", "application/pdf");
extensionToMime.put("pki", "application/pkixcmp");
extensionToMime.put("pkipath", "application/pkix-pkipath");
extensionToMime.put("png", "image/png");
extensionToMime.put("ps", "application/postscript");
extensionToMime.put("pskcxml", "application/pskc+xml");
extensionToMime.put("qt", "video/quicktime");
extensionToMime.put("rmi", "audio/midi");
extensionToMime.put("rng", "application/xml");
extensionToMime.put("roa", "application/rpki-roa");
extensionToMime.put("roff", "text/troff");
extensionToMime.put("rsd", "application/rsd+xml");
extensionToMime.put("rss", "application/rss+xml");
extensionToMime.put("rtf", "application/rtf");
extensionToMime.put("rtx", "text/richtext");
extensionToMime.put("s3m", "audio/s3m");
extensionToMime.put("sgi", "image/sgi");
extensionToMime.put("sgm", "text/sgml");
extensionToMime.put("sgml", "text/sgml");
extensionToMime.put("shex", "text/shex");
extensionToMime.put("shtml", "text/html");
extensionToMime.put("sil", "audio/silk");
extensionToMime.put("silo", "model/mesh");
extensionToMime.put("slim", "text/slim");
extensionToMime.put("slm", "text/slim");
extensionToMime.put("snd", "audio/basic");
extensionToMime.put("spx", "audio/ogg");
extensionToMime.put("stl", "model/stl");
extensionToMime.put("styl", "text/stylus");
extensionToMime.put("stylus", "text/stylus");
extensionToMime.put("svg", "image/svg+xml");
extensionToMime.put("svgz", "image/svg+xml");
extensionToMime.put("t", "text/troff");
extensionToMime.put("t38", "image/t38");
extensionToMime.put("text", "text/plain");
extensionToMime.put("tfx", "image/tiff-fx");
extensionToMime.put("tif", "image/tiff");
extensionToMime.put("tiff", "image/tiff");
extensionToMime.put("tr", "text/troff");
extensionToMime.put("ts", "video/mp2t");
extensionToMime.put("tsv", "text/tab-separated-values");
extensionToMime.put("ttc", "font/collection");
extensionToMime.put("ttf", "font/ttf");
extensionToMime.put("ttl", "text/turtle");
extensionToMime.put("txt", "text/plain");
extensionToMime.put("uri", "text/uri-list");
extensionToMime.put("uris", "text/uri-list");
extensionToMime.put("urls", "text/uri-list");
extensionToMime.put("vcard", "text/vcard");
extensionToMime.put("vrml", "model/vrml");
extensionToMime.put("vtt", "text/vtt");
extensionToMime.put("war", "application/java-archive");
extensionToMime.put("wasm", "application/wasm");
extensionToMime.put("wav", "audio/wav");
extensionToMime.put("weba", "audio/webm");
extensionToMime.put("webm", "video/webm");
extensionToMime.put("webmanifest", "application/manifest+json");
extensionToMime.put("webp", "image/webp");
extensionToMime.put("wmf", "image/wmf");
extensionToMime.put("woff", "font/woff");
extensionToMime.put("woff2", "font/woff2");
extensionToMime.put("wrl", "model/vrml");
extensionToMime.put("x3d", "model/x3d+xml");
extensionToMime.put("x3db", "model/x3d+fastinfoset");
extensionToMime.put("x3dbz", "model/x3d+binary");
extensionToMime.put("x3dv", "model/x3d-vrml");
extensionToMime.put("x3dvz", "model/x3d+vrml");
extensionToMime.put("x3dz", "model/x3d+xml");
extensionToMime.put("xaml", "application/xaml+xml");
extensionToMime.put("xht", "application/xhtml+xml");
extensionToMime.put("xhtml", "application/xhtml+xml");
extensionToMime.put("xm", "audio/xm");
extensionToMime.put("xml", "text/xml");
extensionToMime.put("xsd", "application/xml");
extensionToMime.put("xsl", "application/xml");
extensionToMime.put("xslt", "application/xslt+xml");
extensionToMime.put("yaml", "text/yaml");
extensionToMime.put("yml", "text/yaml");
extensionToMime.put("zip", "application/zip");
}
}
@@ -48,7 +48,7 @@ public class TestBrowser {
}
@Test
void should_click_the_button() {
void version_should_work() {
if (isChromium)
assertTrue(Pattern.matches("^\\d+\\.\\d+\\.\\d+\\.\\d+$", browser.version()));
else
@@ -16,26 +16,40 @@
package com.microsoft.playwright;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.*;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestClick {
private static Playwright playwright;
private static Server server;
private Browser browser;
private boolean isChromium;
private BrowserContext context;
private Page page;
@BeforeAll
static void beforeAll() {
static void createPlaywright() {
playwright = Playwright.create();
}
@BeforeAll
static void startServer() throws IOException {
server = new Server(8907);
}
@AfterAll
static void stopServer() throws IOException {
server.stop();
server = null;
}
@BeforeEach
void setUp() {
browser = playwright.chromium().launch();
BrowserType.LaunchOptions options = new BrowserType.LaunchOptions().withHeadless(false);
browser = playwright.chromium().launch(options);
isChromium = true;
context = browser.newContext();
page = context.newPage();
@@ -47,9 +61,9 @@ public class TestClick {
}
@Test
void version_should_work() {
// page.navigate(server.PREFIX + "/input/button.html");
// page.click("button");
// assertEquals("Clicked", page.evaluate("result"));
void should_click_the_button() {
page.navigate(server.PREFIX + "/input/button.html");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
}
}
View File
@@ -0,0 +1,42 @@
<style>
body, html { margin: 0; padding: 0; }
@keyframes move {
from { marign-left: 0; }
to { margin-left: 100px; }
}
</style>
<script>
function addButton() {
const button = document.createElement('button');
button.textContent = 'Click me';
button.style.animation = '3s linear move';
button.style.animationIterationCount = 'infinite';
button.addEventListener('click', () => window.clicked = true);
document.body.appendChild(button);
}
function stopButton(remove) {
const button = document.querySelector('button');
button.style.marginLeft = button.getBoundingClientRect().left + 'px';
button.style.animation = '';
if (remove)
button.remove();
}
let x = 0;
function jump() {
x += 300;
const button = document.querySelector('button');
button.style.marginLeft = x + 'px';
}
function startJumping() {
x = 0;
const moveIt = () => {
jump();
requestAnimationFrame(moveIt);
};
setInterval(jump, 0);
moveIt();
}
</script>
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>Button test</title>
</head>
<body>
<script src="mouse-helper.js"></script>
<button>Click target</button>
<script>
window.result = 'Was not clicked';
window.offsetX = undefined;
window.offsetY = undefined;
window.pageX = undefined;
window.pageY = undefined;
window.shiftKey = undefined;
window.pageX = undefined;
window.pageY = undefined;
window.bubbles = undefined;
document.querySelector('button').addEventListener('click', e => {
result = 'Clicked';
offsetX = e.offsetX;
offsetY = e.offsetY;
pageX = e.pageX;
pageY = e.pageY;
shiftKey = e.shiftKey;
bubbles = e.bubbles;
cancelable = e.cancelable;
composed = e.composed;
}, false);
</script>
</body>
</html>
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<title>Selection Test</title>
</head>
<body>
<label for="agree">Remember Me</label>
<input id="agree" type="checkbox">
<script>
window.result = {
check: null,
events: [],
};
let checkbox = document.querySelector('input');
const events = [
'change',
'click',
'dblclick',
'input',
'mousedown',
'mouseenter',
'mouseleave',
'mousemove',
'mouseout',
'mouseover',
'mouseup',
];
for (let event of events) {
checkbox.addEventListener(event, () => {
if (['change', 'click', 'dblclick', 'input'].includes(event) === true) {
result.check = checkbox.checked;
}
result.events.push(event);
}, false);
}
</script>
</body>
</html>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>File upload test</title>
</head>
<body>
<form action="/input/fileupload.html">
<input type="file">
<input type="submit">
</form>
</body>
</html>
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<title>Keyboard test</title>
</head>
<body>
<textarea></textarea>
<script>
window.result = "";
let textarea = document.querySelector('textarea');
textarea.focus();
textarea.addEventListener('keydown', event => {
log('Keydown:', event.key, event.code, event.which, modifiers(event));
});
textarea.addEventListener('keypress', event => {
log('Keypress:', event.key, event.code, event.which, event.charCode, modifiers(event));
});
textarea.addEventListener('keyup', event => {
log('Keyup:', event.key, event.code, event.which, modifiers(event));
});
function modifiers(event) {
let m = [];
if (event.altKey)
m.push('Alt')
if (event.ctrlKey)
m.push('Control');
if (event.shiftKey)
m.push('Shift')
return '[' + m.join(' ') + ']';
}
function log(...args) {
console.log.apply(console, args);
result += args.join(' ') + '\n';
}
function getResult() {
let temp = result.trim();
result = "";
return temp;
}
</script>
</body>
</html>
@@ -0,0 +1,62 @@
// This injects a box into the page that moves with the mouse;
// Useful for debugging
(function(){
const box = document.createElement('div');
box.classList.add('mouse-helper');
const styleElement = document.createElement('style');
styleElement.innerHTML = `
.mouse-helper {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
background: rgba(0,0,0,.4);
border: 1px solid white;
border-radius: 10px;
margin-left: -10px;
margin-top: -10px;
transition: background .2s, border-radius .2s, border-color .2s;
}
.mouse-helper.button-1 {
transition: none;
background: rgba(0,0,0,0.9);
}
.mouse-helper.button-2 {
transition: none;
border-color: rgba(0,0,255,0.9);
}
.mouse-helper.button-3 {
transition: none;
border-radius: 4px;
}
.mouse-helper.button-4 {
transition: none;
border-color: rgba(255,0,0,0.9);
}
.mouse-helper.button-5 {
transition: none;
border-color: rgba(0,255,0,0.9);
}
`;
document.head.appendChild(styleElement);
document.body.appendChild(box);
document.addEventListener('mousemove', event => {
box.style.left = event.pageX + 'px';
box.style.top = event.pageY + 'px';
updateButtons(event.buttons);
}, true);
document.addEventListener('mousedown', event => {
updateButtons(event.buttons);
box.classList.add('button-' + event.which);
}, true);
document.addEventListener('mouseup', event => {
updateButtons(event.buttons);
box.classList.remove('button-' + event.which);
}, true);
function updateButtons(buttons) {
for (let i = 0; i < 5; i++)
box.classList.toggle('button-' + i, buttons & (1 << i));
}
})();
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>Rotated button test</title>
</head>
<body>
<script src="mouse-helper.js"></script>
<button onclick="clicked();">Click target</button>
<style>
button {
transform: rotateY(180deg);
}
</style>
<script>
window.result = 'Was not clicked';
function clicked() {
result = 'Clicked';
}
</script>
</body>
</html>
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>Scrollable test</title>
</head>
<body>
<script src='mouse-helper.js'></script>
<script>
for (let i = 0; i < 100; i++) {
let button = document.createElement('button');
button.textContent = i + ': not clicked';
button.id = 'button-' + i;
button.onclick = () => button.textContent = 'clicked';
button.oncontextmenu = event => {
event.preventDefault();
button.textContent = 'context menu';
}
document.body.appendChild(button);
document.body.appendChild(document.createElement('br'));
}
</script>
</body>
</html>
+69
View File
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<title>Selection Test</title>
</head>
<body>
<select>
<option value="black">Black</option>
<option value="blue">Blue</option>
<option value="brown">Brown</option>
<option value="cyan">Cyan</option>
<option value="gray">Gray</option>
<option value="green">Green</option>
<option value="indigo">Indigo</option>
<option value="magenta">Magenta</option>
<option value="orange">Orange</option>
<option value="pink">Pink</option>
<option value="purple">Purple</option>
<option value="red">Red</option>
<option value="violet">Violet</option>
<option value="white" id="whiteOption">White</option>
<option value="yellow">Yellow</option>
</select>
<script>
window.result = {
onInput: null,
onChange: null,
onBubblingChange: null,
onBubblingInput: null,
};
let select = document.querySelector('select');
function makeEmpty() {
for (let i = select.options.length - 1; i >= 0; --i) {
select.remove(i);
}
}
function makeMultiple() {
select.setAttribute('multiple', true);
}
select.addEventListener('input', () => {
result.onInput = Array.from(select.querySelectorAll('option:checked')).map((option) => {
return option.value;
});
}, false);
select.addEventListener('change', () => {
result.onChange = Array.from(select.querySelectorAll('option:checked')).map((option) => {
return option.value;
});
}, false);
document.body.addEventListener('input', () => {
result.onBubblingInput = Array.from(select.querySelectorAll('option:checked')).map((option) => {
return option.value;
});
}, false);
document.body.addEventListener('change', () => {
result.onBubblingChange = Array.from(select.querySelectorAll('option:checked')).map((option) => {
return option.value;
});
}, false);
</script>
</body>
</html>
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Textarea test</title>
</head>
<body>
<textarea spellcheck="false"></textarea>
<input></input>
<div contenteditable="true"></div>
<div class="plain">Plain div</div>
<script src='mouse-helper.js'></script>
<script>
window.result = '';
let textarea = document.querySelector('textarea');
textarea.addEventListener('input', () => result = textarea.value, false);
let input = document.querySelector('input');
input.addEventListener('input', () => result = input.value, false);
</script>
</body>
</html>
+35
View File
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<title>Touch test</title>
</head>
<body>
<script src="mouse-helper.js"></script>
<button onclick="clicked();">Click target</button>
<script>
window.result = [];
const button = document.querySelector('button');
button.style.height = '200px';
button.style.width = '200px';
button.focus();
button.addEventListener('touchstart', event => {
log('Touchstart:', ...Array.from(event.changedTouches).map(touch => touch.identifier));
});
button.addEventListener('touchend', event => {
log('Touchend:', ...Array.from(event.changedTouches).map(touch => touch.identifier));
});
button.addEventListener('touchmove', event => {
log('Touchmove:', ...Array.from(event.changedTouches).map(touch => touch.identifier));
});
function log(...args) {
console.log.apply(console, args);
result.push(args.join(' '));
}
function getResult() {
let temp = result;
result = [];
return temp;
}
</script>
</body>
</html>