Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue-482: show (focused) step specific vars, request/response info #494

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3002f9a
fix for gatling report missing last scenario http request #460
ptrthomas Jul 18, 2018
e84af6e
bump dev version to point one
ptrthomas Jul 18, 2018
1f069ee
Merge branch 'master' into develop
ptrthomas Jul 20, 2018
440f688
Merge branch 'master' into develop
ptrthomas Jul 20, 2018
9d6f26c
Issue-464: proper handling of abort and slight improvement in KarateJ…
Jul 20, 2018
ee222c4
Merge pull request #466 from vmchukky/Issue-464
ptrthomas Jul 23, 2018
0224e78
code doc edit after pr #466 thx @vmchukky
ptrthomas Jul 23, 2018
d3eb6e9
Merge branch 'master' into develop
ptrthomas Jul 24, 2018
19264ea
karate ui was broken with logging change ref #469
ptrthomas Jul 24, 2018
e5faab7
fix for ui mode file in current working dir
ptrthomas Jul 25, 2018
25c6f44
Merge remote-tracking branch 'upstream/master' into develop
Jul 25, 2018
b179f7b
Merge remote-tracking branch 'upstream/develop' into develop
Jul 25, 2018
dd0ce78
Issue-425: fix NPE while importing postman collection with item array
Jul 26, 2018
4944fd7
implemented being able to select tag when calling feature ref #471
ptrthomas Jul 26, 2018
c0b984c
Merge pull request #473 from vmchukky/Issue-425
ptrthomas Jul 26, 2018
43d0cc6
adding example of using Nil for gatling pause-times, doc edits
ptrthomas Jul 27, 2018
0bb003a
added some fun examples for finding index of match in arrays
ptrthomas Jul 27, 2018
6e16094
minor doc edits
ptrthomas Jul 29, 2018
f92c273
defensive coding to ensure well-formed report json / xml output ref #475
ptrthomas Jul 30, 2018
3e3cac3
adding unit test for #475
ptrthomas Jul 30, 2018
d5b5385
configure report had no effect #478
ptrthomas Aug 1, 2018
19cedf1
Issue-479: karate-ui enhanced to create-new/edit-existing/save-modifi…
Aug 1, 2018
3a2e085
Merge pull request #480 from vmchukky/Issue-479
ptrthomas Aug 1, 2018
f681dec
Issue-479-1: UI enhancements to handle runAll / runUpto using JavaFX …
Aug 2, 2018
3fd6e69
Disable JavaFX UI tests that need a DISPLAY (can't run in headless mode)
Aug 2, 2018
b93d58a
Merge pull request #481 from vmchukky/Issue-479-1
ptrthomas Aug 2, 2018
f740bca
renaming test to not run in ci but easier in dev
ptrthomas Aug 2, 2018
70548ae
Fix dead link in README
Aug 3, 2018
a39037c
Merge pull request #483 from matthewmcgarvey/patch-1
ptrthomas Aug 3, 2018
ad4049c
was not possible to over-ride content-type for url-encoded ref #488
ptrthomas Aug 7, 2018
85d9e9f
removing scratchpad code
ptrthomas Aug 7, 2018
3958660
Merge branch 'master' into develop
ptrthomas Aug 8, 2018
72d56c6
adding regression test for #491
ptrthomas Aug 8, 2018
b982c8c
Issue-482: show (focused) step specific vars, request/response info
vmchukky Aug 11, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ And you don't need to create additional Java classes for any of the payloads tha
| <a href="#not-contains"><code>match !contains</code></a>
| <a href="#match-each"><code>match each</code></a>
| <a href="#fuzzy-matching">Fuzzy Matching</a>
| <a href="#contains-shortcuts"><code>contains</code> short-cuts</a>
| <a href="#schema-validation">Schema Validation</a>
| <a href="#schema-validation">Schema Validation</a>
| <a href="#contains-short-cuts"><code>contains</code> short-cuts</a>
</td>
</tr>
<tr>
Expand Down Expand Up @@ -201,7 +201,7 @@ And you don't need to create additional Java classes for any of the payloads tha
* Tests are super-readable - as scenario data can be expressed in-line, in human-friendly [JSON](#json), [XML](#xml), Cucumber [Scenario](#the-cucumber-way) Outline [tables](#table), or a [payload builder](#set-multiple) approach [unique to Karate](https://gist.github.com/ptrthomas/d6beb17e92a43220d254af942e3ed3d9)
* Express expected results as readable, well-formed JSON or XML, and [assert in a single step](#match) that the entire response payload (no matter how complex or deeply nested) - is as expected
* Comprehensive [assertion capabilities](https://github.com/intuit/karate#fuzzy-matching) - and failures clearly report which data element (and path) is not as expected, for easy troubleshooting of even large payloads
* [Embedded UI](https://github.com/intuit/karate/wiki/Karate-UI) for stepping through a script in debug mode where you can even re-play a step while editing it - a huge time-saver
* [Embedded UI](https://github.com/intuit/karate/wiki/Karate-UI) for stepping through a script in debug mode where you can even [re-play a step while editing it](https://twitter.com/ptrthomas/status/889356965461217281) - a huge time-saver
* Simpler and more [powerful alternative](https://twitter.com/KarateDSL/status/878984854012022784) to JSON-schema for [validating payload structure](#schema-validation) and format that even supports cross-field / domain validation logic
* Scripts can [call other scripts](#calling-other-feature-files) - which means that you can easily re-use and maintain authentication and 'set up' flows efficiently, across multiple tests
* Embedded JavaScript engine that allows you to build a library of [re-usable functions](#calling-javascript-functions) that suit your specific environment or organization
Expand Down Expand Up @@ -2823,6 +2823,17 @@ So you get the picture, any kind of complicated 'sign-in' flow can be scripted a

Do look at the documentation and example for [`configure headers`](#configure-headers) also as it goes hand-in-hand with `call`. In the above example, the end-result of the `call` to `my-signin.feature` resulted in the `authToken` variable being initialized. Take a look at how the [`configure headers`](#configure-headers) example uses the `authToken` variable.

### Call Tag Selector
You can "select" a single `Scenario` (or `Scenario`-s or `Scenario Outline`-s) by appending a "tag selector" at the end of the feature-file you are calling. For example:

```cucumber
call read('classpath:my-signin.feature@name=someScenarioName')
```

While the tag does not need to be in the `@key=value` form, it is recommended for readability when you start getting into the business of giving meaningful names to your `Scenario`-s.

This "tag selection" capability is designed for you to be able to "compose" flows out of existing test-suites when using the [Karate Gatling integration](karate-gatling). Normally we recommend that you keep your "re-usable" features lightweight - by limiting them to just one `Scenario`.

### Data-Driven Features
If the argument passed to the [call of a `*.feature` file](#calling-other-feature-files) is a JSON array, something interesting happens. The feature is invoked for each item in the array. Each array element is expected to be a JSON object, and for each object - the behavior will be as described above.

Expand Down
2 changes: 1 addition & 1 deletion karate-apache/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-parent</artifactId>
<version>0.8.0</version>
<version>0.8.1</version>
</parent>
<artifactId>karate-apache</artifactId>
<packaging>jar</packaging>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
Expand Down Expand Up @@ -143,7 +143,12 @@ public static HttpEntity getEntity(MultiValuedMap fields, String mediaType, Char
if (cs == null) {
cs = charset;
}
return new UrlEncodedFormEntity(list, cs);
String raw = URLEncodedUtils.format(list, cs);
int pos = mediaType.indexOf(';');
if (pos != -1) { // strip out charset param from content-type
mediaType = mediaType.substring(0, pos);
}
return new StringEntity(raw, ContentType.create(mediaType, cs));
} catch (Exception e) {
throw new RuntimeException(e);
}
Expand Down
2 changes: 1 addition & 1 deletion karate-archetype/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-parent</artifactId>
<version>0.8.0</version>
<version>0.8.1</version>
</parent>
<artifactId>karate-archetype</artifactId>
<packaging>jar</packaging>
Expand Down
2 changes: 1 addition & 1 deletion karate-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-parent</artifactId>
<version>0.8.0</version>
<version>0.8.1</version>
</parent>
<artifactId>karate-core</artifactId>
<packaging>jar</packaging>
Expand Down
46 changes: 38 additions & 8 deletions karate-core/src/main/java/com/intuit/karate/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ private static String removePrefix(String text) {
int pos = text.indexOf(':');
return pos == -1 ? text : text.substring(pos + 1);
}

private static StringUtils.Pair parsePathAndTags(String text) {
int pos = text.indexOf(':');
text = pos == -1 ? text : text.substring(pos + 1); // remove prefix
pos = text.indexOf('@');
if (pos == -1) {
text = StringUtils.trimToEmpty(text);
return new StringUtils.Pair(text, null);
} else {
String left = StringUtils.trimToEmpty(text.substring(0, pos));
String right = StringUtils.trimToEmpty(text.substring(pos));
return new StringUtils.Pair(left, right);
}
}

private static enum PathPrefix {
NONE,
Expand All @@ -93,25 +107,25 @@ private static enum PathPrefix {
public static ScriptValue readFile(String text, ScriptContext context) {
text = StringUtils.trimToEmpty(text);
PathPrefix prefix = isClassPath(text) ? PathPrefix.CLASSPATH : (isFilePath(text) ? PathPrefix.FILE : PathPrefix.NONE);
String fileName = removePrefix(text);
fileName = StringUtils.trimToEmpty(fileName);
StringUtils.Pair pair = parsePathAndTags(text);
text = pair.left;
if (isJsonFile(text) || isXmlFile(text) || isJavaScriptFile(text)) {
String contents = readFileAsString(fileName, prefix, context);
String contents = readFileAsString(text, prefix, context);
ScriptValue temp = evalKarateExpression(contents, context);
return new ScriptValue(temp.getValue(), text);
} else if (isTextFile(text) || isGraphQlFile(text)) {
String contents = readFileAsString(fileName, prefix, context);
String contents = readFileAsString(text, prefix, context);
return new ScriptValue(contents, text);
} else if (isFeatureFile(text)) {
String contents = readFileAsString(fileName, prefix, context);
FeatureWrapper feature = FeatureWrapper.fromString(contents, context.env, text);
String contents = readFileAsString(text, prefix, context);
FeatureWrapper feature = FeatureWrapper.fromString(contents, context.env, text, pair.right);
return new ScriptValue(feature, text);
} else if (isYamlFile(text)) {
String contents = readFileAsString(fileName, prefix, context);
String contents = readFileAsString(text, prefix, context);
DocumentContext doc = JsonUtils.fromYaml(contents);
return new ScriptValue(doc, text);
} else {
InputStream is = getFileStream(fileName, prefix, context);
InputStream is = getFileStream(text, prefix, context);
return new ScriptValue(is, text);
}
}
Expand Down Expand Up @@ -374,5 +388,21 @@ public static String getKarateVersion() {
return UNKNOWN;
}
}

public static void renameFileIfZeroBytes(String fileName) {
File file = new File(fileName);
if (!file.exists()) {
logger.warn("file not found, previous write operation may have failed: {}", fileName);
} else if (file.length() == 0) {
logger.warn("file size is zero bytes, previous write operation may have failed: {}", fileName);
try {
File dest = new File(fileName + ".fail");
file.renameTo(dest);
logger.warn("renamed zero length file to: {}", dest.getName());
} catch (Exception e) {
logger.warn("failed to rename zero length file: {}", e.getMessage());
}
}
}

}
104 changes: 66 additions & 38 deletions karate-core/src/main/java/com/intuit/karate/convert/ConvertUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,63 +28,91 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;

/**
* Created by rkumar32 on 7/5/17.
*/
public class ConvertUtils {

private static final Logger logger = LoggerFactory.getLogger(ConvertUtils.class);

private ConvertUtils() {
// only static methods
}

public static String toKarateFeature(List<PostmanRequest> requests) {
String scenarios = "";
for (PostmanRequest request : requests) {
scenarios += request.convert();
public static String toKarateFeature(List<PostmanItem> items) {
return toKarateFeature(UUID.randomUUID().toString(), items);
}

public static String toKarateFeature(String collectionName, List<PostmanItem> items) {
StringBuilder sb = new StringBuilder("Feature: ").append(collectionName);
sb.append(System.lineSeparator()).append(System.lineSeparator());
for (PostmanItem item : items) {
sb.append(item.convert());
}
String feature = "Feature: \n\n" + scenarios;
return feature;
return sb.toString();
}
public static List<PostmanRequest> readPostmanJson(String json) {

public static List<PostmanItem> readPostmanJson(String json) {
DocumentContext doc = JsonPath.parse(json);
List<Map<String, Object>> list = (List) doc.read("$.item");
List<PostmanRequest> requests = new ArrayList<>(list.size());
return readPostmanItems(null, list);
}

private static List<PostmanItem> readPostmanItems(PostmanItem parent, List<Map<String, Object>> list) {
PostmanItem item;
List<PostmanItem> requests = new ArrayList<>(list.size());
for (Map<String, Object> map : list) {
logger.debug("map: {}", map);
String name = (String) map.get("name");
Map<String, Object> requestInfo = (Map) map.get("request");
String url = (String) requestInfo.get("url");
String method = (String) requestInfo.get("method");
List<Map<String, Object>> headersList = (List) requestInfo.get("header");
Map<String, String> headers = new HashMap<>();
item = readPosmanItem(parent, map);
requests.add(item);
}
return requests;
}

private static PostmanItem readPosmanItem(PostmanItem parent, Map<String, Object> itemMap) {
PostmanItem item = new PostmanItem();
String name = (String) itemMap.get("name");
item.setName(name);
item.setParent(Optional.ofNullable(parent));
Map<String, Object> requestInfo = (Map) itemMap.get("request");
if (requestInfo != null) {
item.setRequest(readPostmanRequest(requestInfo));
} else { // this may have list of sub-items
List<PostmanItem> subItems = readPostmanItems(item, (List<Map<String, Object>>) itemMap.get("item"));
item.setItems(Optional.of(subItems));
}
return item;
}

private static PostmanRequest readPostmanRequest(Map<String, Object> requestInfo) {
String url = getUrl(requestInfo.get("url"));
String method = (String) requestInfo.get("method");
List<Map<String, Object>> headersList = (List) requestInfo.get("header");
Map<String, String> headers = new HashMap<>();
if (headersList != null) {
for (Map<String, Object> header : headersList) {
headers.put((String) header.get("key"), (String) header.get("value"));
}
Map<String, Object> bodyInfo = (Map) requestInfo.get("body");
String body = null;
if (bodyInfo.containsKey("raw")) {
body = ((String) bodyInfo.get("raw")).replace(System.lineSeparator(), "");
}
else if (bodyInfo.containsKey("formdata")) {
body = ((List) bodyInfo.get("formdata")).toString().replace(System.lineSeparator(), "");
}
PostmanRequest request = new PostmanRequest();
request.setName(name);
request.setUrl(url);
request.setMethod(method);
request.setHeaders(headers);
request.setBody(body);
requests.add(request);
}
return requests;
}

Map<String, Object> bodyInfo = (Map) requestInfo.get("body");
String body = null;
if (bodyInfo.containsKey("raw")) {
body = ((String) bodyInfo.get("raw")).replace(System.lineSeparator(), "");
} else if (bodyInfo.containsKey("formdata")) {
body = ((List) bodyInfo.get("formdata")).toString().replace(System.lineSeparator(), "");
}
PostmanRequest request = new PostmanRequest();
request.setUrl(url);
request.setMethod(method);
request.setHeaders(headers);
request.setBody(body);
return request;
}

private static String getUrl(Object url) {
return (url instanceof String) ? (String) url : (String) ((Map) url).get("raw");
}

}
Loading