How to call a particular javascript function from .js file in karate feature file - karate

Suppose I have saved followings functions in a Utility js file.
function getCurrentDate(){
return 'date';
}
function getMonth(){
return 'Oct';
}
Please help me how any of these methods can be accessed in feature file.
I tried following code but it is not working.
* def fun = call read('Utility.js')
* def result = getData()
or
* def result = fun.getData()

In Karate, a JS file can contain only one function and it does not need a name, take a closer look at the examples.
I don't really recommend combining multiple functions into one file, it just makes things much harder to maintain. But if you really insist, here's how:
function() {
return {
getCurrentDate: function(){ return 'date' },
getMonth: function(){ return 'month' }
}
}
EDIT: a much better answer is here: https://stackoverflow.com/a/49384760/143475

* def data = karate.read('classpath:<path>/getRandomString.js')(<inputToTheFunctionIfAny>);
The above code works.

Related

Karate - How to obtain response using execution hook

I have the following feature file:
Given def query = karate.call('classpath:query/Story/FindStoryByID.js')
And def variables = { id: "xxyy" }
And request { query: '#(query)', variables: '#(variables)' }
When method POST
Then status 200
And match response.data.FindStoryByID.id != '#null'
I am currently trying to do a beforeStep in order to write whole GraphQL request (query) of the feature to a file using karate.write.
So far I have come up with this:
#Override
public boolean beforeStep(Step step, ScenarioContext context) {
if (step.getText().trim().contains("request {")) {
System.out.println(step.getText());
}
return true;
}
This successfully triggers a print, which indicates I am poking on the right direction. The problem is that I haven't still able to figure out what should I do to access a variable (query) like the one we can do on a JS/Feature file (karate.get('query');)
I am wondering if it is even possible to achieve such feat through the execution hook like this?
Thanks a lot!
Ah found it! This do the job
context.vars.get("query"));

javascript function is not recognized as javascript code

I have the following javascript file MyService.js:
function(config) {
config.MyService = function(request) {
return call('classpath:path/to/my.feature#tag', request);
};
return config;
}
I load this js from my karate-config.js in order to reuse it from my feature files.
config = karate.callSingle('classpath:path/to/MyService.js', config);
It works as expected and I can call my.feature from any feature file. For example:
Given def res = call MyService myRequest
The problem appears when I try to add a new level to MyService.js function:
function(config) {
config.ApiOauthService = {
myCall : function(request) {
return call('classpath:path/to/my.feature#tag', request);
}
};
return config;
}
When I add the following code to my feature file:
Given def myCall = call MyService.myCall myRequest
I get the following error: "not a js function or feature file"
Do anybody know where is the problem? Is it possible to do what I am trying to do?
Thanks
Yes in Karate 0.9.3 onwards we have limited JS function nesting to only the top-level. You can find a full explanation in this comment.
So if you need JS function "name spacing" you can easily achieve this as per the documentation: https://github.com/intuit/karate#multiple-functions-in-one-file
And if you need more complex nesting, switch to using Java code where you can nest functions at any level.

Using karate built-in functions and api

Ok, so I am going to explain what my scenario is and how I have implemented it using karate. But i am looking for a better way of doing things which make my tests more readable and also want to use karate's api rather than too much javascript.
Scenario: I have a filter parameter for the api endpoint that i am testing and this filter param takes a json object of key-value pairs. So i have done the following:
Have created a filter-template.js as below:
function() {
var filter_params = {
jobId:null,
customerIds:[],
filterValues1:[],
filterValues2:[],
};
return filter_params;
}
I read this template in my karate scenario and because this is a template, at runtime i set the values in this template and run the test. I will have different values for key-value pairs so each test will set its own filter condition.
This is done by writing custom js function that takes the template as argument and also the filter condition values (referring to arg function argument below), sets the passed conditions to specific key's and then returns json object. Code below:
Scenario: Set filter scenario
* def filter_template = call read('filter-template.js')
* def filter_vals_list = [1001,1002]
* def filter_condition = { cnd1: 'foo', cnd2: '#(filter_vals_list)' }
* def setFilter =
"""
function(arg) {
var i;
var filter = arg.template;
filter.jobId = arg.condition.cnd1;
for(i=0;i<arg.condition.cnd2.length;i++)
{
filter.filterValues1.add(arg.condition.cnd2.get(i));
}
return filter;
}
"""
* def getFilter = call setFilter { template: '#(filter_template)',
condition: '#(filter_condition)' }
I then pass the getFilter as a param to my api request.
What I am hoping to understand is:
How can i get away from using JS loops above when setting filter?
Use karate's in-built functions like karate.map(), karate.forEach() to simplify the tests.
Any better approach if possible on tackling this scenario.
Help and guidance much appreciated.
From what I understood, I have simplified your scenario below:
* def filter_vals_list = [ 1001, 1002 ]
* def job_id = 'foo'
* def filter_template =
"""
{
jobId: '#(job_id)',
customerIds: [],
filterValues1: '#(filter_vals_list)',
filterValues2: [],
}
"""
Let me know if I missed anything. Please refer embedded expressions: https://github.com/intuit/karate#embedded-expressions
Now, you can easily use a re-usable JSON for the template, by changing the last step to the below, and yes - embedded expressions work even in re-usable JSON files !
* def filter_template = read('filter-template.json')
You may get even better ideas once you try the data-driven Scenario Outline. So hope that makes it clear how you are un-necessarily complicating things with JS ! You don't even need karate.map() etc.

Javascript code not working after upgrade to karate 0.9.3

The below function in feature files was working for version 0.9.2. Upgraded to 0.9.3 and this gives error : javascript function call failed: Index: 0.0, Size: 0. Code below:
var cnd = ['test1','test2'];
function set_filter(arg)
{
var i;
var filter = {filterValues:[]};
for(i=0;i<arg.length;i++)
{
filter.filterValues[i] = arg[i];
}
return filter;
}
set_filter(cnd)
Also i was earlier able to push values in a javascript array using below, but this has also stopped working in 0.9.3. Get error:javascript function call failed: TypeError: arr.push is not a function
var arr = [];
arr.push('test1','test2');
Try the scenario below that works in 0.9.2 but reports error (mentioned above) in 0.9.3
Scenario: JS test
* def filter_template =
"""
function() {
var filter_params = {
filterValues:[]
};
return filter_params;
}
"""
* def template = call filter_template
* def filter_condition = ['test1','test2']
* def setFilter =
"""
function(arg) {
var i;
var filter = arg.filter_template;
for(i=0;i<arg.condition.length;i++)
{
filter.filterValues[i] = arg.condition[i];
}
return filter;
}
"""
* def getFilter = call setFilter { filter_template: '#(template)', condition: '#(filter_condition)' }
* print getFilter
Help is much appreciated.
We've made the JS conversions stricter, everything will be a Java collection behind the scenes. If you make this change, things will start working:
filter.filterValues.set(i, arg.condition.get(i));
The same goes for push() - use add() or karate.appendTo(varname, value) instead.
My strong recommendation is don't use too much of JS logic especially loops. Karate has functions such as map(), forEach() and repeat() to solve for these needs. Please refer the docs here: https://github.com/intuit/karate#loops
You will thank me later because it will make your scripts easier to understand and maintain. One reason why this is needed is to pave the way for us to change the JS engine in the future.

How to use variables in a feature file, in a javascript function?

For example i do
* def fooresponse = call read('../getfooid.feature')
* def jsfunction= """(fooresponse){
console.log(fooresponse)}"""
is that possible? what is the recommended way to do it?
Thanks!
edit: fixing js syntax lol
Yes, this will work if the function has been defined after the variable fooresponse:
* def jsfunction = function(){ karate.log(fooresponse) }
Yes, you can pass a single argument to karate.call().
Keep in mind that the #(foo) substitution does NOT apply to pure-JS, explained here: https://github.com/intuit/karate#karate-expressions
I think once you read the above link AND if you are familiar with JS, you will know what to do.
Like this:
* def fun = function(){ karate.call('foo.feature', { bar: 'baz' }) }