Skip to content

Commit 940e15f

Browse files
authored
[java][BiDi] implement emulation.setTimezoneOverride (#16530)
1 parent 2700e7c commit 940e15f

File tree

8 files changed

+298
-32
lines changed

8 files changed

+298
-32
lines changed

java/src/org/openqa/selenium/bidi/Command.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package org.openqa.selenium.bidi;
1919

2020
import java.lang.reflect.Type;
21+
import java.util.Collections;
22+
import java.util.HashMap;
2123
import java.util.Map;
2224
import java.util.function.Function;
2325
import org.openqa.selenium.internal.Require;
@@ -48,7 +50,8 @@ public Command(
4850
Function<JsonInput, X> mapper,
4951
boolean sendsResponse) {
5052
this.method = Require.nonNull("Method name", method);
51-
this.params = Map.copyOf(Require.nonNull("Command parameters", params));
53+
this.params =
54+
Collections.unmodifiableMap(new HashMap<>(Require.nonNull("Command parameters", params)));
5255
this.mapper = Require.nonNull("Mapper for result", mapper);
5356
this.sendsResponse = sendsResponse;
5457
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.bidi.emulation;
19+
20+
import java.util.HashMap;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
public abstract class AbstractOverrideParameters implements OverrideParameters {
25+
protected final Map<String, Object> map = new HashMap<>();
26+
27+
@Override
28+
public OverrideParameters contexts(List<String> contexts) {
29+
if (contexts == null || contexts.isEmpty()) {
30+
throw new IllegalArgumentException("Contexts cannot be null or empty");
31+
}
32+
if (map.containsKey("userContexts")) {
33+
throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
34+
}
35+
map.put("contexts", contexts);
36+
return this;
37+
}
38+
39+
@Override
40+
public OverrideParameters userContexts(List<String> userContexts) {
41+
if (userContexts == null || userContexts.isEmpty()) {
42+
throw new IllegalArgumentException("User contexts cannot be null or empty");
43+
}
44+
if (map.containsKey("contexts")) {
45+
throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
46+
}
47+
map.put("userContexts", userContexts);
48+
return this;
49+
}
50+
51+
@Override
52+
public Map<String, Object> toMap() {
53+
// Validate that either contexts or userContexts is set
54+
if (!map.containsKey("contexts") && !map.containsKey("userContexts")) {
55+
throw new IllegalStateException("Must specify either contexts or userContexts");
56+
}
57+
return new HashMap<>(map);
58+
}
59+
}

java/src/org/openqa/selenium/bidi/emulation/Emulation.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,10 @@ public Map<String, Object> setGeolocationOverride(SetGeolocationOverrideParamete
4343
return bidi.send(
4444
new Command<>("emulation.setGeolocationOverride", parameters.toMap(), Map.class));
4545
}
46+
47+
public Map<String, Object> setTimezoneOverride(SetTimezoneOverrideParameters parameters) {
48+
Require.nonNull("SetTimezoneOverride parameters", parameters);
49+
50+
return bidi.send(new Command<>("emulation.setTimezoneOverride", parameters.toMap(), Map.class));
51+
}
4652
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.bidi.emulation;
19+
20+
import java.util.List;
21+
import java.util.Map;
22+
23+
public interface OverrideParameters {
24+
OverrideParameters contexts(List<String> contexts);
25+
26+
OverrideParameters userContexts(List<String> userContexts);
27+
28+
Map<String, Object> toMap();
29+
}

java/src/org/openqa/selenium/bidi/emulation/SetGeolocationOverrideParameters.java

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,7 @@
1717

1818
package org.openqa.selenium.bidi.emulation;
1919

20-
import java.util.HashMap;
21-
import java.util.List;
22-
import java.util.Map;
23-
24-
public class SetGeolocationOverrideParameters {
25-
private final Map<String, Object> map = new HashMap<>();
20+
public class SetGeolocationOverrideParameters extends AbstractOverrideParameters {
2621

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

43-
public SetGeolocationOverrideParameters contexts(List<String> contexts) {
44-
if (contexts == null || contexts.isEmpty()) {
45-
throw new IllegalArgumentException("Contexts cannot be null or empty");
46-
}
47-
if (map.containsKey("userContexts")) {
48-
throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
49-
}
50-
map.put("contexts", contexts);
38+
@Override
39+
public SetGeolocationOverrideParameters contexts(java.util.List<String> contexts) {
40+
super.contexts(contexts);
5141
return this;
5242
}
5343

54-
public SetGeolocationOverrideParameters userContexts(List<String> userContexts) {
55-
if (userContexts == null || userContexts.isEmpty()) {
56-
throw new IllegalArgumentException("User contexts cannot be null or empty");
57-
}
58-
if (map.containsKey("contexts")) {
59-
throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
60-
}
61-
map.put("userContexts", userContexts);
44+
@Override
45+
public SetGeolocationOverrideParameters userContexts(java.util.List<String> userContexts) {
46+
super.userContexts(userContexts);
6247
return this;
6348
}
64-
65-
public Map<String, Object> toMap() {
66-
// Validate that either contexts or userContexts is set
67-
if (!map.containsKey("contexts") && !map.containsKey("userContexts")) {
68-
throw new IllegalStateException("Must specify either contexts or userContexts");
69-
}
70-
return Map.copyOf(map);
71-
}
7249
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.bidi.emulation;
19+
20+
public class SetTimezoneOverrideParameters extends AbstractOverrideParameters {
21+
22+
public SetTimezoneOverrideParameters(String timezone) {
23+
map.put("timezone", timezone);
24+
}
25+
26+
@Override
27+
public SetTimezoneOverrideParameters contexts(java.util.List<String> contexts) {
28+
super.contexts(contexts);
29+
return this;
30+
}
31+
32+
@Override
33+
public SetTimezoneOverrideParameters userContexts(java.util.List<String> userContexts) {
34+
super.userContexts(userContexts);
35+
return this;
36+
}
37+
}

java/test/org/openqa/selenium/bidi/emulation/EmulationTest.java renamed to java/test/org/openqa/selenium/bidi/emulation/SetGeolocationOverrideTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import org.openqa.selenium.testing.NeedsSecureServer;
3939

4040
@NeedsSecureServer
41-
class EmulationTest extends JupiterTestBase {
41+
class SetGeolocationOverrideTest extends JupiterTestBase {
4242
Object getBrowserGeolocation(WebDriver driver, String userContext, String origin) {
4343
JavascriptExecutor executor = (JavascriptExecutor) driver;
4444
Permission permission = new Permission(driver);
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.bidi.emulation;
19+
20+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
21+
import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;
22+
23+
import java.time.ZoneId;
24+
import java.time.ZonedDateTime;
25+
import java.util.List;
26+
import org.junit.jupiter.api.Test;
27+
import org.openqa.selenium.JavascriptExecutor;
28+
import org.openqa.selenium.WebDriver;
29+
import org.openqa.selenium.WindowType;
30+
import org.openqa.selenium.bidi.browsingcontext.BrowsingContext;
31+
import org.openqa.selenium.bidi.browsingcontext.CreateContextParameters;
32+
import org.openqa.selenium.bidi.browsingcontext.ReadinessState;
33+
import org.openqa.selenium.bidi.module.Browser;
34+
import org.openqa.selenium.testing.Ignore;
35+
import org.openqa.selenium.testing.JupiterTestBase;
36+
import org.openqa.selenium.testing.NeedsFreshDriver;
37+
38+
public class SetTimezoneOverrideTest extends JupiterTestBase {
39+
40+
int getExpectedTimezoneOffset(String timezoneId) {
41+
ZoneId zone = ZoneId.of(timezoneId);
42+
ZonedDateTime now = ZonedDateTime.now(zone);
43+
return now.getOffset().getTotalSeconds()
44+
/ 60
45+
* -1; // Negate to match JavaScript getTimezoneOffset behavior
46+
}
47+
48+
String getTimezoneString(WebDriver driver, String context) {
49+
JavascriptExecutor executor = (JavascriptExecutor) driver;
50+
51+
driver.switchTo().window(context);
52+
return (String)
53+
executor.executeScript("return Intl.DateTimeFormat().resolvedOptions().timeZone;");
54+
}
55+
56+
Number getTimezoneOffset(WebDriver driver, String context) {
57+
JavascriptExecutor executor = (JavascriptExecutor) driver;
58+
59+
driver.switchTo().window(context);
60+
return (Number) executor.executeScript("return new Date().getTimezoneOffset()");
61+
}
62+
63+
@Test
64+
@NeedsFreshDriver
65+
void canSetTimezoneOverrideInContext() {
66+
BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
67+
String contextId = context.getId();
68+
69+
String url = appServer.whereIs("blank.html");
70+
context.navigate(url, ReadinessState.COMPLETE);
71+
72+
Emulation emul = new Emulation(driver);
73+
String timezone = "America/Los_Angeles";
74+
String tzOrg = getTimezoneString(driver, contextId);
75+
emul.setTimezoneOverride(
76+
new SetTimezoneOverrideParameters(timezone).contexts(List.of(contextId)));
77+
78+
String tzString = getTimezoneString(driver, contextId);
79+
Number tzOffset = getTimezoneOffset(driver, contextId);
80+
81+
int expectedOffset = getExpectedTimezoneOffset(timezone);
82+
83+
assertThat(tzString).isEqualTo(timezone);
84+
assertThat(tzOffset.intValue()).isEqualTo(expectedOffset);
85+
86+
emul.setTimezoneOverride(new SetTimezoneOverrideParameters(null).contexts(List.of(contextId)));
87+
String TzNew = getTimezoneString(driver, contextId);
88+
assertThat(TzNew).isEqualTo(tzOrg);
89+
}
90+
91+
@Test
92+
@NeedsFreshDriver
93+
void canSetTimeZoneOverrideInUserContext() {
94+
Browser browser = new Browser(driver);
95+
String userContext = browser.createUserContext();
96+
97+
BrowsingContext context =
98+
new BrowsingContext(
99+
driver, new CreateContextParameters(WindowType.TAB).userContext(userContext));
100+
String contextId = context.getId();
101+
102+
String url = appServer.whereIs("blank.html");
103+
context.navigate(url, ReadinessState.COMPLETE);
104+
105+
Emulation emul = new Emulation(driver);
106+
String timezone = "Europe/London";
107+
String tzOrg = getTimezoneString(driver, contextId);
108+
emul.setTimezoneOverride(
109+
new SetTimezoneOverrideParameters(timezone).userContexts(List.of(userContext)));
110+
111+
String tzString = getTimezoneString(driver, contextId);
112+
Number tzOffset = getTimezoneOffset(driver, contextId);
113+
114+
int expectedOffset = getExpectedTimezoneOffset(timezone);
115+
116+
assertThat(tzString).isEqualTo(timezone);
117+
assertThat(tzOffset.intValue()).isEqualTo(expectedOffset);
118+
119+
emul.setTimezoneOverride(
120+
new SetTimezoneOverrideParameters(null).userContexts(List.of(userContext)));
121+
String TzNew = getTimezoneString(driver, contextId);
122+
assertThat(TzNew).isEqualTo(tzOrg);
123+
124+
context.close();
125+
browser.removeUserContext(userContext);
126+
}
127+
128+
@Test
129+
@NeedsFreshDriver
130+
@Ignore(FIREFOX)
131+
void canSetTimezoneOverrideUsingOffset() {
132+
BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
133+
String contextId = context.getId();
134+
135+
String url = appServer.whereIs("blank.html");
136+
context.navigate(url, ReadinessState.COMPLETE);
137+
138+
Emulation emul = new Emulation(driver);
139+
String timezone = "+05:30";
140+
String tzOrg = getTimezoneString(driver, contextId);
141+
142+
emul.setTimezoneOverride(
143+
new SetTimezoneOverrideParameters(timezone).contexts(List.of(contextId)));
144+
145+
String tzString = getTimezoneString(driver, contextId);
146+
Number tzOffset = getTimezoneOffset(driver, contextId);
147+
148+
assertThat(tzOffset.intValue()).isEqualTo(-330);
149+
assertThat(tzString).isEqualTo("+05:30");
150+
151+
emul.setTimezoneOverride(new SetTimezoneOverrideParameters(null).contexts(List.of(contextId)));
152+
String tzNew = getTimezoneString(driver, contextId);
153+
assertThat(tzNew).isEqualTo(tzOrg);
154+
}
155+
}

0 commit comments

Comments
 (0)