Last active
December 23, 2015 12:19
-
-
Save huntc/6634567 to your computer and use it in GitHub Desktop.
WebDriver browser ideas
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.typesafe.webdriver | |
import akka.actor.{FSM, Props, Actor} | |
import scala.sys.process._ | |
import java.io.File | |
import com.typesafe.webdriver.LocalBrowser._ | |
import scala.Some | |
/** | |
* Provides an Actor on behalf of a browser. Browsers are represented as operating system processes and are | |
* communicated with by using the http/json based WebDriver protocol. | |
* @param port the port that the browser is listening for WebDriver traffic on. | |
* @param args a sequence of command line arguments used to launch the browser from the command line. | |
*/ | |
class LocalBrowser(port: Int, args: Seq[String]) extends Actor with FSM[State, Option[Process]] { | |
startWith(Uninitialized, None) | |
when(Uninitialized) { | |
case Event(Startup, None) => | |
val p = Process(args).run(ProcessLogger(log.debug, log.error)) | |
goto(Started) using Some(p) | |
} | |
when(Started) { | |
case Event(ExecuteJs, p@Some(_)) => stay() // Lots more to be done here | |
case Event(Shutdown, p@Some(_)) => stop(FSM.Normal, p) | |
} | |
whenUnhandled { | |
case Event(e, s) => { | |
log.debug("Received unhandled request {} in state {}/{}", e, stateName, s) | |
stay() | |
} | |
} | |
onTermination { | |
case StopEvent(_, _, maybeProcess) => maybeProcess.foreach(p => p.destroy()) | |
} | |
initialize() | |
} | |
object LocalBrowser { | |
/** | |
* Start a browser. This is typically sent upon having obtained an actor ref to the browser. | |
*/ | |
case object Startup | |
/** | |
* Shutdown a browser. | |
*/ | |
case object Shutdown | |
/** | |
* Execute a JavaScript file given its location on the local file system. | |
* @param f the location of the JS file. | |
*/ | |
case class ExecuteJs(f: File) | |
// Internal FSM states | |
private[LocalBrowser] trait State | |
private case object Uninitialized extends State | |
private case object Started extends State | |
} | |
/** | |
* Used to manage a local instance of PhantomJs | |
*/ | |
object PhantomJs { | |
def props(port: Int = 8910, args: Seq[String] = Seq("phantomjs")): Props = | |
Props(classOf[LocalBrowser], port, args) | |
} |
why is the actor defined as an abstract class?
You might want to put the State trait and its subclasses in a companion object.
Also, it might be a good idea to have an onUnhandled block and provide nice error messages
In general it looks good!
Thanks heaps for the review. I shall heed your recommendations.
LocalBrowser need no longer be abstract.. Forgot to remove that.
There you are - updated - much cleaner - thanks
Nice!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please note, that if you define your message types (Startup, Shutdown, etc) inside a class instead of a companion object they become non-serializable. If a user runs the actor system with the serialize-messages option on, this actor will fail. One option is to move the message definitions to a compantion object (preferred), or mark them with the NoSerializationVerificationNeeded marker trait.