Selenium 4.7
— Test automation, Java, Selenium, Testing framework — 4 min read
Intro
Summary:
- you don't need to download anymore drivers (Selenium Manager)
- there is extended support to interact with browser-based debugging tools
- currently supported CDP versions: 85, 106, 107, 108
- support for JDK Http client
Full changelog
This post is about code examples and what can be done with newest Selenium version.
Selenium Manager
Selenium Manager is a CLI (Command-Line Interface) tool developed in Rust to allow cross platform execution.
Before Selenium Manager, developers had to manually download and configure the web driver for the browser being used. This can be a time-consuming and error-prone process, especially for less experienced developers. With Selenium Manager, developers can now simply select the browser they want to use, and the tool will automatically download and configure the appropriate web driver.
tl;dr - you only need to add Selenium version 4.7 and have installed chrome locally. No driver management anymore.
WebDriver BiDi
BiDi (Bidirectional WebDriver) is a protocol extension for the WebDriver standard that provides support for bidirectional (BiDi) communication between the WebDriver client and the browser. It allows the browser to send commands to the WebDriver client, in addition to the client sending commands to the browser. This allows for more interactive and dynamic automation, as well as better support for browser-based debugging tools.
The protocol extension is based on the WebSocket protocol and it allows for sending and receiving messages in a bidirectional way. The protocol defines a set of commands and events that can be sent between the client and browser. The commands are used to initiate actions on the browser, and the events are used to notify the client of state changes in the browser.
One of the key features of BiDi is the ability to interact with browser-based debugging tools, such as the browser's JavaScript console or the browser's DevTools. This allows for more advanced automation and debugging capabilities.
Test case #1
The test case does the following:
- Initializes a new instance of the ChromeDriver class, which is the WebDriver implementation for the Chrome browser.
- Declares variables for the URL of the website to be accessed, the username and password for the basic HTTP authentication.
- Defines a Predicate object that checks whether the URI of the website to be accessed matches a specific pattern (in this case, the hostname must contain "authenticationtest.com").
- Register the predicate object and the username and password to the Driver.
- Navigates to the website using the driver.get(url) method.
- Finds an HTML element on the webpage using the CSS selector "h1" and extracts the text of the element.
- Prints the text of the element to the console.
- Asserts that the text of the element is equal to the string "Login Success".
- The test case is checking that the website returns the correct header text after successful login. If the header text is not "Login Success", the assertion will fail and the test case will be marked as failed.
Code for test case #1
1@Test2 void basicAuthTest() {3 WebDriver driver = new ChromeDriver();4
5 var url = "https://authenticationtest.com/HTTPAuth/";6 var username = "user";7 var password = "pass";8
9 Predicate<URI> uriPredicate = uri -> uri.getHost().contains("authenticationtest.com");10 ((HasAuthentication) driver).register(uriPredicate, UsernameAndPassword.of(username, password));11 driver.get(url);12
13 WebElement headerText = driver.findElement(By.cssSelector("h1"));14 System.out.println(headerText.getText());15 assertThat(headerText.getText()).isEqualTo("Login Success");16 }
Test case #2
The test case does the following:
- Initializes a new instance of the ChromeDriver class, which is the WebDriver implementation for the Chrome browser.
- Retrieves the DevTools instance of the browser by calling ((HasDevTools) driver).getDevTools() which allows to interact with the browser's developer tools.
- Creates a new DevTools session by calling devTools.createSession().
- Enables logging by calling devTools.send(Log.enable()).
- Registers an event listener on the DevTools instance to listen for log entries added.
- When a new log entry is added, the listener sets the lastLogEntry variable to the text of the log entry and prints the text of the log entry and log level of the log entry to the console.
- Navigates to the website using the driver.get("https://github.com/") method.
- Asserts that the lastLogEntry contains the string "A preload for".
Code for test case #2
1@Test2 void consoleLogTest() {3 WebDriver driver = new ChromeDriver();4 DevTools devTools = ((HasDevTools) driver).getDevTools();5 devTools.createSession();6 devTools.send(Log.enable());7
8 devTools.addListener(Log.entryAdded(),9 logEntry -> {10 setLastLogEntry(logEntry.getText());11 System.out.println("log: "+logEntry.getText());12 System.out.println("level: "+logEntry.getLevel());13 });14
15 driver.get("https://github.com/");16 assertThat(lastLogEntry).contains("A preload for");17 }
Test case #3
The test case does the following:
- Initializes a new instance of the ChromeDriver class, which is the WebDriver implementation for the Chrome browser.
- Retrieves the DevTools instance of the browser by calling ((HasDevTools) driver).getDevTools() which allows to interact with the browser's developer tools.
- Creates a new DevTools session by calling devTools.createSession().
- Overrides the browser's geolocation by calling devTools.send(Emulation.setGeolocationOverride(Optional.of(52.5043), Optional.of(13.4501), Optional.of(1))); to set the geolocation to the latitude and longitude of Berlin, Germany.
- Navigates to the website using the driver.get("https://my-location.org/") method.
- Finds an HTML element on the webpage using the id "address" and extracts the text of the element.
- Asserts that the text of the element contains the string "Berlin, Germany".
Code for test case #3
1@Test2 void mockGeolocation() {3 WebDriver driver = new ChromeDriver();4 DevTools devTools = ((HasDevTools) driver).getDevTools();5 devTools.createSession();6
7 devTools.send(Emulation.setGeolocationOverride(Optional.of(52.5043),8 Optional.of(13.4501),9 Optional.of(1)));10 driver.get("https://my-location.org/");11 WebElement yourLocation = driver.findElement(By.id("address"));12 assertThat(yourLocation.getText()).contains("Berlin, Germany");13 }
Test case #4
The test case does the following:
- Initializes a new instance of the ChromeDriver class, which is the WebDriver implementation for the Chrome browser.
- Retrieves the DevTools instance of the browser by calling ((HasDevTools) driver).getDevTools() which allows to interact with the browser's developer tools.
- Creates a new DevTools session by calling devTools.createSession().
- Creates a new NetworkInterceptor object, this interceptor will block all request to youtube.com
- Navigates to the website using the driver.get("https://www.youtube.com/") method.
- Retrieves the page source of the current webpage and assigns it to the variable source.
- Asserts that the page source contains the string "It's blocked"
Code for test case #4
1@Test2 void mockNetworkTest() {3 WebDriver driver = new ChromeDriver();4 DevTools devTools = ((HasDevTools) driver).getDevTools();5 devTools.createSession();6
7 try (NetworkInterceptor ignored = new NetworkInterceptor(8 driver,9 Route.matching(req -> req.getUri().contains("youtube"))10 .to(() -> req -> new HttpResponse()11 .setStatus(200)12 .addHeader("Content-Type", MediaType.HTML_UTF_8.toString())13 .setContent(utf8String("It's blocked"))))) {14
15 driver.get("https://www.youtube.com/");16
17 String source = driver.getPageSource();18 Assertions.assertTrue(source.contains("It's blocked"));19 }20 }
Test case #5
The test case does the following:
- Initializes a new instance of the ChromeDriver class, which is the WebDriver implementation for the Chrome browser.
- Retrieves the DevTools instance of the browser by calling ((HasDevTools) driver).getDevTools() which allows to interact with the browser's developer tools.
- Creates a new DevTools session by calling devTools.createSession().
- Initializes an empty ArrayList of type JavascriptException
- Registers an event listener on the DevTools instance to listen for JavaScript exceptions and adds the exceptions to the list
- Register a CompletableFuture to hold a JavaScript exception, this future will complete when an exception is thrown
- Navigates to the website using the driver.get("https://the-internet.herokuapp.com/add_remove_elements/") method.
- Finds an HTML element on the webpage using the tagName "button"
- Add an onclick event to throw an Error message
- Executes a click on the button
- Waits for the JavaScript exception to be thrown (with a timeout of 5 seconds)
- Asserts that the list of JavaScript exceptions has a size of 1.
Code for test case #5
1@Test2 void jsExceptionsTest() throws ExecutionException, InterruptedException, TimeoutException {3 WebDriver driver = new ChromeDriver();4 DevTools devTools = ((HasDevTools) driver).getDevTools();5 devTools.createSession();6
7 List<JavascriptException> jsExceptionsList = new ArrayList<>();8 devTools.getDomains().events().addJavascriptExceptionListener(jsExceptionsList::add);9 CompletableFuture<JavascriptException> futureJsExc = new CompletableFuture<>();10 devTools.getDomains().events().addJavascriptExceptionListener(futureJsExc::complete);11
12 driver.get("https://the-internet.herokuapp.com/add_remove_elements/");13
14 WebElement element = driver.findElement(By.tagName("button"));15
16 ((JavascriptExecutor) driver)17 .executeScript("arguments[0].setAttribute(arguments[1], arguments[2]);",18 element, "onclick", "throw new Error('Hello, world!')");19 element.click();20
21 futureJsExc.get(5, TimeUnit.SECONDS);22 assertThat(jsExceptionsList).hasSize(1);23 }
Full code
Full repository with working Java Code is here.