Unit testing 为Play WebSocket编写单元测试

Unit testing 为Play WebSocket编写单元测试,unit-testing,scala,playframework-2.0,Unit Testing,Scala,Playframework 2.0,我正在使用WebSocket开发Scala+Play应用程序。我有一个简单的web套接字,定义如下: def indexWS = WebSocket.using[String] { request => val out = Enumerator("Hello!") val in = Iteratee.foreach[String](println).map { _ => println("Disconnected") } (in,out) } 我已经用Chrome的控制

我正在使用WebSocket开发Scala+Play应用程序。我有一个简单的web套接字,定义如下:

def indexWS =  WebSocket.using[String] { request =>

val out = Enumerator("Hello!")
val in = Iteratee.foreach[String](println).map { _ =>
  println("Disconnected")
}


(in,out)
}
我已经用Chrome的控制台验证了这一点。我遇到的问题是试图为此编写一个单元测试。目前我有:

"send awk for websocket connection" in {
  running(FakeApplication()){
    val js = route(FakeRequest(GET,"/WS")).get

    status(js) must equalTo (OK)
    contentType(js) must beSome.which(_ == "text/javascript")
  }
}
但是,在play console中运行测试时,我收到了这个错误,其中第35行对应于这行“val js=route(FakeRequest(GET,“/WS”))。GET':


我还没有找到一个很好的单元测试scala/play WebSocket的例子,对如何正确编写这个测试感到困惑

我认为您可以检查这一点,它有一个关于使用规范测试WebSocket的非常好的示例

这是typesafe的一个示例:

/*
 * Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
 */
package play.it.http.websocket

import play.api.test._
import play.api.Application
import scala.concurrent.{Future, Promise}
import play.api.mvc.{Handler, Results, WebSocket}
import play.api.libs.iteratee._
import java.net.URI
import org.jboss.netty.handler.codec.http.websocketx._
import org.specs2.matcher.Matcher
import akka.actor.{ActorRef, PoisonPill, Actor, Props}
import play.mvc.WebSocket.{Out, In}
import play.core.Router.HandlerDef
import java.util.concurrent.atomic.AtomicReference
import org.jboss.netty.buffer.ChannelBuffers

object WebSocketSpec extends PlaySpecification with WsTestClient {

  sequential

  def withServer[A](webSocket: Application => Handler)(block: => A): A = {
    val currentApp = new AtomicReference[FakeApplication]
    val app = FakeApplication(
      withRoutes = {
        case (_, _) => webSocket(currentApp.get())
      }
    )
    currentApp.set(app)
    running(TestServer(testServerPort, app))(block)
  }

  def runWebSocket[A](handler: (Enumerator[WebSocketFrame], Iteratee[WebSocketFrame, _]) => Future[A]): A = {
    val innerResult = Promise[A]()
    WebSocketClient { client =>
      await(client.connect(URI.create("ws://localhost:" + testServerPort + "/stream")) { (in, out) =>
        innerResult.completeWith(handler(in, out))
      })
    }
    await(innerResult.future)
  }

  def textFrame(matcher: Matcher[String]): Matcher[WebSocketFrame] = beLike {
    case t: TextWebSocketFrame => t.getText must matcher
  }

  def closeFrame(status: Int = 1000): Matcher[WebSocketFrame] = beLike {
    case close: CloseWebSocketFrame => close.getStatusCode must_== status
  }

  def binaryBuffer(text: String) = ChannelBuffers.wrappedBuffer(text.getBytes("utf-8"))

  /**
   * Iteratee getChunks that invokes a callback as soon as it's done.
   */
  def getChunks[A](chunks: List[A], onDone: List[A] => _): Iteratee[A, List[A]] = Cont {
    case Input.El(c) => getChunks(c :: chunks, onDone)
    case Input.EOF =>
      val result = chunks.reverse
      onDone(result)
      Done(result, Input.EOF)
    case Input.Empty => getChunks(chunks, onDone)
  }

  /*
   * Shared tests
   */
  def allowConsumingMessages(webSocket: Application => Promise[List[String]] => Handler) = {
    val consumed = Promise[List[String]]()
    withServer(app => webSocket(app)(consumed)) {
      val result = runWebSocket { (in, out) =>
        Enumerator(new TextWebSocketFrame("a"), new TextWebSocketFrame("b"), new CloseWebSocketFrame(1000, "")) |>>> out
        consumed.future
      }
      result must_== Seq("a", "b")
    }
  }

  def allowSendingMessages(webSocket: Application => List[String] => Handler) = {
    withServer(app => webSocket(app)(List("a", "b"))) {
      val frames = runWebSocket { (in, out) =>
        in |>>> Iteratee.getChunks[WebSocketFrame]
      }
      frames must contain(exactly(
        textFrame(be_==("a")),
        textFrame(be_==("b")),
        closeFrame()
      ).inOrder)
    }
  }

  def cleanUpWhenClosed(webSocket: Application => Promise[Boolean] => Handler) = {
    val cleanedUp = Promise[Boolean]()
    withServer(app => webSocket(app)(cleanedUp)) {
      runWebSocket { (in, out) =>
        out.run
        cleanedUp.future
      } must beTrue
    }
  }

  def closeWhenTheConsumerIsDone(webSocket: Application => Handler) = {
    withServer(app => webSocket(app)) {
      val frames = runWebSocket { (in, out) =>
        Enumerator[WebSocketFrame](new TextWebSocketFrame("foo")) |>> out
        in |>>> Iteratee.getChunks[WebSocketFrame]
      }
      frames must contain(exactly(
        closeFrame()
      ))
    }
  }

  def allowRejectingTheWebSocketWithAResult(webSocket: Application => Int => Handler) = {
    withServer(app => webSocket(app)(FORBIDDEN)) {
      implicit val port = testServerPort
      await(wsUrl("/stream").withHeaders(
        "Upgrade" -> "websocket",
        "Connection" -> "upgrade"
      ).get()).status must_== FORBIDDEN
    }
  }

  "Plays WebSockets" should {
    "allow consuming messages" in allowConsumingMessages { _ => consumed =>
      WebSocket.using[String] { req =>
        (getChunks[String](Nil, consumed.success _), Enumerator.empty)
      }
    }

    "allow sending messages" in allowSendingMessages { _ => messages =>
      WebSocket.using[String] { req =>
        (Iteratee.ignore, Enumerator.enumerate(messages) >>> Enumerator.eof)
      }
    }

    "close when the consumer is done" in closeWhenTheConsumerIsDone { _ =>
      WebSocket.using[String] { req =>
        (Iteratee.head, Enumerator.empty)
      }
    }

    "clean up when closed" in cleanUpWhenClosed { _ => cleanedUp =>
      WebSocket.using[String] { req =>
        (Iteratee.ignore, Enumerator.empty[String].onDoneEnumerating(cleanedUp.success(true)))
      }
    }

    "allow rejecting a websocket with a result" in allowRejectingTheWebSocketWithAResult { _ => statusCode =>
      WebSocket.tryAccept[String] { req =>
        Future.successful(Left(Results.Status(statusCode)))
      }
    }

    "allow handling a WebSocket with an actor" in {

      "allow consuming messages" in allowConsumingMessages { implicit app => consumed =>
        WebSocket.acceptWithActor[String, String] { req => out =>
          Props(new Actor() {
            var messages = List.empty[String]
            def receive = {
              case msg: String =>
                messages = msg :: messages
            }
            override def postStop() = {
              consumed.success(messages.reverse)
            }
          })
        }
      }

      "allow sending messages" in allowSendingMessages { implicit app => messages =>
        WebSocket.acceptWithActor[String, String] { req => out =>
          Props(new Actor() {
            messages.foreach { msg =>
              out ! msg
            }
            out ! PoisonPill
            def receive = PartialFunction.empty
          })
        }
      }

      "close when the consumer is done" in closeWhenTheConsumerIsDone { implicit app =>
      WebSocket.acceptWithActor[String, String] { req => out =>
          Props(new Actor() {
            out ! PoisonPill
            def receive = PartialFunction.empty
          })
        }
      }

      "clean up when closed" in cleanUpWhenClosed { implicit app => cleanedUp =>
        WebSocket.acceptWithActor[String, String] { req => out =>
          Props(new Actor() {
            def receive = PartialFunction.empty
            override def postStop() = {
              cleanedUp.success(true)
            }
          })
        }
      }

      "allow rejecting a websocket with a result" in allowRejectingTheWebSocketWithAResult { implicit app => statusCode =>
        WebSocket.tryAcceptWithActor[String, String] { req =>
          Future.successful(Left(Results.Status(statusCode)))
        }
      }

      "aggregate text frames" in {
        val consumed = Promise[List[String]]()
        withServer(app => WebSocket.using[String] { req =>
          (getChunks[String](Nil, consumed.success _), Enumerator.empty)
        }) {
          val result = runWebSocket { (in, out) =>
            Enumerator(
              new TextWebSocketFrame("first"),
              new TextWebSocketFrame(false, 0, "se"),
              new ContinuationWebSocketFrame(false, 0, "co"),
              new ContinuationWebSocketFrame(true, 0, "nd"),
              new TextWebSocketFrame("third"),
              new CloseWebSocketFrame(1000, "")) |>>> out
            consumed.future
          }
          result must_== Seq("first", "second", "third")
        }

      }

      "aggregate binary frames" in {
        val consumed = Promise[List[Array[Byte]]]()

        withServer(app => WebSocket.using[Array[Byte]] { req =>
          (getChunks[Array[Byte]](Nil, consumed.success _), Enumerator.empty)
        }) {
          val result = runWebSocket { (in, out) =>
            Enumerator(
              new BinaryWebSocketFrame(binaryBuffer("first")),
              new BinaryWebSocketFrame(false, 0, binaryBuffer("se")),
              new ContinuationWebSocketFrame(false, 0, binaryBuffer("co")),
              new ContinuationWebSocketFrame(true, 0, binaryBuffer("nd")),
              new BinaryWebSocketFrame(binaryBuffer("third")),
              new CloseWebSocketFrame(1000, "")) |>>> out
            consumed.future
          }
          result.map(b => b.toSeq) must_== Seq("first".getBytes("utf-8").toSeq, "second".getBytes("utf-8").toSeq, "third".getBytes("utf-8").toSeq)
        }
      }

      "close the websocket when the buffer limit is exceeded" in {
        withServer(app => WebSocket.using[String] { req =>
          (Iteratee.ignore, Enumerator.empty)
        }) {
          val frames = runWebSocket { (in, out) =>
            Enumerator[WebSocketFrame](
              new TextWebSocketFrame(false, 0, "first frame"),
              new ContinuationWebSocketFrame(true, 0, new String(Array.range(1, 65530).map(_ => 'a')))
            ) |>> out
            in |>>> Iteratee.getChunks[WebSocketFrame]
          }
          frames must contain(exactly(
            closeFrame(1009)
          ))
        }
      }

    }

    "allow handling a WebSocket in java" in {

      import play.core.Router.HandlerInvokerFactory
      import play.core.Router.HandlerInvokerFactory._
      import play.mvc.{ WebSocket => JWebSocket, Results => JResults }
      import play.libs.F

      implicit def toHandler[J <: AnyRef](javaHandler: J)(implicit factory: HandlerInvokerFactory[J]): Handler = {
        val invoker = factory.createInvoker(
          javaHandler,
          new HandlerDef(javaHandler.getClass.getClassLoader, "package", "controller", "method", Nil, "GET", "", "/stream")
        )
        invoker.call(javaHandler)
      }

      "allow consuming messages" in allowConsumingMessages { _ => consumed =>
        new JWebSocket[String] {
          @volatile var messages = List.empty[String]
          def onReady(in: In[String], out: Out[String]) = {
            in.onMessage(new F.Callback[String] {
              def invoke(msg: String) = messages = msg :: messages
            })
            in.onClose(new F.Callback0 {
              def invoke() = consumed.success(messages.reverse)
            })
          }
        }
      }

      "allow sending messages" in allowSendingMessages { _ => messages =>
        new JWebSocket[String] {
          def onReady(in: In[String], out: Out[String]) = {
            messages.foreach { msg =>
              out.write(msg)
            }
            out.close()
          }
        }
      }

      "clean up when closed" in cleanUpWhenClosed { _ => cleanedUp =>
        new JWebSocket[String] {
          def onReady(in: In[String], out: Out[String]) = {
            in.onClose(new F.Callback0 {
              def invoke() = cleanedUp.success(true)
            })
          }
        }
      }

      "allow rejecting a websocket with a result" in allowRejectingTheWebSocketWithAResult { _ => statusCode =>
        JWebSocket.reject[String](JResults.status(statusCode))
      }

      "allow handling a websocket with an actor" in allowSendingMessages { _ => messages =>
        JWebSocket.withActor[String](new F.Function[ActorRef, Props]() {
          def apply(out: ActorRef) = {
            Props(new Actor() {
              messages.foreach { msg =>
                out ! msg
              }
              out ! PoisonPill
              def receive = PartialFunction.empty
            })
          }
        })
      }
    }
  }
}
/*
*版权所有(C)2009-2014 Typesafe公司。
*/
包play.it.http.websocket
导入play.api.test_
导入play.api.Application
导入scala.concurrent.{Future,Promise}
导入play.api.mvc.{Handler,Results,WebSocket}
导入play.api.libs.iteratee_
导入java.net.URI
导入org.jboss.netty.handler.codec.http.websocketx_
导入org.specs2.matcher.matcher
导入akka.actor.{ActorRef,毒药,actor,Props}
导入play.mvc.WebSocket.{Out,In}
导入play.core.Router.HandlerDef
导入java.util.concurrent.AtomicReference
导入org.jboss.netty.buffer.ChannelBuffers
对象WebSocketSpec使用WsTestClient扩展了PlaySpecification{
顺序的
def与服务器[A](webSocket:Application=>Handler)(块=>A):A={
val currentApp=新原子引用[FakeApplication]
val app=伪造应用程序(
withRoutes={
大小写(,)=>webSocket(currentApp.get())
}
)
当前应用程序集(应用程序)
正在运行(TestServer(testServerPort,app))(块)
}
def runWebSocket[A](处理程序:(枚举器[WebSocketFrame],迭代器[WebSocketFrame,)]=>Future[A]):A={
val innerResult=承诺[A]()
WebSocketClient{client=>
等待(client.connect(URI.create(“ws://localhost:+testServerPort+”/stream”){(in,out)=>
innerResult.completeWith(处理程序(输入、输出))
})
}
等待(innerResult.future)
}
def textFrame(matcher:matcher[String]):matcher[WebSocketFrame]=beLike{
案例t:TextWebSocketFrame=>t.getText必须匹配
}
def closeFrame(状态:Int=1000):匹配器[WebSocketFrame]=beLike{
案例关闭:CloseWebSocketFrame=>close.getStatusCode必须=状态
}
def binaryBuffer(text:String)=ChannelBuffers.wrappedBuffer(text.getBytes(“utf-8”))
/**
*Iteratee getChunks,它在回调完成后立即调用回调。
*/
def getChunks[A](chunks:List[A],onDone:List[A]=>\ux):Iteratee[A,List[A]]=Cont{
case Input.El(c)=>getChunks(c::chunks,onDone)
案例输入.EOF=>
val result=chunks.reverse
onDone(结果)
完成(结果,输入.EOF)
case Input.Empty=>getChunks(chunks,onDone)
}
/*
*共享测试
*/
def allowConsumingMessages(webSocket:Application=>Promise[List[String]]=>Handler)={
val消费=承诺[列表[字符串]]()
使用服务器(应用程序=>webSocket(应用程序)(已消费)){
val结果=运行WebSocket{(输入,输出)=>
枚举器(新TextWebSocketFrame(“a”)、新TextWebSocketFrame(“b”)、新CloseWebSocketFrame(1000“”))|>>>输出
未来
}
结果必须=序号(“a”、“b”)
}
}
def allowSendingMessages(webSocket:Application=>List[String]=>Handler)={
使用服务器(应用程序=>webSocket(应用程序)(列表(“a”、“b”)){
val frames=runWebSocket{(输入,输出)=>
在|>>>Iteratee.getChunks[WebSocketFrame]中
}
框架必须包含(精确地)(
textFrame(be_==(“a”),
textFrame(be_==(“b”),
closeFrame()
)(按顺序排列)
}
}
def cleanughenclosed(webSocket:Application=>Promise[Boolean]=>Handler)={
val cleanedUp=Promise[Boolean]()
使用服务器(应用=>webSocket(应用)(cleanedUp)){
runWebSocket{(输入,输出)=>
跑出去
清洁的未来
}一定是真的
}
}
def closewhenthesecondsumerisdone(webSocket:Application=>Handler)={
带服务器(应用=>webSocket(应用)){
val frames=runWebSocket{(输入,输出)=>
枚举器[WebSocketFrame](新文本WebSocketFrame(“foo”))|>>out
在|>>>Iteratee.getChunks[WebSocketFrame]中
}
框架必须包含(精确地)(
closeFrame()
))
}
}
def AllowRejectionTheWebSocket与结果(webSocket:Application=>Int=>Handler)={
带服务器(应用=>webSocket(应用)(禁止)){
隐式val-port=testServerPort
wait(wsUrl(“/stream”).withHeaders(
“升级”->“websocket”,
“连接”->“升级”
).get())。状态必须为禁止
}
}
“播放WebSockets”应该{
allowConsumingMessages{{{u=>consumed=>
使用[String]{req=>
(getChunks[String](Nil,consumered.success),枚举数.empty)
}
}
allowSendingMessages{{{u=>messages=>
使用[String]{req=>
(Iteratee.ignore,Enumerator.enumerate(消息)>>>Enumerator.eof)
}
}
CloseWhenSeconSumerisdone中的“消费完成时关闭”{u=>
使用[String]{req=>
(Iteratee.head,Enumerator.empty)
}
}
cleanUpWhenClosed{{{u=>cleanedUp=>
使用[String]{req=>
(Iteratee.ignore,Enumerator.empty[String].ondoneenuming(cleanedUp.success(true)))
}
}
AllowRejectionTheWebSocket中的“允许拒绝带有结果的websocket”,结果为{{u=>statusCode=>
WebSocket.tryaaccept[String]{req=>
Future.successful(左(Results.Status(statusCode)))
}
}
中的“允许使用参与者处理WebSocket”{
allowConsumingMessages{implicit app=>consumed=>
acceptWithActor[String,String]{req=>out=>
道具(新动作)
/*
 * Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
 */
package play.it.http.websocket

import play.api.test._
import play.api.Application
import scala.concurrent.{Future, Promise}
import play.api.mvc.{Handler, Results, WebSocket}
import play.api.libs.iteratee._
import java.net.URI
import org.jboss.netty.handler.codec.http.websocketx._
import org.specs2.matcher.Matcher
import akka.actor.{ActorRef, PoisonPill, Actor, Props}
import play.mvc.WebSocket.{Out, In}
import play.core.Router.HandlerDef
import java.util.concurrent.atomic.AtomicReference
import org.jboss.netty.buffer.ChannelBuffers

object WebSocketSpec extends PlaySpecification with WsTestClient {

  sequential

  def withServer[A](webSocket: Application => Handler)(block: => A): A = {
    val currentApp = new AtomicReference[FakeApplication]
    val app = FakeApplication(
      withRoutes = {
        case (_, _) => webSocket(currentApp.get())
      }
    )
    currentApp.set(app)
    running(TestServer(testServerPort, app))(block)
  }

  def runWebSocket[A](handler: (Enumerator[WebSocketFrame], Iteratee[WebSocketFrame, _]) => Future[A]): A = {
    val innerResult = Promise[A]()
    WebSocketClient { client =>
      await(client.connect(URI.create("ws://localhost:" + testServerPort + "/stream")) { (in, out) =>
        innerResult.completeWith(handler(in, out))
      })
    }
    await(innerResult.future)
  }

  def textFrame(matcher: Matcher[String]): Matcher[WebSocketFrame] = beLike {
    case t: TextWebSocketFrame => t.getText must matcher
  }

  def closeFrame(status: Int = 1000): Matcher[WebSocketFrame] = beLike {
    case close: CloseWebSocketFrame => close.getStatusCode must_== status
  }

  def binaryBuffer(text: String) = ChannelBuffers.wrappedBuffer(text.getBytes("utf-8"))

  /**
   * Iteratee getChunks that invokes a callback as soon as it's done.
   */
  def getChunks[A](chunks: List[A], onDone: List[A] => _): Iteratee[A, List[A]] = Cont {
    case Input.El(c) => getChunks(c :: chunks, onDone)
    case Input.EOF =>
      val result = chunks.reverse
      onDone(result)
      Done(result, Input.EOF)
    case Input.Empty => getChunks(chunks, onDone)
  }

  /*
   * Shared tests
   */
  def allowConsumingMessages(webSocket: Application => Promise[List[String]] => Handler) = {
    val consumed = Promise[List[String]]()
    withServer(app => webSocket(app)(consumed)) {
      val result = runWebSocket { (in, out) =>
        Enumerator(new TextWebSocketFrame("a"), new TextWebSocketFrame("b"), new CloseWebSocketFrame(1000, "")) |>>> out
        consumed.future
      }
      result must_== Seq("a", "b")
    }
  }

  def allowSendingMessages(webSocket: Application => List[String] => Handler) = {
    withServer(app => webSocket(app)(List("a", "b"))) {
      val frames = runWebSocket { (in, out) =>
        in |>>> Iteratee.getChunks[WebSocketFrame]
      }
      frames must contain(exactly(
        textFrame(be_==("a")),
        textFrame(be_==("b")),
        closeFrame()
      ).inOrder)
    }
  }

  def cleanUpWhenClosed(webSocket: Application => Promise[Boolean] => Handler) = {
    val cleanedUp = Promise[Boolean]()
    withServer(app => webSocket(app)(cleanedUp)) {
      runWebSocket { (in, out) =>
        out.run
        cleanedUp.future
      } must beTrue
    }
  }

  def closeWhenTheConsumerIsDone(webSocket: Application => Handler) = {
    withServer(app => webSocket(app)) {
      val frames = runWebSocket { (in, out) =>
        Enumerator[WebSocketFrame](new TextWebSocketFrame("foo")) |>> out
        in |>>> Iteratee.getChunks[WebSocketFrame]
      }
      frames must contain(exactly(
        closeFrame()
      ))
    }
  }

  def allowRejectingTheWebSocketWithAResult(webSocket: Application => Int => Handler) = {
    withServer(app => webSocket(app)(FORBIDDEN)) {
      implicit val port = testServerPort
      await(wsUrl("/stream").withHeaders(
        "Upgrade" -> "websocket",
        "Connection" -> "upgrade"
      ).get()).status must_== FORBIDDEN
    }
  }

  "Plays WebSockets" should {
    "allow consuming messages" in allowConsumingMessages { _ => consumed =>
      WebSocket.using[String] { req =>
        (getChunks[String](Nil, consumed.success _), Enumerator.empty)
      }
    }

    "allow sending messages" in allowSendingMessages { _ => messages =>
      WebSocket.using[String] { req =>
        (Iteratee.ignore, Enumerator.enumerate(messages) >>> Enumerator.eof)
      }
    }

    "close when the consumer is done" in closeWhenTheConsumerIsDone { _ =>
      WebSocket.using[String] { req =>
        (Iteratee.head, Enumerator.empty)
      }
    }

    "clean up when closed" in cleanUpWhenClosed { _ => cleanedUp =>
      WebSocket.using[String] { req =>
        (Iteratee.ignore, Enumerator.empty[String].onDoneEnumerating(cleanedUp.success(true)))
      }
    }

    "allow rejecting a websocket with a result" in allowRejectingTheWebSocketWithAResult { _ => statusCode =>
      WebSocket.tryAccept[String] { req =>
        Future.successful(Left(Results.Status(statusCode)))
      }
    }

    "allow handling a WebSocket with an actor" in {

      "allow consuming messages" in allowConsumingMessages { implicit app => consumed =>
        WebSocket.acceptWithActor[String, String] { req => out =>
          Props(new Actor() {
            var messages = List.empty[String]
            def receive = {
              case msg: String =>
                messages = msg :: messages
            }
            override def postStop() = {
              consumed.success(messages.reverse)
            }
          })
        }
      }

      "allow sending messages" in allowSendingMessages { implicit app => messages =>
        WebSocket.acceptWithActor[String, String] { req => out =>
          Props(new Actor() {
            messages.foreach { msg =>
              out ! msg
            }
            out ! PoisonPill
            def receive = PartialFunction.empty
          })
        }
      }

      "close when the consumer is done" in closeWhenTheConsumerIsDone { implicit app =>
      WebSocket.acceptWithActor[String, String] { req => out =>
          Props(new Actor() {
            out ! PoisonPill
            def receive = PartialFunction.empty
          })
        }
      }

      "clean up when closed" in cleanUpWhenClosed { implicit app => cleanedUp =>
        WebSocket.acceptWithActor[String, String] { req => out =>
          Props(new Actor() {
            def receive = PartialFunction.empty
            override def postStop() = {
              cleanedUp.success(true)
            }
          })
        }
      }

      "allow rejecting a websocket with a result" in allowRejectingTheWebSocketWithAResult { implicit app => statusCode =>
        WebSocket.tryAcceptWithActor[String, String] { req =>
          Future.successful(Left(Results.Status(statusCode)))
        }
      }

      "aggregate text frames" in {
        val consumed = Promise[List[String]]()
        withServer(app => WebSocket.using[String] { req =>
          (getChunks[String](Nil, consumed.success _), Enumerator.empty)
        }) {
          val result = runWebSocket { (in, out) =>
            Enumerator(
              new TextWebSocketFrame("first"),
              new TextWebSocketFrame(false, 0, "se"),
              new ContinuationWebSocketFrame(false, 0, "co"),
              new ContinuationWebSocketFrame(true, 0, "nd"),
              new TextWebSocketFrame("third"),
              new CloseWebSocketFrame(1000, "")) |>>> out
            consumed.future
          }
          result must_== Seq("first", "second", "third")
        }

      }

      "aggregate binary frames" in {
        val consumed = Promise[List[Array[Byte]]]()

        withServer(app => WebSocket.using[Array[Byte]] { req =>
          (getChunks[Array[Byte]](Nil, consumed.success _), Enumerator.empty)
        }) {
          val result = runWebSocket { (in, out) =>
            Enumerator(
              new BinaryWebSocketFrame(binaryBuffer("first")),
              new BinaryWebSocketFrame(false, 0, binaryBuffer("se")),
              new ContinuationWebSocketFrame(false, 0, binaryBuffer("co")),
              new ContinuationWebSocketFrame(true, 0, binaryBuffer("nd")),
              new BinaryWebSocketFrame(binaryBuffer("third")),
              new CloseWebSocketFrame(1000, "")) |>>> out
            consumed.future
          }
          result.map(b => b.toSeq) must_== Seq("first".getBytes("utf-8").toSeq, "second".getBytes("utf-8").toSeq, "third".getBytes("utf-8").toSeq)
        }
      }

      "close the websocket when the buffer limit is exceeded" in {
        withServer(app => WebSocket.using[String] { req =>
          (Iteratee.ignore, Enumerator.empty)
        }) {
          val frames = runWebSocket { (in, out) =>
            Enumerator[WebSocketFrame](
              new TextWebSocketFrame(false, 0, "first frame"),
              new ContinuationWebSocketFrame(true, 0, new String(Array.range(1, 65530).map(_ => 'a')))
            ) |>> out
            in |>>> Iteratee.getChunks[WebSocketFrame]
          }
          frames must contain(exactly(
            closeFrame(1009)
          ))
        }
      }

    }

    "allow handling a WebSocket in java" in {

      import play.core.Router.HandlerInvokerFactory
      import play.core.Router.HandlerInvokerFactory._
      import play.mvc.{ WebSocket => JWebSocket, Results => JResults }
      import play.libs.F

      implicit def toHandler[J <: AnyRef](javaHandler: J)(implicit factory: HandlerInvokerFactory[J]): Handler = {
        val invoker = factory.createInvoker(
          javaHandler,
          new HandlerDef(javaHandler.getClass.getClassLoader, "package", "controller", "method", Nil, "GET", "", "/stream")
        )
        invoker.call(javaHandler)
      }

      "allow consuming messages" in allowConsumingMessages { _ => consumed =>
        new JWebSocket[String] {
          @volatile var messages = List.empty[String]
          def onReady(in: In[String], out: Out[String]) = {
            in.onMessage(new F.Callback[String] {
              def invoke(msg: String) = messages = msg :: messages
            })
            in.onClose(new F.Callback0 {
              def invoke() = consumed.success(messages.reverse)
            })
          }
        }
      }

      "allow sending messages" in allowSendingMessages { _ => messages =>
        new JWebSocket[String] {
          def onReady(in: In[String], out: Out[String]) = {
            messages.foreach { msg =>
              out.write(msg)
            }
            out.close()
          }
        }
      }

      "clean up when closed" in cleanUpWhenClosed { _ => cleanedUp =>
        new JWebSocket[String] {
          def onReady(in: In[String], out: Out[String]) = {
            in.onClose(new F.Callback0 {
              def invoke() = cleanedUp.success(true)
            })
          }
        }
      }

      "allow rejecting a websocket with a result" in allowRejectingTheWebSocketWithAResult { _ => statusCode =>
        JWebSocket.reject[String](JResults.status(statusCode))
      }

      "allow handling a websocket with an actor" in allowSendingMessages { _ => messages =>
        JWebSocket.withActor[String](new F.Function[ActorRef, Props]() {
          def apply(out: ActorRef) = {
            Props(new Actor() {
              messages.foreach { msg =>
                out ! msg
              }
              out ! PoisonPill
              def receive = PartialFunction.empty
            })
          }
        })
      }
    }
  }
}
import org.specs2.mutable._
import play.api.test.Helpers._
import play.api.test._

class ApplicationSpec extends Specification {

"Application" should {

    "work" in {
      running(TestServer(9000)) {

        val clientInteraction = new ClientInteraction()

        clientInteraction.client.connectBlocking()
        clientInteraction.client.send("Hello Server")

        eventually {
          clientInteraction.messages.contains("Hello Client")
        }
      }  
    }
  }
}
import java.net.URI
import org.java_websocket.client.WebSocketClient
import org.java_websocket.drafts.Draft_17
import org.java_websocket.handshake.ServerHandshake
import collection.JavaConversions._
import scala.collection.mutable.ListBuffer

class ClientInteraction {

  val messages = ListBuffer[String]()

  val client = new     WebSocketClient(URI.create("ws://localhost:9000/wsWithActor"),
    new Draft_17(), Map("HeaderKey1" -> "HeaderValue1"), 0) {

    def onError(p1: Exception) {
      println("onError")
    }

    def onMessage(message: String) {
      messages += message
      println("onMessage, message = " + message)
    }

    def onClose(code: Int, reason: String, remote: Boolean) {                     
      println("onClose")
    }

    def onOpen(handshakedata: ServerHandshake) {
      println("onOpen")
    }
  }
}
libraryDependencies ++= Seq(
  ws,
  "org.java-websocket" % "Java-WebSocket" % "1.3.0",
  "org.specs2" %% "specs2-core" % "3.7" % "test"
)
import java.net.URI
import io.backchat.hookup._
import org.specs2.mutable._
import play.api.test._
import scala.collection.mutable.ListBuffer

class ApplicationSpec extends Specification {

  "Application" should {

    "Test websocket" in new WithServer(port = 9000) {
      val hookupClient = new DefaultHookupClient(HookupClientConfig(URI.create("ws://localhost:9000/ws"))) {
        val messages = ListBuffer[String]()

        def receive = {
          case Connected =>
            println("Connected")

          case Disconnected(_) =>
            println("Disconnected")

          case JsonMessage(json) =>
            println("Json message = " + json)

          case TextMessage(text) =>
            messages += text
            println("Text message = " + text)
        }

        connect() onSuccess {
          case Success => send("Hello Server")
        }
      }

      hookupClient.messages.contains("Hello Client") must beTrue.eventually
    }

  }

}