Skip to content

Commit

Permalink
Create scala-extensions module for Scala 3
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosedp committed Feb 5, 2024
1 parent 034ebfb commit 3ba073b
Show file tree
Hide file tree
Showing 6 changed files with 442 additions and 3 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ target
.settings
.springBeans
.idea
.metals
.vscode
.bloop
classes/
lib/
rest-assured-all/dependency-reduced-pom.xml
rest-assured-all/dependency-reduced-pom.xml
4 changes: 3 additions & 1 deletion modules/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
~ limitations under the License.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>rest-assured-parent</artifactId>
<groupId>io.rest-assured</groupId>
Expand All @@ -29,6 +30,7 @@
<module>json-schema-validator</module>
<module>spring-mock-mvc</module>
<module>scala-support</module>
<module>scala-extensions</module>
<module>spring-web-test-client</module>
<module>spring-commons</module>
<module>kotlin-extensions</module>
Expand Down
110 changes: 110 additions & 0 deletions modules/scala-extensions/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?xml version="1.0"?>
<!--
~ Copyright 2024 the original author or authors.
~
~ 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.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.rest-assured</groupId>
<artifactId>modules</artifactId>
<version>5.4.1-SNAPSHOT</version>
</parent>
<artifactId>scala-extensions</artifactId>
<version>5.4.1-SNAPSHOT</version>
<name>scala-extensions</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<scala-maven-plugin.version>4.8.1</scala-maven-plugin.version>
<scala.version>3.3.1</scala.version>
</properties>

<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala3-library_3</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.4.1-SNAPSHOT</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
<profile>
<id>release</id>
<build>
<plugins>
</plugins>
</build>
</profile>
</profiles>

<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>${scala-maven-plugin.version}</version>
<executions>
<execution>
<?m2e ignore?>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<?m2e ignore?>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>add-source</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
<args>
<arg>-feature</arg>
<arg>-deprecation</arg>
<arg>-Ysemanticdb</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package io.restassured.module.scala.extensions

import scala.util.chaining.*
import scala.reflect.ClassTag
import io.restassured.RestAssured.`given`
import io.restassured.RestAssured.`when`
import io.restassured.internal.{ResponseSpecificationImpl, ValidatableResponseImpl}
import io.restassured.response.{ExtractableResponse, Response, ValidatableResponse}
import io.restassured.specification.{RequestSender, RequestSpecification}

// Main wrappers

/**
* A wrapper around [given] that starts building the request part of the test.
* E.g.
* {{{
* Given(_.params("firstName", "John"))
* .When(_.post("/greetXML"))
* .Then(res =>
* res.statusCode(200)
* res.body("greeting.firstName", equalTo("John"))
* )
* }}}
* will send a POST request to "/greetXML" with request parameters
* `firstName=John` and `lastName=Doe` and expect that the response body
* containing JSON or XML firstName equal to John.
*
* @return
* A request specification.
*
* @see
* io.restassured.RestAssured.given
*/
def Given(block: RequestSpecification => RequestSpecification): RequestSpecification =
`given`().tap(block)

/**
* A wrapper around [given] that starts building the request part of the test.
* This is an overloaded version of [Given] that does not take any arguments.
* E.g.
* {{{
* Given()
* .When(_.post("/greetXML"))
* .Then(_.statusCode(200))
* }}}
* will send a POST request to "/greetXML" request parameters and expect that
* the statusCode 200.
*
* @return
* A request specification.
*
* @see
* io.restassured.RestAssured.given
*/
def Given(): RequestSpecification =
`given`()

/**
* A wrapper around [io.restassured.RestAssured.when] to start building the DSL
* expression by sending a request without any parameters or headers etc. E.g.
* {{{
* Given()
* .When(_.get("/x"))
* .Then(_.body("x.y.z1", equalTo("Z1")))
* }}}
* Note that if you need to add parameters, headers, cookies or other request
* properties use the [[Given()]] method.
*
* @see
* io.restassured.RestAssured.when
* @return
* A request sender interface that let's you call resources on the server
*/
extension (spec: RequestSpecification)
infix def When(block: RequestSpecification => Response): Response =
block(spec.`when`())

/**
* A wrapper around [io.restassured.RestAssured.when] to start building the DSL
* expression by sending a request without any parameters or headers etc. E.g.
* {{{
* Given()
* .When(_.get("/x"))
* .Then(_.body("x.y.z1", equalTo("Z1")))
* }}}
* Note that if you need to add parameters, headers, cookies or other request
* properties use the [[Given()]] method.
*
* @see
* io.restassured.RestAssured.when
* @return
* A request sender interface that let's you call resources on the server
*/
def When(block: RequestSender => Response): Response =
block(`when`())


/**
* A wrapper around [then] that lets you validate the response.
* Usage example:
* {{{
* Given(_.params("firstName", "John")
* .When(_.post("/greetXML"))
* .Then(_.body("greeting.firstName", equalTo("John")))
* }}}
*
* @return
* A validatable response
*/
extension (resp: Response)
infix def Then(block: ValidatableResponse => Unit): ValidatableResponse =
resp.`then`()
.tap(block)

/**
* A wrapper around [ExtractableResponse] that lets you validate the response.
* Usage example:
* {{{
* val firstName: String = Given(_.params("firstName", "John")
* .When(_.post("/greetXML"))
* .Then(_.body("greeting.firstName", equalTo("John")))
* .Extract(_.path("greeting.firstName"))
* }}}
* The above code will send a POST request to "/greetXML" with request parameters
* `firstName=John` and `lastName=Doe` and expect that the response body
* containing JSON or XML firstName equal to John.
* The response is then validated and the firstName is extracted from the response.
* The extracted firstName is then returned. The type of the extracted value is
* needs to be specified as a type parameter.
*
* @return
* The extracted value
*/
extension [T]( resp: Response )
infix def Extract(
block: ExtractableResponse[Response] => T
)(
using ClassTag[T]
): T =
val res = resp.`then`().extract().pipe(block)
summon[ClassTag[T]].runtimeClass match
case cls if cls.isAssignableFrom(res.getClass) => res.asInstanceOf[T]
case _ => throw new Exception("Unsupported type")

/**
* A wrapper around [ValidatableResponse] that lets you validate the response.
* Usage example:
* {{{
* val firstName: String = Given(_.params("firstName", "John")
* .When(_.post("/greetXML"))
* .Then(_.body("greeting.firstName", equalTo("John")))
* .Extract(_.path("greeting.firstName"))
* }}}
* The above code will send a POST request to "/greetXML" with request parameters
* `firstName=John` and `lastName=Doe` and expect that the response body
* containing JSON or XML firstName equal to John.
* The response is then validated and the firstName is extracted from the response.
* The extracted firstName is then returned. The type of the extracted value is
* needs to be specified as a type parameter.
*
* @return
* The extracted value
*/
extension [T]( resp: ValidatableResponse )
infix def Extract(
block: ExtractableResponse[Response] => T
)(
using ClassTag[T]
): T =
val res = resp.extract().pipe(block)
summon[ClassTag[T]].runtimeClass match
case cls if cls.isAssignableFrom(res.getClass) => res.asInstanceOf[T]
case _ => throw new Exception("Unsupported type")

// End main wrappers

private def doIfValidatableResponseImpl(fn: ResponseSpecificationImpl => Unit): ValidatableResponse => Unit =
resp =>
if resp.isInstanceOf[ValidatableResponseImpl] then fn(resp.asInstanceOf[ValidatableResponseImpl].responseSpec)
Loading

0 comments on commit 3ba073b

Please sign in to comment.