Cannot convert to map when re-use one feature on other directory - karate

I have read: https://stackoverflow.com/search?q=%5Bkarate%5Dcannot+convert+to+map and https://github.com/intuit/karate/issues/544
I am using karate-0.8.0
I have one feature which will be re-used on A directory, the content like:
#ignore
Feature:
Background:
* url baseUrl
* def Sign = Java.type('cruiser.token.Sign')
* configure afterScenario =
"""
function() {
if (karate.info.errorMessage != null) {
karate.log(karate.info.errorMessage);
}
}
"""
Scenario:
* def ck = Sign.execute('#(uid)')
* path '/rest/n/rt/upload'
* cookies ck
* multipart fields '#(fo)'
* multipart file rt = { read: 'classpath:cruiser/http/rt/A/123.mp3', filename: '123.mp3', contentType: 'audio/mp3' }
* method post
* status 200
* match response contains { result: 1 }
And have other one feature file on B directory, content like this:
Feature:
Background:
Scenario:
* def fo =
"""
{
'title': '你好!',
'description': '很好听哦'
}
"""
* def x = call read('classpath:cruiser/http/rt/A/upload-base.feature') { uid: 33, fo: '#(fo)' }
* match x.response contains { result: 1 }
* print x.response.feed.id
its runner name is XRunner.java
when mvn test -Dtest=XRunner, the error info:
Running cruiser.http.rt.B.XRunner
11:25:33.138 [main] INFO com.intuit.karate.junit4.Karate - Karate version: 0.8.0
11:25:33.896 [main] ERROR com.intuit.karate - feature call failed: classpath:cruiser/http/rt/A/upload-base.feature
arg: {uid=33, fo={title=你好!, description=很好听哦}}
cannot convert to map: '#(fo)'
Failed scenarios:
cruiser/http/rt/B/x.feature:3 # Scenario:

Both these lines are wrong:
* def ck = Sign.execute('#(uid)')
* multipart fields '#(fo)'
Read this: https://github.com/intuit/karate#rules-for-embedded-expressions
In Karate, expressions are pure JS by default. So just do this:
* def ck = Sign.execute(uid)
* multipart fields fo

Related

How to traverse through a Json in Karate

I am trying to have a Karate test where I need to traverse through a json to get the required test data.
I have the below json.
{
"dev":{
"scenario1":{
"data":"data1"
},
"scenario2":{
"data":"data2"
}
},
"qa":{
"scenario1":{
"data":"data1"
},
"scenario2":{
"data":"data2"
}
}
}
Below is my feature file.
Background:
* def env = dev
Scenario:
* Given url someurl
* def inputData = testdata.env.scenario1.data
* And request { input: '#(inputData)'}
* When method post
* Then status 200
I need the test data that matches the value env that I have defined above.
How can I set the json path to my input data. Basically, the json path should take the value from a variable I have defined previously.
For this you don't even need Json-Path, just JS will do:
* def data = { foo: 1, bar: 2 }
* def env = 'bar'
* def res = data[env]
* match res == 2
Also see: https://stackoverflow.com/a/59162760/143475
You can also do dynamic Json-Path, see: https://stackoverflow.com/a/50855425/143475

Escape response in karate framework [duplicate]

For some reason a variable with a / character get converted to a \/, how do I prevent this?
I start a echo server that listens on localhost:3000 by running npx http-echo-server
I execute the following:
code:
* def CHALLENGE_USER = '/abc/user'
* def loginJson = { user: '#(CHALLENGE_USER)' , name: 'Some Name'}
* print loginJson
* def TEST_URL = 'http://localhost:3000'
Given url TEST_URL+'/session/loginresponse'
And header Content-Type = 'application/json'
And request loginResponseJson
And method put
Then status 200
It prints { "user": "/abc/user", "name": "Some Name" } like I expect.
The http server logs show "--> {"user":"/schemes/ATT_5_55/CH_1","name":"Some Name"}"
Karate shows the result of the echo {"user":"\/abc\/user","name":"Some Name"}
I have tried:
def CHALLENGE_USER = '/abc/user'
def CHALLENGE_USER = "/abc/user"
def CHALLENGE_USER = '/abc/user'
def CHALLENGE_USER = '//abc//user'
also setting the variable after the fact does not work:
* def loginJson = { name: 'Some Name'}
* loginJson.user = CHALLENGE_USER
Yes this is legal as per the JSON spec: JSON: why are forward slashes escaped?
And the Java libraries we use does that.
Does your server have a problem ? If so - then you have a bug that Karate surfaced.
And if you really want to have full control over the request, please use text but IMO it may be a waste of time: https://stackoverflow.com/a/68344856/143475
A nasty workaround, please forgive me Peter Thomas.
You can convert the json to a string and then remove the \ characters.
I only have one use case for this thank goodness.
* def CHALLENGE_USER = '/abc/user'
* def loginJson = { user: '#(CHALLENGE_USER)' , name: 'Some Name'}
* string json = loginJson
* def loginJsonText = json.replaceAll("\\", "")
* print loginJson
* def TEST_URL = 'http://localhost:3000'
Given url TEST_URL+'/session/loginresponse'
And header Content-Type = 'application/json'
And request loginJsonText
And method put
Then status 200

How to access karate ui automation driver details like sessionid in afterScenario? [duplicate]

This question already has an answer here:
KARATE integration with SauceLabs
(1 answer)
Closed 1 year ago.
I am reading the json file which contains the array of configurations of browserstack browsers or devices on which I want run my UI automation tests and defining it in karate-config.js as a global variable, for ex: config.envrironments. That variable I am calling it in Examples Table as envrironments and using it in dynamic scenario outline to initialize driver for different browser/device sessions as envrironments[__num]. The execution is working fine as expected.
After execution, I want to call the browserstack api which updates the test scenario status as passed or failed with a reason in their dashboard or in the browserstack report integrated in jenkins for clear understanding. The api requires driver sessionId as api path param, status(passed or failed) and reason(if failed) as body.
I am able to get the status and reason from karate.info.errorMessage after configuring afterScenario for the feature. But my problem is to get the browserstack sessionId.
To get the driver.sessionId in afterScenario, i'm getting error as "driver" is not defined. I guess driver object is getting killed in between each execution of scenario outline and the afterScenario.
Is there any way to keep the driver alive until the afterScenario is completed?
or
any other alternatives like implementing ExecutionHook class or any other class through which we can get the driver details?
Please help. Thanks in advance.
UPDATE:
I'm using Java 8 and karate version 0.9.6
Below is my code:
Test Feature -
Feature: Test Feature
Background:
* def scenarioStatus =
"""
{
"status": "passed",
"reason": ""
}
"""
* configure afterScenario =
"""
function(){
if(karate.info.errorMessage){
scenarioStatus.status = 'failed';
scenarioStatus.reason = karate.info.errorMessage;
}
karate.log('session id : ' + karate.get('sessionId'));
// karate.call('browserstack.feature', scenarioStatus);
}
"""
#dummyMobileBrowser
Scenario Outline: Dummy Scenario
* def browserTestName = karate.info.scenarioName + ' - '
* call read('driver.feature#initializeDriver')
* def sessionId = driver.sessionId
* call read('loginPage.feature#login')
# I'm defining array of device or browser configs in karate-config.js
Examples:
| deviceConfigs |
driver.feature
Feature: Driver Related Feature
Background:
* def jsUtils = read('classpath:jsUtils.js')
* def getDriverConfig =
"""
function(){
if(browserstack == "yes"){
var configResult = karate.call('driver.feature#createBrowserStackConfig');
var browserstackConfig = configResult.browserstackConfig;
return browserstackConfig;
}else{
return deviceConfigs[__num];
}
}
"""
#initializeDriver
Scenario: Initialize Driver
* def driverConfig = getDriverConfig()
* configure driver = driverConfig
* print 'Driver Config: ', driverConfig
* driver envHost
# also tried to def here
# * def sessionId = driver.sessionId
# * eval scenarioStatus.browserstackSessionId = sessionId
# * karate.write(sessionId, 'classpath:browserstackSessionId.txt')
* driver.fullscreen()
#takeScreenshot
Scenario: Take screenshot
* driver.screenshot()
#createBrowserStackConfig
Scenario: Create Browserstack Config
* def deviceCapabilities = deviceConfigs[__num]
* def driverUrl = 'https://' + browserstackUsername + ':' + browserstackKey + '#' + browserstackUrl + '/wd/hub'
* eval commonCapabilities.build = (karate.match(typeof browserstackBuildName, 'undefined').pass) ? commonCapabilities.build + currentEpochTime : browserstackBuildName
* def desiredCapabilities = karate.merge(deviceCapabilities, commonCapabilities)
* def driverType = (karate.match(desiredCapabilities.browserName, "#notnull").pass) ? desiredCapabilities.browserName : desiredCapabilities.browser + 'driver'
* eval driverType = (karate.match(driverType, 'firefoxdriver').pass) ? 'geckodriver' : driverType
* eval desiredCapabilities.name = (karate.match(desiredCapabilities.browserName, "#notnull").pass) ? desiredCapabilities.browserName : desiredCapabilities.browser
* eval desiredCapabilities.name = browserTestName + desiredCapabilities.name
* def capabilities = karate.merge(deviceCapabilities, commonCapabilities)
* eval capabilities.name = (karate.match(capabilities.browserName, "#notnull").pass) ? capabilities.browserName : capabilities.browser
* eval capabilities.name = browserTestName + capabilities.name
* def browserSession = { desiredCapabilities: '#(desiredCapabilities)', capabilities: '#(capabilities)' }
* def browserstackConfig = { type: '#(driverType)', webDriverSession: '#(browserSession)', start: false, webDriverUrl: '#(driverUrl)' }
Even if the driver is not callable - you should be able to access variables, right ?
So if you did this within the normal test-flow:
* def sessionId = driver.sessionId
Then the sessionId variable should be directly accessible in the after-hooks. Do karate.get('sessionId') if needed, try it and please comment below if it works.

call the first feature from the second feature file, failed without specified clue

Run the first.feature file successfully,however, call it from the second.feature failed without any clue to analysis. Do you have any idea help me find the root cause?
The source of my first.feature:
Feature: 采样管理-样本登记
Background: 读取随机生成的条形码、手机号、采样类型等作为入参
* url baseURL
* def randomData = Java.type('utils.RandomData')
* def barcode = randomData.getRandom(11)
* def randomPhone = randomData.getTelephone()
* def sampletype = randomData.getNum(0,1)
Scenario: 输入合法参数进行正常样本登记,确认能够登记成功
Given path 'iEhr/PersonSample'
# * header Content-type = 'application/x-www-form-urlencoded; charset=UTF-8'
* cookies { JSESSIONID: '#(jsessionID)',SESSION: '#(sessionID)', ACMETMP: '#(acmetmpID)'}
* def autoMotherName = "autoMname"+ barcode
# * def confData = {mothername: "#(autoMotherName)", barcode: "#(barcode)", mobile: '#(randomPhone)', sampletype:"#(sampletype)" }
# 设置sampletype为1,已被采样
* def confData = {mothername: "#(autoMotherName)", barcode: "#(barcode)", mobile: '#(randomPhone)', sampletype:"1" }
# 打印入参变量输出
* print confData
# 用例与数据分离
* def paramObj = read('classpath:mainFlow/sampleSaveReqTest.json')
* print paramObj
* form field param = paramObj
When method post
Then status 200
* json result = response[0].result
* def personId = result[0].personid
* def sampleid = result[0].sampleid
* print personId
* print sampleid
The source of my second.feature:
Feature: 提交递送样本
Background:
* def sampleResult = call read('classpath:mainFlow/first.feature')
* print sampleResult
I run the first.feature singly, it works. However, karate reports the error below after running the second.feature. Any idea how can I debug to find the root cause? I have no idea what's wrong with the second read. Many thanks!
* def sampleResult = call read('classpath:mainFlow/first.feature')
-unknown-:14 - javascript evaluation failed: read('classpath:mainFlow/first.feature'), null
Look for some issue with karate-config.js. As Babu said in the comments, it is very hard to make out what the problem is, I suggest you follow this process: https://github.com/intuit/karate/wiki/How-to-Submit-an-Issue
Also try if the latest preview version 0.9.3.RC2 is better at showing what the error is.
If you can replicate the problem as a small example, it will help us - because we really need to do better at showing more useful error logs, instead of just null.

How to add an element to an array in karate?

How to add an element to an array in karate?
I have a string array(not json array) from response and add a string element to it for next request.
I tried a lot with JS functions but with no luck.
Any help is appreciated.
Scenario:123
* def roles = ["role1"]
* def newrole = "role2"
* def addrolefn =
"""
function(role,roles1) {
var fullrole = [];
for (var i=0; i<roles1.length;i++) {
fullrole = fullrole.push(role);
}
return fullrole;
}
"""
* def fullroles = call addrolefn (newrole,roles)
* print fullroles
Please refer to the set keyword.
* def roles = ["role1"]
* def newrole = "role2"
* set roles[1] = newrole
* print karate.pretty(roles)
Result:
06:26:35.324 [main] INFO com.intuit.karate - [print] [
"role1",
"role2"
]
edit: actually I've just raised an enhancement request to add an append keyword to Karate. Meanwhile, this should answer all your questions and act as a workaround.
* def roles = null
# javascript that assigns an empty array if null
* json roles = (roles || [])
* def newrole = "role2"
# javascript to append to an array. the def void is useless
* def void = (roles.add(newrole))
* print karate.pretty(roles)
edit: type cast to json, and using java list api add method