I am using Karate for UI Automation. In one of my scenarios, part of the flow involves polling an account search in the UI of an application (Oracle Sales Cloud). We search a few times to compensate for delays in integration with other applications.
For that polling functionality, we created a flow like this:
- a search scenario - has the UI functionality for searching once
- a polling scenario - has a karate.repeat() to call the search scenario multiple times until it finds the account. This scenario also has other validations and prints for business reports, so we can't just get rid of it. We keep the polling as a separate component to avoid getting too repetitive, as lots of scenarios use it.
So the final flow looks something along these lines:
@searchAcctOnce Scenario: Do a single search account
- (UI steps for search)
@pollAccount Scenario: poll account
- ...
- def searchAcct = function(i) { karate.call(@searchAcctOnce) }
- karate.repeat(10, searchAcct)
- ...
Scenario: Find account then do stuff
- call read (@pollAccount)
- ...
- ...
As far as that goes, it works fine. The problem is when we're using general functions from a callSingle in karate-config.js:
After calling the nested @searchAcctOnce and returning to the scope of @pollAccount, something seems to change in the context, and we get the following error:
The value 'DynamicObject<JSFunction>@16e99306' cannot be passed from one context to another. The current context is 0x6f9ab79d and the argument value originates from context 0x54af3cb9.
To exemplify, I created this sample project that does basically the same: https://github.com/JVRD270/nestedCallsKarate
In it, we have the karate-config.js:
function fn() {
var config = {};
//var driverConfig={ type: 'chrome', showDriverLog: false, start: true, addOptions:['--disable-extensions','--disable-infobar', '--incognito'] };
var driverConfig={
type: 'msedgedriver', executable: "./msedgedriver.exe", webDriverSession: { capabilities: { browserName: 'edge' } }
}
karate.configure('driver', driverConfig);
karate.configure('report', {showLog: true, showAllSteps: false, showRequest: true, showResponse: true});
config.util = karate.callSingle('util.feature');
return config;
}
Then a feature called nestedCalls.feature:
Feature: web-browser automation
for help, see: https://github.com/intuit/karate/wiki/ZIP-Release
Background:
* configure driver = { type: 'chrome' }
Scenario: Test nested calls context change
* driver 'https://www.youtube.com/'
* def search = call read("search.feature@pollAccount")
search.feature:
Feature:
@pollAccount
Scenario: Poll Account
* def karateVideo = "//*[text()='Shotokan Karate Follow Along Class - 9th Kyu White Belt - Class #1']"
* def searchOnce = function(i) { karate.call('search.feature@searchOnce') }
* karate.repeat(5, searchOnce)
* if(exists(karateVideo)) util.printSomething()
@searchOnce
Scenario: Search Once
* def karateVideo = "//*[text()='Shotokan Karate Follow Along Class - 9th Kyu White Belt - Class #1']"
* delay(5000)
* if(!exists(karateVideo)) input('/html/body/ytd-app/div/div/ytd-masthead/div[3]/div[2]/ytd-searchbox/form/div[1]/div[1]/input', "karate")
* if(!exists(karateVideo)) delay(5000)
* if(!exists(karateVideo)) click('/html/body/ytd-app/div/div/ytd-masthead/div[3]/div[2]/ytd-searchbox/button')
and util.feature:
Feature: Utilities
Scenario: Test nested calls
* def printSomething = function() {karate.log("something")}
When you try to run this, the flow is
nestedCalls.feature -> search.feature@pollAccount -> search.feature@searchOnce -> search.feature@pollAccount (at which point it calls util.printSomething())
Then it fails with
The value 'DynamicObject<JSFunction>@16e99306' cannot be passed from one context to another. The current context is 0x6f9ab79d and the argument value originates from context 0x54af3cb9.
Also I'd like to point out that this happens not only with karate.repeat(), if we changed the repeat to a single call, that would also happen.
Is this a bug? What could be the work around?
Thanks for the help!