I've a response
{ errors: [
{
code: 123,
reason: "this is the cause for a random problem where the last part of this string is dynamically generated"
} ,
{
code: 234,
reason: "Some other error for another random reason"
}
...
...
}
Now when I validate this response
I use following
...
...
And match response.errors[*].reason contains "this is the cause"
This validation fails, because there is an equality check for complete String for every reason ,
I all I want is, to validate that inside the errors array, if there is any error object, which has a reason string type property, starting with this is the cause phrase.
I tried few wild cards but didn't work either, how to do it ?
For complex things like this, just switch to JS.
* def found = response.errors.find(x => x.reason.startsWith('this is the cause'))
* match found == { code: 123, reason: '#string' }
# you can also do
* if (found) karate.log('found')
Any questions :)
Related
I'm able to successfully run the Feature/scenario, When I define the schema inside my feature file .
Here is a simplified example of the schema.
Background:
...
...
* def accountSchema = { id: '#number? _ >= 0', prop1: '#number? _ >= 0', prop2: '#string', prop3: '#string', optionaProp: '##string' }
* def userAccountsSchema = ({ entitledAccounts: '#[] accountSchema', prop5: '#number' , prop6: '##string'})
And here is how I'm validating
Scenario:
...
...
When method GET
Then status 200
* print userAccountsSchema
And match response == userAccountsSchema
But the schema I posted here is simplified to ask this question, the real schema is far more complex.
So for clarity purpose, I decided to put schema in a separate js file response-schemas.js under the same folder as the feature file.
Here is the simplified content of response-schemas.js file.
function schema () {
let accountSchema = {
id: '#number? _ >= 0',
prop1: '#number? _ >= 0',
prop2: '#string',
prop3: '#string',
optionaProp: '##string',
}'
return {
accounts: `#[] ${accountSchema}` ,
prop5: '#string',
prop6: '#string',
};
}
now if I replace the 2 lines I mentioned at the beginning of the question under Background:, with below line
* def userAccountsSchema = call read('response-schemas.js')
I get this error
And match response == schemas
SyntaxError: Unnamed:1:8 Expected comma but found ident
[object Object]
^
I believe, I understand the problem, is this line
accounts: `#[] ${accountSchema}` ,
but unable to figure out the solution. If I tried to change the accountSchema variable in response-schemas.js to use multiline string then I get error in read step in Background
the whole idea to have a dedicated js file for schema is to keep it readable (by using multiple lines, preferably objects not a long string)
The main problem is this part:
accounts: `#[] ${accountSchema}`
Where you are trying to stuff a JSON into the Karate "fuzzy" expression. This is just not supported. Note that the Karate way of defining things like #(foo) and #[] bar has nothing to do with JavaScript, so I recommend not mixing these.
I know there is a desire to achieve the match in just one-line and somehow get one monstrous schema to do everything and I very strongly discourage this. Split your assertions into multiple lines. Split your response into smaller chunks of JSON if needed. There is nothing wrong with that. This also makes the life much easier of people who come along later who have to maintain your test.
For ideas see this answer: https://stackoverflow.com/a/61252709/143475
Other answers: https://stackoverflow.com/search?q=%5Bkarate%5D+array+schema
Tip: you can keep your schema "chunks" as JSON files if needed.
I am filtering some of the characters from the string. I went across few questions which has a same problem ie error in the console, but could not find any good answers.
Here is my string:
response_out1|response_out2|response_out3
Here is the method that i have used:
<vs-select v-model="change">
<vs-select-item :key="index" v-bind="item" v-for="(item,index) in
userFriendly(out.changes)" />
</vs-select>
...
methods: {
userFriendly (str){
return str.replace(/_/g, ' ').split('|').map(value => ({text: value, value }))
}
Here is the output that i am getting in the vs-select:
response out1
response out2
response out3
The error that i am getting in my console:
Here i want to know why i am getting this error and i wanna know how to rectify it and the output that i am expecting is: Response Out1, here how to capitalize first character of each word in the same method.
you're using a method directly in the template which causes multiple calls whenever your data changes,
you can use computed property to avoid such a scenario, not sure about how you are accessing out.changes
this might help you to solve your error and capitalize your text,
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
},
sentenceCase (sentence) {
return sentence.split(' ').map(s => this.capitalize(s)).join(' '));
},
userFriendly (str) {
if (!str) return;
return str.replace(/_/g, ' ').split('|').map(value => ({text: this.sentenceCase(value), value }))
},
I've been searching for some time now, and I'm sure I've missed it, is there any documentation that states what should be thrown when a value is incorrect/blank?
For example, Python has ValueError and the documentation clearly states when to use it.
I have the following method:
proc getJobinfo {question} {
puts -nonewline "$question: "
flush stdout
gets stdin answer
set cleanedanswer [string trim [string totitle $answer]]
if {$cleanedanswer eq ""} {
# What error should be thrown?
}
return $cleanedanswer
}
I've searched throw, error, and catch, but couldn't find it.
Tcl doesn't have a pre-defined hierarchy of exceptions. The throw command takes 2 arguments: type is a list of words; and message is an error message for humans.
You could do something like
proc getJobinfo {question} {
...
if {$cleanedanswer eq ""} {
throw {Value Empty} "Please provide a suitable answer."
} elseif {[string length $cleanedanswer] < 5} {
throw {Value Invalid} "Your answer is too short."
} else ...
return $cleanedanswer
}
If you want to trap that error:
try {
set answer [getJobinfo "What is the answer to this question"]
} trap {Value *} msg {
puts "Value Error: $msg"
}
throw and try interact via the type words of the throw call. We throw "Value Empty" or "Value Invalid". In the trap, we match Value exactly, but we won't match * exactly. In hindsight the * should not be there.
The try manpage is not super clear at first read:
trap pattern variableList script
This clause matches if the evaluation of body resulted in an error and the prefix of the -errorcode from the interpreter's status dictionary is equal to the pattern. The number of prefix words taken from the -errorcode is equal to the list-length of pattern, and inter-word spaces are normalized in both the -errorcode and pattern before comparison.
pattern is not a pattern in the regexp or string match sense: it's a list of words that is matched one-by-one with the list of words thrown in the try body.
The try can be implemented with multiple traps to have cascading "catches":
try {
set answer [getJobinfo "What is the answer to this question"]
} trap {Value Empty} msg {
do something specific here
} trap {Value Invalid} msg {
do something specific here
} trap {Value} msg {
do something general for some other "throw {Value anything} msg"
} on error e {
this can be default catch-all for any other error
} finally {
any cleanup code goes here
}
I have an endpoint which returns this JSON response:
{
"jobs": [
{
"name": "job1",
"id": "d6bd9aa1-0708-436a-81fd-cf22d5042689",
"status": "pending"
},
{
"name": "job2",
"id": "4fdaf09f-51de-4246-88fd-08d4daef6c3e",
"status": "pending"
}
]
I would like to repeatedly GET call this endpoint until the job I care about ("job2") has a "status" of "completed", but I'd like to check this by using a UUID stored in a variable from a previous call.
i.e. by doing something like this:
#NB: code for previous API call is executed
* def uuidVar = response.jobRef
#NB: uuidVar equates to '4fdaf09f-51de-4246-88fd-08d4daef6c3e' for this scenario
* configure retry = { count: 5, interval: 10000 }
Given path /blah
And retry until response.jobs[?(#.id==uuidVar)].status == 'completed'
When method GET
Could anyone suggest the correct syntax for the retry until?
I've tried referencing the fantastic Karate docs & examples (in particular, js-arrays.feature) and some questions on SO (including this one: Karate framework retry until not working as expected) but sadly I haven't been able to get this working.
I also tried using karate.match here as suggested in the link above, but no cigar.
Apologies in advance if I am missing something obvious.
First I recommend you read this answer on Stack Overflow, it is linked from the readme actually, and is intended to be the definitive reference. Let me know if it needs to be improved: https://stackoverflow.com/a/55823180/143475
Short answer, you can't use JsonPath in the retry until expression, it has to be pure JavaScript.
While you can use karate.jsonPath() to bridge the worlds of JsonPath and JS, JsonPath can get very hard to write and comprehend. Which is why I recommend using karate.filter() to do the same thing, but break down the steps into simple, readable chunks. Here is what you can try in a fresh Scenario:. Hint, this is a good way to troubleshoot your code without making any "real" requests.
* def getStatus = function(id){ var temp = karate.filter(response.jobs, function(x){ return x.id == id }); return temp[0].status }
* def response =
"""
{
"jobs": [
{
"name": "job1",
"id": "d6bd9aa1-0708-436a-81fd-cf22d5042689",
"status": "pending"
},
{
"name": "job2",
"id": "4fdaf09f-51de-4246-88fd-08d4daef6c3e",
"status": "pending"
}
]
}
"""
* def selected = '4fdaf09f-51de-4246-88fd-08d4daef6c3e'
* print getStatus(selected)
So if you have getStatus defined up-front, you can do this:
* retry until getStatus(selected) == 'completed'
Note you can use multiple lines for a JS function if you don't like squeezing it all into one line, or even read it from a file.
I am trying the following API using Alamofire, but this API has multiple "to" fields. I tried to pass an array of "to" emails as parameters. It shows no error but did not send to all emails. API is correct, I tested that from terminal. Any suggestions will be cordially welcomed.
http -a email:pass -f POST 'sampleUrl' from="email#email.com" to="ongkur.cse#gmail.com" to="emailgmail#email.com" subject="test_sub" bodyText="testing hello"
I am giving my code:
class func sendMessage(message:MessageModel, delegate:RestAPIManagerDelegate?) {
let urlString = "http://localhost:8080/app/user/messages"
var parameters = [String:AnyObject]()
parameters = [
"from": message.messageFrom.emailAddress
]
var array = [String]()
for to in message.messageTO {
array.append(to)
}
parameters["to"] = array
for cc in message.messageCC {
parameters["cc"] = cc.emailAddress;
}
for bcc in message.messageBCC {
parameters["bcc"] = bcc.emailAddress;
}
parameters["subject"] = message.messageSubject;
parameters["bodyText"] = message.bodyText;
Alamofire.request(.POST, urlString, parameters: parameters)
.authenticate(user: MessageManager.sharedInstance().primaryUserName, password: MessageManager.sharedInstance().primaryPassword)
.validate(statusCode: 200..<201)
.validate(contentType: ["application/json"])
.responseJSON {
(_, _, jsonData, error) in
if(error != nil) {
println("\n sendMessage attempt json response:")
println(error!)
delegate?.messageSent?(false)
return
}
println("Server response during message sending:\n")
let swiftyJSONData = JSON(jsonData!)
println(swiftyJSONData)
delegate?.messageSent?(true)
}
}
First of all if you created the API yourself you should consider changing the API to expect an array of 'to' receivers instead of multiple times the same parameter name.
As back2dos states it in this answer: https://stackoverflow.com/a/1898078/672989
Although POST may be having multiple values for the same key, I'd be cautious using it, since some servers can't even properly handle that, which is probably why this isn't supported ... if you convert "duplicate" parameters to a list, the whole thing might start to choke, if a parameter comes in only once, and suddendly you wind up having a string or something ...
And I think he's right.
In this case I guess this is not possible with Alamofire, just as it is not possible with AFNetworking: https://github.com/AFNetworking/AFNetworking/issues/21
Alamofire probably store's its POST parameter in a Dictionary which doesn't allow duplicate keys.