karatelabs / karate

Test Automation Made Simple
https://karatelabs.github.io/karate
MIT License
8.11k stars 1.94k forks source link

1.0 release thread #1373

Closed ptrthomas closed 3 years ago

ptrthomas commented 3 years ago

this issue is to provide updates and collect feedback. also will hold some images needed for the wiki

this wiki page will hold details: https://github.com/intuit/karate/wiki/1.0-upgrade-guide

ptrthomas commented 3 years ago
image
ptrthomas commented 3 years ago
image
hujunhaorobert commented 3 years ago

@ptrthomas I had rebuilt the rewrite branch locally and generated karate-2.0.0.jar, and below is test finding:

  1. the fixing solution for issue #1365 I reported is not integrated to rewrite branch yet and the issue is still there. java.lang.NullPointerException stack trace: com.intuit.karate.core.ScriptBridge.xmlPath(ScriptBridge.java:426)
  2. When building Standalone JAR and ZIP, under karate-netty folder, launch "mvn install -P fatjar", noticed there are some client testing.feature failures, Can you check and fix it? e.g. 18:05:59.883 [main] DEBUG com.intuit.karate - abort at classpath:com/intuit/karate/client.feature:73 18:05:59.883 [main] INFO c.intuit.karate.netty.FeatureServer - stop: shutting down 18:05:59.884 [main] INFO c.intuit.karate.netty.FeatureServer - stop: shutdown complete 18:05:59.884 [main] INFO com.intuit.karate.netty.ProxyServer - stop: shutting down 18:05:59.885 [main] INFO com.intuit.karate.netty.ProxyServer - stop: shutdown complete [ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.762 s <<< FAILURE! - in com.intuit.karate.ProxyServerSslTest [ERROR] testProxy(com.intuit.karate.ProxyServerSslTest) Time elapsed: 0.747 s <<< ERROR! com.intuit.karate.exception.KarateException: client.feature:11 - io/netty/handler/codec/http/HttpUtil client.feature:60 - io/netty/handler/codec/http/HttpUtil client.feature:66 - io/netty/handler/codec/http/HttpUtil at com.intuit.karate.ProxyServerSslTest.testProxy(ProxyServerSslTest.java:61)

The same issue also found when I use the karate-2.0.0.jar to run local karate API testing against karate mock server.

ptrthomas commented 3 years ago

@hujunhaorobert thanks for trying out the rewrite branch. I have made changes for bodyPath() to work in the new code: https://github.com/intuit/karate/commit/6ce22ce1668e3939aa23e5b5a8b9b7eb0808019b

I also suspect you may not have switched cleanly to the rewrite branch, there should not be anything under karate-netty anymore, it is all moved to karate-core - anyway please see if the mocks are ok

if you still have trouble, I'll try to get a better way of trying RC releases, but I don't want to release JAR files to maven just yet

ptrthomas commented 3 years ago
image

passing JS functions top Java code that will be executed on another thread has to be wrapped using karate.toJava()

this may still have issues, in that case - last resort is to move the whole flow to Java

refer https://github.com/graalvm/graaljs/issues/59

hujunhaorobert commented 3 years ago

@ptrthomas Yes, I met some trouble to get standalone karate-{version}.jar for my mock testing. In order to regenerate that JAR, I was following the Developer Guide of Build Standalone JAR and ZIP from https://github.com/intuit/karate/wiki/Developer-Guide. I see there is nothing except README.md under karate-netty now, can you specify how to generate Karate standalone JAR, or how can I get a RC release? Thanks!

ptrthomas commented 3 years ago

@hujunhaorobert since all the code is in karate-core it is a little simpler.

ptrthomas commented 3 years ago
image

breaking change - but this was the best way to solve the js threading problem

hujunhaorobert commented 3 years ago

@ptrthomas By following your instruction above, I re-generated the karate-2.0.0.jar successfully, which is good.

I copy this jar file to my mockTest project, and put my customised karate-config.js file under the mockTest project folder, same as main-mock-server.feature. When I launching my mock server by CLI: java -cp karate-2.0.0.jar com.intuit.karate.Main -m main-mock-server.feature -p {{port-id}}

The program throw an exception when running below line(11) in main-mock-server.feature

Console log is as below:

12:08:41.630 [main]  INFO  com.intuit.karate.Main - Karate version: 2.0.0
12:08:43.299 [main]  ERROR com.intuit.karate - mock-server background failed - main-mock-server.feature:11
com.intuit.karate.KarateException: mock-server background failed - main-mock-server.feature:11
        at com.intuit.karate.core.MockHandler.<init>(MockHandler.java:103)
        at com.intuit.karate.core.MockServer$Builder.build(MockServer.java:112)
        at com.intuit.karate.Main.call(Main.java:221)
        at com.intuit.karate.Main.call(Main.java:47)
        at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
        at picocli.CommandLine.access$1200(CommandLine.java:145)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
        at picocli.CommandLine.execute(CommandLine.java:2058)
        at com.intuit.karate.Main.main(Main.java:145)
Caused by: com.intuit.karate.KarateException: >>>> js failed:
01: read('classpath:karate-config.js')
<<<<
org.graalvm.polyglot.PolyglotException: not found: karate-config.js
- com.intuit.karate.resource.ResourceUtils.getResource(ResourceUtils.java:111)
- com.intuit.karate.core.ScenarioFileReader.toResource(ScenarioFileReader.java:122)
- com.intuit.karate.core.ScenarioFileReader.readFileAsStream(ScenarioFileReader.java:97)
- com.intuit.karate.core.ScenarioFileReader.readFileAsString(ScenarioFileReader.java:93)
- com.intuit.karate.core.ScenarioFileReader.readFile(ScenarioFileReader.java:56)
- com.intuit.karate.core.ScenarioEngine.lambda$new$0(ScenarioEngine.java:134)
- <js>.:program(Unnamed:1)

main-mock-server.feature:11

I had a quick debugging, and noticed that there was a bug when parsing the path in getResource(path), here the path=classpath:karate-config.js. After calling path = removePrefix(path); the path will be a file name(karate-config.js), rather than a real path, which causes throwing the exception. I will leave the bug with you.

ptrthomas commented 3 years ago

@hujunhaorobert it is likely the previous behavior was a bug. in mocks, there is not really a classpath: concept unless you know what classpath is set when you start the JVM. in this case use a relative path or set the directory that karate will use for finding the karate-config.js as per the documentation. if you still feel this is an issue please provide a sample project

sumargoraymond commented 3 years ago

Returning object literal with karate.call('some_js_file.js') does not work on the RC.

Example js file:

//SampleJS.js
function fn(){
var A = "2";
var B = "3";
return {
varA:A,
varB:B
}

}

Calling the js file's method using karate.call with below code returns undefined:

var variables = karate.callSingle('SampleJS.js");
console.log(variables.varA);
sumargoraymond commented 3 years ago

Calling Java function using obtained variable raises an error.

The following is the sample feature file:

Scenario: Get All Queries
        * def query = karate.call('classpath:schema/GetAllQueries.js')
        * request { query: '#(query)'}
        * method POST
        * def queriesName = $response.data.__schema.queryType.fields[*].name

        * def query = karate.call('classpath:schema/GetAllMutations.js')
        * request { query: '#(query)'}
        * method POST
        * def mutationsName = $response.data.__schema.mutationType.fields[*].name

        * def allQueriesName = karate.append(mutationsName, queriesName)

       # allQueriesName = ['[QueryA, QueryB, QueryC, QueryD, MutationA, MutationB, MutationC, MutationD...']

        * def Utilities = Java.type('features._GQLCoverageTools.FilterQueries')
        * Utilities.printToFile(allQueriesName)

Java file is as below:

package features._GQLCoverageTools;
import com.intuit.karate.graal.JsList;

public class FilterQueries {

    public static void printToFile(JsList array) throws Exception{
        System.out.println(array);
    }
}

The error raised is:

org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (printToFile) on features._GQLCoverageTools.FilterQueries failed due to: Cannot convert '[QueryA, QueryB, QueryC, QueryD, MutationA, MutationB, MutationC, MutationD....'(language: Java, type: com.intuit.karate.graal.JsList) to Java type 'com.intuit.karate.graal.JsList': Unsupported target type.
- <js>.:program(Unnamed:1)

Quite weird considering the data type used is an appropriate one.

ptrthomas commented 3 years ago

@sumargoraymond for your first comment, I cannot replicate: https://github.com/intuit/karate/commit/263ce53140ee1960e27ef655850fcd7b58ead872

and regarding the second, JsList is not intended for end-users, just use normal Java Map / List / primitives like before, please

sumargoraymond commented 3 years ago

EDITED:

It works well using List

sumargoraymond commented 3 years ago

Hi @ptrthomas , can you try outputting https://github.com/intuit/karate/commit/263ce53140ee1960e27ef655850fcd7b58ead872 with result.varA instead?

Thanks

ptrthomas commented 3 years ago

@sumargoraymond you are absolutely right ! thanks :) should be fixed now

ptrthomas commented 3 years ago
image

some improvements to the Runner.builder() that are subtle but you are going to enjoy:

sumargoraymond commented 3 years ago

-Dkarate.options passed from the terminal does not seem to be honored when using hook on test runner.

E.g. running mvn clean test -Dkarate.options="--tags @test" -Dtest=TestRunner -Dkarate.env=production should run test with the annotation: @test

// TestRunner.java
 Results result = Runner.path("classpath:features").hook(new KarateExecutionHook()).tags("~@ignore")
              .parallel(10);
class KarateExecutionHook implements RuntimeHook {
// Here we override some of the before and after hooks
}

The test runner will run tests with the default annotation (~@ignore) instead of the @test from the terminal input.

ptrthomas commented 3 years ago

@sumargoraymond this is a tough one. I'm planning to change the way this works. what I propose is:

in fact the code example in the post just above is an example why this is needed. here, karate.env=docker and server.port will be set by Java code and cannot be changed by setting karate.options

that said - if you want to have this flexibility at run time, you have 2 options:

comments from anyone welcome

ptrthomas commented 3 years ago

@sumargoraymond please ignore what I said above, I remembered that frameworks e.g spring / boot give highest priority to command-line, then system properties - and this is the right way to be able to change things at deployment / ci

so I made it work now like it used to, can you please take a look again and confirm

and thanks for trying out the early versions like this - it makes things so much easier 👍

sumargoraymond commented 3 years ago

Sure thing, will do. Thanks @ptrthomas

ptrthomas commented 3 years ago

@sumargoraymond can you provide the values of the request and response headers, specifically the Cookie and Set-Cookie ones

sumargoraymond commented 3 years ago

@ptrthomas Edited: I just tried hitting the same request again and it successfully loaded an xml. Yet on the RC the error is still the same:

io.netty.handler.codec.http.cookie.DefaultCookie.sameSite()Lio/netty/handler/codec/http/cookie/CookieHeaderNames$SameSite;

Cannot seem to find any Cookie nor Set-Cookie on both the request and responses though.

ptrthomas commented 3 years ago

@sumargoraymond unfortunately I can't do anything unless I get some way to replicate, so I'm going to move on to other things. it is possible that your server has a bug and it doesn't support some cookies: https://web.dev/samesite-cookies-explained/

I'm sure you can find a way to replicate. for e.g.

* url 'https://httpbin.org/cookies'
* cookie foo = { value: 'bar', samesite: 'Strict' }
* method get
* status 200
* match response == { cookies : { foo: 'bar', SameSite: 'Strict' } }

* url 'https://widget-pixels.outbrain.com/widget/detect/px.gif?ch=1&rn=1.3815335980445032'
* method get
* status 200
* match responseCookies.akacd_widgets_routing.samesite == 'None'
sumargoraymond commented 3 years ago

My bad @ptrthomas , the server does indeed require a certain key value pair for the cookie. After implementing the necessary cookie on the header, it works well on RC.

ptrthomas commented 3 years ago

@sumargoraymond great. I'm still worried about the error message, since it is not helpful and I'm not able to understand how that will happen. please look out for ways you can replicate it - I'm sure you can mask all the values and figure out a way to replicate, thanks

sumargoraymond commented 3 years ago

@ptrthomas Just realized it today, here is the set-cookie value from the response header of the failure. Just in case it might be useful:

__cfduid=df9780asomecloudflarecookieid; expires=Wed, 30-Dec-20 09:25:45 GMT; path=/; domain=.ourdomain.com; HttpOnly; SameSite=Lax; Secure
--
ptrthomas commented 3 years ago

@sumargoraymond that helps. I made some config changes, so do test when possible

by the way everything is back on the develop branch, I've updated the developer guide: https://github.com/intuit/karate/wiki/Developer-Guide

sumargoraymond commented 3 years ago

Thanks @ptrthomas tests now run like a charm

ptrthomas commented 3 years ago

reading of karate.options from the system properties / command-line is broken in 0.9.9.RC1 - thanks @mihirmehta for the report. this is fixed in the develop branch

corp-ray commented 3 years ago

If an exception is thrown in karate-config.js, all data about it is swallowed (no stack trace or error message). The only thing shown is:

    karate-config.js failed
    - com.intuit.karate.core.ScenarioRuntime.evalConfigJs(ScenarioRuntime.java:321)
    - com.intuit.karate.core.ScenarioRuntime.beforeRun(ScenarioRuntime.java:299)

Workaround is to catch the exception in JS and log it in JS, though even that doesn't show the whole error. This happens both when a JS error is manually thrown and when Karate throws an error (like when the file path is wrong in a karate.call statement)

ptrthomas commented 3 years ago

@corp-ray you mean 0.9.9.RC1 right, try now from the develop branch

corp-ray commented 3 years ago

Yes I meant 0.9.9.RC1, sorry for not clarifying. Okay will test from develop.

corp-ray commented 3 years ago

On develop, I do now see a better error message. There are some minor things I still notice : -When karate-config throws, Karate seems to be implying the error comes from the first line of code in each feature file (it prints that line out as an ERROR log) -If a JS file is called by a JS file, both blocks of JS are printed out in their entirety -Neither the file name nor the function name of the second JS file are displayed. -While the line number of the error is now printed, it's not quite easy to see. Looks like this:

    org.graalvm.polyglot.PolyglotException: not found: src/test/KarateUtilities/someFile.json
    - com.intuit.karate.resource.ResourceUtils.getResource(ResourceUtils.java:114)
    - com.intuit.karate.core.ScenarioFileReader.toResource(ScenarioFileReader.java:122)
    - com.intuit.karate.core.ScenarioFileReader.readFileAsStream(ScenarioFileReader.java:97)
    - com.intuit.karate.core.ScenarioFileReader.readFileAsString(ScenarioFileReader.java:93)
    - com.intuit.karate.core.ScenarioFileReader.readFile(ScenarioFileReader.java:52)
    - com.intuit.karate.core.ScenarioEngine.lambda$new$0(ScenarioEngine.java:137)
    - <js>.:anonymous(Unnamed:20)

The error is on line 20 of the JS file, but because the file name isn't printed here, that might not be clear.

ptrthomas commented 3 years ago

@corp-ray good. this is the best I could do given the fact that we execute the JS in-memory. you are welcome to find ways to improve it further and contribute code. thanks

ivangsa commented 3 years ago

Hi vscode debuger doesn't stop when using 0.9.9.RC1 I put together this minimal example, it comes with all required configuration required by vscode karate-runner and two maven profiles one for 0.9.6 and 0.9.9.RC1

Steps:

when running with 0.9.6 dependencies it will stop but it will continue with dep. 0.9.9 debug port seem listening in both cases

cc/ @kirksl

karate-1.0.0-tests.zip

ptrthomas commented 3 years ago

@ivangsa took one look at your pom.xml and it is not in the quick start format. I'll pass on this one, anyone else is welcome to take a look

joelpramos commented 3 years ago

@ptrthomas fyi I can't seem to find the outputCucumberJson() described here method in the Runner API (neither in the RC on maven central nor in any branch).

joelpramos commented 3 years ago

I had the following feature file:

Feature: Reusable function
Scenario:
  * def uuid = function(){ return java.util.UUID.randomUUID() + '' };

The Graal engine complains of the semi-colon when using this inline syntax.

org.graalvm.polyglot.PolyglotException: SyntaxError: Unnamed:1:54 Expected ) but found ;
(function(){ return java.util.UUID.randomUUID() + '' };)

Removing the semi-colon works and it's something I noticed in the example commit for the Javascript differences but seems like just "code cleanup" in the diff.

Might be good to explicitly call it out in the breaking changes on the Wiki page

Edit - another minor potential update to the docs: I personally use the Java Interop API a lot for several generic functions and utilities for all tests and with the changes on Lists and Maps had to do a lot of updates (no longer receiving a java.util.List but rater com.intuit.karate.graal.JsList or JsMap). Something very simply handled through override but I think it's worth a bullet point in the upgrade page (maybe under here)

ptrthomas commented 3 years ago

@joelpramos I hope you eventually found the outputCucumberJson(true), and I updated the wiki for trailing semi-colons

regarding the JsList and JsMap those should normally never be visible to end-users. but I guess you do Java stuff in a JS block, and are still within a JS block, then this happens. if you have an example or two, I'll add it to the docs, but I'm not inclined to support this. but if you feel it is worth it - it is possible with a little work to make JsList and JsMap implement List and Map, so feel free to take a shot at this - and if it works, go ahead with a PR !

ptrthomas commented 3 years ago

@joelpramos hang on - let me take a look at JsList and JsMap

joelpramos commented 3 years ago

A few more things that seem to break with the new JS engine.

1

Scenario:
  * def now =
    """
      function() {
        return java.time.LocalDate.now();
      }
    """
  * def dateToString =
    """
    function(date, format) {
      var date_obj;
      if(date instanceof java.lang.String) {
        if(date.length == 10) {
          date_obj = java.time.LocalDate.parse(date);
        } else {
          if(date.length == 19 && date.charAt(10) == ' ') {
            var date_copy = date;
            date = date_copy.substring(0, 10) + 'T' + date_copy.substring(11);
          }

          date_obj = java.time.LocalDateTime.parse(date);
        }
      } else if(date instanceof java.time.LocalDate) {
        date_obj = date;
      }

      var formatter = (Java.type('java.time.format.DateTimeFormatter')).ofPattern(format);
      return formatter.format(date_obj);
    }
    """

  * def dateToShortString =
    """
    function(date, separator) {
      return dateToString(date, "yyyy"+separator+"MM"+separator+"dd");
    }
    """

The Graal engine doesn't seem to have access to the variable that was previously defined. I'm looking into if it's something easily solved as I'd like to avoid refactoring this as it's a pattern I had our users do quite frequently to make tests look very simple.

2

Had the following in my karate-config-unit-test.js . Graal now fails cause "config" is not defined. A bad implementation of mine anyways (and I'm not really certain how it worked before) but just flagging in case anyone does the same. Just create the var config = {} as it's a local variable anyways and you return it as part of the javascript function which will then get added to the Karate context.

function fn() {
    // config file for the 'unit-test' environment
    var env = karate.env; // get java system property 'karate.env'

    // sample
    if (env == 'unit-test') {
        config.env.httpScheme = 'http';
        config.env.appUrl = 'localhost';
        config.env.appPort = karate.properties['demo.server.port']
        if(!config.env.appPort) {
            config.env.appPort = 8080
        }
    }
}
joelpramos commented 3 years ago

@joelpramos I hope you eventually found the outputCucumberJson(true), and I updated the wiki for trailing semi-colons

Yes it's in the develop branch, just not in the RC :)

regarding the JsList and JsMap those should normally never be visible to end-users. but I guess you do Java stuff in a JS block, and are still within a JS block, then this happens. if you have an example or two, I'll add it to the docs, but I'm not inclined to support this. but if you feel it is worth it - it is possible with a little work to make JsList and JsMap implement List and Map, so feel free to take a shot at this - and if it works, go ahead with a PR !

I actually didn't mind at all with JsList - just overloaded methods. Was just suggesting the note in the documentation. It's not challenging and actually makes it even clearer in the code that method is being used by Karate so it should have a unit test being done from Karate (using a feature file) and the overloaded method can have a unit test directly from Java. I only had 20 or so methods so it was fine, not sure if you'll find people complaining a lot of this change.

ptrthomas commented 3 years ago

@joelpramos I went ahead and did the JsList / Map tweak anyway, I do remember / think that other users are using java-isms in JS: https://github.com/intuit/karate/commit/b122ca8800da99126f6665dcad70e9fb2fae0765

would be good if you can confirm it does work as expected - so things like list.add() should be sufficient instead of list.push()

for the (1) in your example above, pls do try simplify it, I don't get it, I think (2) is fine without any docs

joelpramos commented 3 years ago

@joelpramos I went ahead and did the JsList / Map tweak anyway, I do remember / think that other users are using java-isms in JS: b122ca8

would be good if you can confirm it does work as expected - so things like list.add() should be sufficient instead of list.push()

Just tested - works fine for my changes but the following is not working in "pure" Karate/JS. I assume that's the intended behavior - to align with Graal like specified in the the docs for 1.0.0.

  And def list = []
  And list.add("test")

for the (1) in your example above, pls do try simplify it, I don't get it, I think (2) is fine without any docs

There are a lot of methods and common functions in testing and too often I've just seen duplication so my preference is to define functions which reuse functions - like any other piece of software. In order to maintain the karate syntax my preference has been to use def and define functions but then within the javascript code itself I want to have access to those Karate variables.

We use it quite frequently even to reuse features that are core or "larger" pieces of tests (e.g. pre-conditions of creating data across several systems which can be reused etc.).

Simpler example which won't work:

  * def karateNow =
    """
      function() {
        return java.time.LocalDate.now();
      }
    """

  * def tomorrow =
    """
      function() {
        return karateNow().plusDays(1);
      }
    """

The Graal Engine doesn't seem to recognize the function karateNow() which was defined as part of Karate. I think with the older engine there was a concept of Bindings that were always kept up to date after any evaluation of the Karate code / line and this reusability concept worked nicely.

ptrthomas commented 3 years ago

@joelpramos that's weird - this works perfectly for me. in fact the JS engine bindings are kept up-to-date with def - see setVariable(String key, Object value) in ScenarioEngine

* def karateNow =
"""
function() {
  return java.time.LocalDate.now();
}
"""

* def tomorrow =
"""
function() {
  return karateNow().plusDays(1);
}
"""
* def temp = tomorrow()
* print temp

output:

23:00:30.650 [main] INFO  com.intuit.karate - [print] 2020-12-14 
joelpramos commented 3 years ago

In that case it might be something else... I have a .feature file (named framework-utils.feature) which is called in my karate-config.js using karate.callSingle() and it's the function inside that framework-utils.feature that is failing to find the reference. Maybe the issue with the bindings could be from the callSingle().

Minimal code: karate-config.js

function fn() {
   var config = {};
   config.Utils = karate.callSingle('classpath:karate/framework-utils.feature');
   return config;
}

framework-utils.feature

Scenario:
  * def dateToString =
    """
    function(date, format) {
      // long function to format dates
     // posted before but don't think it's relevant for the issue
      return "formatted/date/here";
    }
    """

  * def dateToShortString =
    """
    function(date, separator) {
      return dateToString(date, "yyyy"+separator+"MM"+separator+"dd");
    }
    """

And then on my karate-utils-js.feature unit test I have the following:

Feature: unit test
@dateToShortString-method
Scenario:
  When def date = "2020-12-22T03:39:21"
  And def dateShortString = Utils.dateToShortString(date, "/")
  Then assert dateShortString == '2020/12/22'

And I get the following error below. Note that it's not that the function I'm calling is not found, it's that function can't find the other "reusable" function.

12:40:53.308 [main] ERROR com.intuit.karate - classpath:input/karate/karate-utils-js.feature:32
And def dateShortString = Utils.dateToShortString(date, "/")
>>>> js failed:
01: Utils.dateToShortString(date, "/")
<<<<
org.graalvm.polyglot.PolyglotException: ReferenceError: "dateToString" is not defined
- <js>.:anonymous(Unnamed:2)
joelpramos commented 3 years ago

Adding the line engine.setVariables((Map)result); (or for me quickly calling with the debugger) in ScenarioBridge.java:226 (after the callOnce is performed) seemed to fix this but I just did as a quick hack. Not sure whether there are any implications to it as I didn't run the unit tests or anything but seems to be the solution.

ptrthomas commented 3 years ago

@joelpramos you mean callSingle() because a LOT of work went into callonce to be able to re-hydrate functions. I am proposing that callSingle() have the limitation that only "pure-data" can be cached. especially since only that would work with the option to cache to file. if you can get callSingle() to work the way you want, great - but it may be tricky. remember callSingle() has to span features

joelpramos commented 3 years ago

Yes - sorry callSingle(). I'd still like to have the option to have that call done from within the karate-config.js cause it basically allows me to "extend" the Karate DSL.

I understanding the principle you're proposing callSingle() to be for pure-data. At first I tried adding the line as I suggested above but then one of the unit tests failed - the resulting variable had the same values but it was a new / different object.

I ended up extending the ScriptBridge with a new function which is basically a wrapper for the callonce. The same principle as callSingle() but caches the Karate Result and so it avoids parsing that file with every single execution in a Scenario Outline and at the same time retains the functionality I'm looking for.

    public Object readAndCallOnce(String filePath) {
        String karateReadExp = "read('" + filePath + "')";
        Variable variable = this.getEngine().call(true, karateReadExp, false);
        return JsValue.fromJava(variable);
    }

And in my karate-config.js: config.Utils = karate.readAndCallOnce('classpath:karate/framework-utils.feature');

Let me know if you're good with this and I can open the PR - or if you think it's just best to expose the entire parameters of callonce or have any other suggestions.

Edit I also changed my "utils" feature to use the prefix in the function call that I assign to the result of the feature. Note the Utils in the return. Not a big deal for me as I see it as the namespace of my additions to the DSL.

  * def dateToShortString =
    """
    function(date, separator) {
      return Utils.dateToString(date, "yyyy"+separator+"MM"+separator+"dd");
    }
    """