diff --git a/java/src/org/openqa/selenium/bidi/Browser.java b/java/src/org/openqa/selenium/bidi/Browser.java new file mode 100644 index 0000000000000..6cbd326fc5155 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/Browser.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; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.json.JsonInput; + +public class Browser { + private final BiDi bidi; + + private final Function userContextInfoMapper = + jsonInput -> { + Map response = jsonInput.read(Map.class); + return (String) response.get("userContext"); + }; + + private final Function> userContextsInfoMapper = + jsonInput -> { + Map response = jsonInput.read(Map.class); + List> userContextsResponse = + (List>) response.get("userContexts"); + + List userContexts = new ArrayList<>(); + userContextsResponse.forEach( + map -> { + String userContext = map.get("userContext"); + userContexts.add(userContext); + }); + + return userContexts; + }; + + public Browser(WebDriver driver) { + this.bidi = ((HasBiDi) driver).getBiDi(); + } + + public String createUserContext() { + return bidi.send(new Command<>("browser.createUserContext", Map.of(), userContextInfoMapper)); + } + + public List getUserContexts() { + return bidi.send(new Command<>("browser.getUserContexts", Map.of(), userContextsInfoMapper)); + } + + public void removeUserContext(String userContext) { + bidi.send(new Command<>("browser.removeUserContext", Map.of("userContext", userContext))); + } +} diff --git a/java/test/org/openqa/selenium/bidi/browser/BUILD.bazel b/java/test/org/openqa/selenium/bidi/browser/BUILD.bazel new file mode 100644 index 0000000000000..fcfc9fb62c973 --- /dev/null +++ b/java/test/org/openqa/selenium/bidi/browser/BUILD.bazel @@ -0,0 +1,28 @@ +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("com.google.guava:guava"), + artifact("org.junit.jupiter:junit-jupiter-api"), + artifact("org.assertj:assertj-core"), + ] + JUNIT5_DEPS, +) diff --git a/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java b/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java new file mode 100644 index 0000000000000..70bf0a3bf13e4 --- /dev/null +++ b/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java @@ -0,0 +1,106 @@ +// 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.browser; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.openqa.selenium.testing.Safely.safelyCall; +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.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.bidi.Browser; +import org.openqa.selenium.environment.webserver.AppServer; +import org.openqa.selenium.environment.webserver.NettyAppServer; +import org.openqa.selenium.testing.JupiterTestBase; +import org.openqa.selenium.testing.NotYetImplemented; + +class BrowserCommandsTest extends JupiterTestBase { + + private AppServer server; + private Browser browser; + + @BeforeEach + public void setUp() { + server = new NettyAppServer(); + server.start(); + browser = new Browser(driver); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(FIREFOX) + void canCreateAUserContext() { + String userContext = browser.createUserContext(); + + assertThat(userContext).isNotNull(); + + browser.removeUserContext(userContext); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(FIREFOX) + void canGetUserContexts() { + String userContext1 = browser.createUserContext(); + String userContext2 = browser.createUserContext(); + + List userContexts = browser.getUserContexts(); + assertThat(userContexts.size()).isGreaterThanOrEqualTo(2); + + browser.removeUserContext(userContext1); + browser.removeUserContext(userContext2); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + @NotYetImplemented(EDGE) + @NotYetImplemented(FIREFOX) + void canRemoveUserContext() { + String userContext1 = browser.createUserContext(); + String userContext2 = browser.createUserContext(); + + List userContexts = browser.getUserContexts(); + assertThat(userContexts.size()).isGreaterThanOrEqualTo(2); + + browser.removeUserContext(userContext2); + + List updatedUserContexts = browser.getUserContexts(); + assertThat(userContext1).isIn(updatedUserContexts); + assertThat(userContext2).isNotIn(updatedUserContexts); + + browser.removeUserContext(userContext1); + } + + @AfterEach + public void quitDriver() { + if (driver != null) { + driver.quit(); + } + safelyCall(server::stop); + } +} diff --git a/javascript/node/selenium-webdriver/bidi/browser.js b/javascript/node/selenium-webdriver/bidi/browser.js new file mode 100644 index 0000000000000..35a1b8adef4c2 --- /dev/null +++ b/javascript/node/selenium-webdriver/bidi/browser.js @@ -0,0 +1,78 @@ +// 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. + +class Browser { + constructor(driver) { + this._driver = driver + } + + async init() { + if (!(await this._driver.getCapabilities()).get('webSocketUrl')) { + throw Error('WebDriver instance must support BiDi protocol') + } + + this.bidi = await this._driver.getBidi() + } + + async createUserContext() { + const command = { + method: 'browser.createUserContext', + params: {}, + } + + let response = await this.bidi.send(command) + + return response.result.userContext + } + + async getUserContexts() { + const command = { + method: 'browser.getUserContexts', + params: {}, + } + + let response = await this.bidi.send(command) + + let userContexts = [] + + let userContextsArray = response.result.userContexts + + for (let userContextJson of userContextsArray) { + userContexts.push(userContextJson.userContext) + } + + return userContexts + } + + async removeUserContext(userContext) { + const command = { + method: 'browser.removeUserContext', + params: { userContext: userContext }, + } + + await this.bidi.send(command) + } + +} + +async function getBrowserInstance(driver) { + let instance = new Browser(driver) + await instance.init() + return instance +} + +module.exports = getBrowserInstance diff --git a/javascript/node/selenium-webdriver/test/bidi/browser_test.js b/javascript/node/selenium-webdriver/test/bidi/browser_test.js new file mode 100644 index 0000000000000..885433550d820 --- /dev/null +++ b/javascript/node/selenium-webdriver/test/bidi/browser_test.js @@ -0,0 +1,85 @@ +// 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. + +'use strict' + +const assert = require('assert') +const firefox = require('../../firefox') +const {ignore, Pages, suite} = require('../../lib/test') +const {Browser, By, until} = require('../..') +const BrowserBiDi = require('../../bidi/browser') + +suite( + function (env) { + describe('BiDi Browser', function () { + let driver + + beforeEach(async function () { + driver = await env.builder().setFirefoxOptions(new firefox.Options().enableBidi()).build() + }) + + afterEach(function () { + return driver.quit() + }) + + xit('can create a user context', async function () { + const browser = await BrowserBiDi(driver) + + const userContext = await browser.createUserContext() + + assert.notEqual(userContext, null) + + await browser.removeUserContext(userContext) + }) + + xit('can get user contexts', async function () { + const browser = await BrowserBiDi(driver) + + const userContext1 = await browser.createUserContext() + const userContext2 = await browser.createUserContext() + + const userContexts = await browser.getUserContexts() + + assert.strictEqual(userContexts.length >= 2, true) + + await browser.removeUserContext(userContext1) + await browser.removeUserContext(userContext2) + }) + + xit('can remove user context', async function () { + const browser = await BrowserBiDi(driver) + + const userContext1 = await browser.createUserContext() + const userContext2 = await browser.createUserContext() + + const userContexts = await browser.getUserContexts() + + assert.strictEqual(userContexts.length >= 2, true) + + await browser.removeUserContext(userContext2) + + const updatedUserContexts = await browser.getUserContexts() + + assert.strictEqual(updatedUserContexts.includes(userContext1), true) + assert.strictEqual(updatedUserContexts.includes(userContext2), false) + + await browser.removeUserContext(userContext1) + }) + }) + }, + {browsers: [Browser.FIREFOX]}, +)