Skip to content

Commit e5422e6

Browse files
authored
[bidi][java] Add high-level logging APIs (#14225)
1 parent 8cf9a59 commit e5422e6

File tree

5 files changed

+293
-0
lines changed

5 files changed

+293
-0
lines changed

java/src/org/openqa/selenium/remote/BUILD.bazel

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ java_library(
5757
deps = [
5858
"//java/src/org/openqa/selenium:core",
5959
"//java/src/org/openqa/selenium/bidi",
60+
"//java/src/org/openqa/selenium/bidi/log",
61+
"//java/src/org/openqa/selenium/bidi/module",
6062
"//java/src/org/openqa/selenium/concurrent",
6163
"//java/src/org/openqa/selenium/devtools",
6264
"//java/src/org/openqa/selenium/json",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.remote;
19+
20+
import java.util.function.Consumer;
21+
import org.openqa.selenium.Beta;
22+
import org.openqa.selenium.WebDriver;
23+
import org.openqa.selenium.bidi.BiDi;
24+
import org.openqa.selenium.bidi.HasBiDi;
25+
import org.openqa.selenium.bidi.log.ConsoleLogEntry;
26+
import org.openqa.selenium.bidi.log.JavascriptLogEntry;
27+
import org.openqa.selenium.bidi.module.LogInspector;
28+
29+
@Beta
30+
class RemoteScript implements Script {
31+
private final BiDi biDi;
32+
private final LogInspector logInspector;
33+
34+
public RemoteScript(WebDriver driver) {
35+
this.biDi = ((HasBiDi) driver).getBiDi();
36+
this.logInspector = new LogInspector(driver);
37+
}
38+
39+
@Override
40+
public long addConsoleMessageHandler(Consumer<ConsoleLogEntry> consumer) {
41+
return this.logInspector.onConsoleEntry(consumer);
42+
}
43+
44+
@Override
45+
public void removeConsoleMessageHandler(long id) {
46+
this.biDi.removeListener(id);
47+
}
48+
49+
@Override
50+
public long addJavaScriptErrorHandler(Consumer<JavascriptLogEntry> consumer) {
51+
return this.logInspector.onJavaScriptException(consumer);
52+
}
53+
54+
@Override
55+
public void removeJavaScriptErrorHandler(long id) {
56+
this.biDi.removeListener(id);
57+
}
58+
}

java/src/org/openqa/selenium/remote/RemoteWebDriver.java

+9
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ public class RemoteWebDriver
124124
private Logs remoteLogs;
125125
private LocalLogs localLogs;
126126

127+
private Script remoteScript;
128+
127129
// For cglib
128130
protected RemoteWebDriver() {
129131
this.capabilities = init(new ImmutableCapabilities());
@@ -486,6 +488,13 @@ public Options manage() {
486488
return new RemoteWebDriverOptions();
487489
}
488490

491+
public Script script() {
492+
if (this.remoteScript == null) {
493+
this.remoteScript = new RemoteScript(this);
494+
}
495+
return this.remoteScript;
496+
}
497+
489498
protected JsonToWebElementConverter getElementConverter() {
490499
return converter;
491500
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.remote;
19+
20+
import java.util.function.Consumer;
21+
import org.openqa.selenium.Beta;
22+
import org.openqa.selenium.bidi.log.ConsoleLogEntry;
23+
import org.openqa.selenium.bidi.log.JavascriptLogEntry;
24+
25+
@Beta
26+
public interface Script {
27+
28+
long addConsoleMessageHandler(Consumer<ConsoleLogEntry> consumer);
29+
30+
void removeConsoleMessageHandler(long id);
31+
32+
long addJavaScriptErrorHandler(Consumer<JavascriptLogEntry> consumer);
33+
34+
void removeJavaScriptErrorHandler(long id);
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium;
19+
20+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
21+
import static org.assertj.core.api.AssertionsForClassTypes.fail;
22+
23+
import java.util.concurrent.CompletableFuture;
24+
import java.util.concurrent.ExecutionException;
25+
import java.util.concurrent.TimeUnit;
26+
import java.util.concurrent.TimeoutException;
27+
import java.util.function.Consumer;
28+
import org.junit.jupiter.api.AfterEach;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
31+
import org.openqa.selenium.bidi.log.ConsoleLogEntry;
32+
import org.openqa.selenium.bidi.log.JavascriptLogEntry;
33+
import org.openqa.selenium.bidi.log.LogLevel;
34+
import org.openqa.selenium.environment.webserver.AppServer;
35+
import org.openqa.selenium.environment.webserver.NettyAppServer;
36+
import org.openqa.selenium.remote.RemoteWebDriver;
37+
import org.openqa.selenium.testing.JupiterTestBase;
38+
39+
class WebScriptTest extends JupiterTestBase {
40+
41+
String page;
42+
private AppServer server;
43+
44+
@BeforeEach
45+
public void setUp() {
46+
server = new NettyAppServer();
47+
server.start();
48+
}
49+
50+
@AfterEach
51+
public void cleanUp() {
52+
driver.quit();
53+
}
54+
55+
@Test
56+
void canAddConsoleMessageHandler()
57+
throws ExecutionException, InterruptedException, TimeoutException {
58+
CompletableFuture<ConsoleLogEntry> future = new CompletableFuture<>();
59+
60+
long id = ((RemoteWebDriver) driver).script().addConsoleMessageHandler(future::complete);
61+
62+
page = server.whereIs("/bidi/logEntryAdded.html");
63+
driver.get(page);
64+
driver.findElement(By.id("consoleLog")).click();
65+
66+
ConsoleLogEntry logEntry = future.get(5, TimeUnit.SECONDS);
67+
68+
assertThat(logEntry.getText()).isEqualTo("Hello, world!");
69+
assertThat(logEntry.getArgs().size()).isEqualTo(1);
70+
assertThat(logEntry.getArgs().get(0).getType()).isEqualTo("string");
71+
assertThat(logEntry.getType()).isEqualTo("console");
72+
assertThat(logEntry.getLevel()).isEqualTo(LogLevel.INFO);
73+
assertThat(logEntry.getMethod()).isEqualTo("log");
74+
75+
((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id);
76+
}
77+
78+
@Test
79+
void canRemoveConsoleMessageHandler()
80+
throws ExecutionException, InterruptedException, TimeoutException {
81+
CompletableFuture<ConsoleLogEntry> future1 = new CompletableFuture<>();
82+
CompletableFuture<ConsoleLogEntry> future2 = new CompletableFuture<>();
83+
84+
// Adding two consumers
85+
Consumer<ConsoleLogEntry> consumer1 = future1::complete;
86+
Consumer<ConsoleLogEntry> consumer2 = future2::complete;
87+
88+
long id1 = ((RemoteWebDriver) driver).script().addConsoleMessageHandler(consumer1);
89+
long id2 = ((RemoteWebDriver) driver).script().addConsoleMessageHandler(consumer2);
90+
91+
// Removing the second consumer, so it will no longer get the console message.
92+
((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id2);
93+
94+
page = server.whereIs("/bidi/logEntryAdded.html");
95+
driver.get(page);
96+
driver.findElement(By.id("consoleLog")).click();
97+
98+
ConsoleLogEntry logEntry = future1.get(5, TimeUnit.SECONDS);
99+
assertThat(logEntry.getText()).isEqualTo("Hello, world!");
100+
101+
try {
102+
future2.get(5, TimeUnit.SECONDS);
103+
fail("Should be able to read the console messages");
104+
} catch (TimeoutException e) {
105+
assertThat(e).isNotNull();
106+
}
107+
((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id1);
108+
}
109+
110+
@Test
111+
void canAddJsErrorHandler() throws ExecutionException, InterruptedException, TimeoutException {
112+
CompletableFuture<JavascriptLogEntry> future = new CompletableFuture<>();
113+
114+
long id = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(future::complete);
115+
116+
page = server.whereIs("/bidi/logEntryAdded.html");
117+
driver.get(page);
118+
driver.findElement(By.id("jsException")).click();
119+
120+
JavascriptLogEntry logEntry = future.get(5, TimeUnit.SECONDS);
121+
122+
assertThat(logEntry.getText()).isEqualTo("Error: Not working");
123+
assertThat(logEntry.getType()).isEqualTo("javascript");
124+
assertThat(logEntry.getLevel()).isEqualTo(LogLevel.ERROR);
125+
126+
((RemoteWebDriver) driver).script().removeJavaScriptErrorHandler(id);
127+
}
128+
129+
@Test
130+
void canRemoveJsErrorHandler() throws ExecutionException, InterruptedException, TimeoutException {
131+
CompletableFuture<JavascriptLogEntry> future1 = new CompletableFuture<>();
132+
CompletableFuture<JavascriptLogEntry> future2 = new CompletableFuture<>();
133+
134+
// Adding two consumers
135+
Consumer<JavascriptLogEntry> consumer1 = future1::complete;
136+
Consumer<JavascriptLogEntry> consumer2 = future2::complete;
137+
138+
long id1 = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(consumer1);
139+
long id2 = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(consumer2);
140+
141+
// Removing the second consumer, so it will no longer get the JS error.
142+
((RemoteWebDriver) driver).script().removeJavaScriptErrorHandler(id2);
143+
144+
page = server.whereIs("/bidi/logEntryAdded.html");
145+
driver.get(page);
146+
driver.findElement(By.id("jsException")).click();
147+
148+
JavascriptLogEntry logEntry = future1.get(5, TimeUnit.SECONDS);
149+
assertThat(logEntry.getText()).isEqualTo("Error: Not working");
150+
assertThat(logEntry.getType()).isEqualTo("javascript");
151+
assertThat(logEntry.getLevel()).isEqualTo(LogLevel.ERROR);
152+
153+
try {
154+
future2.get(5, TimeUnit.SECONDS);
155+
fail("Should be able to read the JS errors");
156+
} catch (TimeoutException e) {
157+
assertThat(e).isNotNull();
158+
}
159+
160+
((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id1);
161+
}
162+
163+
@Test
164+
void canAddMultipleHandlers() throws ExecutionException, InterruptedException, TimeoutException {
165+
CompletableFuture<JavascriptLogEntry> future1 = new CompletableFuture<>();
166+
CompletableFuture<JavascriptLogEntry> future2 = new CompletableFuture<>();
167+
168+
// Adding two consumers
169+
Consumer<JavascriptLogEntry> consumer1 = future1::complete;
170+
Consumer<JavascriptLogEntry> consumer2 = future2::complete;
171+
172+
long id1 = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(consumer1);
173+
long id2 = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(consumer2);
174+
175+
page = server.whereIs("/bidi/logEntryAdded.html");
176+
driver.get(page);
177+
driver.findElement(By.id("jsException")).click();
178+
179+
JavascriptLogEntry logEntry1 = future1.get(5, TimeUnit.SECONDS);
180+
assertThat(logEntry1.getText()).isEqualTo("Error: Not working");
181+
assertThat(logEntry1.getType()).isEqualTo("javascript");
182+
assertThat(logEntry1.getLevel()).isEqualTo(LogLevel.ERROR);
183+
184+
JavascriptLogEntry logEntry2 = future2.get(5, TimeUnit.SECONDS);
185+
assertThat(logEntry2.getText()).isEqualTo("Error: Not working");
186+
assertThat(logEntry2.getType()).isEqualTo("javascript");
187+
assertThat(logEntry2.getLevel()).isEqualTo(LogLevel.ERROR);
188+
}
189+
}

0 commit comments

Comments
 (0)