diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 07c18ba8e..44c5f0c73 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,22 +1,12 @@
# Contribution Guidelines
-First of all, thanks for thinking of contributing to this project. :smile:
+First of all, thank you for your interest in contributing to this project !
-- Before sending a Pull Request, please make sure that you have had a discussion with the project admins.
- - If a relevant issue already exists, discuss on the issue and make sure that the admins are okay with your approach
- - If no relevant issue exists, open a new issue and discuss
+* Before submitting a [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) (PR), please make sure that you have had a discussion with the project-leads
+* If a [relevant issue](https://github.com/intuit/karate/issues) already exists, have a discussion within that issue (by commenting) - and make sure that the project-leads are okay with your approach
+* If no relevant issue exists, please [open a new issue](https://github.com/intuit/karate/issues) to start a discussion
+* Please proceed with a PR only *after* the project admins or owners are okay with your approach. We don't want you to spend time and effort working on something - only to find out later that it was not aligned with how the project developers were thinking about it !
+* You can refer to the [Developer Guide](https://github.com/intuit/karate/wiki/Developer-Guide) for information on how to build and test the project on your local / developer machine
+* **IMPORTANT**: Submit your PR(s) against the [`develop`](https://github.com/intuit/karate/tree/develop) branch of this repository
- Please proceed with a Pull Request only after the project admins or owners are okay with your approach. It'd be sad if your Pull Request (and your hard work) isn't accepted just because it isn't ideologically compatible.
-
-- Install the required dependencies.
- - Install Git so that you can clone and later submit a PR for this project.
- - Install Java JDK (>= 1.8.0_112) installed, from [this link](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html).
- - Install Eclipse from [this link](http://www.eclipse.org/downloads/).
- - (optional) Install Maven from [this link](http://maven.apache.org), if you need to build the project from the command-line.
-
-- Have some issue with setting up the project?
- - [How to open the project in Eclipse as a Maven project?](https://stackoverflow.com/a/36242422/143475)
- - [Maven is not able to install the dependencies behind proxy!]()
- - Not listed here? Kindly search on Google / Stack Overflow. If you don't find a solution, feel free to open up a new issue in the issue tracker and maybe subsequently add it here.
-
-- Send in your Pull Request(s) to the `develop` branch of this repository.
+If you are interested in project road-map items that you can potentially contribute to, please refer to the [Project Board](https://github.com/intuit/karate/projects/3).
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 8f1c8f661..4b5610910 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
### Description
-Thanks for contributing this Pull Request. Make sure that you send in this Pull Request to the `develop` branch of this repository, add a brief description, and tag the relevant issue(s) and PR(s) below.
+Thanks for contributing this Pull Request. Make sure that you submit this Pull Request against the `develop` branch of this repository, add a brief description, and tag the relevant issue(s) and PR(s) below.
- Relevant Issues : (compulsory)
- Relevant PRs : (optional)
diff --git a/.gitignore b/.gitignore
index 83239337e..f5fa0b340 100755
--- a/.gitignore
+++ b/.gitignore
@@ -4,13 +4,22 @@ target/
.project
.settings
.classpath
+.vscode
*.iml
build/
+bin/
.gradle
gradle
gradlew
gradlew.*
dependency-reduced-pom.xml
+examples/zip-release/*.jar
karate-demo/activemq-data/
+karate-demo/*.pem
+karate-demo/*.jks
+karate-demo/*.der
+karate-netty/*.pem
+karate-netty/*.jks
+karate-netty/*.der
karate-junit4/src/test/java/com/intuit/karate/junit4/dev
diff --git a/.travis.yml b/.travis.yml
index aa6f1ac2a..a838b08b7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,7 @@
language: java
+cache:
+ directories:
+ - "$HOME/.m2"
jdk:
- - oraclejdk8
-sudo: false
-addons:
- apt:
- packages:
- - oracle-java8-installer
-install: true
-script: mvn install -P pre-release -Dmaven.javadoc.skip=true -B -V
-
+ - openjdk8
+script: mvn install -P pre-release -Dmaven.javadoc.skip=true -B -V -Djavacpp.platform=linux-x86_64
diff --git a/README.md b/README.md
index 76b2f60c4..ed42c91dc 100755
--- a/README.md
+++ b/README.md
@@ -1,12 +1,10 @@
# Karate
-## Web-Services Testing Made `Simple.`
+## Test Automation Made `Simple.`
[![Maven Central](https://img.shields.io/maven-central/v/com.intuit.karate/karate-core.svg)](https://mvnrepository.com/artifact/com.intuit.karate/karate-core) [![Build Status](https://travis-ci.org/intuit/karate.svg?branch=master)](https://travis-ci.org/intuit/karate) [![GitHub release](https://img.shields.io/github/release/intuit/karate.svg)](https://github.com/intuit/karate/releases) [![Support Slack](https://img.shields.io/badge/support-slack-red.svg)](https://github.com/intuit/karate/wiki/Support) [![Twitter Follow](https://img.shields.io/twitter/follow/KarateDSL.svg?style=social&label=Follow)](https://twitter.com/KarateDSL)
-Karate is the only open-source tool to combine API test-automation, [mocks](karate-netty) and [performance-testing](karate-gatling) into a **single**, *unified* framework. The BDD syntax popularized by Cucumber is language-neutral, and easy for even non-programmers. Besides powerful JSON & XML assertions, you can run tests in parallel for speed - which is critical for HTTP API testing.
+Karate is the only open-source tool to combine API test-automation, [mocks](karate-netty), [performance-testing](karate-gatling) and even [UI automation](karate-core) into a **single**, *unified* framework. The BDD syntax popularized by Cucumber is language-neutral, and easy for even non-programmers. Powerful JSON & XML assertions are built-in, and you can run tests in parallel for speed.
-You can easily build (or re-use) complex request payloads, and dynamically construct more requests from response data. The payload and schema validation engine can perform a 'smart compare' (deep-equals) of two JSON or XML documents, and you can even ignore dynamic values where needed.
-
-Test execution and report generation feels like any standard Java project. But there's also a [stand-alone executable](karate-netty#standalone-jar) for teams not comfortable with Java. Just write tests in a **simple**, *readable* syntax - carefully designed for HTTP, JSON, GraphQL and XML.
+Test execution and report generation feels like any standard Java project. But there's also a [stand-alone executable](karate-netty#standalone-jar) for teams not comfortable with Java. You don't have to compile code. Just write tests in a **simple**, *readable* syntax - carefully designed for HTTP, JSON, GraphQL and XML. And you can mix API and [UI test-automation](karate-core) within the same test script.
## Hello World
@@ -141,6 +139,7 @@ And you don't need to create additional Java classes for any of the payloads tha
| responseHeaders
| responseCookies
| responseTime
+ | responseType
| requestTimeStamp
@@ -183,6 +182,7 @@ And you don't need to create additional Java classes for any of the payloads tha
| Header Manipulation
| GraphQL
| Websockets / Async
+ | call
vs read()
karate.abort()
| you can prematurely exit a `Scenario` by combining this with [conditional logic](#conditional-logic) like so: `* if (condition) karate.abort()` - please use [sparingly](https://martinfowler.com/articles/nonDeterminism.html) !
+karate.abort()
| you can prematurely exit a `Scenario` by combining this with [conditional logic](#conditional-logic) like so: `* if (condition) karate.abort()` - please use [sparingly](https://martinfowler.com/articles/nonDeterminism.html) ! and also see [`configure abortedStepsShouldPass`](#configure)
karate.append(... items)
| useful to create lists out of items (which can be lists as well), see [JSON transforms](#json-transforms)
-karate.appendTo(name, ... items)
| useful to append to a list-like variable (that has to exist) in scope, see [JSON transforms](#json-transforms)
+karate.appendTo(name, ... items)
| useful to append to a list-like variable (that has to exist) in scope, see [JSON transforms](#json-transforms) - the first argument can be a reference to an array-like variable or even the name (string) of an existing variable which is list-like
karate.call(fileName, [arg])
| invoke a [`*.feature` file](#calling-other-feature-files) or a [JavaScript function](#calling-javascript-functions) the same way that [`call`](#call) works (with an optional solitary argument)
karate.callSingle(fileName, [arg])
| like the above, but guaranteed to run **only once** even across multiple features *and* parallel threads (recommended only for advanced users) - refer to this example: [`karate-config.js`](karate-demo/src/test/java/karate-config.js) / [`headers-single.feature`](karate-demo/src/test/java/demo/headers/headers-single.feature)
karate.configure(key, value)
| does the same thing as the [`configure`](#configure) keyword, and a very useful example is to do `karate.configure('connectTimeout', 5000);` in [`karate-config.js`](#configuration) - which has the 'global' effect of not wasting time if a connection cannot be established within 5 seconds
@@ -3070,12 +3176,12 @@ Operation | Description
karate.filter(list, predicate)
| functional-style 'filter' operation useful to filter list-like objects (e.g. JSON arrays), see [example](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/js-arrays.feature), the second argument has to be a JS function (item, [index]) that returns a `boolean`
karate.filterKeys(map, keys)
| extracts a sub-set of key-value pairs from the first argument, the second argument can be a list (or varargs) of keys - or even another JSON where only the keys would be used for extraction, [example](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/js-arrays.feature)
`karate.forEach(list, function)` | functional-style 'loop' operation useful to traverse list-like (or even map-like) objects (e.g. JSON / arrays), see [example](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/js-arrays.feature), the second argument has to be a JS function (item, [index]) for lists and (key, [value], [index]) for JSON / maps
-karate.get(name)
| get the value of a variable by name (or JsonPath expression), if not found - this returns `null` which is easier to handle in JavaScript (than `undefined`)
+karate.get(name, [default])
| get the value of a variable by name (or JsonPath expression), if not found - this returns `null` which is easier to handle in JavaScript (than `undefined`), and an optional second argument can be used to return a "default" value, very useful to set variables in called features that have not been pre-defined
karate.info
| within a test (or within the [`afterScenario`](#configure) function if configured) you can access metadata such as the `Scenario` name, refer to this example: [`hooks.feature`](karate-demo/src/test/java/demo/hooks/hooks.feature)
karate.jsonPath(json, expression)
| brings the power of [JsonPath](https://github.com/json-path/JsonPath) into JavaScript, and you can find an example [here](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/js-arrays.feature).
karate.keysOf(object)
| returns only the keys of a map-like object
karate.listen(timeout)
| wait until [`karate.signal(result)`](#karate-signal) has been called or time-out after `timeout` milliseconds, see [async](#async)
-karate.log(... args)
| log to the same logger (and log file) being used by the parent process, logging can be suppressed with [`configure printEnabled`](#configure) set to `false`
+karate.log(... args)
| log to the same logger (and log file) being used by the parent process, logging can be suppressed with [`configure printEnabled`](#configure) set to `false`, and just like [`print`](#print) - use comma-separated values to "pretty print" JSON or XML
karate.lowerCase(object)
| useful to brute-force all keys and values in a JSON or XML payload to lower-case, useful in some cases, see [example](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/lower-case.feature)
karate.map(list, function)
| functional-style 'map' operation useful to transform list-like objects (e.g. JSON arrays), see [example](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/js-arrays.feature), the second argument has to be a JS function (item, [index])
karate.mapWithKey(list, string)
| convenient for the common case of transforming an array of primitives into an array of objects, see [JSON transforms](#json-transforms)
@@ -3096,13 +3202,17 @@ Operation | Description
karate.setXml(name, xmlString)
| rarely used, refer to the example above
karate.signal(result)
| trigger an event that [`karate.listen(timeout)`](#karate-listen) is waiting for, and pass the data, see [async](#async)
karate.sizeOf(object)
| returns the size of the map-like or list-like object
+karate.stop()
| will pause the test execution until a socket connection is made to the port logged to the console, useful for troubleshooting UI tests without using a [de-bugger](https://twitter.com/KarateDSL/status/1167533484560142336), of course - *NEVER* forget to remove this after use !
+karate.target(object)
| currently for web-ui automation only, see [target lifecycle](karate-core#target-lifecycle)
karate.tags
| for advanced users - scripts can introspect the tags that apply to the current scope, refer to this example: [`tags.feature`](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/tags.feature)
karate.tagValues
| for even more advanced users - Karate natively supports tags in a `@name=val1,val2` format, and there is an inheritance mechanism where `Scenario` level tags can over-ride `Feature` level tags, refer to this example: [`tags.feature`](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/tags.feature)
karate.toBean(json, className)
| converts a JSON string or map-like object into a Java object, given the Java class name as the second argument, refer to this [file](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/type-conv.feature) for an example
karate.toJson(object)
| converts a Java object into JSON, and `karate.toJson(object, true)` will strip all keys that have `null` values from the resulting JSON, convenient for unit-testing Java code, see [example](karate-demo/src/test/java/demo/unit/cat.feature)
+karate.toList(object)
| rarely used - when still within a JS block, you need to convert a JSON array into a Java `List`
+karate.toMap(object)
| rarely used - when still within a JS block, you need to convert a JSON object into a Java `Map`, or you are setting up [complex global variables](#restrictions-on-global-variables)
karate.valuesOf(object)
| returns only the values of a map-like object (or itself if a list-like object)
karate.webSocket(url, handler)
| see [websocket](#websocket)
-karate.write(object, path)
| writes the bytes of `object` to a path which will *always* be relative to the "build" directory (typically `target`), see this example: [`embed-pdf.js`](karate-demo/src/test/java/demo/embed/embed-pdf.js) - and this method returns a `java.io.File` reference to the file created / written to
+karate.write(object, path)
| *normally not recommended, please [read this first](https://stackoverflow.com/a/54593057/143475)* - writes the bytes of `object` to a path which will *always* be relative to the "build" directory (typically `target`), see this example: [`embed-pdf.js`](karate-demo/src/test/java/demo/embed/embed-pdf.js) - and this method returns a `java.io.File` reference to the file created / written to
karate.xmlPath(xml, expression)
| Just like [`karate.jsonPath()`](#karate-jsonpath) - but for XML, and allows you to use dynamic XPath if needed, see [example](karate-junit4/src/test/java/com/intuit/karate/junit4/xml/xml.feature).
# Code Reuse / Common Routines
@@ -3116,6 +3226,8 @@ When you have a sequence of HTTP calls that need to be repeated for multiple tes
Here is an example of using the `call` keyword to invoke another feature file, loaded using the [`read`](#reading-files) function:
+> If you find this hard to understand at first, try looking at this [set of examples](karate-demo/src/test/java/demo/callfeature/call-feature.feature).
+
```cucumber
Feature: which makes a 'call' to another re-usable feature
@@ -3228,6 +3340,16 @@ Variable | Refers To
Refer to this [demo feature](karate-demo) for an example: [`kitten-create.feature`](karate-demo/src/test/java/demo/calltable/kitten-create.feature)
+### Default Values
+Some users need "callable" features that are re-usable even when variables have not been defined by the calling feature. Normally an undefined variable results in nasty JavaScript errors. But there is an elegant way you can specify a default value using the [`karate.get()`](#karate-get) API:
+
+```cucumber
+# if foo is not defined, it will default to 42
+* def foo = karate.get('foo', 42)
+```
+
+> A word of caution: we recommend that you should not over-use Karate's capability of being able to re-use features. Re-use can sometimes result in negative benefits - especially when applied to test-automation. Prefer readability over re-use. See this for an [example](https://stackoverflow.com/a/54126724/143475).
+
### `copy`
For a [`call`](#call) (or [`callonce`](#callonce)) - payload / data structures (JSON, XML, Map-like or List-like) variables are 'passed by reference' which means that steps within the 'called' feature can update or 'mutate' them, for e.g. using the [`set`](#set) keyword. This is actually the intent most of the time and is convenient. If you want to pass a 'clone' to a 'called' feature, you can do so using the rarely used `copy` keyword that works very similar to [type conversion](#type-conversion). This is best explained in the last scenario of this example: [`copy-caller.feature`](karate-junit4/src/test/java/com/intuit/karate/junit4/demos/copy-caller.feature)
@@ -3294,6 +3416,24 @@ Shared | [`call-updates-config.feature`](karate-demo/src/test/java/demo/headers/
> Once you get comfortable with Karate, you can consider moving your authentication flow into a 'global' one-time flow using [`karate.callSingle()`](#karate-callsingle), think of it as '[`callonce`](#callonce) on steroids'.
+#### `call` vs `read()`
+Since this is a frequently asked question, the different ways of being able to re-use code (or data) are summarized below.
+
+Code | Description
+---- | -----------
+`* def login = read('login.feature')`