Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion java/src/org/openqa/selenium/bidi/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

package org.openqa.selenium.bidi;

import static java.util.Collections.unmodifiableMap;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.openqa.selenium.internal.Require;
Expand Down Expand Up @@ -48,7 +51,8 @@ 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 =
unmodifiableMap(new HashMap<String, Object>(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
Expand Up @@ -22,5 +22,6 @@ java_library(
"//java/src/org/openqa/selenium/json",
"//java/src/org/openqa/selenium/remote/http",
artifact("com.google.auto.service:auto-service-annotations"),
"@maven//:org_jspecify_jspecify",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WindowType;
import org.openqa.selenium.bidi.BiDi;
Expand All @@ -36,6 +38,7 @@
import org.openqa.selenium.json.TypeToken;
import org.openqa.selenium.print.PrintOptions;

@NullMarked
public class BrowsingContext {

private static final Json JSON = new Json();
Expand Down Expand Up @@ -91,6 +94,7 @@ public BrowsingContext(WebDriver driver, String id) {

public BrowsingContext(WebDriver driver, WindowType type) {
Require.nonNull("WebDriver", driver);
Require.nonNull("WindowType", type);

if (!(driver instanceof HasBiDi)) {
throw new IllegalArgumentException("WebDriver instance must support BiDi protocol");
Expand All @@ -103,6 +107,7 @@ public BrowsingContext(WebDriver driver, WindowType type) {

public BrowsingContext(WebDriver driver, CreateContextParameters parameters) {
Require.nonNull("WebDriver", driver);
Require.nonNull("CreateContextParameters", parameters);

if (!(driver instanceof HasBiDi)) {
throw new IllegalArgumentException("WebDriver instance must support BiDi protocol");
Expand Down Expand Up @@ -302,31 +307,68 @@ public String captureElementScreenshot(String elementId, String handle) {
}));
}

public void setViewport(double width, double height) {
Require.positive("Viewport width", width);
Require.positive("Viewport height", height);
public void setViewport(int width, int height) {
setViewport((double) width, (double) height);
}

this.bidi.send(
new Command<>(
"browsingContext.setViewport",
Map.of(CONTEXT, id, "viewport", Map.of("width", width, "height", height))));
public void setViewport(int width, int height, double devicePixelRatio) {
setViewport((double) width, (double) height, devicePixelRatio);
}

public void setViewport(double width, double height, double devicePixelRatio) {
Require.positive("Viewport width", width);
Require.positive("Viewport height", height);
Require.positive("Device pixel ratio.", devicePixelRatio);
/**
* Set viewport size to given width and height (aka "mobile emulation" mode).
*
* <p>If both {@code width} and {@code height} are null, then resets viewport to the initial size
* (aka "desktop" mode).
*
* @param width null or positive
* @param height null or positive
*/
public void setViewport(@Nullable Double width, @Nullable Double height) {
validate(width, height);

this.bidi.send(
new Command<>(
"browsingContext.setViewport",
Map.of(
CONTEXT,
id,
"viewport",
Map.of("width", width, "height", height),
"devicePixelRatio",
devicePixelRatio)));
Map<String, Object> params = new HashMap<>();
params.put(CONTEXT, id);
params.put("viewport", width == null ? null : Map.of("width", width, "height", height));
this.bidi.send(new Command<>("browsingContext.setViewport", params));
}

/**
* Set viewport's size and pixel ratio (aka "mobile emulation" mode).
*
* <p>If both {@code width} and {@code height} are null then resets viewport to the initial size
* (aka "desktop" mode).
*
* <p>If {@code devicePixelRatio} is null then resets DPR to browser’s default DPR (usually 1.0 on
* desktop).
*
* @param width null or positive
* @param height null or positive
* @param devicePixelRatio null or positive
*/
public void setViewport(
@Nullable Double width, @Nullable Double height, @Nullable Double devicePixelRatio) {
validate(width, height);
validate(devicePixelRatio);

Map<String, Object> params = new HashMap<>();
params.put(CONTEXT, id);
params.put("viewport", width == null ? null : Map.of("width", width, "height", height));
params.put("devicePixelRatio", devicePixelRatio);
this.bidi.send(new Command<>("browsingContext.setViewport", params));
}

private void validate(@Nullable Double width, @Nullable Double height) {
if (width != null || height != null) {
Require.positive("Viewport width", width);
Require.positive("Viewport height", height);
}
}

private void validate(@Nullable Double devicePixelRatio) {
if (devicePixelRatio != null) {
Require.positive("Device pixel ratio.", devicePixelRatio);
}
}

public void activate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,9 @@ public static BrowsingContextInfo fromJson(JsonInput input) {
return new BrowsingContextInfo(
id, url, children, clientWindow, originalOpener, userContext, parentBrowsingContext);
}

@Override
public String toString() {
return String.format("BrowsingContextInfo(%s %s)", id, url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ java_selenium_test_suite(
"selenium-remote",
],
deps = [
"//java/src/org/openqa/selenium:core",
"//java/src/org/openqa/selenium/bidi",
"//java/src/org/openqa/selenium/bidi/browsingcontext",
"//java/src/org/openqa/selenium/bidi/log",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@

package org.openqa.selenium.bidi.browsingcontext;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent;
import static org.openqa.selenium.support.ui.ExpectedConditions.titleIs;
import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOfElementLocated;

import java.util.List;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.WebElement;
Expand Down Expand Up @@ -136,9 +137,9 @@ void canGetTreeWithAChild() {

List<BrowsingContextInfo> contextInfoList = parentWindow.getTree();

assertThat(contextInfoList.size()).isEqualTo(1);
assertThat(contextInfoList).hasSize(1);
BrowsingContextInfo info = contextInfoList.get(0);
assertThat(info.getChildren().size()).isEqualTo(1);
assertThat(info.getChildren()).hasSize(1);
assertThat(info.getId()).isEqualTo(referenceContextId);
assertThat(info.getChildren().get(0).getUrl()).contains("formPage.html");
}
Expand All @@ -155,7 +156,7 @@ void canGetTreeWithDepth() {

List<BrowsingContextInfo> contextInfoList = parentWindow.getTree(0);

assertThat(contextInfoList.size()).isEqualTo(1);
assertThat(contextInfoList).hasSize(1);
BrowsingContextInfo info = contextInfoList.get(0);
assertThat(info.getChildren()).isNull(); // since depth is 0
assertThat(info.getId()).isEqualTo(referenceContextId);
Expand All @@ -176,7 +177,7 @@ void canGetTreeWithRootAndDepth() {

List<BrowsingContextInfo> contextInfoList = parentWindow.getTree(referenceContextId, 1);

assertThat(contextInfoList.size()).isEqualTo(1);
assertThat(contextInfoList).hasSize(1);
BrowsingContextInfo info = contextInfoList.get(0);
assertThat(info.getChildren()).isNotNull(); // since depth is 1
assertThat(info.getId()).isEqualTo(referenceContextId);
Expand All @@ -199,7 +200,7 @@ void canGetTreeWithRoot() {

List<BrowsingContextInfo> contextInfoList = parentWindow.getTree(tab.getId());

assertThat(contextInfoList.size()).isEqualTo(1);
assertThat(contextInfoList).hasSize(1);
BrowsingContextInfo info = contextInfoList.get(0);
assertThat(info.getId()).isEqualTo(tab.getId());
assertThat(info.getOriginalOpener()).isNull();
Expand All @@ -215,7 +216,7 @@ void canGetAllTopLevelContexts() {

List<BrowsingContextInfo> contextInfoList = window1.getTopLevelContexts();

assertThat(contextInfoList.size()).isEqualTo(2);
assertThat(contextInfoList).hasSize(2);
}

@Test
Expand All @@ -226,7 +227,9 @@ void canCloseAWindow() {

window2.close();

assertThatExceptionOfType(BiDiException.class).isThrownBy(window2::getTree);
assertThatThrownBy(window2::getTree)
.isInstanceOf(BiDiException.class)
.hasMessageContaining("not found");
}

@Test
Expand All @@ -237,7 +240,9 @@ void canCloseATab() {

tab2.close();

assertThatExceptionOfType(BiDiException.class).isThrownBy(tab2::getTree);
assertThatThrownBy(tab2::getTree)
.isInstanceOf(BiDiException.class)
.hasMessageContaining("not found");
}

@Test
Expand Down Expand Up @@ -397,7 +402,7 @@ void canCaptureScreenshot() {

String screenshot = browsingContext.captureScreenshot();

assertThat(screenshot.length()).isPositive();
assertThat(screenshot).isNotEmpty();
}

@Test
Expand All @@ -423,7 +428,7 @@ void canCaptureScreenshotWithAllParameters() {
.origin(CaptureScreenshotParameters.Origin.DOCUMENT)
.clipRectangle(clipRectangle));

assertThat(screenshot.length()).isPositive();
assertThat(screenshot).isNotEmpty();
}

@Test
Expand All @@ -440,7 +445,7 @@ void canCaptureScreenshotOfViewport() {
browsingContext.captureBoxScreenshot(
elementRectangle.getX(), elementRectangle.getY(), 5, 5);

assertThat(screenshot.length()).isPositive();
assertThat(screenshot).isNotEmpty();
}

@Test
Expand All @@ -455,46 +460,41 @@ void canCaptureElementScreenshot() {
String screenshot =
browsingContext.captureElementScreenshot(((RemoteWebElement) element).getId());

assertThat(screenshot.length()).isPositive();
assertThat(screenshot).isNotEmpty();
}

@Test
@NeedsFreshDriver
void canSetViewport() {
Dimension initialViewportSize = getViewportSize();

BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
driver.get(appServer.whereIs("formPage.html"));

browsingContext.setViewport(250, 300);
assertThat(getViewportSize()).isEqualTo(new Dimension(250, 300));

List<Long> newViewportSize =
(List<Long>)
((JavascriptExecutor) driver)
.executeScript("return [window.innerWidth, window.innerHeight];");

assertThat(newViewportSize.get(0)).isEqualTo(250);
assertThat(newViewportSize.get(1)).isEqualTo(300);
browsingContext.setViewport(null, null);
assertThat(getViewportSize()).isEqualTo(initialViewportSize);
}

@Test
@NeedsFreshDriver
void canSetViewportWithDevicePixelRatio() {
Dimension initialViewportSize = getViewportSize();
double initialPixelRation = getDevicePixelRatio();

BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
driver.get(appServer.whereIs("formPage.html"));

browsingContext.setViewport(250, 300, 5);
browsingContext.setViewport(250, 300, 5.5);

List<Long> newViewportSize =
(List<Long>)
((JavascriptExecutor) driver)
.executeScript("return [window.innerWidth, window.innerHeight];");
assertThat(getViewportSize()).isEqualTo(new Dimension(250, 300));
assertThat(getDevicePixelRatio()).isEqualTo(5.5);

assertThat(newViewportSize.get(0)).isEqualTo(250);
assertThat(newViewportSize.get(1)).isEqualTo(300);

Long newDevicePixelRatio =
(Long) ((JavascriptExecutor) driver).executeScript("return window.devicePixelRatio");

assertThat(newDevicePixelRatio).isEqualTo(5);
browsingContext.setViewport(null, null, null);
assertThat(getViewportSize()).isEqualTo(initialViewportSize);
assertThat(getDevicePixelRatio()).isEqualTo(initialPixelRation);
}

@Test
Expand All @@ -507,7 +507,7 @@ void canPrintPage() {

String printPage = browsingContext.print(printOptions);

assertThat(printPage.length()).isPositive();
assertThat(printPage).isNotEmpty();
// Comparing expected PDF is a hard problem.
// As long as we are sending the parameters correctly it should be fine.
// Trusting the browsers to do the right thing.
Expand Down Expand Up @@ -568,7 +568,20 @@ private String promptPage() {
"<p id=\"result\"></p>"));
}

private <T> T executeScript(String js) {
return (T) ((JavascriptExecutor) driver).executeScript(js);
}

private boolean getDocumentFocus() {
return (boolean) ((JavascriptExecutor) driver).executeScript("return document.hasFocus();");
return executeScript("return document.hasFocus();");
}

private Dimension getViewportSize() {
List<Number> dimensions = executeScript("return [window.innerWidth, window.innerHeight];");
return new Dimension(dimensions.get(0).intValue(), dimensions.get(1).intValue());
}

private double getDevicePixelRatio() {
return ((Number) executeScript("return window.devicePixelRatio")).doubleValue();
}
}
Loading