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

net.liftweb.mockweb.WebSpec.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2011 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.liftweb
package mockweb

import jakarta.servlet.http.HttpServletRequest

import scala.xml.NodeSeq

import org.specs2.matcher.XmlMatchers
import org.specs2.mutable._
import org.specs2.execute.Result

import common.{Box,Empty,Full}
import http._
import net.liftweb.json.JsonAST._
import mocks.MockHttpServletRequest


/**
 * This trait provides Lift-specific extensions to the Specification
 * base trait to simplify unit testing of your code. In addition to
 * the Scaladoc, Please see the
 * source to WebSpecSpec.scala for an example of how to use this. This class
 * allows you to optionally configure LiftRules for your spec. LiftRules rules
 * are always used, so if you don't want that to happen you can just use MockWeb directly
 * in your own custom spec and simply omit any LiftRules setup.
 *
 * @param boot defines a method that is called prior to
 * testing to set up the Lift environment. This is where you'll
 * initialize LiftRules, Mapper, etc. The simplest approach
 * is to just point this at your Boostrap.boot method.
 */
abstract class WebSpec(boot : () => Any = () => {}) extends Specification with XmlMatchers {
  /**
   * This is our private spec instance of Liftrules. Everything we run will
   * use this instance instead of the global instance
   */
  private val liftRules = new LiftRules()

  LiftRulesMocker.devTestLiftRulesInstance.doWith(liftRules) {
    boot()
  }

  /**
   * A class that bridges between the description string
   * and classes that provide the Lift-specific wrapping
   * of the Expectation code. The method names here
   * should be self-explanatory. For methods that take a
   * Box[LiftSession], an Empty Box will result in the
   * creation of a new Session.
   */
  class WebSpecBridge (description : String) {
    def withSFor (url : String, session : Box[LiftSession] = Empty, contextPath : String = "") =
      new SessionSpecification(description, url, session, contextPath)

    def withSFor (url : String, session : LiftSession) : SessionSpecification =
      new SessionSpecification(description, url, Box.!!(session), "")

    def withSFor (req : HttpServletRequest) =
      new SessionSpecification(description, req, Empty)

    def withSFor (req : HttpServletRequest, session : Box[LiftSession])  =
      new SessionSpecification(description, req, session)

    def withSFor (req : HttpServletRequest, session : LiftSession) =
      new SessionSpecification(description, req, Box.!!(session))

    def withReqFor (url : String, contextPath : String = "") =
      new ReqSpecification(description, url, contextPath)

    def withReqFor (req : HttpServletRequest) = 
      new ReqSpecification(description, req)

    def withTemplateFor (url : String, session : Box[LiftSession] = Empty, contextPath : String = "") =
      new TemplateSpecification(description, url, session, contextPath)

    def withTemplateFor (url : String, session : LiftSession) =
      new TemplateSpecification(description, url, Box.!!(session), "")

    def withTemplateFor (req : HttpServletRequest) =
      new TemplateSpecification(description, req, Empty)

    def withTemplateFor (req : HttpServletRequest, session : Box[LiftSession])  =
      new TemplateSpecification(description, req, session)

    def withTemplateFor (req : HttpServletRequest, session : LiftSession) =
      new TemplateSpecification(description, req, Box.!!(session))
  }

  /**
   * Converts a String description into a WebSpecBridge that can
   * then be used to set up either an S or Req instance.
   */
  implicit def strToWebSpecBridge (description : String) : WebSpecBridge =
    new WebSpecBridge(description)

  /**
   * A comon trait to provide utility methods for mutating the
   * underlying HttpServletRequest.
   */
  trait ModifiableRequest [T <: ModifiableRequest[T]] {
    // Make sure that our return values are for the supertype, not ModifiableRequest
    self : T =>

    val req : HttpServletRequest

    /**
     * Modifies the request to POST the given request body text. Optionally,
     * you can set the content type (defaults to "text/plain")
     */
    def withPost (text : String, contentType : String = "text/plain") : T =
      withMods { mockReq =>
        mockReq.body = text
        mockReq.contentType = contentType
        mockReq.method = "POST"
      }

    /**
     * Modifies the request to POST the given request body JSON.
     */
    def withPost (jval : JValue) = withMods { mockReq =>
      mockReq.body = jval
      mockReq.method = "POST"
    }

    /**
     * Modifies the request to POST the given request body XML.
     */    
    def withPost (node : NodeSeq) = withMods { mockReq =>
      mockReq.body = node
      mockReq.method = "POST"
    }

    /**
     * Modifies the request to PUT the given request body text. Optionally,
     * you can set the content type (defaults to "text/plain")
     */
    def withPut (text : String, contentType : String = "text/plain") =
      withMods { mockReq =>
        mockReq.body = text
        mockReq.contentType = contentType
        mockReq.method = "PUT"
      }

    /**
     * Modifies the request to PUT the given request body JSON.
     */
    def withPut (jval : JValue) = withMods { mockReq =>
      mockReq.body = jval
      mockReq.method = "PUT"
    }

    /**
     * Modifies the request to PUT the given request body XML.
     */    
    def withPut (node : NodeSeq) = withMods { mockReq =>
      mockReq.body = node
      mockReq.method = "PUT"
    }

    /**
     * Allows you to specify your own modification function for the servlet request
     * prior to initialization.
     */
    def withMods (f : MockHttpServletRequest => Unit) : T = req match {
      case r : MockHttpServletRequest => f(r); this
      case _ => throw new IllegalArgumentException("We can only mutate MockHttpServletRequest instances")
    }
  }

  /**
   * This class provides a wrapper to test methods that require an
   * initialized S.
   */
  class SessionSpecification (description : String, 
                              val req : HttpServletRequest, 
                              session : Box[LiftSession]) extends ModifiableRequest[SessionSpecification] {
    def this (description : String, url : String, session : Box[LiftSession], contextPath : String) = {
      this(description, new MockHttpServletRequest(url, contextPath), session)
    }

    def in(expectations : => Result) = {
      addFragments(
        fragmentFactory.example(description, {
          LiftRulesMocker.devTestLiftRulesInstance.doWith(liftRules) {
            MockWeb.useLiftRules.doWith(true) {
              MockWeb.testS(req, session) {
                expectations
              }
            }
          }
        }) ^
        fragmentFactory.break
      )
    }
  }

  /**
   * This class provides a wrapper to test methods that require an
   * initialized Req.
   */
  class ReqSpecification (description : String, 
                          val req : HttpServletRequest) extends ModifiableRequest[ReqSpecification] {
    def this (description : String, url : String, contextPath : String) =
      this(description, new MockHttpServletRequest(url, contextPath))

    def in(expectations : Req => Result) = {
      addFragments(
        fragmentFactory.example(description, {
          LiftRulesMocker.devTestLiftRulesInstance.doWith(liftRules) {
            MockWeb.useLiftRules.doWith(true) {
              MockWeb.testReq(req)(expectations)
            }
          }
        }) ^
        fragmentFactory.break
      )
    }
  }

  /**
   * This class provides a wrapper to test methods that require
   * a processed template.
   */
  class TemplateSpecification (description : String,
                               val req : HttpServletRequest,
                               session : Box[LiftSession]) extends ModifiableRequest[TemplateSpecification] {
    def this (description : String, url : String, session : Box[LiftSession], contextPath : String) =
      this(description, new MockHttpServletRequest(url, contextPath), session)

    def in(expectations : Box[NodeSeq] => Result) = {
      addFragments(
        fragmentFactory.example(description, {
          LiftRulesMocker.devTestLiftRulesInstance.doWith(liftRules) {
            MockWeb.useLiftRules.doWith(true) {
              MockWeb.testS(req, session) {
                S.request match {
                  case Full(sReq) => expectations(S.runTemplate(sReq.path.partPath))
                  case other => failure("Error: withTemplateFor call did not result in " +
                    "request initialization (S.request = " + other + ")")
                }
              }
            }
          }
        }) ^
        fragmentFactory.break
      )
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy