feat: geolocation (#50)

This commit is contained in:
Yury Semikhatsky 2020-10-27 17:47:06 -07:00 committed by GitHub
parent ec49645c16
commit a721ef2456
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 230 additions and 105 deletions

View File

@ -259,7 +259,10 @@ class Types {
add("BrowserType.launchPersistentContext.options.geolocation.longitude", "number", "double");
add("BrowserType.launchPersistentContext.options.geolocation.accuracy", "number", "double");
add("BrowserContext.setGeolocation.geolocation", "null|Object", "Geolocation");
add("BrowserContext.setGeolocation.geolocation", "null|Object", "Geolocation", new Empty());
add("Browser.newContext.options.geolocation", "Object", "Geolocation", new Empty());
add("Browser.newPage.options.geolocation", "Object", "Geolocation", new Empty());
add("BrowserType.launchPersistentContext.options.geolocation", "Object", "Geolocation", new Empty());
// Single field options
add("Keyboard.type.options", "Object", "int", new Empty());

View File

@ -27,30 +27,6 @@ public interface Browser {
void removeListener(EventType type, Listener<EventType> listener);
class NewContextOptions {
public enum ColorScheme { DARK, LIGHT, NO_PREFERENCE }
public class Geolocation {
public double latitude;
public double longitude;
public double accuracy;
Geolocation() {
}
public NewContextOptions done() {
return NewContextOptions.this;
}
public Geolocation withLatitude(double latitude) {
this.latitude = latitude;
return this;
}
public Geolocation withLongitude(double longitude) {
this.longitude = longitude;
return this;
}
public Geolocation withAccuracy(double accuracy) {
this.accuracy = accuracy;
return this;
}
}
public class VideoSize {
public int width;
public int height;
@ -131,9 +107,9 @@ public interface Browser {
this.timezoneId = timezoneId;
return this;
}
public Geolocation setGeolocation() {
this.geolocation = new Geolocation();
return this.geolocation;
public NewContextOptions withGeolocation(Geolocation geolocation) {
this.geolocation = geolocation;
return this;
}
public NewContextOptions withLocale(String locale) {
this.locale = locale;
@ -174,30 +150,6 @@ public interface Browser {
}
class NewPageOptions {
public enum ColorScheme { DARK, LIGHT, NO_PREFERENCE }
public class Geolocation {
public double latitude;
public double longitude;
public double accuracy;
Geolocation() {
}
public NewPageOptions done() {
return NewPageOptions.this;
}
public Geolocation withLatitude(double latitude) {
this.latitude = latitude;
return this;
}
public Geolocation withLongitude(double longitude) {
this.longitude = longitude;
return this;
}
public Geolocation withAccuracy(double accuracy) {
this.accuracy = accuracy;
return this;
}
}
public class VideoSize {
public int width;
public int height;
@ -278,9 +230,9 @@ public interface Browser {
this.timezoneId = timezoneId;
return this;
}
public Geolocation setGeolocation() {
this.geolocation = new Geolocation();
return this.geolocation;
public NewPageOptions withGeolocation(Geolocation geolocation) {
this.geolocation = geolocation;
return this;
}
public NewPageOptions withLocale(String locale) {
this.locale = locale;

View File

@ -160,24 +160,6 @@ public interface BrowserContext {
return this;
}
}
class Geolocation {
public int latitude;
public int longitude;
public Integer accuracy;
public Geolocation withLatitude(int latitude) {
this.latitude = latitude;
return this;
}
public Geolocation withLongitude(int longitude) {
this.longitude = longitude;
return this;
}
public Geolocation withAccuracy(Integer accuracy) {
this.accuracy = accuracy;
return this;
}
}
void close();
void addCookies(List<AddCookie> cookies);
default void addInitScript(String script) {

View File

@ -185,30 +185,6 @@ public interface BrowserType {
return this;
}
}
public class Geolocation {
public double latitude;
public double longitude;
public double accuracy;
Geolocation() {
}
public LaunchPersistentContextOptions done() {
return LaunchPersistentContextOptions.this;
}
public Geolocation withLatitude(double latitude) {
this.latitude = latitude;
return this;
}
public Geolocation withLongitude(double longitude) {
this.longitude = longitude;
return this;
}
public Geolocation withAccuracy(double accuracy) {
this.accuracy = accuracy;
return this;
}
}
public class VideoSize {
public int width;
public int height;
@ -363,9 +339,9 @@ public interface BrowserType {
this.timezoneId = timezoneId;
return this;
}
public Geolocation setGeolocation() {
this.geolocation = new Geolocation();
return this.geolocation;
public LaunchPersistentContextOptions withGeolocation(Geolocation geolocation) {
this.geolocation = geolocation;
return this;
}
public LaunchPersistentContextOptions withLocale(String locale) {
this.locale = locale;

View File

@ -0,0 +1,49 @@
/*
* 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;
public class Geolocation {
public double latitude;
public double longitude;
public Double accuracy;
public Geolocation() {
}
public Geolocation(double latitude, double longitude) {
this(latitude, longitude, null);
}
public Geolocation(double latitude, double longitude, Double accuracy) {
this.latitude = latitude;
this.longitude = longitude;
this.accuracy = accuracy;
}
public Geolocation withLatitude(double latitude) {
this.latitude = latitude;
return this;
}
public Geolocation withLongitude(double longitude) {
this.longitude = longitude;
return this;
}
public Geolocation withAccuracy(double accuracy) {
this.accuracy = accuracy;
return this;
}
}

View File

@ -17,9 +17,7 @@
package com.microsoft.playwright.impl;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.microsoft.playwright.*;
@ -36,7 +34,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
final List<PageImpl> pages = new ArrayList<>();
final Router routes = new Router();
private boolean isClosedOrClosing;
final Map<String, Page.Binding> bindings = new HashMap<String, Page.Binding>();
final Map<String, Page.Binding> bindings = new HashMap<>();
PageImpl ownerPage;
private final ListenerCollection<EventType> listeners = new ListenerCollection<>();
final TimeoutSettings timeoutSettings = new TimeoutSettings();
@ -166,7 +164,15 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
@Override
public void grantPermissions(List<String> permissions, GrantPermissionsOptions options) {
if (options == null) {
options = new GrantPermissionsOptions();
}
if (permissions == null) {
permissions = Collections.emptyList();
}
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
params.add("permissions", new Gson().toJsonTree(permissions));
sendMessage("grantPermissions", params);
}
@Override
@ -239,7 +245,11 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
@Override
public void setGeolocation(Geolocation geolocation) {
JsonObject params = new JsonObject();
if (geolocation != null) {
params.add("geolocation", new Gson().toJsonTree(geolocation));
}
sendMessage("setGeolocation", params);
}
@Override

View File

@ -0,0 +1,153 @@
/*
* 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.util.ArrayList;
import java.util.List;
import static com.microsoft.playwright.Page.EventType.CONSOLE;
import static com.microsoft.playwright.Page.EventType.POPUP;
import static com.microsoft.playwright.Utils.mapOf;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestGeolocation extends TestBase {
@Test
void shouldWork() {
context.grantPermissions(asList("geolocation"));
page.navigate(server.EMPTY_PAGE);
context.setGeolocation(new Geolocation(10, 10));
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 shouldThrowWhenInvalidLongitude() {
try {
context.setGeolocation(new Geolocation(10, 200));
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed."));
}
}
@Test
void shouldIsolateContexts() {
context.grantPermissions(asList("geolocation"));
context.setGeolocation(new Geolocation(10, 10));
page.navigate(server.EMPTY_PAGE);
BrowserContext context2 = browser.newContext(new Browser.NewContextOptions()
.withPermissions(asList("geolocation"))
.withGeolocation(new Geolocation(20, 20)));
Page page2 = context2.newPage();
page2.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);
Object geolocation2 = page2.evaluate("() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {\n" +
" resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});\n" +
"}))");
assertEquals(mapOf("latitude", 20, "longitude", 20), geolocation2);
context2.close();
}
void shouldThrowWithMissingLatitude() {
// Not applicable in Java.
}
@Test
void shouldNotModifyPassedDefaultOptionsObject() {
Geolocation geolocation = new Geolocation(10, 10);
Browser.NewContextOptions options = new Browser.NewContextOptions().withGeolocation(geolocation);
BrowserContext context = browser.newContext(options);
context.setGeolocation(new Geolocation(20, 20));
assertEquals(geolocation, options.geolocation);
context.close();
}
void shouldThrowWithMissingLongitudeInDefaultOptions() {
// Not applicable in Java.
}
@Test
void shouldUseContextOptions() {
Browser.NewContextOptions options = new Browser.NewContextOptions()
.withGeolocation(new Geolocation(10, 10))
.withPermissions(asList("geolocation"));
BrowserContext context = browser.newContext(options);
Page page = context.newPage();
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);
context.close();
}
@Test
void watchPositionShouldBeNotified() {
context.grantPermissions(asList("geolocation"));
page.navigate(server.EMPTY_PAGE);
List<String> messages = new ArrayList<>();
page.addListener(CONSOLE, event -> messages.add(((ConsoleMessage) event.data()).text()));
context.setGeolocation(new Geolocation());
page.evaluate("() => {\n" +
" navigator.geolocation.watchPosition(pos => {\n" +
" const coords = pos.coords;\n" +
" console.log(`lat=${coords.latitude} lng=${coords.longitude}`);\n" +
" }, err => {});\n" +
"}");
{
Deferred<Event<Page.EventType>> deferred = page.waitForEvent(CONSOLE, event -> ((ConsoleMessage) event.data()).text().contains("lat=0 lng=10"));
context.setGeolocation(new Geolocation(0, 10));
deferred.get();
}
{
Deferred<Event<Page.EventType>> deferred = page.waitForEvent(CONSOLE, event -> ((ConsoleMessage) event.data()).text().contains("lat=20 lng=30"));
context.setGeolocation(new Geolocation(20, 30));
deferred.get();
}
{
Deferred<Event<Page.EventType>> deferred = page.waitForEvent(CONSOLE, event -> ((ConsoleMessage) event.data()).text().contains("lat=40 lng=50"));
context.setGeolocation(new Geolocation(40, 50));
deferred.get();
}
assertTrue(messages.contains("lat=0 lng=10"));
assertTrue(messages.contains("lat=20 lng=30"));
assertTrue(messages.contains("lat=40 lng=50"));
}
@Test
void shouldUseContextOptionsForPopup() {
context.grantPermissions(asList("geolocation"));
context.setGeolocation(new Geolocation(10, 10));
Deferred<Event<Page.EventType>> popupEvent = page.waitForEvent(POPUP);
page.evaluate("url => window['_popup'] = window.open(url)", server.PREFIX + "/geolocation.html");
Page popup = (Page) popupEvent.get().data();
popup.waitForLoadState();
Object geolocation = popup.evaluate("window['geolocationPromise']");
assertEquals(mapOf("longitude", 10, "latitude", 10), geolocation);
}
}