Skip to content

Commit

Permalink
Merge pull request #94 from agourlay/0.9.1
Browse files Browse the repository at this point in the history
0.9.1
  • Loading branch information
agourlay committed Sep 7, 2016
2 parents 742a494 + 6253daa commit c05c25f
Show file tree
Hide file tree
Showing 43 changed files with 1,023 additions and 504 deletions.
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,22 @@ save_body_path("city" -> "batman-city")
- asserting value in ```session```

```scala
session_contains("favorite-superhero" "Batman")
session_value("favorite-superhero").is("Batman")
```

- asserting JSON value in ```session```

```scala
session_value("my-json-response").asJson.path("a.b.c").ignoring("d").is(...)
```


- asserting existence of value in ```session```

```scala
session_value("favorite-superhero").isPresent
session_value("favorite-superhero").isAbsence
````

### Wrapper steps

Expand Down Expand Up @@ -641,7 +654,7 @@ Most built-in steps can use placeholders in their arguments, those will be autom
```scala
Given I save("favorite-superhero" "Batman")

Then assert session_contains("favorite-superhero" "Batman")
Then assert session_value("favorite-superhero").is("Batman")

When I get("http://localhost:8080/superheroes/<favorite-superhero>")

Expand All @@ -658,7 +671,7 @@ Then assert body.is(

And I save_body_path("city" -> "batman-city")

Then assert session_contains("batman-city" "Gotham city")
Then assert session_value("batman-city").is("Gotham city")

Then assert body.is(
"""
Expand Down
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,22 @@ ScalariformKeys.preferences := ScalariformKeys.preferences.value

libraryDependencies ++= {
val scalaTestV = "3.0.0"
val akkaHttpV = "2.4.9"
val akkaHttpV = "2.4.10"
val catsV = "0.7.2"
val logbackV = "1.1.7"
val parboiledV = "2.1.3"
val akkaSseV = "1.10.0"
val scalaCheckV = "1.13.2"
val sangriaCirceV = "0.5.0"
val circeVersion = "0.5.0"
val circeVersion = "0.5.1"
val sangriaV = "0.7.3"
val fansiV = "0.2.0"
val akkaHttpCirce = "1.9.0"
val catsScalaTest = "1.4.0"
val ficusV = "1.1.2"
Seq(
"com.typesafe.akka" %% "akka-http-core" % akkaHttpV
,"com.typesafe.akka" %% "akka-http-experimental" % akkaHttpV
,"de.heikoseeberger" %% "akka-sse" % akkaSseV
,"org.typelevel" %% "cats-macros" % catsV
,"org.typelevel" %% "cats-core" % catsV
Expand All @@ -66,7 +67,6 @@ libraryDependencies ++= {
,"io.circe" %% "circe-parser" % circeVersion
//,"io.circe" %% "circe-optics" % circeVersion Remove if cursors are used instead or lenses for JsonPath.
,"de.heikoseeberger" %% "akka-http-circe" % akkaHttpCirce % "test"
,"com.typesafe.akka" %% "akka-http-experimental" % akkaHttpV % "test"
,"com.ironcorelabs" %% "cats-scalatest" % catsScalaTest % "test"
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class DeckOfCard extends CornichonFeature with DeckSteps {
}

trait DeckSteps {
this : CornichonFeature =>

def verify_hand_score = AssertStep(
title = "value of 'c1' with 'c2' is 'score'",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package com.github.agourlay.cornichon

import java.util.concurrent.Executors

import akka.stream.ActorMaterializer
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.dsl.Dsl
import com.github.agourlay.cornichon.http.client.HttpClient
import com.github.agourlay.cornichon.http.{ HttpDsl, HttpService }
import com.github.agourlay.cornichon.json.JsonDsl
import com.github.agourlay.cornichon.resolver.{ Mapper, Resolver }

import scala.concurrent.ExecutionContext
Expand All @@ -13,7 +16,7 @@ import com.typesafe.config.ConfigFactory
import net.ceedubs.ficus.Ficus._
import net.ceedubs.ficus.readers.ArbitraryTypeReader._

trait CornichonFeature extends HttpDsl with ScalatestIntegration {
trait CornichonFeature extends HttpDsl with JsonDsl with Dsl with ScalatestIntegration {
import com.github.agourlay.cornichon.CornichonFeature._

lazy val config = ConfigFactory.load().as[Config]("cornichon")
Expand All @@ -24,7 +27,7 @@ trait CornichonFeature extends HttpDsl with ScalatestIntegration {
protected var beforeEachScenario: Seq[Step] = Nil
protected var afterEachScenario: Seq[Step] = Nil

private lazy val (globalClient, ec) = globalRuntime
private lazy val (globalClient, ec, _, _) = globalRuntime
private lazy val engine = Engine.withStepTitleResolver(resolver, ec)

lazy val requestTimeout = config.requestTimeout
Expand Down Expand Up @@ -72,6 +75,7 @@ private object CornichonFeature {
import com.github.agourlay.cornichon.http.client.AkkaHttpClient

implicit private lazy val system = ActorSystem("cornichon-actor-system")
implicit private lazy val mat = ActorMaterializer()
implicit private lazy val ec = ExecutionContext.fromExecutorService(Executors.newCachedThreadPool)

private lazy val client: HttpClient = new AkkaHttpClient()
Expand All @@ -87,13 +91,14 @@ private object CornichonFeature {
if (safePassInRow.get() == 3) {
client.shutdown().map { _
ec.shutdown()
mat.shutdown()
system.terminate()
}
}
} else if (safePassInRow.get() > 0) safePassInRow.decrementAndGet()
}

lazy val globalRuntime = (client, ec)
lazy val globalRuntime = (client, ec, system, mat)
def reserveGlobalRuntime(): Unit = registeredUsage.incrementAndGet()
def releaseGlobalRuntime(): Unit = registeredUsage.decrementAndGet()
}
21 changes: 17 additions & 4 deletions src/main/scala/com/github/agourlay/cornichon/core/Session.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.github.agourlay.cornichon.core

import cats.Show
import cats.data.Xor
import com.github.agourlay.cornichon.json.{ JsonPath, NotStringFieldError }
import com.github.agourlay.cornichon.json.CornichonJson._
import com.github.agourlay.cornichon.util.Formats
import com.github.agourlay.cornichon.util.ShowInstances._
import io.circe.Json

import scala.collection.immutable.HashMap
Expand All @@ -24,9 +26,13 @@ case class Session(content: Map[String, Vector[String]]) {

}

def get(key: String, stackingIndice: Option[Int] = None): String = getOpt(key, stackingIndice).getOrElse(throw KeyNotFoundInSession(key, this))
def get(key: String, stackingIndice: Option[Int] = None): String =
getOpt(key, stackingIndice).getOrElse(throw KeyNotFoundInSession(key, stackingIndice, this))

def getXor(key: String, stackingIndice: Option[Int] = None) = Xor.fromOption(getOpt(key, stackingIndice), KeyNotFoundInSession(key, this))
def get(sessionKey: SessionKey): String = get(sessionKey.name, sessionKey.index)

def getXor(key: String, stackingIndice: Option[Int] = None) =
Xor.fromOption(getOpt(key, stackingIndice), KeyNotFoundInSession(key, stackingIndice, this))

def getJsonXor(key: String, stackingIndice: Option[Int] = None, path: String = JsonPath.root): Xor[CornichonError, Json] =
for {
Expand Down Expand Up @@ -72,12 +78,19 @@ case class Session(content: Map[String, Vector[String]]) {

object Session {
def newSession = Session(HashMap.empty)
implicit val showSession = new Show[Session] {
def show(s: Session) = s.prettyPrint
}
}

case class SessionKey(name: String, index: Option[Int] = None) {
def atIndex(index: Int) = copy(index = Some(index))
}

case class EmptyKeyException(s: Session) extends CornichonError {
val msg = s"key value can not be empty - session is \n${s.prettyPrint}"
}

case class KeyNotFoundInSession(key: String, s: Session) extends CornichonError {
val msg = s"key '$key' can not be found in session : \n${s.prettyPrint}"
case class KeyNotFoundInSession(key: String, indice: Option[Int], s: Session) extends CornichonError {
val msg = s"key '$key'${indice.fold("")(i s" at indice '$i'")} can not be found in session \n${s.prettyPrint}"
}
30 changes: 0 additions & 30 deletions src/main/scala/com/github/agourlay/cornichon/dsl/Assertions.scala

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.agourlay.cornichon.dsl

import com.github.agourlay.cornichon.core.Session

import scala.concurrent.Future

trait BlockScopedResource {

val sessionTarget: String

val openingTitle: String
val closingTitle: String

def startResource(): Future[Unit]

def stopResource(): Future[Unit]

def resourceResults(): Session
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.agourlay.cornichon.dsl

import com.github.agourlay.cornichon.steps.regular.AssertStep

trait CollectionAssertionSyntax[A, B] {
def is(expected: A*): AssertStep[Iterable[B]]
def hasSize(expected: Int): AssertStep[Int]
def inOrder: CollectionAssertionSyntax[A, B]
def contain(elements: A*): AssertStep[Boolean]
}

10 changes: 8 additions & 2 deletions src/main/scala/com/github/agourlay/cornichon/dsl/DataTable.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.github.agourlay.cornichon.dsl

import com.github.agourlay.cornichon.core.CornichonError
import io.circe.{ Json, JsonObject }
import org.parboiled2._

import com.github.agourlay.cornichon.json.CornichonJson._

import scala.util.{ Failure, Success }
Expand Down Expand Up @@ -92,4 +92,10 @@ trait StringHeaderParserSupport extends StringBuilding {
}

def Unicode = rule { 'u' ~ capture(4 times CharPredicate.HexDigit) ~> (Integer.parseInt(_, 16)) }
}
}

case class DataTableError(error: Throwable, input: String) extends CornichonError {
val msg = s"error thrown '${error.getMessage}' while parsing data table $input"
}

case class DataTableParseError(msg: String) extends CornichonError
25 changes: 9 additions & 16 deletions src/main/scala/com/github/agourlay/cornichon/dsl/Dsl.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.github.agourlay.cornichon.dsl

import cats.Show
import com.github.agourlay.cornichon.CornichonFeature
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.core.{ Scenario ScenarioDef }
import com.github.agourlay.cornichon.dsl.CoreAssertion.{ SessionAssertion, SessionValuesAssertion }
import com.github.agourlay.cornichon.steps.regular._
import com.github.agourlay.cornichon.steps.wrapped._
import com.github.agourlay.cornichon.util.Formats._
Expand All @@ -13,6 +14,7 @@ import scala.language.dynamics
import scala.concurrent.duration.{ Duration, FiniteDuration }

trait Dsl extends ShowInstances {
this: CornichonFeature

def Feature(name: String, ignored: Boolean = false) =
BodyElementCollector[Scenario, FeatureDef](scenarios FeatureDef(name, scenarios, ignored))
Expand Down Expand Up @@ -103,21 +105,12 @@ trait Dsl extends ShowInstances {
effect = s s.removeKey(key)
)

def session_contains(input: (String, String)): AssertStep[String] = session_contains(input._1, input._2)
def session_value(key: String) = SessionAssertion(resolver, key)

def session_value(key: String) = SessionAssertion(key)
def show_session = DebugStep(s s"Session content is\n${s.prettyPrint}")

def session_values(k1: String, k2: String) = SessionValuesAssertion(k1, k2)

def session_contains(key: String, value: String) =
AssertStep(
title = s"session key '$key' equals '$value'",
action = s GenericAssertion(value, s.get(key))
)

def show_session = DebugStep(s s"Session content : \n${s.prettyPrint}")

def show_session(key: String, transform: String String = identity) = DebugStep(s s"Session content for key '$key' is '${transform(s.get(key))}'")
def show_session(key: String, transform: String String = identity) =
DebugStep(s s"Session content for key '$key' is\n${transform(s.get(key))}")

def print_step(message: String) = DebugStep(s message)
}
Expand All @@ -139,7 +132,7 @@ object Dsl {
)
}

def from_session_step[A](key: String, expected: Session A, mapValue: (Session, String) A, title: String) =
def from_session_step[A: Show](key: SessionKey, expected: Session A, mapValue: (Session, String) A, title: String) =
AssertStep(
title,
s GenericAssertion(
Expand All @@ -148,7 +141,7 @@ object Dsl {
)
)

def from_session_detail_step[A](key: String, expected: Session A, mapValue: (Session, String) (A, A String), title: String) =
def from_session_detail_step[A: Show](key: SessionKey, expected: Session A, mapValue: (Session, String) (A, A String), title: String) =
AssertStep(
title,
s {
Expand Down
11 changes: 0 additions & 11 deletions src/main/scala/com/github/agourlay/cornichon/dsl/DslErrors.scala

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.github.agourlay.cornichon.dsl

import com.github.agourlay.cornichon.core.SessionKey
import com.github.agourlay.cornichon.steps.regular.{ AssertStep, CustomMessageAssertion, GenericAssertion }
import com.github.agourlay.cornichon.dsl.SessionAssertionErrors._
import com.github.agourlay.cornichon.json.JsonAssertions.JsonAssertion
import com.github.agourlay.cornichon.resolver.Resolver
import com.github.agourlay.cornichon.util.ShowInstances._

case class SessionAssertion(
private val resolver: Resolver,
private val key: String,
private val indice: Option[Int] = None
) {

def atIndex(indice: Int) = copy(indice = Some(indice))

def is(expected: String) = AssertStep(
title = s"session key '$key' is '$expected'",
action = s GenericAssertion(expected, s.get(key, indice))
)

def isEqualToSessionValue(other: String, indice: Option[Int] = None) = AssertStep(
title = s"content of session key '$key' is equal to the content of key '$other'",
action = s GenericAssertion(s.get(key), s.get(other))
)

def isPresent = AssertStep[Boolean](
title = s"session contains key '$key'",
action = s {
val predicate = s.getOpt(key, indice).isDefined
CustomMessageAssertion(true, predicate, keyIsAbsentError(key, s.prettyPrint))
}
)

def isAbsent = AssertStep(
title = s"session does not contain key '$key'",
action = s CustomMessageAssertion(None, s.getOpt(key, indice), keyIsPresentError(key))
)

def asJson = JsonAssertion(resolver, SessionKey(key))

}
Loading

0 comments on commit c05c25f

Please sign in to comment.