Skip to content

Instantly share code, notes, and snippets.

@huntc
Last active December 23, 2015 12:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save huntc/6634567 to your computer and use it in GitHub Desktop.
Save huntc/6634567 to your computer and use it in GitHub Desktop.
WebDriver browser ideas
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)
}
@drewhk
Copy link

drewhk commented Sep 20, 2013

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.

@drewhk
Copy link

drewhk commented Sep 20, 2013

why is the actor defined as an abstract class?

@drewhk
Copy link

drewhk commented Sep 20, 2013

You might want to put the State trait and its subclasses in a companion object.

@drewhk
Copy link

drewhk commented Sep 20, 2013

Also, it might be a good idea to have an onUnhandled block and provide nice error messages

@drewhk
Copy link

drewhk commented Sep 20, 2013

In general it looks good!

@huntc
Copy link
Author

huntc commented Sep 20, 2013

Thanks heaps for the review. I shall heed your recommendations.

LocalBrowser need no longer be abstract.. Forgot to remove that.

@huntc
Copy link
Author

huntc commented Sep 21, 2013

There you are - updated - much cleaner - thanks

@viktorklang
Copy link

Nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment