From 36d74b7d2a2adbfccf8e86aa241c6c2a383084d2 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Mon, 29 Sep 2025 11:52:11 +0200 Subject: [PATCH 1/5] Drop fluentlenium --- project/Dependencies.scala | 21 ------------------- .../src/main/java/play/test/TestBrowser.java | 1 - .../main/scala/play/api/test/Selenium.scala | 3 --- 3 files changed, 25 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 0439d6b9b35..0252b0e56c7 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -259,31 +259,10 @@ object Dependencies { "org.apache.pekko" %% "pekko-cluster-sharding-typed" % pekkoVersion ) - val fluentleniumVersion = "6.0.0" - // This is the selenium version compatible with the FluentLenium version declared above. - // See https://repo1.maven.org/maven2/io/fluentlenium/fluentlenium-parent/6.0.0/fluentlenium-parent-6.0.0.pom val seleniumVersion = "4.14.1" val htmlunitVersion = "4.13.0" val testDependencies = Seq(junit, junitInterface, guava, logback) ++ Seq( - ("io.fluentlenium" % "fluentlenium-core" % fluentleniumVersion) - .exclude("org.jboss.netty", "netty") - .excludeAll(ExclusionRule("commons-beanutils", "commons-beanutils")) // comes with CVE-2025-48734 - .excludeAll(ExclusionRule("commons-io", "commons-io")), // comes with outdated commons-io - // htmlunit-driver uses an open range to selenium dependencies. This is slightly - // slowing down the build. So the open range deps were removed and we can re-add - // them using a specific version. Using an open range is also not good for the - // local cache. - ("org.seleniumhq.selenium" % "htmlunit-driver" % htmlunitVersion).excludeAll( - ExclusionRule("org.seleniumhq.selenium", "selenium-api"), - ExclusionRule("org.seleniumhq.selenium", "selenium-support"), - ExclusionRule("commons-io", "commons-io") // comes with outdated commons-io - ), - "commons-beanutils" % "commons-beanutils" % "1.11.0", // explicitly bump for fluentlenium and htmlunit to fix CVE-2025-48734 - "commons-io" % "commons-io" % "2.20.0", // explicitly bump commons-io to newer version for fluentlenium and htmlunit - "org.seleniumhq.selenium" % "selenium-api" % seleniumVersion, - "org.seleniumhq.selenium" % "selenium-support" % seleniumVersion, - "org.seleniumhq.selenium" % "selenium-firefox-driver" % seleniumVersion ) ++ guiceDeps ++ specs2Deps.map(_ % Test) :+ mockitoAll % Test val playCacheDeps = specs2Deps.map(_ % Test) :+ logback % Test diff --git a/testkit/play-test/src/main/java/play/test/TestBrowser.java b/testkit/play-test/src/main/java/play/test/TestBrowser.java index 681ce3795cc..439ff846a3f 100644 --- a/testkit/play-test/src/main/java/play/test/TestBrowser.java +++ b/testkit/play-test/src/main/java/play/test/TestBrowser.java @@ -4,7 +4,6 @@ package play.test; -import io.fluentlenium.adapter.FluentAdapter; import java.time.Duration; import java.util.function.Function; import org.openqa.selenium.WebDriver; diff --git a/testkit/play-test/src/main/scala/play/api/test/Selenium.scala b/testkit/play-test/src/main/scala/play/api/test/Selenium.scala index 3981357fbbc..4732d60e20b 100644 --- a/testkit/play-test/src/main/scala/play/api/test/Selenium.scala +++ b/testkit/play-test/src/main/scala/play/api/test/Selenium.scala @@ -8,9 +8,6 @@ import java.util.concurrent.TimeUnit import scala.jdk.FunctionConverters._ -import io.fluentlenium.adapter.FluentAdapter -import io.fluentlenium.core.domain.FluentList -import io.fluentlenium.core.domain.FluentWebElement import org.openqa.selenium._ import org.openqa.selenium.firefox._ import org.openqa.selenium.htmlunit._ From ab1b790bd0f8d5a57c80c5952f2ec4eaea652a61 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Mon, 29 Sep 2025 12:47:38 +0200 Subject: [PATCH 2/5] Switch to selenide --- .../main/tests/JavaFunctionalTest.md | 2 +- .../tests/BrowserFunctionalTest.java | 5 ++- .../code/javaguide/tests/FunctionalTest.java | 11 ++++-- .../tests/ScalaFunctionalTestingWithSpecs2.md | 2 +- .../code/specs2/ScalaFunctionalTestSpec.scala | 12 +++--- project/Dependencies.scala | 5 +-- .../src/main/scala/play/api/test/Specs.scala | 6 +-- .../src/main/java/play/test/TestBrowser.java | 37 +++++++++---------- .../main/scala/play/api/test/Selenium.scala | 34 +++++------------ .../test/java/play/test/WithBrowserTest.java | 6 ++- 10 files changed, 54 insertions(+), 66 deletions(-) diff --git a/documentation/manual/working/javaGuide/main/tests/JavaFunctionalTest.md b/documentation/manual/working/javaGuide/main/tests/JavaFunctionalTest.md index 2cc095367fe..a6d63a13eb9 100644 --- a/documentation/manual/working/javaGuide/main/tests/JavaFunctionalTest.md +++ b/documentation/manual/working/javaGuide/main/tests/JavaFunctionalTest.md @@ -68,7 +68,7 @@ Just as there exists a `WithApplication` class, there is also a [`WithServer`](a ## Testing with a browser -If you want to test your application from with a Web browser, you can use [Selenium WebDriver](https://github.com/seleniumhq/selenium). Play will start the WebDriver for you, and wrap it in the convenient API provided by [FluentLenium](https://github.com/FluentLenium/FluentLenium). +If you want to test your application from with a Web browser, you can use [Selenium WebDriver](https://github.com/seleniumhq/selenium). Play will start the WebDriver for you, and wrap it in the convenient API provided by [Selenide](https://github.com/selenide/selenide). @[test-browser](code/javaguide/tests/FunctionalTest.java) diff --git a/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/BrowserFunctionalTest.java b/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/BrowserFunctionalTest.java index 18bd339c167..0f65ff07c4c 100644 --- a/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/BrowserFunctionalTest.java +++ b/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/BrowserFunctionalTest.java @@ -4,6 +4,7 @@ package javaguide.tests; +import static com.codeborne.selenide.Selenide.*; import static org.junit.Assert.*; import org.junit.*; @@ -14,8 +15,8 @@ public class BrowserFunctionalTest extends WithBrowser { @Test public void runInBrowser() { - browser.goTo("/"); - assertNotNull(browser.el("title").text()); + open("/"); + assertNotNull($("title").text()); } } // #test-withbrowser diff --git a/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/FunctionalTest.java b/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/FunctionalTest.java index e658b294461..12d57520385 100644 --- a/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/FunctionalTest.java +++ b/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/FunctionalTest.java @@ -14,6 +14,9 @@ import play.test.*; import play.libs.ws.*; +import static com.codeborne.selenide.Selenide.*; +import com.codeborne.selenide.WebDriverRunner; + import static play.test.Helpers.*; import static org.junit.Assert.*; @@ -89,10 +92,10 @@ public void runInBrowser() { testServer(), HTMLUNIT, browser -> { - browser.goTo("/"); - assertEquals("Welcome to Play!", browser.el("#title").text()); - browser.$("a").click(); - assertEquals("login", browser.url()); + open("/"); + assertEquals("Welcome to Play!", $("#title").text()); + $("a").click(); + assertTrue(WebDriverRunner.url().endsWith("/login")); }); } // #test-browser diff --git a/documentation/manual/working/scalaGuide/main/tests/ScalaFunctionalTestingWithSpecs2.md b/documentation/manual/working/scalaGuide/main/tests/ScalaFunctionalTestingWithSpecs2.md index e32ccf08f49..5759ab84967 100644 --- a/documentation/manual/working/scalaGuide/main/tests/ScalaFunctionalTestingWithSpecs2.md +++ b/documentation/manual/working/scalaGuide/main/tests/ScalaFunctionalTestingWithSpecs2.md @@ -36,7 +36,7 @@ An application can also be passed to the test server, which is useful for settin ## WithBrowser -If you want to test your application using a browser, you can use [Selenium WebDriver](https://github.com/seleniumhq/selenium). Play will start the WebDriver for you, and wrap it in the convenient API provided by [FluentLenium](https://github.com/FluentLenium/FluentLenium) using [`WithBrowser`](api/scala/play/api/test/WithBrowser.html). Like [`WithServer`](api/scala/play/api/test/WithServer.html), you can change the port, [`Application`](api/scala/play/api/Application.html), and you can also select the web browser to use: +If you want to test your application using a browser, you can use [Selenium WebDriver](https://github.com/seleniumhq/selenium). Play will start the WebDriver for you, and wrap it in the convenient API provided by [Selenide](https://github.com/selenide/selenide) using [`WithBrowser`](api/scala/play/api/test/WithBrowser.html). Like [`WithServer`](api/scala/play/api/test/WithServer.html), you can change the port, [`Application`](api/scala/play/api/Application.html), and you can also select the web browser to use: @[scalafunctionaltest-testwithbrowser](code/specs2/ScalaFunctionalTestSpec.scala) diff --git a/documentation/manual/working/scalaGuide/main/tests/code/specs2/ScalaFunctionalTestSpec.scala b/documentation/manual/working/scalaGuide/main/tests/code/specs2/ScalaFunctionalTestSpec.scala index 403707138c8..c084509a7ab 100644 --- a/documentation/manual/working/scalaGuide/main/tests/code/specs2/ScalaFunctionalTestSpec.scala +++ b/documentation/manual/working/scalaGuide/main/tests/code/specs2/ScalaFunctionalTestSpec.scala @@ -4,6 +4,8 @@ package scalaguide.tests.specs2 +import com.codeborne.selenide.Selenide._ +import com.codeborne.selenide.WebDriverRunner import org.specs2.mutable.Specification import play.api.inject.guice.GuiceApplicationBuilder import play.api.libs.ws._ @@ -112,15 +114,15 @@ class ScalaFunctionalTestSpec extends ExampleSpecification { "run in a browser" in new WithBrowser(webDriver = WebDriverFactory(HTMLUNIT), app = applicationWithBrowser) { override def running() = { - browser.goTo("/") + open("/"); // Check the page - browser.el("#title").text() must equalTo("Hello Guest") + $("#title").text() must equalTo("Hello Guest") - browser.el("a").click() + $("a").click() - browser.url must equalTo("login") - browser.el("#title").text() must equalTo("Hello Coco") + WebDriverRunner.url() must endWith("/login") + $("#title").text() must equalTo("Hello Coco") } } // #scalafunctionaltest-testwithbrowser diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 0252b0e56c7..6f50e25078c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -259,10 +259,9 @@ object Dependencies { "org.apache.pekko" %% "pekko-cluster-sharding-typed" % pekkoVersion ) - val seleniumVersion = "4.14.1" - val htmlunitVersion = "4.13.0" - val testDependencies = Seq(junit, junitInterface, guava, logback) ++ Seq( + "com.codeborne" % "selenide" % "7.11.1", + "org.seleniumhq.selenium" % "htmlunit3-driver" % "4.36.0", ) ++ guiceDeps ++ specs2Deps.map(_ % Test) :+ mockitoAll % Test val playCacheDeps = specs2Deps.map(_ % Test) :+ logback % Test diff --git a/testkit/play-specs2/src/main/scala/play/api/test/Specs.scala b/testkit/play-specs2/src/main/scala/play/api/test/Specs.scala index d9f5bfd4653..e8f1692ff9e 100644 --- a/testkit/play-specs2/src/main/scala/play/api/test/Specs.scala +++ b/testkit/play-specs2/src/main/scala/play/api/test/Specs.scala @@ -195,19 +195,19 @@ abstract class WithBrowser[WEBDRIVER <: WebDriver]( implicit def implicitApp: Application = app implicit def implicitPort: Port = port - lazy val browser: TestBrowser = TestBrowser(webDriver, Some("http://localhost:" + port)) - override def wrap[T: AsResult](t: => T): Result = { + var browser: TestBrowser = null try { val currentPort = port val result = Helpers.runningWithPort(TestServer(port, app)) { assignedPort => port = assignedPort // if port was 0, the OS assigns a random port + browser = new TestBrowser(webDriver, Some("http://localhost:" + assignedPort)) AsResult.effectively(t) } port = currentPort result } finally { - browser.quit() + Option(browser).foreach(_.quit()) } } } diff --git a/testkit/play-test/src/main/java/play/test/TestBrowser.java b/testkit/play-test/src/main/java/play/test/TestBrowser.java index 439ff846a3f..5eee40eca7e 100644 --- a/testkit/play-test/src/main/java/play/test/TestBrowser.java +++ b/testkit/play-test/src/main/java/play/test/TestBrowser.java @@ -4,20 +4,22 @@ package play.test; +import com.codeborne.selenide.Configuration; +import com.codeborne.selenide.WebDriverRunner; import java.time.Duration; import java.util.function.Function; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.FluentWait; /** - * A test browser (Using Selenium WebDriver) with the FluentLenium API - * (https://github.com/Fluentlenium/FluentLenium). + * A test browser (Using Selenium WebDriver) with the Selenide API + * (https://github.com/selenide/selenide). */ -public class TestBrowser extends FluentAdapter { +public class TestBrowser { /** - * A test browser (Using Selenium WebDriver) with the FluentLenium API - * (https://github.com/Fluentlenium/FluentLenium). + * A test browser (Using Selenium WebDriver) with the Selenide API + * (https://github.com/selenide/selenide). * * @param webDriver The WebDriver instance to use. * @param baseUrl The base url to use for relative requests. @@ -28,15 +30,15 @@ public TestBrowser(Class webDriver, String baseUrl) throws } /** - * A test browser (Using Selenium WebDriver) with the FluentLenium API - * (https://github.com/Fluentlenium/FluentLenium). + * A test browser (Using Selenium WebDriver) with the Selenide API + * (https://github.com/selenide/selenide). * * @param webDriver The WebDriver instance to use. * @param baseUrl The base url to use for relative requests. */ public TestBrowser(WebDriver webDriver, String baseUrl) { - super.initFluent(webDriver); - super.getConfiguration().setBaseUrl(baseUrl); + WebDriverRunner.setWebDriver(webDriver); // super.initFluent(webDriver); + Configuration.baseUrl = baseUrl; // super.getConfiguration().setBaseUrl(baseUrl); } /** @@ -45,7 +47,7 @@ public TestBrowser(WebDriver webDriver, String baseUrl) { * @return the webdriver contained in a fluent wait. */ public FluentWait fluentWait() { - return new FluentWait<>(super.getDriver()); + return new FluentWait<>(WebDriverRunner.getWebDriver()); } /** @@ -53,9 +55,6 @@ public FluentWait fluentWait() { * occurs: the function returns neither null nor false, the function throws an unignored * exception, the timeout expires * - *

Useful in situations where FluentAdapter#await is too specific (for example to check against - * page source) - * * @param the return type * @param wait generic {@code FluentWait} instance * @param f function to execute @@ -75,9 +74,6 @@ public T waitUntil(FluentWait wait, Function f) { *

  • the default timeout expires * * - * useful in situations where FluentAdapter#await is too specific (for example to check against - * page source or title) - * * @param f function to execute * @param the return type * @return the return value. @@ -94,14 +90,15 @@ public T waitUntil(Function f) { * @return the web driver options. */ public WebDriver.Options manage() { - return super.getDriver().manage(); + return WebDriverRunner.getWebDriver().manage(); } /** Quits and releases the {@link WebDriver} */ void quit() { - if (getDriver() != null) { - getDriver().quit(); + final WebDriver driver = WebDriverRunner.getWebDriver(); + if (driver != null) { + driver.quit(); } - releaseFluent(); + // releaseFluent(); } } diff --git a/testkit/play-test/src/main/scala/play/api/test/Selenium.scala b/testkit/play-test/src/main/scala/play/api/test/Selenium.scala index 4732d60e20b..cfb26f0eb95 100644 --- a/testkit/play-test/src/main/scala/play/api/test/Selenium.scala +++ b/testkit/play-test/src/main/scala/play/api/test/Selenium.scala @@ -8,37 +8,21 @@ import java.util.concurrent.TimeUnit import scala.jdk.FunctionConverters._ +import com.codeborne.selenide.Configuration +import com.codeborne.selenide.WebDriverRunner import org.openqa.selenium._ import org.openqa.selenium.firefox._ import org.openqa.selenium.htmlunit._ import org.openqa.selenium.support.ui.FluentWait /** - * A test browser (Using Selenium WebDriver) with the FluentLenium API (https://github.com/Fluentlenium/FluentLenium). + * A test browser (Using Selenium WebDriver) with the Selenide API (https://github.com/selenide/selenide). * * @param webDriver The WebDriver instance to use. */ -case class TestBrowser(webDriver: WebDriver, baseUrl: Option[String]) extends FluentAdapter() { - super.initFluent(webDriver) - baseUrl.foreach(baseUrl => super.getConfiguration.setBaseUrl(baseUrl)) - - /** - * Submits a form with the given field values - * - * @example {{{ - * submit("#login", fields = - * "email" -> email, - * "password" -> password - * ) - * }}} - */ - def submit(selector: String, fields: (String, String)*): FluentList[FluentWebElement] = { - fields.foreach { - case (fieldName, fieldValue) => - $(s"$selector *[name=$fieldName]").fill.`with`(fieldValue) - } - $(selector).submit() - } +case class TestBrowser(webDriver: WebDriver, baseUrl: Option[String]) { + WebDriverRunner.setWebDriver(webDriver); // super.initFluent(webDriver) + baseUrl.foreach(baseUrl => Configuration.baseUrl = baseUrl) // super.getConfiguration.setBaseUrl(baseUrl)) /** * Repeatedly applies this instance's input value to the given block until one of the following occurs: @@ -85,11 +69,11 @@ case class TestBrowser(webDriver: WebDriver, baseUrl: Option[String]) extends Fl * retrieves the underlying option interface that can be used * to set cookies, manage timeouts among other things */ - def manage: WebDriver.Options = super.getDriver.manage + def manage: WebDriver.Options = WebDriverRunner.getWebDriver().manage def quit(): Unit = { - Option(super.getDriver).foreach(_.quit()) - releaseFluent() + Option(WebDriverRunner.getWebDriver()).foreach(_.quit()) + // releaseFluent() } } diff --git a/testkit/play-test/src/test/java/play/test/WithBrowserTest.java b/testkit/play-test/src/test/java/play/test/WithBrowserTest.java index 2facb935633..12baab43fd4 100644 --- a/testkit/play-test/src/test/java/play/test/WithBrowserTest.java +++ b/testkit/play-test/src/test/java/play/test/WithBrowserTest.java @@ -4,16 +4,18 @@ package play.test; +import static com.codeborne.selenide.Selenide.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNotNull; +import com.codeborne.selenide.WebDriverRunner; import org.junit.Test; public class WithBrowserTest extends WithBrowser { @Test public void withBrowserShouldProvideABrowser() { assertNotNull(browser); - browser.goTo("/"); - assertThat(browser.pageSource()).contains("Action Not Found"); + open("/"); // browser.goTo("/"); + assertThat(WebDriverRunner.source()).contains("Action Not Found"); } } From 43f26ce0143fa999491d9250a6d539f734b8ef3d Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Mon, 29 Sep 2025 15:56:33 +0200 Subject: [PATCH 3/5] MiMa filters --- project/BuildSettings.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/project/BuildSettings.scala b/project/BuildSettings.scala index 9e2400ec247..490d9b90274 100644 --- a/project/BuildSettings.scala +++ b/project/BuildSettings.scala @@ -459,6 +459,11 @@ object BuildSettings { ProblemFilters.exclude[MissingClassProblem]("play.utils.ReadingMap$"), // Remove unused, package-private method ProblemFilters.exclude[DirectMissingMethodProblem]("play.api.libs.ws.ahc.AhcWSClient.loggerFactory"), + // Replace FluentLenium with Selenide + ProblemFilters.exclude[MissingTypesProblem]("play.api.test.TestBrowser"), + ProblemFilters.exclude[DirectMissingMethodProblem]("play.api.test.TestBrowser.submit"), + ProblemFilters.exclude[MissingTypesProblem]("play.test.TestBrowser"), + ProblemFilters.exclude[DirectMissingMethodProblem]("play.api.test.WithBrowser.browser"), ), (Compile / unmanagedSourceDirectories) += { val suffix = CrossVersion.partialVersion(scalaVersion.value) match { From 134bc935dc1c81db8c238f0add71c1cbcea8751d Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Tue, 30 Sep 2025 21:49:40 +0200 Subject: [PATCH 4/5] Revert all tests to original --- .../code/javaguide/tests/BrowserFunctionalTest.java | 5 ++--- .../tests/code/javaguide/tests/FunctionalTest.java | 11 ++++------- .../tests/code/specs2/ScalaFunctionalTestSpec.scala | 12 +++++------- .../src/test/java/play/test/WithBrowserTest.java | 6 ++---- 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/BrowserFunctionalTest.java b/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/BrowserFunctionalTest.java index 0f65ff07c4c..18bd339c167 100644 --- a/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/BrowserFunctionalTest.java +++ b/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/BrowserFunctionalTest.java @@ -4,7 +4,6 @@ package javaguide.tests; -import static com.codeborne.selenide.Selenide.*; import static org.junit.Assert.*; import org.junit.*; @@ -15,8 +14,8 @@ public class BrowserFunctionalTest extends WithBrowser { @Test public void runInBrowser() { - open("/"); - assertNotNull($("title").text()); + browser.goTo("/"); + assertNotNull(browser.el("title").text()); } } // #test-withbrowser diff --git a/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/FunctionalTest.java b/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/FunctionalTest.java index 12d57520385..e658b294461 100644 --- a/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/FunctionalTest.java +++ b/documentation/manual/working/javaGuide/main/tests/code/javaguide/tests/FunctionalTest.java @@ -14,9 +14,6 @@ import play.test.*; import play.libs.ws.*; -import static com.codeborne.selenide.Selenide.*; -import com.codeborne.selenide.WebDriverRunner; - import static play.test.Helpers.*; import static org.junit.Assert.*; @@ -92,10 +89,10 @@ public void runInBrowser() { testServer(), HTMLUNIT, browser -> { - open("/"); - assertEquals("Welcome to Play!", $("#title").text()); - $("a").click(); - assertTrue(WebDriverRunner.url().endsWith("/login")); + browser.goTo("/"); + assertEquals("Welcome to Play!", browser.el("#title").text()); + browser.$("a").click(); + assertEquals("login", browser.url()); }); } // #test-browser diff --git a/documentation/manual/working/scalaGuide/main/tests/code/specs2/ScalaFunctionalTestSpec.scala b/documentation/manual/working/scalaGuide/main/tests/code/specs2/ScalaFunctionalTestSpec.scala index c084509a7ab..403707138c8 100644 --- a/documentation/manual/working/scalaGuide/main/tests/code/specs2/ScalaFunctionalTestSpec.scala +++ b/documentation/manual/working/scalaGuide/main/tests/code/specs2/ScalaFunctionalTestSpec.scala @@ -4,8 +4,6 @@ package scalaguide.tests.specs2 -import com.codeborne.selenide.Selenide._ -import com.codeborne.selenide.WebDriverRunner import org.specs2.mutable.Specification import play.api.inject.guice.GuiceApplicationBuilder import play.api.libs.ws._ @@ -114,15 +112,15 @@ class ScalaFunctionalTestSpec extends ExampleSpecification { "run in a browser" in new WithBrowser(webDriver = WebDriverFactory(HTMLUNIT), app = applicationWithBrowser) { override def running() = { - open("/"); + browser.goTo("/") // Check the page - $("#title").text() must equalTo("Hello Guest") + browser.el("#title").text() must equalTo("Hello Guest") - $("a").click() + browser.el("a").click() - WebDriverRunner.url() must endWith("/login") - $("#title").text() must equalTo("Hello Coco") + browser.url must equalTo("login") + browser.el("#title").text() must equalTo("Hello Coco") } } // #scalafunctionaltest-testwithbrowser diff --git a/testkit/play-test/src/test/java/play/test/WithBrowserTest.java b/testkit/play-test/src/test/java/play/test/WithBrowserTest.java index 12baab43fd4..2facb935633 100644 --- a/testkit/play-test/src/test/java/play/test/WithBrowserTest.java +++ b/testkit/play-test/src/test/java/play/test/WithBrowserTest.java @@ -4,18 +4,16 @@ package play.test; -import static com.codeborne.selenide.Selenide.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNotNull; -import com.codeborne.selenide.WebDriverRunner; import org.junit.Test; public class WithBrowserTest extends WithBrowser { @Test public void withBrowserShouldProvideABrowser() { assertNotNull(browser); - open("/"); // browser.goTo("/"); - assertThat(WebDriverRunner.source()).contains("Action Not Found"); + browser.goTo("/"); + assertThat(browser.pageSource()).contains("Action Not Found"); } } From 27ffb34b2a9687acc66027627e8755c1f9c214f7 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Tue, 30 Sep 2025 22:10:10 +0200 Subject: [PATCH 5/5] Not static anymore --- .../src/main/scala/play/api/test/Specs.scala | 2 +- .../src/main/java/play/test/TestBrowser.java | 52 +++++++++++++++---- .../main/scala/play/api/test/Selenium.scala | 35 ++++++++++--- 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/testkit/play-specs2/src/main/scala/play/api/test/Specs.scala b/testkit/play-specs2/src/main/scala/play/api/test/Specs.scala index e8f1692ff9e..a98afa38ac3 100644 --- a/testkit/play-specs2/src/main/scala/play/api/test/Specs.scala +++ b/testkit/play-specs2/src/main/scala/play/api/test/Specs.scala @@ -194,9 +194,9 @@ abstract class WithBrowser[WEBDRIVER <: WebDriver]( implicit def implicitApp: Application = app implicit def implicitPort: Port = port + var browser: TestBrowser = null // TODO not var! just temporary now. either def or private[test] setBaseUrl override def wrap[T: AsResult](t: => T): Result = { - var browser: TestBrowser = null try { val currentPort = port val result = Helpers.runningWithPort(TestServer(port, app)) { assignedPort => diff --git a/testkit/play-test/src/main/java/play/test/TestBrowser.java b/testkit/play-test/src/main/java/play/test/TestBrowser.java index 5eee40eca7e..69637420433 100644 --- a/testkit/play-test/src/main/java/play/test/TestBrowser.java +++ b/testkit/play-test/src/main/java/play/test/TestBrowser.java @@ -4,8 +4,9 @@ package play.test; -import com.codeborne.selenide.Configuration; -import com.codeborne.selenide.WebDriverRunner; +import com.codeborne.selenide.SelenideConfig; +import com.codeborne.selenide.SelenideDriver; +import com.codeborne.selenide.SelenideElement; import java.time.Duration; import java.util.function.Function; import org.openqa.selenium.WebDriver; @@ -17,6 +18,8 @@ */ public class TestBrowser { + private SelenideDriver driver; + /** * A test browser (Using Selenium WebDriver) with the Selenide API * (https://github.com/selenide/selenide). @@ -37,8 +40,38 @@ public TestBrowser(Class webDriver, String baseUrl) throws * @param baseUrl The base url to use for relative requests. */ public TestBrowser(WebDriver webDriver, String baseUrl) { - WebDriverRunner.setWebDriver(webDriver); // super.initFluent(webDriver); - Configuration.baseUrl = baseUrl; // super.getConfiguration().setBaseUrl(baseUrl); + SelenideConfig config = new SelenideConfig(); + config.baseUrl(baseUrl); + driver = new SelenideDriver(config, webDriver, null); + } + + public void open(String relativeOrAbsoluteUrl) { + driver.open(relativeOrAbsoluteUrl); + } + + public void goTo(String relativeOrAbsoluteUrl) { + open(relativeOrAbsoluteUrl); + } + + public String source() { + return driver.source(); + } + + public String pageSource() { + return source(); + } + + public SelenideElement el(String cssSelector) { + return driver.find(cssSelector); + } + + public SelenideElement $(String cssSelector) { + return el(cssSelector); + } + + public String url() { + // return the relative url + return driver.url().substring(driver.config().baseUrl().length() + 1); } /** @@ -47,7 +80,7 @@ public TestBrowser(WebDriver webDriver, String baseUrl) { * @return the webdriver contained in a fluent wait. */ public FluentWait fluentWait() { - return new FluentWait<>(WebDriverRunner.getWebDriver()); + return new FluentWait<>(driver.getWebDriver()); } /** @@ -90,14 +123,15 @@ public T waitUntil(Function f) { * @return the web driver options. */ public WebDriver.Options manage() { - return WebDriverRunner.getWebDriver().manage(); + return driver.getWebDriver().manage(); } /** Quits and releases the {@link WebDriver} */ void quit() { - final WebDriver driver = WebDriverRunner.getWebDriver(); - if (driver != null) { - driver.quit(); + // TODO siehe dprecation comment in WebDriverRunner.closeWebDriver + final WebDriver webDriver = driver.getWebDriver(); + if (webDriver != null) { + webDriver.quit(); } // releaseFluent(); } diff --git a/testkit/play-test/src/main/scala/play/api/test/Selenium.scala b/testkit/play-test/src/main/scala/play/api/test/Selenium.scala index cfb26f0eb95..017a5a0c239 100644 --- a/testkit/play-test/src/main/scala/play/api/test/Selenium.scala +++ b/testkit/play-test/src/main/scala/play/api/test/Selenium.scala @@ -8,8 +8,9 @@ import java.util.concurrent.TimeUnit import scala.jdk.FunctionConverters._ -import com.codeborne.selenide.Configuration -import com.codeborne.selenide.WebDriverRunner +import com.codeborne.selenide.SelenideConfig +import com.codeborne.selenide.SelenideDriver +import com.codeborne.selenide.SelenideElement import org.openqa.selenium._ import org.openqa.selenium.firefox._ import org.openqa.selenium.htmlunit._ @@ -21,8 +22,30 @@ import org.openqa.selenium.support.ui.FluentWait * @param webDriver The WebDriver instance to use. */ case class TestBrowser(webDriver: WebDriver, baseUrl: Option[String]) { - WebDriverRunner.setWebDriver(webDriver); // super.initFluent(webDriver) - baseUrl.foreach(baseUrl => Configuration.baseUrl = baseUrl) // super.getConfiguration.setBaseUrl(baseUrl)) + private val config = new SelenideConfig() + baseUrl.foreach(baseUrl => config.baseUrl(baseUrl)) + private val driver = new SelenideDriver(config, webDriver, null) + + def open(relativeOrAbsoluteUrl: String): Unit = { + driver.open(relativeOrAbsoluteUrl) + } + + def goTo(relativeOrAbsoluteUrl: String): Unit = { + open(relativeOrAbsoluteUrl) + } + + def source: String = driver.source + + def pageSource: String = source + + def el(cssSelector: String): SelenideElement = driver.find(cssSelector) + + def $(cssSelector: String): SelenideElement = el(cssSelector) + + def url: String = { + // return the relative url + driver.url().substring(driver.config().baseUrl().length + 1) + } /** * Repeatedly applies this instance's input value to the given block until one of the following occurs: @@ -69,10 +92,10 @@ case class TestBrowser(webDriver: WebDriver, baseUrl: Option[String]) { * retrieves the underlying option interface that can be used * to set cookies, manage timeouts among other things */ - def manage: WebDriver.Options = WebDriverRunner.getWebDriver().manage + def manage: WebDriver.Options = driver.getWebDriver().manage def quit(): Unit = { - Option(WebDriverRunner.getWebDriver()).foreach(_.quit()) + Option(driver.getWebDriver()).foreach(_.quit()) // releaseFluent() } }