All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalatestplus.play.OneBrowserPerSuite.scala Maven / Gradle / Ivy

There is a newer version: 7.0.1
Show newest version
/*
 * Copyright 2001-2022 Artima, Inc.
 *
 * Licensed 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.scalatestplus.play

import org.scalatest._
import concurrent.Eventually
import concurrent.IntegrationPatience
import org.openqa.selenium.WebDriver
import BrowserFactory.UnavailableDriver
import org.openqa.selenium.safari.SafariDriver
import org.openqa.selenium.chrome.ChromeDriver
import org.scalatestplus.selenium.WebBrowser

import scala.util.Try

/* TODO: Make ConfiguredBrowser require a ServerProvider also, I think. */

/**
 * Trait that provides a new Selenium `WebDriver` instance per ScalaTest `Suite`.
 *
 * This `TestSuiteMixin` trait's overridden `run` method
 * places a reference to the `WebDriver` provided by `webDriver` under the key `org.scalatestplus.play.webDriver`.
 * This allows any nested `Suite`s to access the `Suite`'s
 * `WebDriver` as well, most easily by having the nested `Suite`s mix in the
 * [[org.scalatestplus.play.ConfiguredServer ConfiguredBrowser]] trait. On the status returned by `super.run`, this
 * trait's overridden `run` method registers a block of code to close the `WebDriver` to be executed when the `Status`
 * completes, and returns the same `Status`. This ensures the `WebDriver` will continue to be available until
 * all nested suites have completed, after which the `WebDriver` will be closed.
 * This trait also overrides `Suite.withFixture` to cancel tests automatically if the related
 * `WebDriver` is not available on the host platform.
 *
 * This trait's self-type, [[org.scalatestplus.play.ServerProvider ServerProvider]],  will ensure
 * a `TestServer` and `Application` are available to each test. The self-type will require that you mix in either
 * [[org.scalatestplus.play.guice.GuiceOneServerPerSuite GuiceOneServerPerSuite]], [[org.scalatestplus.play.guice.GuiceOneServerPerTest OneServerPerTest]],
 * [[org.scalatestplus.play.ConfiguredServer ConfiguredServer]] before you mix in this trait. Your choice among these three
 * `ServerProvider`s will determine the extent to which one or more `TestServer`s are shared by multiple tests.
 *
 * Here's an example that shows demonstrates of the services provided by this trait. Note that
 * to use this trait, you must mix in one of the driver factories (this example
 * mixes in [[org.scalatestplus.play.FirefoxFactory FirefoxFactory]]):
 *
 * 
 * package org.scalatestplus.play.examples.onebrowserpersuite
 *
 * import org.scalatest.tags.FirefoxBrowser
 * import org.scalatestplus.play._
 * import play.api.{Play, Application}
 * import play.api.inject.guice._
 * import play.api.routing._
 *
 * @FirefoxBrowser
 * class ExampleSpec extends PlaySpec with OneServerPerSuite with OneBrowserPerSuite with FirefoxFactory {
 *
 *   // Override fakeApplication() if you need a Application with other than non-default parameters.
 *   def fakeApplication(): Application = new GuiceApplicationBuilder()
 *     .configure("foo" -> "bar", "ehcacheplugin" -> "disabled")
 *     .router(TestRoutes.router)
 *     .build()
 *
 *   "The OneBrowserPerSuite trait" must {
 *     "provide an Application" in {
 *       app.configuration.getOptional[String]("ehcacheplugin") mustBe Some("disabled")
 *     }
 *     "make the Application available implicitly" in {
 *       def getConfig(key: String)(implicit app: Application) = app.configuration.getOptional[String](key)
 *       getConfig("ehcacheplugin") mustBe Some("disabled")
 *     }
 *     "provide an http endpoint" in {
 *       runningServer.endpoints.httpEndpoint must not be empty
 *     }
 *     "provide an actual running server" in {
 *       import java.net._
 *       val url = new URL("http://localhost:" + port + "/boum")
 *       val con = url.openConnection().asInstanceOf[HttpURLConnection]
 *       try con.getResponseCode mustBe 404
 *       finally con.disconnect()
 *     }
 *     "provide a web driver" in {
 *       go to ("http://localhost:" + port + "/testing")
 *       pageTitle mustBe "Test Page"
 *       click on find(name("b")).value
 *       eventually { pageTitle mustBe "scalatest" }
 *     }
 *   }
 * }
 * 
* * If you have many tests that can share the same `Application`, `TestServer`, and `WebDriver`, and you don't want to put them all into one * test class, you can place them into different "nested" `Suite` classes. * Create a master suite that extends `OneServerPerSuite` and declares the nested * `Suite`s. Annotate the nested suites with `@DoNotDiscover` and have them extend `ConfiguredBrowser`. Here's an example: * *
 * package org.scalatestplus.play.examples.onebrowserpersuite
 *
 * import org.scalatest._
 * import tags.FirefoxBrowser
 * import org.scalatestplus.play._
 * import play.api.{Play, Application}
 *
 * // This is the "master" suite
 * class NestedExampleSpec extends Suites(
 *   new OneSpec,
 *   new TwoSpec,
 *   new RedSpec,
 *   new BlueSpec
 * ) with OneServerPerSuite with OneBrowserPerSuite with FirefoxFactory {
 *   // Override fakeApplication() if you need a Application with other than non-default parameters.
 *   def fakeApplication(): Application =
 *     new GuiceApplicationBuilder(
 *       additionalConfiguration = Map("ehcacheplugin" -> "disabled"),
 *       withRoutes = TestRoute
 *     ).build()
 * }
 *
 * // These are the nested suites
 * @DoNotDiscover class OneSpec extends PlaySpec with ConfiguredServer with ConfiguredBrowser
 * @DoNotDiscover class TwoSpec extends PlaySpec with ConfiguredServer with ConfiguredBrowser
 * @DoNotDiscover class RedSpec extends PlaySpec with ConfiguredServer with ConfiguredBrowser
 *
 * @DoNotDiscover
 * class BlueSpec extends PlaySpec with ConfiguredServer with ConfiguredBrowser {
 *
 *   "The OneBrowserPerSuite trait" must {
 *     "provide an Application" in {
 *       app.configuration.getOptional[String]("ehcacheplugin") mustBe Some("disabled")
 *     }
 *     "make the Application available implicitly" in {
 *       def getConfig(key: String)(implicit app: Application) = app.configuration.getOptional[String](key)
 *       getConfig("ehcacheplugin") mustBe Some("disabled")
 *     }
 *     "provide an http endpoint" in {
 *       runningServer.endpoints.httpEndpoint must not be empty
 *     }
 *     "provide an actual running server" in {
 *       import Helpers._
 *       import java.net._
 *       val url = new URL("http://localhost:" + port + "/boum")
 *       val con = url.openConnection().asInstanceOf[HttpURLConnection]
 *       try con.getResponseCode mustBe 404
 *       finally con.disconnect()
 *     }
 *   }
 * }
 * 
* * It is possible to use `OneBrowserPerSuite` to run the same tests in more than one browser. Nevertheless, * you should consider the approach taken by [[org.scalatestplus.play.AllBrowsersPerSuite AllBrowsersPerSuite]] * and [[org.scalatestplus.play.AllBrowsersPerTest AllBrowsersPerTest]] * instead, as it requires a bit less boilerplate code than `OneBrowserPerSuite` to test in multiple browsers. * If you prefer to use `OneBrowserPerSuite`, however, simply place your tests in an abstract superclass, then define concrete subclasses * for each browser you wish to test against. Here's an example: * *
 * package org.scalatestplus.play.examples.onebrowserpersuite
 *
 * import play.api.test._
 * import org.scalatest._
 * import tags._
 * import org.scalatestplus.play._
 * import play.api.{Play, Application}
 *
 * // Place your tests in an abstract class
 * abstract class MultiBrowserExampleSpec extends PlaySpec with OneServerPerSuite with OneBrowserPerSuite {
 *
 *   // Override app if you need an Application with other than non-default parameters.
 *   def fakeApplication(): Application =
 *     new GuiceApplicationBuilder(
 *       additionalConfiguration = Map("ehcacheplugin" -> "disabled"),
 *       withRoutes = TestRoute
 *     ).build
 *
 *   "The OneBrowserPerSuite trait" must {
 *     "provide an Application" in {
 *       app.configuration.getOptional[String]("ehcacheplugin") mustBe Some("disabled")
 *     }
 *     "make the Application available implicitly" in {
 *       def getConfig(key: String)(implicit app: Application) = app.configuration.getOptional[String](key)
 *       getConfig("ehcacheplugin") mustBe Some("disabled")
 *     }
 *     "provide an http endpoint" in {
 *       runningServer.endpoints.httpEndpoint must not be empty
 *     }
 *     "provide an actual running server" in {
 *       import Helpers._
 *       import java.net._
 *       val url = new URL("http://localhost:" + port + "/boum")
 *       val con = url.openConnection().asInstanceOf[HttpURLConnection]
 *       try con.getResponseCode mustBe 404
 *       finally con.disconnect()
 *     }
 *     "provide a web driver" in {
 *       go to ("http://localhost:" + port + "/testing")
 *       pageTitle mustBe "Test Page"
 *       click on find(name("b")).value
 *       eventually { pageTitle mustBe "scalatest" }
 *     }
 *   }
 * }
 *
 * // Then make a subclass that mixes in the factory for each
 * // Selenium driver you want to test with.
 * @FirefoxBrowser class FirefoxExampleSpec extends MultiBrowserExampleSpec with FirefoxFactory
 * @SafariBrowser class SafariExampleSpec extends MultiBrowserExampleSpec with SafariFactory
 * @InternetExplorerBrowser class InternetExplorerExampleSpec extends MultiBrowserExampleSpec with InternetExplorerFactory
 * @ChromeBrowser class ChromeExampleSpec extends MultiBrowserExampleSpec with ChromeFactory
 * @HtmlUnitBrowser class HtmlUnitExampleSpec extends MultiBrowserExampleSpec with HtmlUnitFactory
 * 
* * The concrete subclasses include tag annotations describing the browser used to make it * easier to include or exclude browsers in specific runs. This is not strictly necessary since if a browser is not supported * on the host platform the tests will be automatically canceled. For example, here's how the output would look * if you ran the above tests on a platform that did not support Selenium drivers for Chrome or Internet Explorer * using sbt: * *
 * > test-only *onebrowserpersuite*
 * [info] FirefoxExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] - must provide an Application
 * [info] - must make the Application available implicitly
 * [info] - must start the Application
 * [info] - must provide the port number
 * [info] - must provide an actual running server
 * [info] - must provide a web driver
 * [info] SafariExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] - must provide an Application
 * [info] - must make the Application available implicitly
 * [info] - must start the Application
 * [info] - must provide the port number
 * [info] - must provide an actual running server
 * [info] - must provide a web driver
 * [info] InternetExplorerExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] - must provide an Application !!! CANCELED !!!
 * [info]   Was unable to create a Selenium InternetExplorerDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must make the Application available implicitly !!! CANCELED !!!
 * [info]   Was unable to create a Selenium InternetExplorerDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must start the Application !!! CANCELED !!!
 * [info]   Was unable to create a Selenium InternetExplorerDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must provide the port number !!! CANCELED !!!
 * [info]   Was unable to create a Selenium InternetExplorerDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must provide an actual running server !!! CANCELED !!!
 * [info]   Was unable to create a Selenium InternetExplorerDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must provide a web driver !!! CANCELED !!!
 * [info]   Was unable to create a Selenium InternetExplorerDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] ChromeExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] - must provide an Application !!! CANCELED !!!
 * [info]   Was unable to create a Selenium ChromeDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must make the Application available implicitly !!! CANCELED !!!
 * [info]   Was unable to create a Selenium ChromeDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must start the Application !!! CANCELED !!!
 * [info]   Was unable to create a Selenium ChromeDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must provide the port number !!! CANCELED !!!
 * [info]   Was unable to create a Selenium ChromeDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must provide an actual running server !!! CANCELED !!!
 * [info]   Was unable to create a Selenium ChromeDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] - must provide a web driver !!! CANCELED !!!
 * [info]   Was unable to create a Selenium ChromeDriver on this platform. (OneBrowserPerSuite.scala:201)
 * [info] HtmlUnitExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] - must provide an Application
 * [info] - must make the Application available implicitly
 * [info] - must start the Application
 * [info] - must provide the port number
 * [info] - must provide an actual running server
 * [info] - must provide a web driver
 * 
* * For comparison, here is what the output would look like if you just selected tests tagged with `FirefoxBrowser` in sbt: * *
 * > test-only *onebrowserpersuite* -- -n org.scalatest.tags.FirefoxBrowser
 * [info] FirefoxExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] - must provide an Application
 * [info] - must make the Application available implicitly
 * [info] - must start the Application
 * [info] - must provide the port number
 * [info] - must provide an actual running server
 * [info] - must provide a web driver
 * [info] SafariExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] InternetExplorerExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] ChromeExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * [info] HtmlUnitExampleSpec:
 * [info] The OneBrowserPerSuite trait
 * 
*/ trait OneBrowserPerSuite extends TestSuiteMixin with WebBrowser with Eventually with IntegrationPatience with BrowserFactory { this: TestSuite with ServerProvider => /** * An implicit instance of `WebDriver`, created by calling `createWebDriver`. * If there is an error when creating the `WebDriver`, `UnavailableDriver` will be assigned * instead. */ implicit lazy val webDriver: WebDriver = createWebDriver() /** * Automatically cancels tests with an appropriate error message when the `webDriver` field is a `UnavailableDriver`, * else calls `super.withFixture(test)` */ abstract override def withFixture(test: NoArgTest): Outcome = { webDriver match { case UnavailableDriver(ex, errorMessage) => ex match { case Some(e) => cancel(errorMessage, e) case None => cancel(errorMessage) } case _ => super.withFixture(test) } } /** * Places the `WebDriver` provided by `webDriver` into the `ConfigMap` under the key * `org.scalatestplus.play.webDriver` to make * it available to nested suites; calls `super.run`; and lastly ensures the `WebDriver` is stopped after * all tests and nested suites have completed. * * @param testName an optional name of one test to run. If `None`, all relevant tests should be run. * I.e., `None` acts like a wildcard that means run all relevant tests in this `Suite`. * @param args the `Args` for this run * @return a `Status` object that indicates when all tests and nested suites started by this method have completed, and whether or not a failure occurred. */ abstract override def run(testName: Option[String], args: Args): Status = { val cleanup: Try[Boolean] => Unit = { _ => webDriver match { case _: UnavailableDriver => // do nothing for UnavailableDriver case _ => webDriver.quit() } } try { val newConfigMap = args.configMap + ("org.scalatestplus.play.webDriver" -> webDriver) val newArgs = args.copy(configMap = newConfigMap) val status = super.run(testName, newArgs) status.whenCompleted(cleanup) status } catch { case ex: Throwable => cleanup(Try(false)) throw ex } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy