diff --git a/.travis.yml b/.travis.yml index abb3131..66e78fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,12 @@ language: scala before_script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" - - sleep 3 # give xvfb some time to start + - python -m SimpleHTTPServer 8080 & script: - - sbt ++$TRAVIS_SCALA_VERSION seleniumJSEnv/scalastyle seleniumJSEnv/test:scalastyle seleniumJSEnvTest/scalastyle seleniumJSEnvTest/test:scalastyle + - sbt ++$TRAVIS_SCALA_VERSION seleniumJSEnv/scalastyle seleniumJSEnv/test:scalastyle seleniumJSEnvTest/scalastyle seleniumJSHttpEnvTest/test:scalastyle seleniumJSEnvTest/test:scalastyle - sbt ++$TRAVIS_SCALA_VERSION seleniumJSEnvTest/run seleniumJSEnvTest/test 'set scalaJSStage in Global := FullOptStage' seleniumJSEnvTest/run seleniumJSEnvTest/test - sbt ++$TRAVIS_SCALA_VERSION 'set inScope(ThisScope in seleniumJSEnvTest)(jsEnv := new org.scalajs.jsenv.selenium.SeleniumJSEnv(org.scalajs.jsenv.selenium.Firefox).withKeepAlive())' seleniumJSEnvTest/run seleniumJSEnvTest/test 'set scalaJSStage in Global := FullOptStage' seleniumJSEnvTest/run seleniumJSEnvTest/test + - sbt ++$TRAVIS_SCALA_VERSION 'set scalaJSStage in Global := FullOptStage' seleniumJSHttpEnvTest/test scala: - 2.10.6 - 2.11.7 diff --git a/build.sbt b/build.sbt index 5cf7e00..14f0cd7 100644 --- a/build.sbt +++ b/build.sbt @@ -2,6 +2,7 @@ import sbt.Keys._ import org.scalajs.jsenv.selenium.SeleniumJSEnv import org.scalajs.jsenv.selenium.Firefox +import org.scalajs.jsenv.selenium.CustomFileMaterializer val commonSettings: Seq[Setting[_]] = Seq( version := "0.1.2-SNAPSHOT", @@ -18,6 +19,15 @@ val commonSettings: Seq[Setting[_]] = Seq( Some("scm:git:git@github.com:scala-js/scala-js-env-selenium.git"))) ) +val testSettings: Seq[Setting[_]] = commonSettings ++ Seq( + testOptions += + Tests.Argument(TestFramework("com.novocode.junit.JUnitFramework"), "-v", "-a"), + jsDependencies ++= Seq( + RuntimeDOM % "test", + "org.webjars" % "jquery" % "1.10.2" / "jquery.js" + ) +) + // We'll need the name scalajs-env-selenium for the `seleniumJSEnv` project name := "root" @@ -65,13 +75,16 @@ lazy val seleniumJSEnv: Project = project. lazy val seleniumJSEnvTest: Project = project. enablePlugins(ScalaJSPlugin). enablePlugins(ScalaJSJUnitPlugin). - settings(commonSettings). + settings(testSettings). settings( - testOptions += - Tests.Argument(TestFramework("com.novocode.junit.JUnitFramework"), "-v", "-a"), - jsDependencies ++= Seq( - RuntimeDOM % "test", - "org.webjars" % "jquery" % "1.10.2" / "jquery.js" - ), jsEnv := new SeleniumJSEnv(Firefox) ) + +lazy val seleniumJSHttpEnvTest: Project = project. + enablePlugins(ScalaJSPlugin). + enablePlugins(ScalaJSJUnitPlugin). + settings(testSettings). + settings( + jsEnv := new SeleniumJSEnv(Firefox). + withMaterializer(new CustomFileMaterializer("tmp", "http://localhost:8080/tmp")) + ) diff --git a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/AbstractSeleniumJSRunner.scala b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/AbstractSeleniumJSRunner.scala index 05e7f05..bbc7840 100644 --- a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/AbstractSeleniumJSRunner.scala +++ b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/AbstractSeleniumJSRunner.scala @@ -3,10 +3,10 @@ package org.scalajs.jsenv.selenium import org.scalajs.core.tools.io.{MemVirtualJSFile, VirtualJSFile} import org.scalajs.core.tools.jsdep.ResolvedJSDependency import org.scalajs.core.tools.logging.Logger -import org.scalajs.jsenv.{VirtualFileMaterializer, JSConsole} +import org.scalajs.jsenv.JSConsole abstract class AbstractSeleniumJSRunner(browserProvider: SeleniumBrowser, - libs: Seq[ResolvedJSDependency], code: VirtualJSFile) { + libs: Seq[ResolvedJSDependency], code: VirtualJSFile, materializer: FileMaterializer) { protected val browser = browserProvider.newDriver @@ -22,30 +22,23 @@ abstract class AbstractSeleniumJSRunner(browserProvider: SeleniumBrowser, _console = console } - protected[this] val libCache = new VirtualFileMaterializer(true) - protected def initFiles(): Seq[VirtualJSFile] = browserProvider.initFiles() ++ runtimeEnv() protected def runAllScripts(): Unit = { val inits = initFiles() - val cacheDir = libCache.cacheDir.getAbsolutePath - def absolutePath(fileName: String): String = - "file://" + cacheDir + "/" + fileName val jsFiles = { - inits.map(file => absolutePath(file.path)) ++ - libs.map(dep => absolutePath(dep.info.relPath.split('/').last)) :+ + inits.map(materializer.materialize(_).toString) ++ + libs.map(dep => materializer.materialize(dep.lib).toString) :+ code.path } val page = htmlPage(jsFiles) - inits.foreach(libCache.materialize) - libs.foreach(dep => libCache.materialize(dep.lib)) - libCache.materialize(code) - libCache.materialize(page) + materializer.materialize(code) + val pageURL = materializer.materialize(page) - browser.getWebDriver.get("file://" + cacheDir + "/" + page.path) + browser.getWebDriver.get(pageURL.toString) browser.processConsoleLogs(console) } diff --git a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/CustomFileMaterializer.scala b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/CustomFileMaterializer.scala new file mode 100644 index 0000000..13d6ff3 --- /dev/null +++ b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/CustomFileMaterializer.scala @@ -0,0 +1,55 @@ +package org.scalajs.jsenv.selenium + +import java.io.File +import java.net.URL + +import org.scalajs.core.tools.io.{IO, VirtualTextFile, WritableFileVirtualTextFile} + +/** Materializes files on the filesystem and specifies a custom url to access stored files. + * This can be used to bypass cross origin access policies as shown below. + * + * @param fsRoot Location on the filesystem where to store the generated files + * @param webRoot Corresponding url to access the files + * + * @example + * + * The following illustrates how to configure a project such that the browser fetches + * files by http:// instead of file://. + * This example assumes a local webserver is running and serving the ".tmp" + * directory at http://localhost:8080 + * + *
+ * jsSettings( + * // ... + * jsEnv := new SeleniumJSEnv(org.scalajs.jsenv.selenium.Firefox) + * .withMaterializer(new SpecificFileMaterializer(".tmp", "http://localhost:8080")) + * ) + *+ */ +class CustomFileMaterializer(val fsRoot: String, val webRoot: String) extends FileMaterializer { + + val storageDir = createStorageDir() + + /** Create a target file to write/copy to. Will also call + * deleteOnExit on the file. + */ + private def trgFile(name: String): File = { + val f = new File(storageDir, name) + f.deleteOnExit() + f + } + + /** Creates the storage directory if it does not exist. */ + private def createStorageDir(): File = { + val storageDir = new File(fsRoot) + storageDir.mkdir() + storageDir + } + + override def materialize(vf: VirtualTextFile): URL = { + val trg = trgFile(vf.name) + IO.copyTo(vf, WritableFileVirtualTextFile(trg)) + new URL(webRoot + "/" + vf.name) + } + +} diff --git a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/DefaultFileMaterializer.scala b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/DefaultFileMaterializer.scala new file mode 100644 index 0000000..27cd0a0 --- /dev/null +++ b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/DefaultFileMaterializer.scala @@ -0,0 +1,17 @@ +package org.scalajs.jsenv.selenium + +import org.scalajs.core.tools.io.VirtualTextFile +import org.scalajs.jsenv.VirtualFileMaterializer +import java.net.URL + +/** Materializes virtual files in a temporary directory and links to them + * via file:// + */ +object DefaultFileMaterializer extends FileMaterializer { + + private val materializer = new VirtualFileMaterializer(true) + + override def materialize(vf: VirtualTextFile): URL = { + materializer.materialize(vf).toURI.toURL + } +} diff --git a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/FileMaterializer.scala b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/FileMaterializer.scala new file mode 100644 index 0000000..7bc8fee --- /dev/null +++ b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/FileMaterializer.scala @@ -0,0 +1,9 @@ +package org.scalajs.jsenv.selenium + +import java.net.URL + +import org.scalajs.core.tools.io.VirtualTextFile + +trait FileMaterializer { + def materialize(vf: VirtualTextFile): URL +} diff --git a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumAsyncJSRunner.scala b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumAsyncJSRunner.scala index e0ee308..82a3485 100644 --- a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumAsyncJSRunner.scala +++ b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumAsyncJSRunner.scala @@ -10,8 +10,8 @@ import scala.concurrent.{Future, Promise} import scala.util.Try class SeleniumAsyncJSRunner(browserProvider: SeleniumBrowser, - libs: Seq[ResolvedJSDependency], code: VirtualJSFile, keepAlive: Boolean) - extends AbstractSeleniumJSRunner(browserProvider, libs, code) + libs: Seq[ResolvedJSDependency], code: VirtualJSFile, keepAlive: Boolean, materializer: FileMaterializer) + extends AbstractSeleniumJSRunner(browserProvider, libs, code, materializer) with AsyncJSRunner { private[this] var promise = Promise[Unit]() diff --git a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumComJSRunner.scala b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumComJSRunner.scala index 37ceb0d..1ba15cb 100644 --- a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumComJSRunner.scala +++ b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumComJSRunner.scala @@ -12,8 +12,8 @@ import scala.concurrent.duration.Duration import scala.util.Try class SeleniumComJSRunner(browserProvider: SeleniumBrowser, - libs: Seq[ResolvedJSDependency], code: VirtualJSFile, keepAlive: Boolean) - extends SeleniumAsyncJSRunner(browserProvider, libs, code, keepAlive) + libs: Seq[ResolvedJSDependency], code: VirtualJSFile, keepAlive: Boolean, materializer: FileMaterializer) + extends SeleniumAsyncJSRunner(browserProvider, libs, code, keepAlive, materializer) with ComJSRunner { protected def envName: String = diff --git a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumJSEnv.scala b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumJSEnv.scala index 5a2701e..cfb985d 100644 --- a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumJSEnv.scala +++ b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumJSEnv.scala @@ -5,29 +5,32 @@ import org.scalajs.core.tools.jsdep.ResolvedJSDependency import org.scalajs.jsenv.{AsyncJSEnv, ComJSEnv} import org.scalajs.jsenv.{JSRunner, AsyncJSRunner, ComJSRunner} -class SeleniumJSEnv private (browser: SeleniumBrowser, keepAlive: Boolean) +class SeleniumJSEnv private (browser: SeleniumBrowser, keepAlive: Boolean, materializer: FileMaterializer) extends AsyncJSEnv with ComJSEnv { def this(browser: SeleniumBrowser) = - this(browser, keepAlive = false) + this(browser, keepAlive = false, materializer = DefaultFileMaterializer) + + def withMaterializer(materializer: FileMaterializer): SeleniumJSEnv = + new SeleniumJSEnv(browser, keepAlive, materializer) def withKeepAlive(): SeleniumJSEnv = - new SeleniumJSEnv(browser, keepAlive = true) + new SeleniumJSEnv(browser, keepAlive = true, materializer) def browserName: String = browser.name def name: String = "SeleniumJSEnv for " + browserName def jsRunner(libs: Seq[ResolvedJSDependency], code: VirtualJSFile): JSRunner = - new SeleniumRunner(browser, libs, code, keepAlive) + new SeleniumRunner(browser, libs, code, keepAlive, materializer) def asyncRunner(libs: Seq[ResolvedJSDependency], code: VirtualJSFile): AsyncJSRunner = { - new SeleniumAsyncJSRunner(browser, libs, code, keepAlive) + new SeleniumAsyncJSRunner(browser, libs, code, keepAlive, materializer) } def comRunner(libs: Seq[ResolvedJSDependency], code: VirtualJSFile): ComJSRunner = { - new SeleniumComJSRunner(browser, libs, code, keepAlive) + new SeleniumComJSRunner(browser, libs, code, keepAlive, materializer) } } diff --git a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumRunner.scala b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumRunner.scala index 8e8eb18..d13652a 100644 --- a/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumRunner.scala +++ b/seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumRunner.scala @@ -6,8 +6,8 @@ import org.scalajs.core.tools.logging.Logger import org.scalajs.jsenv.{JSConsole, JSRunner} class SeleniumRunner(browserProvider: SeleniumBrowser, - libs: Seq[ResolvedJSDependency], code: VirtualJSFile, keepAlive: Boolean) - extends AbstractSeleniumJSRunner(browserProvider, libs, code) with JSRunner { + libs: Seq[ResolvedJSDependency], code: VirtualJSFile, keepAlive: Boolean, materializer: FileMaterializer) + extends AbstractSeleniumJSRunner(browserProvider, libs, code, materializer) with JSRunner { def run(logger: Logger, console: JSConsole): Unit = { setupLoggerAndConsole(logger, console) diff --git a/seleniumJSHttpEnvTest/src/test/scala/org/scalajs/jsenv/selenium/LocationTest.scala b/seleniumJSHttpEnvTest/src/test/scala/org/scalajs/jsenv/selenium/LocationTest.scala new file mode 100644 index 0000000..bd8d2d8 --- /dev/null +++ b/seleniumJSHttpEnvTest/src/test/scala/org/scalajs/jsenv/selenium/LocationTest.scala @@ -0,0 +1,14 @@ +package org.scalajs.jsenv.selenium + +import org.junit.Assert._ +import org.junit.Test + +import scala.scalajs.js.Dynamic.global + +class LocationTest { + + @Test def LocationTest(): Unit = { + assertEquals("http:", global.window.location.protocol.toString()) + assertEquals("localhost:8080", global.window.location.host.toString()) + } +}