Skip to content
2 changes: 1 addition & 1 deletion java/src/org/openqa/selenium/bidi/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public Command(
Function<JsonInput, X> mapper,
boolean sendsResponse) {
this.method = Require.nonNull("Method name", method);
this.params = Map.copyOf(Require.nonNull("Command parameters", params));
this.params = Require.nonNull("Command parameters", params);
this.mapper = Require.nonNull("Mapper for result", mapper);
this.sendsResponse = sendsResponse;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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 org.openqa.selenium.bidi.emulation;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AbstractOverrideParameters implements OverrideParameters {
protected final Map<String, Object> map = new HashMap<>();

@Override
public OverrideParameters contexts(List<String> contexts) {
if (contexts == null || contexts.isEmpty()) {
throw new IllegalArgumentException("Contexts cannot be null or empty");
}
if (map.containsKey("userContexts")) {
throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
}
map.put("contexts", contexts);
return this;
}

@Override
public OverrideParameters userContexts(List<String> userContexts) {
if (userContexts == null || userContexts.isEmpty()) {
throw new IllegalArgumentException("User contexts cannot be null or empty");
}
if (map.containsKey("contexts")) {
throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
}
map.put("userContexts", userContexts);
return this;
}

@Override
public Map<String, Object> toMap() {
// Validate that either contexts or userContexts is set
if (!map.containsKey("contexts") && !map.containsKey("userContexts")) {
throw new IllegalStateException("Must specify either contexts or userContexts");
}
return map;
}
}
6 changes: 6 additions & 0 deletions java/src/org/openqa/selenium/bidi/emulation/Emulation.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@ public Map<String, Object> setGeolocationOverride(SetGeolocationOverrideParamete
return bidi.send(
new Command<>("emulation.setGeolocationOverride", parameters.toMap(), Map.class));
}

public Map<String, Object> setTimezoneOverride(SetTimezoneOverrideParameters parameters) {
Require.nonNull("SetTimezoneOverride parameters", parameters);

return bidi.send(new Command<>("emulation.setTimezoneOverride", parameters.toMap(), Map.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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 org.openqa.selenium.bidi.emulation;

import java.util.List;
import java.util.Map;

public interface OverrideParameters {
OverrideParameters contexts(List<String> contexts);

OverrideParameters userContexts(List<String> userContexts);

Map<String, Object> toMap();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@

package org.openqa.selenium.bidi.emulation;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SetGeolocationOverrideParameters {
private final Map<String, Object> map = new HashMap<>();
public class SetGeolocationOverrideParameters extends AbstractOverrideParameters {

// Constructor for coordinates - must specify either contexts or userContexts later
public SetGeolocationOverrideParameters(GeolocationCoordinates coordinates) {
Expand All @@ -40,33 +35,15 @@ public SetGeolocationOverrideParameters(GeolocationPositionError error) {
map.put("error", error.toMap());
}

public SetGeolocationOverrideParameters contexts(List<String> contexts) {
if (contexts == null || contexts.isEmpty()) {
throw new IllegalArgumentException("Contexts cannot be null or empty");
}
if (map.containsKey("userContexts")) {
throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
}
map.put("contexts", contexts);
@Override
public SetGeolocationOverrideParameters contexts(java.util.List<String> contexts) {
super.contexts(contexts);
return this;
}

public SetGeolocationOverrideParameters userContexts(List<String> userContexts) {
if (userContexts == null || userContexts.isEmpty()) {
throw new IllegalArgumentException("User contexts cannot be null or empty");
}
if (map.containsKey("contexts")) {
throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
}
map.put("userContexts", userContexts);
@Override
public SetGeolocationOverrideParameters userContexts(java.util.List<String> userContexts) {
super.userContexts(userContexts);
return this;
}

public Map<String, Object> toMap() {
// Validate that either contexts or userContexts is set
if (!map.containsKey("contexts") && !map.containsKey("userContexts")) {
throw new IllegalStateException("Must specify either contexts or userContexts");
}
return Map.copyOf(map);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.openqa.selenium.bidi.emulation;

public class SetTimezoneOverrideParameters extends AbstractOverrideParameters {

public SetTimezoneOverrideParameters(String timezone) {
map.put("timezone", timezone);
}

@Override
public SetTimezoneOverrideParameters contexts(java.util.List<String> contexts) {
super.contexts(contexts);
return this;
}

@Override
public SetTimezoneOverrideParameters userContexts(java.util.List<String> userContexts) {
super.userContexts(userContexts);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
import org.openqa.selenium.testing.NeedsSecureServer;

@NeedsSecureServer
class EmulationTest extends JupiterTestBase {
class SetGeolocationOverrideTest extends JupiterTestBase {
Object getBrowserGeolocation(WebDriver driver, String userContext, String origin) {
JavascriptExecutor executor = (JavascriptExecutor) driver;
Permission permission = new Permission(driver);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package org.openqa.selenium.bidi.emulation;

import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;

import java.util.List;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WindowType;
import org.openqa.selenium.bidi.browsingcontext.BrowsingContext;
import org.openqa.selenium.bidi.browsingcontext.CreateContextParameters;
import org.openqa.selenium.bidi.browsingcontext.ReadinessState;
import org.openqa.selenium.bidi.module.Browser;
import org.openqa.selenium.testing.Ignore;
import org.openqa.selenium.testing.JupiterTestBase;
import org.openqa.selenium.testing.NeedsFreshDriver;
import org.openqa.selenium.testing.NeedsSecureServer;

@NeedsSecureServer
public class SetTimezoneOverrideTest extends JupiterTestBase {
String getTimezoneString(WebDriver driver, String context) {
JavascriptExecutor executor = (JavascriptExecutor) driver;

driver.switchTo().window(context);
return (String)
executor.executeScript("return Intl.DateTimeFormat().resolvedOptions().timeZone;");
}

Number getTimezoneOffset(WebDriver driver, String context) {
JavascriptExecutor executor = (JavascriptExecutor) driver;

driver.switchTo().window(context);
return (Number) executor.executeScript("return new Date().getTimezoneOffset()");
}

@Test
@NeedsFreshDriver
void canSetTimezoneOverrideInContext() {
BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
String contextId = context.getId();

String url = appServer.whereIsSecure("blank.html");
context.navigate(url, ReadinessState.COMPLETE);

Emulation emul = new Emulation(driver);
String timezone = "America/Los_Angeles";
String tzOrg = getTimezoneString(driver, contextId);
emul.setTimezoneOverride(
new SetTimezoneOverrideParameters(timezone).contexts(List.of(contextId)));

String tzString = getTimezoneString(driver, contextId);
Number tzOffset = getTimezoneOffset(driver, contextId);

assert tzString.equals(timezone)
: "Timezone string mismatch: expected " + timezone + ", got " + tzString;
assert tzOffset.intValue() == 420 : "Timezone offset mismatch: expected 420, got " + tzOffset;

emul.setTimezoneOverride(new SetTimezoneOverrideParameters(null).contexts(List.of(contextId)));
String TzNew = getTimezoneString(driver, contextId);
assert TzNew.equals(tzOrg) : "Timezone reset failed: expected " + tzOrg + ", got " + TzNew;
}

@Test
@NeedsFreshDriver
void canSetTimeZoneOverrideInUserContext() {
Browser browser = new Browser(driver);
String userContext = browser.createUserContext();

BrowsingContext context =
new BrowsingContext(
driver, new CreateContextParameters(WindowType.TAB).userContext(userContext));
String contextId = context.getId();

String url = appServer.whereIsSecure("blank.html");
context.navigate(url, ReadinessState.COMPLETE);

Emulation emul = new Emulation(driver);
String timezone = "Europe/London";
String tzOrg = getTimezoneString(driver, contextId);
emul.setTimezoneOverride(
new SetTimezoneOverrideParameters(timezone).userContexts(List.of(userContext)));

String tzString = getTimezoneString(driver, contextId);
Number tzOffset = getTimezoneOffset(driver, contextId);

assert tzString.equals(timezone)
: "Timezone string mismatch: expected " + timezone + ", got " + tzString;
assert tzOffset.intValue() == 0 : "Timezone offset mismatch: expected 0, got " + tzOffset;

emul.setTimezoneOverride(
new SetTimezoneOverrideParameters(null).userContexts(List.of(userContext)));
String TzNew = getTimezoneString(driver, contextId);
assert TzNew.equals(tzOrg) : "Timezone reset failed: expected " + tzOrg + ", got " + TzNew;

context.close();
browser.removeUserContext(userContext);
}

@Test
@NeedsFreshDriver
@Ignore(FIREFOX)
void canSetTimezoneOverrideUsingOffset() {
BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
String contextId = context.getId();

String url = appServer.whereIsSecure("blank.html");
context.navigate(url, ReadinessState.COMPLETE);

Emulation emul = new Emulation(driver);
String timezone = "+05:30";
String tzOrg = getTimezoneString(driver, contextId);

emul.setTimezoneOverride(
new SetTimezoneOverrideParameters(timezone).contexts(List.of(contextId)));

String tzString = getTimezoneString(driver, contextId);
Number tzOffset = getTimezoneOffset(driver, contextId);

assert tzOffset.intValue() == -330 : "Expected timezone offset -330, got " + tzOffset;
assert tzString.equals("+05:30") : "Expected timezone '+05:30', got " + tzString;

emul.setTimezoneOverride(new SetTimezoneOverrideParameters(null).contexts(List.of(contextId)));
String tzNew = getTimezoneString(driver, contextId);
assert tzNew.equals(tzOrg) : "Timezone reset failed: expected " + tzOrg + ", got " + tzNew;
}
}
Loading