feat: launchPersistentContext (#53)

This commit is contained in:
Yury Semikhatsky 2020-10-28 18:33:22 -07:00 committed by GitHub
parent 707efeebbb
commit dca206d28d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 215 additions and 48 deletions

View File

@ -238,6 +238,9 @@ class Method extends Element {
customSignature.put("Page.setViewportSize", new String[]{"void setViewportSize(int width, int height);"});
// The method is deprecated in ts, just remove it in Java.
customSignature.put("BrowserContext.setHTTPCredentials", new String[0]);
// No connect for now.
customSignature.put("BrowserType.connect", new String[0]);
customSignature.put("BrowserType.launchServer", new String[0]);
customSignature.put("BrowserContext.route", new String[]{
"void route(String url, BiConsumer<Route, Request> handler);",
"void route(Pattern url, BiConsumer<Route, Request> handler);",
@ -928,6 +931,9 @@ public class ApiGenerator {
System.out.println("Writing files to: " + dir.getCanonicalPath());
for (Map.Entry<String, JsonElement> entry: api.entrySet()) {
String name = entry.getKey();
if ("BrowserServer".equals(name)) {
continue;
}
List<String> lines = new ArrayList<>();
new Interface(entry.getValue().getAsJsonObject()).writeTo(lines, "");
String text = String.join("\n", lines);

View File

@ -1,27 +0,0 @@
/*
* 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 java.util.*;
public interface BrowserServer {
void close();
void kill();
Object process();
String wsEndpoint();
}

View File

@ -488,7 +488,6 @@ public interface BrowserType {
return this;
}
}
Browser connect(ConnectOptions options);
String executablePath();
default Browser launch() {
return launch(null);
@ -498,10 +497,6 @@ public interface BrowserType {
return launchPersistentContext(userDataDir, null);
}
BrowserContext launchPersistentContext(String userDataDir, LaunchPersistentContextOptions options);
default BrowserServer launchServer() {
return launchServer(null);
}
BrowserServer launchServer(LaunchServerOptions options);
String name();
}

View File

@ -42,7 +42,11 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
protected BrowserContextImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
if (parent instanceof BrowserImpl) {
browser = (BrowserImpl) parent;
} else {
browser = null;
}
}
@Override

View File

@ -16,24 +16,21 @@
package com.microsoft.playwright.impl;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.Browser;
import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.BrowserServer;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.PlaywrightException;
import java.io.IOException;
import java.util.Map;
class BrowserTypeImpl extends ChannelOwner implements BrowserType {
BrowserTypeImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
}
@Override
public Browser connect(ConnectOptions options) {
return null;
}
@Override
public BrowserImpl launch(LaunchOptions options) {
if (options == null) {
@ -48,14 +45,60 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
return initializer.get("executablePath").getAsString();
}
private static class ColorSchemeAdapter extends TypeAdapter<LaunchPersistentContextOptions.ColorScheme> {
@Override
public BrowserContext launchPersistentContext(String userDataDir, LaunchPersistentContextOptions options) {
return null;
public void write(JsonWriter out, LaunchPersistentContextOptions.ColorScheme value) throws IOException {
String stringValue;
switch (value) {
case DARK:
stringValue = "dark";
break;
case LIGHT:
stringValue = "light";
break;
case NO_PREFERENCE:
stringValue = "no-preference";
break;
default:
throw new PlaywrightException("Unexpected value: " + value);
}
out.value(stringValue);
}
@Override
public BrowserServer launchServer(LaunchServerOptions options) {
return null;
public LaunchPersistentContextOptions.ColorScheme read(JsonReader in) throws IOException {
String value = in.nextString();
switch (value) {
case "dark": return LaunchPersistentContextOptions.ColorScheme.DARK;
case "light": return LaunchPersistentContextOptions.ColorScheme.LIGHT;
case "no-preference": return LaunchPersistentContextOptions.ColorScheme.NO_PREFERENCE;
default: throw new PlaywrightException("Unexpected value: " + value);
}
}
}
@Override
public BrowserContext launchPersistentContext(String userDataDir, LaunchPersistentContextOptions options) {
if (options == null) {
options = new LaunchPersistentContextOptions();
}
Gson gson = new GsonBuilder().registerTypeAdapter(LaunchPersistentContextOptions.ColorScheme.class, new ColorSchemeAdapter().nullSafe()).create();
JsonObject params = gson.toJsonTree(options).getAsJsonObject();
if (options.extraHTTPHeaders != null) {
params.remove("extraHTTPHeaders");
JsonArray jsonArray = new JsonArray();
for (Map.Entry<String, String> e : options.extraHTTPHeaders.entrySet()) {
JsonObject header = new JsonObject();
header.addProperty("name", e.getKey());
header.addProperty("value", e.getValue());
jsonArray.add(header);
}
params.add("extraHTTPHeaders", jsonArray);
}
params.addProperty("userDataDir", userDataDir);
JsonObject json = sendMessage("launchPersistentContext", params).getAsJsonObject();
return connection.getExistingObject(json.getAsJsonObject("context").get("guid").getAsString());
}
public String name() {

View File

@ -0,0 +1,146 @@
package com.microsoft.playwright;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static com.microsoft.playwright.BrowserType.LaunchPersistentContextOptions.ColorScheme.DARK;
import static com.microsoft.playwright.Utils.mapOf;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
public class TestDefaultBrowserContext2 extends TestBase {
private BrowserContext persistentContext;
@AfterEach
private void closePersistentContext() {
if (persistentContext != null) {
persistentContext.close();
}
}
private Page launchPersistent() {
return launchPersistent(null);
}
private Page launchPersistent(BrowserType.LaunchPersistentContextOptions options) {
Path userDataDir = null;
try {
userDataDir = Files.createTempDirectory("user-data-dir-");
} catch (IOException e) {
throw new RuntimeException(e);
}
assertNull(persistentContext);
persistentContext = browserType.launchPersistentContext(userDataDir.toString(), options);
return persistentContext.pages().get(0);
}
@Test
void shouldSupportHasTouchOption() {
Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions().withHasTouch(true));
page.navigate(server.PREFIX + "/mobile.html");
assertEquals(true, page.evaluate("() => 'ontouchstart' in window"));
}
@Test
void shouldWorkInPersistentContext() {
// TODO: test.skip(browserName === "firefox");
// Firefox does not support mobile.
Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions()
.withViewport(320,480).withIsMobile(true));
page.navigate(server.PREFIX + "/empty.html");
assertEquals(980, page.evaluate("() => window.innerWidth"));
}
@Test
void shouldSupportColorSchemeOption() {
Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions().withColorScheme(DARK));
assertEquals(false, page.evaluate("matchMedia('(prefers-color-scheme: light)').matches"));
assertEquals(true, page.evaluate("matchMedia('(prefers-color-scheme: dark)').matches"));
}
@Test
void shouldSupportTimezoneIdOption() {
Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions()
.withLocale("en-US").withTimezoneId("America/Jamaica"));
assertEquals("Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)",
page.evaluate("new Date(1479579154987).toString()"));
}
@Test
void shouldSupportLocaleOption() {
Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions()
.withLocale("fr-CH"));
assertEquals("fr-CH", page.evaluate("navigator.language"));
}
@Test
void shouldSupportGeolocationAndPermissionsOptions() {
Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions()
.withGeolocation(new Geolocation(10, 10))
.withPermissions(asList("geolocation")));
page.navigate(server.EMPTY_PAGE);
Object geolocation = page.evaluate("() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {\n" +
" resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});\n" +
"}))");
assertEquals(mapOf("latitude", 10, "longitude", 10), geolocation);
}
// @Test
void shouldSupportIgnoreHTTPSErrorsOption() {
// TODO: net::ERR_EMPTY_RESPONSE at https://localhost:8908/empty.html
// Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions().withIgnoreHTTPSErrors(true));
// Response response = page.navigate(httpsServer.EMPTY_PAGE);
// assertTrue(response.ok());
}
@Test
void shouldSupportExtraHTTPHeadersOption() throws ExecutionException, InterruptedException {
// TODO: test.flaky(browserName === "firefox" && headful && platform === "linux", "Intermittent timeout on bots");
Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions().withExtraHTTPHeaders(mapOf("foo", "bar")));
Future<Server.Request> request = server.waitForRequest("/empty.html");
page.navigate(server.EMPTY_PAGE);
assertEquals(asList("bar"), request.get().headers.get("foo"));
}
@Test
void shouldAcceptUserDataDir() throws IOException {
// TODO: test.flaky(browserName === "chromium");
Path userDataDir = Files.createTempDirectory("user-data-dir-");
BrowserContext context = browserType.launchPersistentContext(userDataDir.toString());
assertTrue(userDataDir.toFile().listFiles().length > 0);
context.close();
assertTrue(userDataDir.toFile().listFiles().length > 0);
}
@Test
void shouldRestoreStateFromUserDataDir() throws IOException {
// TODO: test.slow();
Path userDataDir = Files.createTempDirectory("user-data-dir-");
BrowserType.LaunchPersistentContextOptions browserOptions = null;
BrowserContext browserContext = browserType.launchPersistentContext(userDataDir.toString(), browserOptions);
Page page = browserContext.newPage();
page.navigate(server.EMPTY_PAGE);
page.evaluate("() => localStorage.hey = 'hello'");
browserContext.close();
BrowserContext browserContext2 = browserType.launchPersistentContext(userDataDir.toString(), browserOptions);
Page page2 = browserContext2.newPage();
page2.navigate(server.EMPTY_PAGE);
assertEquals("hello", page2.evaluate("localStorage.hey"));
browserContext2.close();
Path userDataDir2 = Files.createTempDirectory("user-data-dir-");
BrowserContext browserContext3 = browserType.launchPersistentContext(userDataDir2.toString(), browserOptions);
Page page3 = browserContext3.newPage();
page3.navigate(server.EMPTY_PAGE);
assertNotEquals("hello", page3.evaluate("localStorage.hey"));
browserContext3.close();
}
}