diff --git a/java/src/org/openqa/selenium/bidi/BUILD.bazel b/java/src/org/openqa/selenium/bidi/BUILD.bazel index 79b00c803d8ce..6bb56893eabaf 100644 --- a/java/src/org/openqa/selenium/bidi/BUILD.bazel +++ b/java/src/org/openqa/selenium/bidi/BUILD.bazel @@ -28,6 +28,7 @@ java_library( "browsingcontext/*.java", "network/*.java", "script/*.java", + "storage/*.java", ], exclude = AUGMENTER_SRCS, ), diff --git a/java/src/org/openqa/selenium/bidi/Storage.java b/java/src/org/openqa/selenium/bidi/Storage.java new file mode 100644 index 0000000000000..d2291960ac91a --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/Storage.java @@ -0,0 +1,74 @@ +// 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; + +import java.io.StringReader; +import java.util.Map; +import java.util.function.Function; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.bidi.storage.DeleteCookiesParameters; +import org.openqa.selenium.bidi.storage.GetCookiesParameters; +import org.openqa.selenium.bidi.storage.GetCookiesResult; +import org.openqa.selenium.bidi.storage.PartitionKey; +import org.openqa.selenium.bidi.storage.SetCookieParameters; +import org.openqa.selenium.internal.Require; +import org.openqa.selenium.json.Json; +import org.openqa.selenium.json.JsonInput; + +public class Storage { + private static final Json JSON = new Json(); + + private final BiDi bidi; + + private final Function getCookiesResultMapper = + jsonInput -> jsonInput.read(GetCookiesResult.class); + + private final Function partitionKeyResultMapper = + jsonInput -> { + Map response = jsonInput.read(Map.class); + try (StringReader reader = new StringReader(JSON.toJson(response.get("partitionKey"))); + JsonInput input = JSON.newInput(reader)) { + return input.read(PartitionKey.class); + } + }; + + public Storage(WebDriver driver) { + Require.nonNull("WebDriver", driver); + + if (!(driver instanceof HasBiDi)) { + throw new IllegalArgumentException("WebDriver instance must support BiDi protocol"); + } + + this.bidi = ((HasBiDi) driver).getBiDi(); + } + + public GetCookiesResult getCookies(GetCookiesParameters params) { + return this.bidi.send( + new Command<>("storage.getCookies", params.toMap(), getCookiesResultMapper)); + } + + public PartitionKey setCookie(SetCookieParameters params) { + return this.bidi.send( + new Command<>("storage.setCookie", params.toMap(), partitionKeyResultMapper)); + } + + public PartitionKey deleteCookies(DeleteCookiesParameters params) { + return this.bidi.send( + new Command<>("storage.deleteCookies", params.toMap(), partitionKeyResultMapper)); + } +} diff --git a/java/src/org/openqa/selenium/bidi/network/BytesValue.java b/java/src/org/openqa/selenium/bidi/network/BytesValue.java index 5c04580e65bca..d3cd739e0f29b 100644 --- a/java/src/org/openqa/selenium/bidi/network/BytesValue.java +++ b/java/src/org/openqa/selenium/bidi/network/BytesValue.java @@ -21,7 +21,7 @@ public class BytesValue { - enum Type { + public enum Type { STRING("string"), BASE64("base64"); @@ -41,7 +41,7 @@ public String toString() { private final String value; - private BytesValue(Type type, String value) { + public BytesValue(Type type, String value) { this.type = type; this.value = value; } diff --git a/java/src/org/openqa/selenium/bidi/network/Cookie.java b/java/src/org/openqa/selenium/bidi/network/Cookie.java index b7ccdada5bd17..7b946e64b11d2 100644 --- a/java/src/org/openqa/selenium/bidi/network/Cookie.java +++ b/java/src/org/openqa/selenium/bidi/network/Cookie.java @@ -111,7 +111,7 @@ public static Cookie fromJson(JsonInput input) { case "secure": isSecure = input.read(Boolean.class); break; - case "isHttpOnly": + case "httpOnly": isHttpOnly = input.read(Boolean.class); break; case "sameSite": @@ -119,7 +119,7 @@ public static Cookie fromJson(JsonInput input) { sameSite = SameSite.findByName(sameSiteValue); break; case "expiry": - expiry = input.read(Long.class); + expiry = Optional.of(input.read(Long.class)); break; default: input.skipValue(); diff --git a/java/src/org/openqa/selenium/bidi/storage/BrowsingContextPartitionDescriptor.java b/java/src/org/openqa/selenium/bidi/storage/BrowsingContextPartitionDescriptor.java new file mode 100644 index 0000000000000..633a4b5ae9ba4 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/BrowsingContextPartitionDescriptor.java @@ -0,0 +1,38 @@ +// 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.storage; + +import java.util.HashMap; +import java.util.Map; + +public class BrowsingContextPartitionDescriptor extends PartitionDescriptor { + private final String context; + + public BrowsingContextPartitionDescriptor(String context) { + super(Type.CONTEXT); + this.context = context; + } + + public Map toMap() { + Map map = new HashMap<>(); + map.put("type", super.type.toString()); + map.put("context", this.context); + + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/storage/CookieFilter.java b/java/src/org/openqa/selenium/bidi/storage/CookieFilter.java new file mode 100644 index 0000000000000..8da53370b443e --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/CookieFilter.java @@ -0,0 +1,76 @@ +// 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.storage; + +import java.util.HashMap; +import java.util.Map; +import org.openqa.selenium.bidi.network.BytesValue; +import org.openqa.selenium.bidi.network.Cookie; + +public class CookieFilter { + private final Map map = new HashMap<>(); + + public CookieFilter name(String name) { + map.put("name", name); + return this; + } + + public CookieFilter value(BytesValue value) { + map.put("value", value); + return this; + } + + public CookieFilter domain(String domain) { + map.put("domain", domain); + return this; + } + + public CookieFilter path(String path) { + map.put("path", path); + return this; + } + + public CookieFilter size(long size) { + map.put("size", size); + return this; + } + + public CookieFilter httpOnly(boolean httpOnly) { + map.put("httpOnly", httpOnly); + return this; + } + + public CookieFilter secure(boolean secure) { + map.put("secure", secure); + return this; + } + + public CookieFilter sameSite(Cookie.SameSite sameSite) { + map.put("sameSite", sameSite.toString()); + return this; + } + + public CookieFilter expiry(long expiry) { + map.put("expiry", expiry); + return this; + } + + public Map toMap() { + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/storage/DeleteCookiesParameters.java b/java/src/org/openqa/selenium/bidi/storage/DeleteCookiesParameters.java new file mode 100644 index 0000000000000..b57a11f4b3ba6 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/DeleteCookiesParameters.java @@ -0,0 +1,48 @@ +// 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.storage; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class DeleteCookiesParameters { + private Optional cookieFilter = Optional.empty(); + private Optional partitionDescriptor = Optional.empty(); + + public DeleteCookiesParameters( + CookieFilter cookieFilter, PartitionDescriptor partitionDescriptor) { + this.cookieFilter = Optional.of(cookieFilter); + this.partitionDescriptor = Optional.of(partitionDescriptor); + } + + public DeleteCookiesParameters(CookieFilter cookieFilter) { + this.cookieFilter = Optional.of(cookieFilter); + } + + public DeleteCookiesParameters(PartitionDescriptor partitionDescriptor) { + this.partitionDescriptor = Optional.of(partitionDescriptor); + } + + public Map toMap() { + Map map = new HashMap<>(); + cookieFilter.ifPresent(filter -> map.put("filter", cookieFilter)); + partitionDescriptor.ifPresent(descriptor -> map.put("partition", partitionDescriptor)); + + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/storage/GetCookiesParameters.java b/java/src/org/openqa/selenium/bidi/storage/GetCookiesParameters.java new file mode 100644 index 0000000000000..472a17c5c86fd --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/GetCookiesParameters.java @@ -0,0 +1,47 @@ +// 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.storage; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class GetCookiesParameters { + private Optional cookieFilter = Optional.empty(); + private Optional partitionDescriptor = Optional.empty(); + + public GetCookiesParameters(CookieFilter cookieFilter, PartitionDescriptor partitionDescriptor) { + this.cookieFilter = Optional.of(cookieFilter); + this.partitionDescriptor = Optional.of(partitionDescriptor); + } + + public GetCookiesParameters(CookieFilter cookieFilter) { + this.cookieFilter = Optional.of(cookieFilter); + } + + public GetCookiesParameters(PartitionDescriptor partitionDescriptor) { + this.partitionDescriptor = Optional.of(partitionDescriptor); + } + + public Map toMap() { + Map map = new HashMap<>(); + cookieFilter.ifPresent(filter -> map.put("filter", filter)); + partitionDescriptor.ifPresent(descriptor -> map.put("partition", descriptor)); + + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/storage/GetCookiesResult.java b/java/src/org/openqa/selenium/bidi/storage/GetCookiesResult.java new file mode 100644 index 0000000000000..62f953d0815a8 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/GetCookiesResult.java @@ -0,0 +1,68 @@ +// 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.storage; + +import java.util.ArrayList; +import java.util.List; +import org.openqa.selenium.bidi.network.Cookie; +import org.openqa.selenium.json.JsonInput; +import org.openqa.selenium.json.TypeToken; + +public class GetCookiesResult { + private final List cookies; + + private final PartitionKey partitionKey; + + public GetCookiesResult(List cookies, PartitionKey partitionKey) { + this.cookies = cookies; + this.partitionKey = partitionKey; + } + + public static GetCookiesResult fromJson(JsonInput input) { + List cookies = new ArrayList<>(); + PartitionKey partitionKey = null; + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "cookies": + cookies = input.read(new TypeToken>() {}.getType()); + break; + + case "partitionKey": + partitionKey = input.read(PartitionKey.class); + break; + + default: + input.skipValue(); + break; + } + } + + input.endObject(); + + return new GetCookiesResult(cookies, partitionKey); + } + + public List getCookies() { + return cookies; + } + + public PartitionKey getPartitionKey() { + return partitionKey; + } +} diff --git a/java/src/org/openqa/selenium/bidi/storage/PartialCookie.java b/java/src/org/openqa/selenium/bidi/storage/PartialCookie.java new file mode 100644 index 0000000000000..3109eb6298428 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/PartialCookie.java @@ -0,0 +1,67 @@ +// 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.storage; + +import java.util.HashMap; +import java.util.Map; +import org.openqa.selenium.bidi.network.BytesValue; +import org.openqa.selenium.bidi.network.Cookie; + +public class PartialCookie { + private final Map map = new HashMap<>(); + + public PartialCookie(String name, BytesValue value, String domain) { + map.put("name", name); + map.put("value", value); + map.put("domain", domain); + } + + public PartialCookie path(String path) { + map.put("path", path); + return this; + } + + public PartialCookie size(long size) { + map.put("size", size); + return this; + } + + public PartialCookie httpOnly(boolean httpOnly) { + map.put("httpOnly", httpOnly); + return this; + } + + public PartialCookie secure(boolean secure) { + map.put("secure", secure); + return this; + } + + public PartialCookie sameSite(Cookie.SameSite sameSite) { + map.put("sameSite", sameSite.toString()); + return this; + } + + public PartialCookie expiry(long expiry) { + map.put("expiry", expiry); + return this; + } + + public Map toMap() { + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/storage/PartitionDescriptor.java b/java/src/org/openqa/selenium/bidi/storage/PartitionDescriptor.java new file mode 100644 index 0000000000000..d101ca4b3b40d --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/PartitionDescriptor.java @@ -0,0 +1,47 @@ +// 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.storage; + +import java.util.Map; + +public abstract class PartitionDescriptor { + + enum Type { + CONTEXT("context"), + STORAGE_KEY("storageKey"); + + private final String type; + + Type(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + } + + Type type; + + PartitionDescriptor(Type type) { + this.type = type; + } + + abstract Map toMap(); +} diff --git a/java/src/org/openqa/selenium/bidi/storage/PartitionKey.java b/java/src/org/openqa/selenium/bidi/storage/PartitionKey.java new file mode 100644 index 0000000000000..7a3603b084511 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/PartitionKey.java @@ -0,0 +1,64 @@ +// 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.storage; + +import org.openqa.selenium.json.JsonInput; + +public class PartitionKey { + private final String userContext; + private final String sourceOrigin; + + public PartitionKey(String userContext, String sourceOrigin) { + this.userContext = userContext; + this.sourceOrigin = sourceOrigin; + } + + public static PartitionKey fromJson(JsonInput input) { + String userContext = null; + String sourceOrigin = null; + + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "userContext": + userContext = input.read(String.class); + break; + + case "sourceOrigin": + sourceOrigin = input.read(String.class); + break; + + default: + input.skipValue(); + break; + } + } + + input.endObject(); + + return new PartitionKey(userContext, sourceOrigin); + } + + public String getUserContext() { + return userContext; + } + + public String getSourceOrigin() { + return sourceOrigin; + } +} diff --git a/java/src/org/openqa/selenium/bidi/storage/SetCookieParameters.java b/java/src/org/openqa/selenium/bidi/storage/SetCookieParameters.java new file mode 100644 index 0000000000000..585c6048c8ad2 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/SetCookieParameters.java @@ -0,0 +1,43 @@ +// 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.storage; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class SetCookieParameters { + private final PartialCookie cookie; + private Optional partitionDescriptor = Optional.empty(); + + public SetCookieParameters(PartialCookie cookie) { + this.cookie = cookie; + } + + public SetCookieParameters(PartialCookie cookie, PartitionDescriptor partitionDescriptor) { + this.cookie = cookie; + this.partitionDescriptor = Optional.of(partitionDescriptor); + } + + public Map toMap() { + Map map = new HashMap<>(); + map.put("cookie", cookie.toMap()); + partitionDescriptor.ifPresent(descriptor -> map.put("partition", descriptor)); + + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/storage/StorageKeyPartitionDescriptor.java b/java/src/org/openqa/selenium/bidi/storage/StorageKeyPartitionDescriptor.java new file mode 100644 index 0000000000000..0d67d1dce6d21 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/storage/StorageKeyPartitionDescriptor.java @@ -0,0 +1,44 @@ +// 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.storage; + +import java.util.HashMap; +import java.util.Map; + +public class StorageKeyPartitionDescriptor extends PartitionDescriptor { + Map map = new HashMap<>(); + + public StorageKeyPartitionDescriptor() { + super(Type.STORAGE_KEY); + map.put("type", super.type.toString()); + } + + public StorageKeyPartitionDescriptor userContext(String userContext) { + map.put("userContext", userContext); + return this; + } + + public StorageKeyPartitionDescriptor sourceOrigin(String sourceOrigin) { + map.put("sourceOrigin", sourceOrigin); + return this; + } + + public Map toMap() { + return map; + } +} diff --git a/java/test/org/openqa/selenium/bidi/storage/BUILD.bazel b/java/test/org/openqa/selenium/bidi/storage/BUILD.bazel new file mode 100644 index 0000000000000..c0db6bf565e6f --- /dev/null +++ b/java/test/org/openqa/selenium/bidi/storage/BUILD.bazel @@ -0,0 +1,27 @@ +load("@rules_jvm_external//:defs.bzl", "artifact") +load("//java:defs.bzl", "JUNIT5_DEPS", "java_selenium_test_suite") + +java_selenium_test_suite( + name = "large-tests", + size = "large", + srcs = glob(["*Test.java"]), + browsers = [ + "firefox", + ], + tags = [ + "selenium-remote", + ], + deps = [ + "//java/src/org/openqa/selenium/bidi", + "//java/src/org/openqa/selenium/firefox", + "//java/src/org/openqa/selenium/json", + "//java/src/org/openqa/selenium/remote", + "//java/src/org/openqa/selenium/support", + "//java/test/org/openqa/selenium/environment", + "//java/test/org/openqa/selenium/testing:annotations", + "//java/test/org/openqa/selenium/testing:test-base", + "//java/test/org/openqa/selenium/testing/drivers", + artifact("org.junit.jupiter:junit-jupiter-api"), + artifact("org.assertj:assertj-core"), + ] + JUNIT5_DEPS, +) diff --git a/java/test/org/openqa/selenium/bidi/storage/StorageCommandsTest.java b/java/test/org/openqa/selenium/bidi/storage/StorageCommandsTest.java new file mode 100644 index 0000000000000..47d201a40125b --- /dev/null +++ b/java/test/org/openqa/selenium/bidi/storage/StorageCommandsTest.java @@ -0,0 +1,439 @@ +// 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.storage; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.openqa.selenium.testing.drivers.Browser.CHROME; +import static org.openqa.selenium.testing.drivers.Browser.EDGE; +import static org.openqa.selenium.testing.drivers.Browser.FIREFOX; +import static org.openqa.selenium.testing.drivers.Browser.IE; +import static org.openqa.selenium.testing.drivers.Browser.SAFARI; + +import java.time.Instant; +import java.util.Date; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.Cookie; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WindowType; +import org.openqa.selenium.bidi.Storage; +import org.openqa.selenium.bidi.network.BytesValue; +import org.openqa.selenium.testing.JupiterTestBase; +import org.openqa.selenium.testing.NotYetImplemented; + +class StorageCommandsTest extends JupiterTestBase { + private String cookiePage; + private static final Random random = ThreadLocalRandom.current(); + + private Storage storage; + + @BeforeEach + public void setUp() { + cookiePage = appServer.whereIs("/common/cookie"); + + deleteAllCookiesOnServerSide(); + + try { + driver.get(appServer.whereIs("/common/animals")); + } catch (IllegalArgumentException e) { + return; + } + + driver.manage().deleteAllCookies(); + assertNoCookiesArePresent(); + + storage = new Storage(driver); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(CHROME) + @NotYetImplemented(FIREFOX) + public void canGetCookieByName() { + String key = generateUniqueKey(); + String value = "set"; + assertCookieIsNotPresentWithName(key); + + addCookieOnServerSide(new Cookie(key, value)); + + CookieFilter cookieFilter = new CookieFilter(); + cookieFilter.name(key); + cookieFilter.value(new BytesValue(BytesValue.Type.STRING, "set")); + + GetCookiesParameters params = new GetCookiesParameters(cookieFilter); + GetCookiesResult result = storage.getCookies(params); + + assertThat(result.getCookies().get(0).getValue().getValue()).isEqualTo(value); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(CHROME) + @NotYetImplemented(FIREFOX) + public void canGetCookieInDefaultUserContext() { + String windowHandle = driver.getWindowHandle(); + String key = generateUniqueKey(); + String value = "set"; + assertCookieIsNotPresentWithName(key); + + addCookieOnServerSide(new Cookie(key, value)); + + CookieFilter cookieFilter = new CookieFilter(); + cookieFilter.name(key); + cookieFilter.value(new BytesValue(BytesValue.Type.STRING, "set")); + + driver.switchTo().newWindow(WindowType.WINDOW); + + PartitionDescriptor descriptor = + new BrowsingContextPartitionDescriptor(driver.getWindowHandle()); + + GetCookiesParameters params = new GetCookiesParameters(cookieFilter, descriptor); + GetCookiesResult resultAfterSwitchingContext = storage.getCookies(params); + + assertThat(resultAfterSwitchingContext.getCookies().get(0).getValue().getValue()) + .isEqualTo(value); + + driver.switchTo().window(windowHandle); + + descriptor = new BrowsingContextPartitionDescriptor(driver.getWindowHandle()); + + GetCookiesParameters params1 = new GetCookiesParameters(cookieFilter, descriptor); + + GetCookiesResult result = storage.getCookies(params1); + + assertThat(result.getCookies().get(0).getValue().getValue()).isEqualTo(value); + PartitionKey partitionKey = result.getPartitionKey(); + + assertThat(partitionKey.getSourceOrigin()).isNotNull(); + assertThat(partitionKey.getUserContext()).isNotNull(); + assertThat(partitionKey.getUserContext()).isEqualTo("default"); + } + + @Test + // TODO: Once Browser module is added this test needs to be added + public void canGetCookieInAUserContext() {} + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(CHROME) + @NotYetImplemented(FIREFOX) + public void canAddCookie() { + String key = generateUniqueKey(); + String value = "foo"; + + SetCookieParameters parameters = + new SetCookieParameters( + new PartialCookie( + key, new BytesValue(BytesValue.Type.STRING, value), appServer.getHostName())); + assertCookieIsNotPresentWithName(key); + + storage.setCookie(parameters); + + assertCookieHasValue(key, value); + openAnotherPage(); + assertCookieHasValue(key, value); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(CHROME) + @NotYetImplemented(FIREFOX) + public void canAddAndGetCookie() { + driver.get(appServer.whereIs("/common/animals")); + + BytesValue value = new BytesValue(BytesValue.Type.STRING, "cod"); + String domain = appServer.getHostName(); + + long expiry = Instant.now().toEpochMilli() + 3600 * 1000; + + String path = "/common/animals"; + + PartialCookie cookie = + new PartialCookie("fish", value, appServer.getHostName()) + .path("/common/animals") + .size(7L) + .httpOnly(true) + .secure(false) + .sameSite(org.openqa.selenium.bidi.network.Cookie.SameSite.LAX) + .expiry(expiry); + + SetCookieParameters cookieParameters = new SetCookieParameters(cookie); + + storage.setCookie(cookieParameters); + + driver.get(appServer.whereIs(path)); + + CookieFilter cookieFilter = + new CookieFilter() + .name("fish") + .value(value) + .domain(domain) + .path(path) + .size(7L) + .httpOnly(true) + .secure(false) + .sameSite(org.openqa.selenium.bidi.network.Cookie.SameSite.LAX) + .expiry(expiry); + + PartitionDescriptor descriptor = + new BrowsingContextPartitionDescriptor(driver.getWindowHandle()); + + GetCookiesParameters getCookiesParameters = new GetCookiesParameters(cookieFilter, descriptor); + + GetCookiesResult result = storage.getCookies(getCookiesParameters); + PartitionKey key = result.getPartitionKey(); + + org.openqa.selenium.bidi.network.Cookie resultCookie = result.getCookies().get(0); + + assertThat(resultCookie.getName()).isEqualTo("fish"); + assertThat(resultCookie.getValue().getValue()).isEqualTo(value.getValue()); + assertThat(resultCookie.getDomain()).isEqualTo(domain); + assertThat(resultCookie.getPath()).isEqualTo(path); + assertThat(resultCookie.getSize()).isEqualTo(7L); + assertThat(resultCookie.isHttpOnly()).isEqualTo(true); + assertThat(resultCookie.isSecure()).isEqualTo(false); + assertThat(resultCookie.getSameSite()) + .isEqualTo(org.openqa.selenium.bidi.network.Cookie.SameSite.LAX); + assertThat(resultCookie.getExpiry().get()).isEqualTo(expiry); + assertThat(key.getSourceOrigin()).isNotNull(); + assertThat(key.getUserContext()).isNotNull(); + assertThat(key.getUserContext()).isEqualTo("default"); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(CHROME) + @NotYetImplemented(FIREFOX) + public void canGetAllCookies() { + String key1 = generateUniqueKey(); + String key2 = generateUniqueKey(); + + assertCookieIsNotPresentWithName(key1); + assertCookieIsNotPresentWithName(key2); + + GetCookiesParameters params = new GetCookiesParameters(new CookieFilter()); + GetCookiesResult result = storage.getCookies(params); + + int countBefore = result.getCookies().size(); + + Cookie one = new Cookie.Builder(key1, "value").build(); + Cookie two = new Cookie.Builder(key2, "value").build(); + + driver.manage().addCookie(one); + driver.manage().addCookie(two); + + openAnotherPage(); + result = storage.getCookies(params); + assertThat(result.getCookies().size()).isEqualTo(countBefore + 2); + + assertThat(result.getCookies().get(0).getName().contains(key1)).isTrue(); + assertThat(result.getCookies().get(1).getName().contains(key2)).isTrue(); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(CHROME) + @NotYetImplemented(FIREFOX) + public void canDeleteAllCookies() { + addCookieOnServerSide(new Cookie("foo", "set")); + assertSomeCookiesArePresent(); + + storage.deleteCookies(new DeleteCookiesParameters(new CookieFilter())); + + assertNoCookiesArePresent(); + + openAnotherPage(); + assertNoCookiesArePresent(); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(CHROME) + @NotYetImplemented(FIREFOX) + public void canDeleteCookieWithName() { + String key1 = generateUniqueKey(); + String key2 = generateUniqueKey(); + + addCookieOnServerSide(new Cookie(key1, "set")); + addCookieOnServerSide(new Cookie(key2, "set")); + + assertCookieIsPresentWithName(key1); + assertCookieIsPresentWithName(key2); + + storage.deleteCookies(new DeleteCookiesParameters(new CookieFilter().name(key1))); + + assertCookieIsNotPresentWithName(key1); + assertCookieIsPresentWithName(key2); + + openAnotherPage(); + assertCookieIsNotPresentWithName(key1); + assertCookieIsPresentWithName(key2); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(CHROME) + @NotYetImplemented(FIREFOX) + public void testAddCookiesWithDifferentPathsThatAreRelatedToOurs() { + driver.get(appServer.whereIs("/common/animals")); + + PartialCookie cookie1 = + new PartialCookie( + "fish", new BytesValue(BytesValue.Type.STRING, "cod"), appServer.getHostName()) + .path("/common/animals"); + + PartialCookie cookie2 = + new PartialCookie( + "planet", new BytesValue(BytesValue.Type.STRING, "earth"), appServer.getHostName()) + .path("/common/"); + + SetCookieParameters cookie1Parameters = new SetCookieParameters(cookie1); + SetCookieParameters cookie2Parameters = new SetCookieParameters(cookie2); + + storage.setCookie(cookie1Parameters); + storage.setCookie(cookie2Parameters); + + driver.get(appServer.whereIs("/common/animals")); + + assertCookieIsPresentWithName("fish"); + assertCookieIsPresentWithName("planet"); + + driver.get(appServer.whereIs("/common/simpleTest.html")); + assertCookieIsNotPresentWithName("fish"); + } + + @AfterEach + public void quitDriver() { + if (driver != null) { + driver.quit(); + } + } + + private String generateUniqueKey() { + return String.format("key_%d", random.nextInt()); + } + + private void assertNoCookiesArePresent() { + assertThat(driver.manage().getCookies()).isEmpty(); + String documentCookie = getDocumentCookieOrNull(); + if (documentCookie != null) { + assertThat(documentCookie).isEmpty(); + } + } + + private void assertSomeCookiesArePresent() { + assertThat(driver.manage().getCookies()).isNotEmpty(); + String documentCookie = getDocumentCookieOrNull(); + if (documentCookie != null) { + assertThat(documentCookie).as("Cookies were empty").isNotEqualTo(""); + } + } + + private void assertCookieIsNotPresentWithName(final String key) { + assertThat(driver.manage().getCookieNamed(key)).as("Cookie with name " + key).isNull(); + String documentCookie = getDocumentCookieOrNull(); + if (documentCookie != null) { + assertThat(documentCookie).as("Cookie with name " + key).doesNotContain((key + "=")); + } + } + + private void assertCookieIsPresentWithName(final String key) { + assertThat(driver.manage().getCookieNamed(key)).as("Cookie with name " + key).isNotNull(); + String documentCookie = getDocumentCookieOrNull(); + if (documentCookie != null) { + assertThat(documentCookie) + .as("Cookie was not present with name " + key + ", got: " + documentCookie) + .contains(key + "="); + } + } + + private void assertCookieHasValue(final String key, final String value) { + assertThat(driver.manage().getCookieNamed(key).getValue()).isEqualTo(value); + String documentCookie = getDocumentCookieOrNull(); + if (documentCookie != null) { + assertThat(documentCookie) + .as("Cookie was present with name " + key) + .contains(key + "=" + value); + } + } + + private String getDocumentCookieOrNull() { + if (!(driver instanceof JavascriptExecutor)) { + return null; + } + try { + return (String) ((JavascriptExecutor) driver).executeScript("return document.cookie"); + } catch (UnsupportedOperationException e) { + return null; + } + } + + private Date someTimeInTheFuture() { + return new Date(System.currentTimeMillis() + 100000); + } + + private void openAnotherPage() { + driver.get(appServer.whereIs("simpleTest.html")); + } + + private void deleteAllCookiesOnServerSide() { + driver.get(cookiePage + "?action=deleteAll"); + } + + private void addCookieOnServerSide(Cookie cookie) { + StringBuilder url = new StringBuilder(cookiePage); + url.append("?action=add"); + url.append("&name=").append(cookie.getName()); + url.append("&value=").append(cookie.getValue()); + if (cookie.getDomain() != null) { + url.append("&domain=").append(cookie.getDomain()); + } + if (cookie.getPath() != null) { + url.append("&path=").append(cookie.getPath()); + } + if (cookie.getExpiry() != null) { + url.append("&expiry=").append(cookie.getExpiry().getTime()); + } + if (cookie.isSecure()) { + url.append("&secure=").append(cookie.isSecure()); + } + if (cookie.isHttpOnly()) { + url.append("&httpOnly=").append(cookie.isHttpOnly()); + } + driver.get(url.toString()); + } +}