Skip to content

Commit

Permalink
[bidi][java] Add high-level logging APIs (SeleniumHQ#14225)
Browse files Browse the repository at this point in the history
  • Loading branch information
pujagani authored and sandeepsuryaprasad committed Oct 29, 2024
1 parent 57e212e commit 7e7e5f0
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 0 deletions.
2 changes: 2 additions & 0 deletions java/src/org/openqa/selenium/remote/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ java_library(
deps = [
"//java/src/org/openqa/selenium:core",
"//java/src/org/openqa/selenium/bidi",
"//java/src/org/openqa/selenium/bidi/log",
"//java/src/org/openqa/selenium/bidi/module",
"//java/src/org/openqa/selenium/concurrent",
"//java/src/org/openqa/selenium/devtools",
"//java/src/org/openqa/selenium/json",
Expand Down
58 changes: 58 additions & 0 deletions java/src/org/openqa/selenium/remote/RemoteScript.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.remote;

import java.util.function.Consumer;
import org.openqa.selenium.Beta;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.bidi.BiDi;
import org.openqa.selenium.bidi.HasBiDi;
import org.openqa.selenium.bidi.log.ConsoleLogEntry;
import org.openqa.selenium.bidi.log.JavascriptLogEntry;
import org.openqa.selenium.bidi.module.LogInspector;

@Beta
class RemoteScript implements Script {
private final BiDi biDi;
private final LogInspector logInspector;

public RemoteScript(WebDriver driver) {
this.biDi = ((HasBiDi) driver).getBiDi();
this.logInspector = new LogInspector(driver);
}

@Override
public long addConsoleMessageHandler(Consumer<ConsoleLogEntry> consumer) {
return this.logInspector.onConsoleEntry(consumer);
}

@Override
public void removeConsoleMessageHandler(long id) {
this.biDi.removeListener(id);
}

@Override
public long addJavaScriptErrorHandler(Consumer<JavascriptLogEntry> consumer) {
return this.logInspector.onJavaScriptException(consumer);
}

@Override
public void removeJavaScriptErrorHandler(long id) {
this.biDi.removeListener(id);
}
}
9 changes: 9 additions & 0 deletions java/src/org/openqa/selenium/remote/RemoteWebDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ public class RemoteWebDriver
private Logs remoteLogs;
private LocalLogs localLogs;

private Script remoteScript;

// For cglib
protected RemoteWebDriver() {
this.capabilities = init(new ImmutableCapabilities());
Expand Down Expand Up @@ -486,6 +488,13 @@ public Options manage() {
return new RemoteWebDriverOptions();
}

public Script script() {
if (this.remoteScript == null) {
this.remoteScript = new RemoteScript(this);
}
return this.remoteScript;
}

protected JsonToWebElementConverter getElementConverter() {
return converter;
}
Expand Down
35 changes: 35 additions & 0 deletions java/src/org/openqa/selenium/remote/Script.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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.remote;

import java.util.function.Consumer;
import org.openqa.selenium.Beta;
import org.openqa.selenium.bidi.log.ConsoleLogEntry;
import org.openqa.selenium.bidi.log.JavascriptLogEntry;

@Beta
public interface Script {

long addConsoleMessageHandler(Consumer<ConsoleLogEntry> consumer);

void removeConsoleMessageHandler(long id);

long addJavaScriptErrorHandler(Consumer<JavascriptLogEntry> consumer);

void removeJavaScriptErrorHandler(long id);
}
189 changes: 189 additions & 0 deletions java/test/org/openqa/selenium/WebScriptTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// 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;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.fail;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.bidi.log.ConsoleLogEntry;
import org.openqa.selenium.bidi.log.JavascriptLogEntry;
import org.openqa.selenium.bidi.log.LogLevel;
import org.openqa.selenium.environment.webserver.AppServer;
import org.openqa.selenium.environment.webserver.NettyAppServer;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.testing.JupiterTestBase;

class WebScriptTest extends JupiterTestBase {

String page;
private AppServer server;

@BeforeEach
public void setUp() {
server = new NettyAppServer();
server.start();
}

@AfterEach
public void cleanUp() {
driver.quit();
}

@Test
void canAddConsoleMessageHandler()
throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<ConsoleLogEntry> future = new CompletableFuture<>();

long id = ((RemoteWebDriver) driver).script().addConsoleMessageHandler(future::complete);

page = server.whereIs("/bidi/logEntryAdded.html");
driver.get(page);
driver.findElement(By.id("consoleLog")).click();

ConsoleLogEntry logEntry = future.get(5, TimeUnit.SECONDS);

assertThat(logEntry.getText()).isEqualTo("Hello, world!");
assertThat(logEntry.getArgs().size()).isEqualTo(1);
assertThat(logEntry.getArgs().get(0).getType()).isEqualTo("string");
assertThat(logEntry.getType()).isEqualTo("console");
assertThat(logEntry.getLevel()).isEqualTo(LogLevel.INFO);
assertThat(logEntry.getMethod()).isEqualTo("log");

((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id);
}

@Test
void canRemoveConsoleMessageHandler()
throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<ConsoleLogEntry> future1 = new CompletableFuture<>();
CompletableFuture<ConsoleLogEntry> future2 = new CompletableFuture<>();

// Adding two consumers
Consumer<ConsoleLogEntry> consumer1 = future1::complete;
Consumer<ConsoleLogEntry> consumer2 = future2::complete;

long id1 = ((RemoteWebDriver) driver).script().addConsoleMessageHandler(consumer1);
long id2 = ((RemoteWebDriver) driver).script().addConsoleMessageHandler(consumer2);

// Removing the second consumer, so it will no longer get the console message.
((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id2);

page = server.whereIs("/bidi/logEntryAdded.html");
driver.get(page);
driver.findElement(By.id("consoleLog")).click();

ConsoleLogEntry logEntry = future1.get(5, TimeUnit.SECONDS);
assertThat(logEntry.getText()).isEqualTo("Hello, world!");

try {
future2.get(5, TimeUnit.SECONDS);
fail("Should be able to read the console messages");
} catch (TimeoutException e) {
assertThat(e).isNotNull();
}
((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id1);
}

@Test
void canAddJsErrorHandler() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<JavascriptLogEntry> future = new CompletableFuture<>();

long id = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(future::complete);

page = server.whereIs("/bidi/logEntryAdded.html");
driver.get(page);
driver.findElement(By.id("jsException")).click();

JavascriptLogEntry logEntry = future.get(5, TimeUnit.SECONDS);

assertThat(logEntry.getText()).isEqualTo("Error: Not working");
assertThat(logEntry.getType()).isEqualTo("javascript");
assertThat(logEntry.getLevel()).isEqualTo(LogLevel.ERROR);

((RemoteWebDriver) driver).script().removeJavaScriptErrorHandler(id);
}

@Test
void canRemoveJsErrorHandler() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<JavascriptLogEntry> future1 = new CompletableFuture<>();
CompletableFuture<JavascriptLogEntry> future2 = new CompletableFuture<>();

// Adding two consumers
Consumer<JavascriptLogEntry> consumer1 = future1::complete;
Consumer<JavascriptLogEntry> consumer2 = future2::complete;

long id1 = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(consumer1);
long id2 = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(consumer2);

// Removing the second consumer, so it will no longer get the JS error.
((RemoteWebDriver) driver).script().removeJavaScriptErrorHandler(id2);

page = server.whereIs("/bidi/logEntryAdded.html");
driver.get(page);
driver.findElement(By.id("jsException")).click();

JavascriptLogEntry logEntry = future1.get(5, TimeUnit.SECONDS);
assertThat(logEntry.getText()).isEqualTo("Error: Not working");
assertThat(logEntry.getType()).isEqualTo("javascript");
assertThat(logEntry.getLevel()).isEqualTo(LogLevel.ERROR);

try {
future2.get(5, TimeUnit.SECONDS);
fail("Should be able to read the JS errors");
} catch (TimeoutException e) {
assertThat(e).isNotNull();
}

((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id1);
}

@Test
void canAddMultipleHandlers() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<JavascriptLogEntry> future1 = new CompletableFuture<>();
CompletableFuture<JavascriptLogEntry> future2 = new CompletableFuture<>();

// Adding two consumers
Consumer<JavascriptLogEntry> consumer1 = future1::complete;
Consumer<JavascriptLogEntry> consumer2 = future2::complete;

long id1 = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(consumer1);
long id2 = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(consumer2);

page = server.whereIs("/bidi/logEntryAdded.html");
driver.get(page);
driver.findElement(By.id("jsException")).click();

JavascriptLogEntry logEntry1 = future1.get(5, TimeUnit.SECONDS);
assertThat(logEntry1.getText()).isEqualTo("Error: Not working");
assertThat(logEntry1.getType()).isEqualTo("javascript");
assertThat(logEntry1.getLevel()).isEqualTo(LogLevel.ERROR);

JavascriptLogEntry logEntry2 = future2.get(5, TimeUnit.SECONDS);
assertThat(logEntry2.getText()).isEqualTo("Error: Not working");
assertThat(logEntry2.getType()).isEqualTo("javascript");
assertThat(logEntry2.getLevel()).isEqualTo(LogLevel.ERROR);
}
}

0 comments on commit 7e7e5f0

Please sign in to comment.