Selenium WebDriver Bidiโ€Šโ€”โ€ŠLogging Practice in Java

Published on December 12, 2024

Master Logging Automation with Selenium WebDriver Bidi in Java

BiDi LogInspector Domainโ€” Automate Console Logs & JS Exceptions

Thumbnail for article: ๐˜”๐˜ข๐˜ด๐˜ต๐˜ฆ๐˜ณ ๐˜“๐˜ฐ๐˜จ๐˜จ๐˜ช๐˜ฏ๐˜จ ๐˜ˆ๐˜ถ๐˜ต๐˜ฐ๐˜ฎ๐˜ข๐˜ต๐˜ช๐˜ฐ๐˜ฏ ๐˜ธ๐˜ช๐˜ต๐˜ฉ ๐˜š๐˜ฆ๐˜ญ๐˜ฆ๐˜ฏ๐˜ช๐˜ถ๐˜ฎ ๐˜ž๐˜ฆ๐˜ฃ๐˜‹๐˜ณ๐˜ช๐˜ท๐˜ฆ๐˜ณ ๐˜‰๐˜ช๐˜ฅ๐˜ช ๐˜ช๐˜ฏ ๐˜‘๐˜ข๐˜ท๐˜ข
Article agenda

Introduction

How can a tester listen to console logs while running tests?

With Selenium WebDriverโ€™s BiDi (Bidirectional) support, testers can now capture browser events like console messages and JavaScript exceptions directly during test execution. This article demonstrates how to automate these logging practices in Java using Selenium WebDriver.

Application Under Test (AUT)

The test focuses on a sample webpage, which emits console messages and JavaScript exceptions upon button clicks. By listening to these events, testers can validate their occurrence and content, enhancing test robustness.

AUT: https://www.selenium.dev/selenium/web/bidi/logEntryAdded.htmlโ€Šโ€”โ€Štest console and JS logging

Setup and Teardown Boilerplateโ€Šโ€”โ€ŠCommon for Both Console Messages and JS Exceptions

The setup initializes the WebDriver with BiDi support enabled, and the teardown ensures proper cleanup. This code is common for both console messages and JavaScript exceptions.

public class ListenToLogs {

private WebDriver driver;
private LogInspector logInspector;

private String webPage = "https://selenium.dev/selenium/web/bidi/logEntryAdded.html";

@BeforeTest
public void setup() {
FirefoxOptions options = new FirefoxOptions();
options.enableBiDi();
driver = new FirefoxDriver(options);
logInspector = new LogInspector(driver);
}

@AfterTest
public void teardown() {
logInspector.close();
driver.quit();

Listening to Console Messages

The following test demonstrates how to listen for console messages emitted by the browser during test execution.

@Test
public void consoleMessageTest() throws InterruptedException, ExecutionException, TimeoutException {
CompletableFuture future = new CompletableFuture<>();
logInspector.onConsoleEntry(future::complete);

driver.get(webPage);
driver.findElement(By.id("consoleLog")).click();

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

Assert.assertEquals(consoleLogEntry.getText(), "Hello, world!");
Assert.assertEquals(consoleLogEntry.getType(), "console");
Assert.assertEquals(consoleLogEntry.getLevel(), LogLevel.INFO);
}

Explanation

  1. Event Listener Setup: A CompletableFuture is created and the logInspector subscribes to console log events.
  2. Triggering the Event: The test navigates to the webpage and clicks the button that generates a console log.
  3. Validation: The log entry is retrieved and its properties are validated.

Listening to JavaScript Exceptions

Similarly, we can listen for JavaScript exceptions triggered on the webpage.

@Test
public void javascriptExceptionTest() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture future = new CompletableFuture<>();
logInspector.onJavaScriptLog(future::complete);

driver.get(webPage);
driver.findElement(By.id("jsException")).click();

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

Assert.assertEquals(jsLogEntry.getText(), "Error: Not working");
Assert.assertEquals(jsLogEntry.getType(), "javascript");
Assert.assertEquals(jsLogEntry.getLevel(), LogLevel.ERROR);
}

Explanation

  1. Event Listener Setup: Similar to the console message test, the logInspector listens for JavaScript exceptions.
  2. Triggering the Exception: A button click triggers the JavaScript error on the webpage.
  3. Validation: The error details are retrieved and verified.

Full Codeโ€Šโ€”โ€ŠTest Run

Below captioned is the complete Java code that includes logging for both console messages and JS errors.

ListenToLogsTest.java:

package BiDiLogging;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
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.bidi.module.LogInspector;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import org.testng.Assert;

public class ListenToLogs {

private WebDriver driver;
private LogInspector logInspector;

private String webPage = "https://selenium.dev/selenium/web/bidi/logEntryAdded.html";

@BeforeTest
public void setup() {
FirefoxOptions options = new FirefoxOptions();
options.enableBiDi();
driver = new FirefoxDriver(options);
logInspector = new LogInspector(driver);
}

@AfterTest
public void teardown() {
logInspector.close();
driver.quit();
}

@Test
public void consoleMessageTest() throws InterruptedException, ExecutionException, TimeoutException {
CompletableFuture future = new CompletableFuture<>();
logInspector.onConsoleEntry(future::complete);

driver.get(webPage);
driver.findElement(By.id("consoleLog")).click();

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

Assert.assertEquals(consoleLogEntry.getText(), "Hello, world!");
Assert.assertEquals(consoleLogEntry.getType(), "console");
Assert.assertEquals(consoleLogEntry.getLevel(), LogLevel.INFO);
}

@Test
public void javascriptExceptionTest() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture future = new CompletableFuture<>();
logInspector.onJavaScriptLog(future::complete);

driver.get(webPage);
driver.findElement(By.id("jsException")).click();

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

Assert.assertEquals(jsLogEntry.getText(), "Error: Not working");
Assert.assertEquals(jsLogEntry.getType(), "javascript");
Assert.assertEquals(jsLogEntry.getLevel(), LogLevel.ERROR);
}

}

When we run this program, our two tests pass.

Code execution demo: https://youtu.be/qQI56nKl14g

The TestNG reports shows both positive test results.

TestNG execution results: all tests passed.

Conclusion

Automating console messages and JavaScript exceptions with Selenium WebDriverโ€™s BiDi API empowers testers to enhance their test suites with real-time browser insights. By adopting these practices, teams can improve the reliability and maintainability of their automation frameworks.

๐‘ฒ๐’†๐’š ๐‘ป๐’‚๐’Œ๐’†๐’‚๐’˜๐’‚๐’š๐’”:

  • Unified Logging: Selenium WebDriver BiDi simplifies the process of capturing browser logs during tests.
  • Enhanced Debugging: By automating log and exception capture, testers can identify and address issues more efficiently.
  • Cross-Browser Support: While this example uses Firefox, similar functionality is available in other browsers with BiDi support. In a previous article, we ran the tests with Chrome in Pythonโ€Šโ€”โ€Šbidi_console_logs.py and bidi_js_exceptions.py.

๐“—๐’ถ๐“…๐“…๐“Ž ๐“‰๐“ฎ๐“ˆ๐“‰๐’พ๐“ƒ๐“ฐ ๐’ถ๐“ƒ๐’น ๐’น๐“ฎ๐’ท๐“Š๐“ฐ๐“ฐ๐’พ๐“ƒ๐“ฐ!

I welcome any comments and contributions to the subject. Connect with me on LinkedIn, X , GitHub, or Insta. Check out my website.

If you find this post useful, please consider buying me a coffee.