All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.specs2.specification.FragmentsBuilder.scala Maven / Gradle / Ivy

package org.specs2
package specification

import execute._
import main._
import internal.scalaz.Scalaz._
import control.Functions._

/**
 * This trait provides function to create specification Fragments:
 *  - a SpecStart fragment with title
 *  - a Text fragment with text
 *  - an Example fragment with a body
 *  - a group of fragments (when including another specification for example)
 * 
 */
private[specs2]
trait FragmentsBuilder extends RegexSteps with ExamplesFactory { outer =>

  /**
   * Methods for chaining fragments
   */

  /** @return a Fragments object from a single Fragment */
  implicit def fragments(f: =>Fragment): Fragments = Fragments.createList(f)
  implicit def fragmentFragments(f: =>Fragment): FragmentsFragment = new FragmentsFragment(fragments(f))
  implicit def fragmentsFragments(fs: =>Fragments): FragmentsFragment = new FragmentsFragment(fs)
  /** reverse conversion from a Fragment containing a Fragments object to the Fragments object*/
  implicit def fragmentsFragmentToFragments(fs: FragmentsFragment): Fragments = fs.fragments

  /**
   * Methods for creating fragments
   */
  /** @return a Fragments object from a string */
  implicit def textStart(s: String): Fragments = Fragments.createList(Text(s))
  /** @return create a Text Fragment from a string and allow it to be chained to other fragments */
  implicit def textFragment(s: String): FragmentsFragment = textStart(s)
  /**
   * This method allows to add a title to the Specification. It can be used as an operation on a String:
   * `"spec title".title`
   *
   * @return a Fragments object from a string, with the given title 
   */
  implicit def title(s: String): SpecTitle = new SpecTitle(s)
  class SpecTitle(name: String) {
    def title = Fragments(SpecName(name))
  }
  /**
   * Example creation
   * @return an Example description from a string, to create a full Example once the body is defined
   */
  implicit def forExample(s: String): ExampleDesc = new ExampleDesc(s)

  /** transient class to hold an example description before creating a full Example */
  class ExampleDesc(s: String) {
    /** @return an Example, using anything that can be translated to a Result, e.g. a Boolean */
	  def ![T : AsResult](t: =>T): Example = exampleFactory.newExample(s, t)
    /** @return an Example, using the example description */
	  def ![T : AsResult](f: String => T): Example = exampleFactory.newExample(s, f(s))
    /** @return an Example which a function using values extracted from the text */
	  def !(gt: GivenThen): Example = exampleFactory.newExample(s, gt)
  }

  /**
   * Arguments creation
   * @return a Fragments object which can be chained with other fragments
   */
  implicit def argumentsFragment(a: Arguments): FragmentsFragment = new Fragments().overrideArgs(a)

  /**
   * Links to other specifications creation
   * 
   * @see org.specs2.UserGuide
   */
  implicit def stringToHtmlLinkFragments(s: String): HtmlLinkFragments = new HtmlLinkFragments(HtmlLink(SpecName(""), s, "", "", ""))
  class HtmlLinkFragments(link: HtmlLink) {
    def ~(s: SpecificationStructure) = outer.link(HtmlLink(s.content.specName, "", link.beforeText), s)
    def ~(p: (String, SpecificationStructure)) = outer.link(HtmlLink(p._2.content.specName, link.beforeText, p._1), p._2)
    def ~(p: (String, SpecificationStructure, String)) = outer.link(HtmlLink(p._2.content.specName, link.beforeText, p._1, p._3), p._2)
    def ~(p: (String, SpecificationStructure, String, String)) = outer.link(HtmlLink(p._2.content.specName, link.beforeText, p._1, p._3, p._4), p._2)
    def ~/(s: SpecificationStructure) = outer.see(HtmlLink(s.content.specName, "", link.beforeText), s)
    def ~/(p: (String, SpecificationStructure)) = outer.see(HtmlLink(p._2.content.specName, link.beforeText, p._1), p._2)
    def ~/(p: (String, SpecificationStructure, String)) = outer.see(HtmlLink(p._2.content.specName, link.beforeText, p._1, p._3), p._2)
    def ~/(p: (String, SpecificationStructure, String, String)) = outer.see(HtmlLink(p._2.content.specName, link.beforeText, p._1, p._3, p._4), p._2)
  }

  implicit def stringToHtmlLinkFragments2(s: String): HtmlLinkFragments2 = new HtmlLinkFragments2(HtmlLink(SpecName(""), s, "", "", ""))
  class HtmlLinkFragments2(link: HtmlLink) {
    def ~(p: (SpecificationStructure, String)) = outer.link(HtmlLink(p._1.content.specName, "", link.beforeText, p._2), p._1)
    def ~(p: (SpecificationStructure, String, String)) = outer.link(HtmlLink(p._1.content.specName, "", link.beforeText, p._2, p._3), p._1)
    def ~/(p: (SpecificationStructure, String)) = outer.see(HtmlLink(p._1.content.specName, "", link.beforeText, p._2), p._1)
    def ~/(p: (SpecificationStructure, String, String)) = outer.see(HtmlLink(p._1.content.specName, "", link.beforeText, p._2, p._3), p._1)
  }

  /** this implicit allows to call some functions directly on a specification, like 'hide' */
  implicit def specificationStructureToFragments(s: SpecificationStructure): AsFragments = AsFragments(s.content)
  case class AsFragments(fs: Fragments) {
    def hide = fs.hide
    def overrideArgs(args: Arguments) = fs.overrideArgs(args)
  }

  /** create a link directly on a specification */
  def link(s: SpecificationStructure): Fragments                              = link(s.content)
  def link(s: SpecificationStructure, ss: SpecificationStructure*): Fragments = link(s +: ss)
  def link(ss: Seq[SpecificationStructure], dummy: Int = 0): Fragments        = link(ss.map(_.content))
  def link(fs: Fragments): Fragments                                          = link(HtmlLink(fs), fs)
  def link(fs: Fragments, fss: Fragments*): Fragments                         = link(fs +: fss)
  def link(fss: Seq[Fragments]): Fragments                                    = ma(fss.map(link)).sum
  /** create a link directly on a specification, with a given link */
  def link(htmlLink: HtmlLink, s: SpecificationStructure): Fragments          = link(htmlLink, s.content)
  def link(htmlLink: HtmlLink, f: Fragments): Fragments                       = f.linkIs(htmlLink)

  /** create a html link without including the other specification fragments */
  def see(s: SpecificationStructure, ss: SpecificationStructure*): Fragments  = see(s.content, ss.map(_.content):_*)
  def see(ss: Seq[SpecificationStructure], dummy: Int = 0): Fragments         = see(ss.map(_.content))
  def see(s: SpecificationStructure): Fragments                               = see(s.content)
  def see(fs: Fragments): Fragments                                           = see(HtmlLink(fs), fs)
  def see(fs: Fragments, fss: Fragments*): Fragments                          = see(fs +: fss)
  def see(fss: Seq[Fragments]): Fragments                                     = ma(fss.map(see)).sum
  /** create a see-only link directly on a specification, with a given link */
  def see(htmlLink: HtmlLink, s: SpecificationStructure): Fragments            = see(htmlLink, s.content)
  def see(htmlLink: HtmlLink, fs: Fragments): Fragments                        = fs.seeIs(htmlLink)

  /** create markdown links from string + spec identification */
  implicit def specIdentificationMarkdownLink(s: String): SpecIdentificationMarkdownLink = new SpecIdentificationMarkdownLink(s)
  case class SpecIdentificationMarkdownLink(s: String) {
    def markdownLink(specIdentification: SpecIdentification) = specIdentification.markdownLink(s)
  }

  /** transform a scope to a success to be able to create traits containing any variables and usable in any Examples */
  implicit def inScope(s: Scope): Success = Success()
  /** typeclass to transform a Scope to a Result */
  implicit def scopeAsResult[S <: Scope]: AsResult[S] = new AsResult[S] {
    def asResult(t: =>S) = inScope(t)
  }
}

/**
 * This trait can be used to deactivate implicits for building fragments
 */
trait NoFragmentsBuilder extends FragmentsBuilder {
  override def textStart(s: String): Fragments = super.textStart(s)
  override def textFragment(s: String): FragmentsFragment = super.textFragment(s)
  override def title(s: String): SpecTitle = super.title(s)
  override def stringToHtmlLinkFragments(s: String): HtmlLinkFragments = super.stringToHtmlLinkFragments(s)
  override def stringToHtmlLinkFragments2(s: String): HtmlLinkFragments2 = super.stringToHtmlLinkFragments2(s)
  override def specificationStructureToFragments(s: SpecificationStructure): AsFragments = super.specificationStructureToFragments(s)
}

/**
 * Fragments can be chained with the ^ method
 */
private[specs2]
class FragmentsFragment(fs: =>Fragments) {
  def fragments = fs
  def ^(t: String) = fs add Text(t)
  def ^(f: Fragment) = f match {
    case s @ SpecStart(_,_,_) => (fs specTitleIs s.specName).overrideArgs(s.arguments)
    case _                    => fs add f
  }
  def ^(other: Seq[Fragment]) = fs add other

  def ^(other: Fragments) = {
    other match {
      case Fragments(t, m, a, Linked(Some(l), so, h))    => fs add other.fragments
      case Fragments(Some(t), m, a, Linked(None, so, h)) => (fs add other.middle).specTitleIs(t).overrideArgs(a)
      case Fragments(None, m, a, Linked(None, so, h))    => (fs add other.middle).overrideArgs(a)
      case _                                             => fs add other.middle
    }
  }

  def ^(other: FragmentsFragment) = fs add other.fragments
  def ^(a: Arguments) = fs add a

  /** start a given-when-then block */
  def ^[T](step: Given[T]): PreStep[T] = {
    val text = fs.fragments.collect { case t: Text => t.t }.lastOption.getOrElse("A Text must precede a Given object!")
    lazy val extracted = step.extractContext(text)
    def strip(fragments: Fragments) = fragments.map(step.strip)
    new PreStep(() => extracted, new FragmentsFragment(strip(fs)) ^ Step.fromEither(extracted))
  }

}


object FragmentsBuilder extends FragmentsBuilder

import org.specs2.internal.scalaz._
/**
 * Implementation of the Show trait to display Fragments
 */
private[specs2]
trait FragmentsShow {
  implicit object showFragments extends Show[Fragment] {
	  def show(f: Fragment) = f.toString.toList
  }
}

private[specs2]
object FragmentsShow extends FragmentsShow




© 2015 - 2025 Weber Informatics LLC | Privacy Policy