Skip to content

Commit 3dc9bc8

Browse files
committed
[bidi][java] Adhering to low-level design with extensiblity
1 parent 4ed0f70 commit 3dc9bc8

7 files changed

Lines changed: 441 additions & 55 deletions

File tree

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

Lines changed: 195 additions & 50 deletions
Large diffs are not rendered by default.

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,6 @@ public final <X> String subscribe(Event<X> event, Consumer<X> handler) {
5757
return handle.subscribe(event, handler);
5858
}
5959

60-
public final <X> String subscribe(Event<X> event, Consumer<X> handler, SubscriptionScope scope) {
61-
return handle.subscribe(event, handler, scope);
62-
}
63-
6460
/**
6561
* Cancels a previously registered event subscription.
6662
*
@@ -69,4 +65,4 @@ public final <X> String subscribe(Event<X> event, Consumer<X> handler, Subscript
6965
public final void unsubscribe(String subscriptionId) {
7066
handle.unsubscribe(subscriptionId);
7167
}
72-
}
68+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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.protocol.network;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
22+
23+
import java.util.Map;
24+
import org.junit.jupiter.api.Tag;
25+
import org.junit.jupiter.api.Test;
26+
import org.openqa.selenium.json.Json;
27+
28+
@Tag("UnitTests")
29+
class CookieTest {
30+
31+
private static final String BASE_FIELDS =
32+
"\"name\": \"sid\", \"value\": {\"type\": \"string\", \"value\": \"abc\"},"
33+
+ " \"domain\": \"example.com\", \"path\": \"/\", \"size\": 6,"
34+
+ " \"httpOnly\": false, \"secure\": true, \"sameSite\": \"strict\"";
35+
36+
@Test
37+
void undeclaredWireFieldIsPreservedAsAnExtension() {
38+
// network.Cookie is extensible but receive-only (Selenium sets cookies through the
39+
// differently-typed storage.PartialCookie), so this confirms extras get kept regardless of
40+
// whether the type can also be sent back out.
41+
Cookie cookie = new Json().toType("{" + BASE_FIELDS + ", \"sameParty\": true}", Cookie.class);
42+
43+
assertThat(cookie.getName()).isEqualTo("sid");
44+
assertThat(cookie.getExtensions()).containsExactly(Map.entry("sameParty", true));
45+
}
46+
47+
@Test
48+
void multipleUndeclaredFieldsAreAllPreserved() {
49+
Cookie cookie =
50+
new Json()
51+
.toType(
52+
"{" + BASE_FIELDS + ", \"sameParty\": true, \"partitionKey\": \"top-level\"}",
53+
Cookie.class);
54+
55+
assertThat(cookie.getExtensions())
56+
.containsExactly(Map.entry("sameParty", true), Map.entry("partitionKey", "top-level"));
57+
}
58+
59+
@Test
60+
void noUndeclaredFieldsMeansAnEmptyExtensionsMap() {
61+
Cookie cookie = new Json().toType("{" + BASE_FIELDS + "}", Cookie.class);
62+
63+
assertThat(cookie.getExtensions()).isEmpty();
64+
}
65+
66+
@Test
67+
void extensionsMapIsUnmodifiable() {
68+
Cookie cookie = new Json().toType("{" + BASE_FIELDS + "}", Cookie.class);
69+
70+
assertThatThrownBy(() -> cookie.getExtensions().put("x", "y"))
71+
.isInstanceOf(UnsupportedOperationException.class);
72+
}
73+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("@rules_jvm_external//:defs.bzl", "artifact")
2+
load("//java:defs.bzl", "JUNIT5_DEPS", "java_test_suite")
3+
4+
java_test_suite(
5+
name = "SmallTests",
6+
size = "small",
7+
srcs = glob(["*Test.java"]),
8+
deps = [
9+
"//java/src/org/openqa/selenium/bidi",
10+
"//java/src/org/openqa/selenium/bidi:bidi-generated",
11+
"//java/src/org/openqa/selenium/json",
12+
artifact("org.junit.jupiter:junit-jupiter-api"),
13+
artifact("org.assertj:assertj-core"),
14+
] + JUNIT5_DEPS,
15+
)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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.protocol.script;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
22+
23+
import java.util.Map;
24+
import org.junit.jupiter.api.Tag;
25+
import org.junit.jupiter.api.Test;
26+
import org.openqa.selenium.bidi.BiDiException;
27+
import org.openqa.selenium.json.Json;
28+
29+
@Tag("UnitTests")
30+
class SharedReferenceTest {
31+
32+
// script.SharedReference is both sent (as a script argument) and received (inside a
33+
// RemoteValue), so it is the bidirectional case: addExtension lives on the Builder, and
34+
// Builder.build() and the deserializer's fromJson both funnel through the same shared,
35+
// validated constructor.
36+
37+
@Test
38+
void builderAddedExtensionAppearsOnTheBuiltInstanceAndOnTheWire() {
39+
SharedReference ref =
40+
SharedReference.builder("shared-1").addExtension("vendorHint", "chrome").build();
41+
42+
assertThat(ref.getSharedId()).isEqualTo("shared-1");
43+
assertThat(ref.getExtensions()).containsExactly(Map.entry("vendorHint", "chrome"));
44+
assertThat(ref.toMap()).containsEntry("vendorHint", "chrome");
45+
}
46+
47+
@Test
48+
void builderRejectsAnExtensionThatShadowsADeclaredField() {
49+
SharedReference.Builder builder = SharedReference.builder("shared-1");
50+
51+
assertThatThrownBy(() -> builder.addExtension("sharedId", "collides"))
52+
.isInstanceOf(BiDiException.class)
53+
.hasMessageContaining("sharedId");
54+
}
55+
56+
@Test
57+
void undeclaredWireFieldOnAReceivedInstanceIsPreservedAsAnExtension() {
58+
SharedReference ref =
59+
new Json()
60+
.toType(
61+
"{\"sharedId\": \"shared-2\", \"handle\": \"h1\", \"vendorHint\": \"firefox\"}",
62+
SharedReference.class);
63+
64+
assertThat(ref.getHandle()).contains("h1");
65+
assertThat(ref.getExtensions()).containsExactly(Map.entry("vendorHint", "firefox"));
66+
}
67+
68+
@Test
69+
void aReceivedInstanceWithNoUndeclaredFieldsHasEmptyExtensions() {
70+
SharedReference ref = new Json().toType("{\"sharedId\": \"shared-3\"}", SharedReference.class);
71+
72+
assertThat(ref.getExtensions()).isEmpty();
73+
}
74+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("@rules_jvm_external//:defs.bzl", "artifact")
2+
load("//java:defs.bzl", "JUNIT5_DEPS", "java_test_suite")
3+
4+
java_test_suite(
5+
name = "SmallTests",
6+
size = "small",
7+
srcs = glob(["*Test.java"]),
8+
deps = [
9+
"//java/src/org/openqa/selenium/bidi",
10+
"//java/src/org/openqa/selenium/bidi:bidi-generated",
11+
"//java/src/org/openqa/selenium/json",
12+
artifact("org.junit.jupiter:junit-jupiter-api"),
13+
artifact("org.assertj:assertj-core"),
14+
] + JUNIT5_DEPS,
15+
)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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.protocol.storage;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
22+
23+
import java.util.Map;
24+
import org.junit.jupiter.api.Tag;
25+
import org.junit.jupiter.api.Test;
26+
import org.openqa.selenium.bidi.BiDiException;
27+
import org.openqa.selenium.bidi.protocol.network.StringValue;
28+
29+
@Tag("UnitTests")
30+
class PartialCookieTest {
31+
32+
private static PartialCookie cookie() {
33+
return new PartialCookie("sid", new StringValue("string", "abc"), "example.com");
34+
}
35+
36+
@Test
37+
void addedExtensionIsSerializedOntoTheWireAlongsideDeclaredFields() {
38+
// storage.PartialCookie is extensible and sendable, so an added extra field should reach the
39+
// wire alongside the declared ones.
40+
Map<String, Object> map = cookie().addExtension("sameParty", true).toMap();
41+
42+
assertThat(map).containsEntry("name", "sid");
43+
assertThat(map).containsEntry("domain", "example.com");
44+
assertThat(map).containsEntry("sameParty", true);
45+
}
46+
47+
@Test
48+
void addExtensionReturnsThisForFluentChaining() {
49+
PartialCookie built = cookie().addExtension("a", 1).addExtension("b", 2);
50+
51+
assertThat(built.getExtensions()).containsExactly(Map.entry("a", 1), Map.entry("b", 2));
52+
}
53+
54+
@Test
55+
void addingAnExtensionForAnAlreadyDeclaredFieldIsRejected() {
56+
// A caller-added extension must never shadow a declared field's wire key.
57+
assertThatThrownBy(() -> cookie().addExtension("name", "collides"))
58+
.isInstanceOf(BiDiException.class)
59+
.hasMessageContaining("name");
60+
}
61+
62+
@Test
63+
void noExtensionsAddedMeansNoExtraWireKeys() {
64+
Map<String, Object> map = cookie().toMap();
65+
66+
assertThat(map).containsOnlyKeys("name", "value", "domain");
67+
}
68+
}

0 commit comments

Comments
 (0)