Java - Wait for JavaScript event (e.g. onchange) to complete using Selenium
up vote
0
down vote
favorite
A set of logically related input
fields have an onchange
event. After iterating over the fields and modifying their values some get updated correctly and some don't because of the stuff done by onchange
event.
The moment the onchange
event triggers on a field, it starts some processing (involving other related fields), stores the value somewhere and clears other related fields if they weren't previously processed by their own onchange
event.
I could put the thread to sleep for an arbitrary amount of time but that doesn't look good. It would be just guessing how much time is going to take the processing and choosing between wasting time in a generous sleep or having a script that can abort due timeouts.
Is there a way to know when the JavaScript code (which is called by the onchange
event) has finished doing its work?
Original Code
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
elementId = "field$" + i;
wait.until(ExpectedConditions.elementToBeClickable(By.id(elementId)));
driver.findElementById(elementId).sendKeys(data);
//The mess happens if I don't sleep
Thread.sleep(3000);
}
Output
With sleep: Field1
:_w_
... Field2
:_x_
... Field3
:_y_
... FieldN
:_z_
Without sleep: Field1
:_w_
... Field2
:___
... Field3
:_y_
... FieldN
:___
Notes:
I experienced some issues in the way so I just think it's worth mentioning the lessons learned in brief notes:
WARNING: Do not mix implicit and explicit waits.
Use
WebDriverWait
(specialization ofFluentWait
) instead ofFluentWait
, unless you have a very specific requirement. E.g.,WebDriverWait
ignoresNotFoundException
(NoSuchElementException
's superclass) by default. See recomendation.
javascript java selenium events onchange
add a comment |
up vote
0
down vote
favorite
A set of logically related input
fields have an onchange
event. After iterating over the fields and modifying their values some get updated correctly and some don't because of the stuff done by onchange
event.
The moment the onchange
event triggers on a field, it starts some processing (involving other related fields), stores the value somewhere and clears other related fields if they weren't previously processed by their own onchange
event.
I could put the thread to sleep for an arbitrary amount of time but that doesn't look good. It would be just guessing how much time is going to take the processing and choosing between wasting time in a generous sleep or having a script that can abort due timeouts.
Is there a way to know when the JavaScript code (which is called by the onchange
event) has finished doing its work?
Original Code
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
elementId = "field$" + i;
wait.until(ExpectedConditions.elementToBeClickable(By.id(elementId)));
driver.findElementById(elementId).sendKeys(data);
//The mess happens if I don't sleep
Thread.sleep(3000);
}
Output
With sleep: Field1
:_w_
... Field2
:_x_
... Field3
:_y_
... FieldN
:_z_
Without sleep: Field1
:_w_
... Field2
:___
... Field3
:_y_
... FieldN
:___
Notes:
I experienced some issues in the way so I just think it's worth mentioning the lessons learned in brief notes:
WARNING: Do not mix implicit and explicit waits.
Use
WebDriverWait
(specialization ofFluentWait
) instead ofFluentWait
, unless you have a very specific requirement. E.g.,WebDriverWait
ignoresNotFoundException
(NoSuchElementException
's superclass) by default. See recomendation.
javascript java selenium events onchange
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
A set of logically related input
fields have an onchange
event. After iterating over the fields and modifying their values some get updated correctly and some don't because of the stuff done by onchange
event.
The moment the onchange
event triggers on a field, it starts some processing (involving other related fields), stores the value somewhere and clears other related fields if they weren't previously processed by their own onchange
event.
I could put the thread to sleep for an arbitrary amount of time but that doesn't look good. It would be just guessing how much time is going to take the processing and choosing between wasting time in a generous sleep or having a script that can abort due timeouts.
Is there a way to know when the JavaScript code (which is called by the onchange
event) has finished doing its work?
Original Code
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
elementId = "field$" + i;
wait.until(ExpectedConditions.elementToBeClickable(By.id(elementId)));
driver.findElementById(elementId).sendKeys(data);
//The mess happens if I don't sleep
Thread.sleep(3000);
}
Output
With sleep: Field1
:_w_
... Field2
:_x_
... Field3
:_y_
... FieldN
:_z_
Without sleep: Field1
:_w_
... Field2
:___
... Field3
:_y_
... FieldN
:___
Notes:
I experienced some issues in the way so I just think it's worth mentioning the lessons learned in brief notes:
WARNING: Do not mix implicit and explicit waits.
Use
WebDriverWait
(specialization ofFluentWait
) instead ofFluentWait
, unless you have a very specific requirement. E.g.,WebDriverWait
ignoresNotFoundException
(NoSuchElementException
's superclass) by default. See recomendation.
javascript java selenium events onchange
A set of logically related input
fields have an onchange
event. After iterating over the fields and modifying their values some get updated correctly and some don't because of the stuff done by onchange
event.
The moment the onchange
event triggers on a field, it starts some processing (involving other related fields), stores the value somewhere and clears other related fields if they weren't previously processed by their own onchange
event.
I could put the thread to sleep for an arbitrary amount of time but that doesn't look good. It would be just guessing how much time is going to take the processing and choosing between wasting time in a generous sleep or having a script that can abort due timeouts.
Is there a way to know when the JavaScript code (which is called by the onchange
event) has finished doing its work?
Original Code
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
elementId = "field$" + i;
wait.until(ExpectedConditions.elementToBeClickable(By.id(elementId)));
driver.findElementById(elementId).sendKeys(data);
//The mess happens if I don't sleep
Thread.sleep(3000);
}
Output
With sleep: Field1
:_w_
... Field2
:_x_
... Field3
:_y_
... FieldN
:_z_
Without sleep: Field1
:_w_
... Field2
:___
... Field3
:_y_
... FieldN
:___
Notes:
I experienced some issues in the way so I just think it's worth mentioning the lessons learned in brief notes:
WARNING: Do not mix implicit and explicit waits.
Use
WebDriverWait
(specialization ofFluentWait
) instead ofFluentWait
, unless you have a very specific requirement. E.g.,WebDriverWait
ignoresNotFoundException
(NoSuchElementException
's superclass) by default. See recomendation.
javascript java selenium events onchange
javascript java selenium events onchange
edited Nov 11 at 19:04
asked Nov 7 at 22:56
CamelCamelius
439
439
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
up vote
0
down vote
You might try this method which waits until JQuery is set to inactive:
/**
* Wait until JQuery is inactive
* @author Bill Hileman
*/
public void waitForJQueryToBeInactive() {
Boolean isJqueryUsed = (Boolean) ((JavascriptExecutor) driver)
.executeScript("return (typeof(jQuery) != 'undefined')");
if (isJqueryUsed) {
while (true) {
// JavaScript test to verify jQuery is active or not
Boolean ajaxIsComplete = (Boolean) (((JavascriptExecutor) driver)
.executeScript("return jQuery.active == 0"));
if (ajaxIsComplete)
break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}
Thanks. JQuery is not being used. InterestingjQuery.active
. Didn't know about it. According to question it would work only if the code in execution is an ajax call, did I get it right ? Is there anything like that but that works with JavaScript ?
– CamelCamelius
Nov 8 at 16:49
I do have a more general waitForPageToLoad method that uses javascript to check the document ready state for "complete" It might help, since that flag might be getting changed while the web elements are "busy" I'll post it as a separate answer.
– Bill Hileman
Nov 8 at 17:41
add a comment |
up vote
0
down vote
The following routine checks the document ready state for "complete" and returns when the page is finished loading/changing.
/**
* Wait for the web page to finish loading
* @author Bill Hileman
*/
public void waitForPageToLoad() {
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
});
}
Thanks Bill! Unfortunately I am already executingreturn document.readyState
so everything is already loaded. Sometimes I even have to get the element everytime I want to execute an action on it or I end up getting an error saying that the element is no longer in cache, so I verify elements are there.
– CamelCamelius
Nov 8 at 22:07
add a comment |
up vote
0
down vote
To my best of knowledge, you can't wait for an asynchronous event using Selenium Web Driver. You may, however, be able to wait for effects of that event using WebDriverWait class. If events You mentioned, make some changes in the DOM, you can detect those changes by using selected ExpectedConditions.
Wait<WebDriver> wait = new WebDriverWait(driver, 15, 500);
// here you make some change to the input
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input")));
This simple example would wait until a button is active.
If the expected conditions are not met in the next 15s exception will be thrown.
In case, that provided set of ExpectedConditions is insufficient you can always create your own by implementing ExpectedCondition interface.
For more information visit the documentation.
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/package-summary.html
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/package-summary.html
Thanks. Just a side note, I am working with input fields instead of buttons. I would useExpectedConditions.elementToBeClickable
but I get an exception so I just checkExpectedConditions.visibilityOf
. The issue is that theonchange
events on fields seem to be clearing other fields if they haven't been validated so I wanted to see a way to determine when the event finishes
– CamelCamelius
Nov 8 at 16:21
That was just an idea, maybe I could give a better solution, but I'm still unclear about what You're trying to achieve. If the idea is to see if the fields are empty you could write some custom ExpectedCondition to wait for that specific situation.
– Mateusz
Nov 8 at 17:01
Got it. Fields are empty and I setup a value. After that it runs some JavaScript. If I populate the next related field immediately, it loses it's content but if I give some time so the JavaScript finishes whatever it is doing, I can update the next related field without issues. Also they look the same before and after theonchange
event...it's just the background processing what's giving issues and wanted to check if there was a way to do it without hardcoding a sleep time. Thanks
– CamelCamelius
Nov 8 at 19:41
Ok, I think I understand now. You have a set of connected input fields, that if you change one, the asynchronous task prohibits you from changing the next one, however, it does not change its original value (the next one).
– Mateusz
Nov 8 at 20:19
I also must ask are there any visible indications for the user of such process taking place in the background?
– Mateusz
Nov 8 at 20:30
|
show 3 more comments
up vote
0
down vote
accepted
After serious refactoring and some research, I finally made it. The onchange
event fires when the value of the input
field is changed and the element loses focus. Manipulating the fields with WebElement
(e.g. sendKeys()
) is not an option because you are not in control of the background processing, so using JavascriptExecutor
is the choice. First, I updated the value of the field using JavaScript (which does NOT trigger the event), and after that, I triggered the onchange
event, also using JavaScript:
//Setting implicit wait to 0 to avoid mess with explicit waits
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
//Use WebDriverWait instead of FluentWait (it's superclass)
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
String elementId = "field$" + i;
String javaScript = String.format("document.getElementById('%s').value='%s';", elementId , myValue);
Object jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
javaScript = String.format("return document.getElementById('%s').dispatchEvent(new Event('change'));", elementId);
jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
}
There are some key aspects here.
- Do NOT mix implicit and explicit waits (I learnt it the hard way), it will cause unexpected results.
- Use
WebDriverWait
(specialization ofFluentWait
) instead of its superclass, unless you have a very specific requirement. If you useFluentWait
make sure to ignore appropriate exceptions; otherwise you will start gettingNoSuchElementException
. - The
onchange
event fires when the value of theinput
field is changed and the element loses focus.
dispatchEvent()
dispatches an Event at the specified EventTarget, (synchronously) invoking the affected EventListeners in the appropriate order. This applies for custom events as well. This is a very nice post for events.
I used the following code to get a better grasp of ExpectedConditions.javaScriptThrowsNoExceptions
and ExpectedConditions.jsReturnsValue
. It is a JavaScript function call that just keeps the engine busy for few seconds. That way you can see the interaction of the explicit wait with JavaScript and inspect the return values. Notice that the JS code is slightly different for each ExpectedCondition
:
//ExpectedConditions.jsReturnsValue
String javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);return 'success';";
log.trace("javaScript={}", javaScript);
Object jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
log.trace("jsResult={}", jsResult);
//ExpectedConditions.javaScriptThrowsNoExceptions
javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);";
log.trace("javaScript={}", javaScript);
jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
log.trace("jsResult={}", jsResult);
add a comment |
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
You might try this method which waits until JQuery is set to inactive:
/**
* Wait until JQuery is inactive
* @author Bill Hileman
*/
public void waitForJQueryToBeInactive() {
Boolean isJqueryUsed = (Boolean) ((JavascriptExecutor) driver)
.executeScript("return (typeof(jQuery) != 'undefined')");
if (isJqueryUsed) {
while (true) {
// JavaScript test to verify jQuery is active or not
Boolean ajaxIsComplete = (Boolean) (((JavascriptExecutor) driver)
.executeScript("return jQuery.active == 0"));
if (ajaxIsComplete)
break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}
Thanks. JQuery is not being used. InterestingjQuery.active
. Didn't know about it. According to question it would work only if the code in execution is an ajax call, did I get it right ? Is there anything like that but that works with JavaScript ?
– CamelCamelius
Nov 8 at 16:49
I do have a more general waitForPageToLoad method that uses javascript to check the document ready state for "complete" It might help, since that flag might be getting changed while the web elements are "busy" I'll post it as a separate answer.
– Bill Hileman
Nov 8 at 17:41
add a comment |
up vote
0
down vote
You might try this method which waits until JQuery is set to inactive:
/**
* Wait until JQuery is inactive
* @author Bill Hileman
*/
public void waitForJQueryToBeInactive() {
Boolean isJqueryUsed = (Boolean) ((JavascriptExecutor) driver)
.executeScript("return (typeof(jQuery) != 'undefined')");
if (isJqueryUsed) {
while (true) {
// JavaScript test to verify jQuery is active or not
Boolean ajaxIsComplete = (Boolean) (((JavascriptExecutor) driver)
.executeScript("return jQuery.active == 0"));
if (ajaxIsComplete)
break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}
Thanks. JQuery is not being used. InterestingjQuery.active
. Didn't know about it. According to question it would work only if the code in execution is an ajax call, did I get it right ? Is there anything like that but that works with JavaScript ?
– CamelCamelius
Nov 8 at 16:49
I do have a more general waitForPageToLoad method that uses javascript to check the document ready state for "complete" It might help, since that flag might be getting changed while the web elements are "busy" I'll post it as a separate answer.
– Bill Hileman
Nov 8 at 17:41
add a comment |
up vote
0
down vote
up vote
0
down vote
You might try this method which waits until JQuery is set to inactive:
/**
* Wait until JQuery is inactive
* @author Bill Hileman
*/
public void waitForJQueryToBeInactive() {
Boolean isJqueryUsed = (Boolean) ((JavascriptExecutor) driver)
.executeScript("return (typeof(jQuery) != 'undefined')");
if (isJqueryUsed) {
while (true) {
// JavaScript test to verify jQuery is active or not
Boolean ajaxIsComplete = (Boolean) (((JavascriptExecutor) driver)
.executeScript("return jQuery.active == 0"));
if (ajaxIsComplete)
break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}
You might try this method which waits until JQuery is set to inactive:
/**
* Wait until JQuery is inactive
* @author Bill Hileman
*/
public void waitForJQueryToBeInactive() {
Boolean isJqueryUsed = (Boolean) ((JavascriptExecutor) driver)
.executeScript("return (typeof(jQuery) != 'undefined')");
if (isJqueryUsed) {
while (true) {
// JavaScript test to verify jQuery is active or not
Boolean ajaxIsComplete = (Boolean) (((JavascriptExecutor) driver)
.executeScript("return jQuery.active == 0"));
if (ajaxIsComplete)
break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}
answered Nov 8 at 14:29
Bill Hileman
1,9902617
1,9902617
Thanks. JQuery is not being used. InterestingjQuery.active
. Didn't know about it. According to question it would work only if the code in execution is an ajax call, did I get it right ? Is there anything like that but that works with JavaScript ?
– CamelCamelius
Nov 8 at 16:49
I do have a more general waitForPageToLoad method that uses javascript to check the document ready state for "complete" It might help, since that flag might be getting changed while the web elements are "busy" I'll post it as a separate answer.
– Bill Hileman
Nov 8 at 17:41
add a comment |
Thanks. JQuery is not being used. InterestingjQuery.active
. Didn't know about it. According to question it would work only if the code in execution is an ajax call, did I get it right ? Is there anything like that but that works with JavaScript ?
– CamelCamelius
Nov 8 at 16:49
I do have a more general waitForPageToLoad method that uses javascript to check the document ready state for "complete" It might help, since that flag might be getting changed while the web elements are "busy" I'll post it as a separate answer.
– Bill Hileman
Nov 8 at 17:41
Thanks. JQuery is not being used. Interesting
jQuery.active
. Didn't know about it. According to question it would work only if the code in execution is an ajax call, did I get it right ? Is there anything like that but that works with JavaScript ?– CamelCamelius
Nov 8 at 16:49
Thanks. JQuery is not being used. Interesting
jQuery.active
. Didn't know about it. According to question it would work only if the code in execution is an ajax call, did I get it right ? Is there anything like that but that works with JavaScript ?– CamelCamelius
Nov 8 at 16:49
I do have a more general waitForPageToLoad method that uses javascript to check the document ready state for "complete" It might help, since that flag might be getting changed while the web elements are "busy" I'll post it as a separate answer.
– Bill Hileman
Nov 8 at 17:41
I do have a more general waitForPageToLoad method that uses javascript to check the document ready state for "complete" It might help, since that flag might be getting changed while the web elements are "busy" I'll post it as a separate answer.
– Bill Hileman
Nov 8 at 17:41
add a comment |
up vote
0
down vote
The following routine checks the document ready state for "complete" and returns when the page is finished loading/changing.
/**
* Wait for the web page to finish loading
* @author Bill Hileman
*/
public void waitForPageToLoad() {
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
});
}
Thanks Bill! Unfortunately I am already executingreturn document.readyState
so everything is already loaded. Sometimes I even have to get the element everytime I want to execute an action on it or I end up getting an error saying that the element is no longer in cache, so I verify elements are there.
– CamelCamelius
Nov 8 at 22:07
add a comment |
up vote
0
down vote
The following routine checks the document ready state for "complete" and returns when the page is finished loading/changing.
/**
* Wait for the web page to finish loading
* @author Bill Hileman
*/
public void waitForPageToLoad() {
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
});
}
Thanks Bill! Unfortunately I am already executingreturn document.readyState
so everything is already loaded. Sometimes I even have to get the element everytime I want to execute an action on it or I end up getting an error saying that the element is no longer in cache, so I verify elements are there.
– CamelCamelius
Nov 8 at 22:07
add a comment |
up vote
0
down vote
up vote
0
down vote
The following routine checks the document ready state for "complete" and returns when the page is finished loading/changing.
/**
* Wait for the web page to finish loading
* @author Bill Hileman
*/
public void waitForPageToLoad() {
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
});
}
The following routine checks the document ready state for "complete" and returns when the page is finished loading/changing.
/**
* Wait for the web page to finish loading
* @author Bill Hileman
*/
public void waitForPageToLoad() {
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
});
}
answered Nov 8 at 17:43
Bill Hileman
1,9902617
1,9902617
Thanks Bill! Unfortunately I am already executingreturn document.readyState
so everything is already loaded. Sometimes I even have to get the element everytime I want to execute an action on it or I end up getting an error saying that the element is no longer in cache, so I verify elements are there.
– CamelCamelius
Nov 8 at 22:07
add a comment |
Thanks Bill! Unfortunately I am already executingreturn document.readyState
so everything is already loaded. Sometimes I even have to get the element everytime I want to execute an action on it or I end up getting an error saying that the element is no longer in cache, so I verify elements are there.
– CamelCamelius
Nov 8 at 22:07
Thanks Bill! Unfortunately I am already executing
return document.readyState
so everything is already loaded. Sometimes I even have to get the element everytime I want to execute an action on it or I end up getting an error saying that the element is no longer in cache, so I verify elements are there.– CamelCamelius
Nov 8 at 22:07
Thanks Bill! Unfortunately I am already executing
return document.readyState
so everything is already loaded. Sometimes I even have to get the element everytime I want to execute an action on it or I end up getting an error saying that the element is no longer in cache, so I verify elements are there.– CamelCamelius
Nov 8 at 22:07
add a comment |
up vote
0
down vote
To my best of knowledge, you can't wait for an asynchronous event using Selenium Web Driver. You may, however, be able to wait for effects of that event using WebDriverWait class. If events You mentioned, make some changes in the DOM, you can detect those changes by using selected ExpectedConditions.
Wait<WebDriver> wait = new WebDriverWait(driver, 15, 500);
// here you make some change to the input
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input")));
This simple example would wait until a button is active.
If the expected conditions are not met in the next 15s exception will be thrown.
In case, that provided set of ExpectedConditions is insufficient you can always create your own by implementing ExpectedCondition interface.
For more information visit the documentation.
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/package-summary.html
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/package-summary.html
Thanks. Just a side note, I am working with input fields instead of buttons. I would useExpectedConditions.elementToBeClickable
but I get an exception so I just checkExpectedConditions.visibilityOf
. The issue is that theonchange
events on fields seem to be clearing other fields if they haven't been validated so I wanted to see a way to determine when the event finishes
– CamelCamelius
Nov 8 at 16:21
That was just an idea, maybe I could give a better solution, but I'm still unclear about what You're trying to achieve. If the idea is to see if the fields are empty you could write some custom ExpectedCondition to wait for that specific situation.
– Mateusz
Nov 8 at 17:01
Got it. Fields are empty and I setup a value. After that it runs some JavaScript. If I populate the next related field immediately, it loses it's content but if I give some time so the JavaScript finishes whatever it is doing, I can update the next related field without issues. Also they look the same before and after theonchange
event...it's just the background processing what's giving issues and wanted to check if there was a way to do it without hardcoding a sleep time. Thanks
– CamelCamelius
Nov 8 at 19:41
Ok, I think I understand now. You have a set of connected input fields, that if you change one, the asynchronous task prohibits you from changing the next one, however, it does not change its original value (the next one).
– Mateusz
Nov 8 at 20:19
I also must ask are there any visible indications for the user of such process taking place in the background?
– Mateusz
Nov 8 at 20:30
|
show 3 more comments
up vote
0
down vote
To my best of knowledge, you can't wait for an asynchronous event using Selenium Web Driver. You may, however, be able to wait for effects of that event using WebDriverWait class. If events You mentioned, make some changes in the DOM, you can detect those changes by using selected ExpectedConditions.
Wait<WebDriver> wait = new WebDriverWait(driver, 15, 500);
// here you make some change to the input
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input")));
This simple example would wait until a button is active.
If the expected conditions are not met in the next 15s exception will be thrown.
In case, that provided set of ExpectedConditions is insufficient you can always create your own by implementing ExpectedCondition interface.
For more information visit the documentation.
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/package-summary.html
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/package-summary.html
Thanks. Just a side note, I am working with input fields instead of buttons. I would useExpectedConditions.elementToBeClickable
but I get an exception so I just checkExpectedConditions.visibilityOf
. The issue is that theonchange
events on fields seem to be clearing other fields if they haven't been validated so I wanted to see a way to determine when the event finishes
– CamelCamelius
Nov 8 at 16:21
That was just an idea, maybe I could give a better solution, but I'm still unclear about what You're trying to achieve. If the idea is to see if the fields are empty you could write some custom ExpectedCondition to wait for that specific situation.
– Mateusz
Nov 8 at 17:01
Got it. Fields are empty and I setup a value. After that it runs some JavaScript. If I populate the next related field immediately, it loses it's content but if I give some time so the JavaScript finishes whatever it is doing, I can update the next related field without issues. Also they look the same before and after theonchange
event...it's just the background processing what's giving issues and wanted to check if there was a way to do it without hardcoding a sleep time. Thanks
– CamelCamelius
Nov 8 at 19:41
Ok, I think I understand now. You have a set of connected input fields, that if you change one, the asynchronous task prohibits you from changing the next one, however, it does not change its original value (the next one).
– Mateusz
Nov 8 at 20:19
I also must ask are there any visible indications for the user of such process taking place in the background?
– Mateusz
Nov 8 at 20:30
|
show 3 more comments
up vote
0
down vote
up vote
0
down vote
To my best of knowledge, you can't wait for an asynchronous event using Selenium Web Driver. You may, however, be able to wait for effects of that event using WebDriverWait class. If events You mentioned, make some changes in the DOM, you can detect those changes by using selected ExpectedConditions.
Wait<WebDriver> wait = new WebDriverWait(driver, 15, 500);
// here you make some change to the input
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input")));
This simple example would wait until a button is active.
If the expected conditions are not met in the next 15s exception will be thrown.
In case, that provided set of ExpectedConditions is insufficient you can always create your own by implementing ExpectedCondition interface.
For more information visit the documentation.
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/package-summary.html
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/package-summary.html
To my best of knowledge, you can't wait for an asynchronous event using Selenium Web Driver. You may, however, be able to wait for effects of that event using WebDriverWait class. If events You mentioned, make some changes in the DOM, you can detect those changes by using selected ExpectedConditions.
Wait<WebDriver> wait = new WebDriverWait(driver, 15, 500);
// here you make some change to the input
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input")));
This simple example would wait until a button is active.
If the expected conditions are not met in the next 15s exception will be thrown.
In case, that provided set of ExpectedConditions is insufficient you can always create your own by implementing ExpectedCondition interface.
For more information visit the documentation.
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/package-summary.html
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/package-summary.html
edited Nov 11 at 1:14
CamelCamelius
439
439
answered Nov 7 at 23:49
Mateusz
263
263
Thanks. Just a side note, I am working with input fields instead of buttons. I would useExpectedConditions.elementToBeClickable
but I get an exception so I just checkExpectedConditions.visibilityOf
. The issue is that theonchange
events on fields seem to be clearing other fields if they haven't been validated so I wanted to see a way to determine when the event finishes
– CamelCamelius
Nov 8 at 16:21
That was just an idea, maybe I could give a better solution, but I'm still unclear about what You're trying to achieve. If the idea is to see if the fields are empty you could write some custom ExpectedCondition to wait for that specific situation.
– Mateusz
Nov 8 at 17:01
Got it. Fields are empty and I setup a value. After that it runs some JavaScript. If I populate the next related field immediately, it loses it's content but if I give some time so the JavaScript finishes whatever it is doing, I can update the next related field without issues. Also they look the same before and after theonchange
event...it's just the background processing what's giving issues and wanted to check if there was a way to do it without hardcoding a sleep time. Thanks
– CamelCamelius
Nov 8 at 19:41
Ok, I think I understand now. You have a set of connected input fields, that if you change one, the asynchronous task prohibits you from changing the next one, however, it does not change its original value (the next one).
– Mateusz
Nov 8 at 20:19
I also must ask are there any visible indications for the user of such process taking place in the background?
– Mateusz
Nov 8 at 20:30
|
show 3 more comments
Thanks. Just a side note, I am working with input fields instead of buttons. I would useExpectedConditions.elementToBeClickable
but I get an exception so I just checkExpectedConditions.visibilityOf
. The issue is that theonchange
events on fields seem to be clearing other fields if they haven't been validated so I wanted to see a way to determine when the event finishes
– CamelCamelius
Nov 8 at 16:21
That was just an idea, maybe I could give a better solution, but I'm still unclear about what You're trying to achieve. If the idea is to see if the fields are empty you could write some custom ExpectedCondition to wait for that specific situation.
– Mateusz
Nov 8 at 17:01
Got it. Fields are empty and I setup a value. After that it runs some JavaScript. If I populate the next related field immediately, it loses it's content but if I give some time so the JavaScript finishes whatever it is doing, I can update the next related field without issues. Also they look the same before and after theonchange
event...it's just the background processing what's giving issues and wanted to check if there was a way to do it without hardcoding a sleep time. Thanks
– CamelCamelius
Nov 8 at 19:41
Ok, I think I understand now. You have a set of connected input fields, that if you change one, the asynchronous task prohibits you from changing the next one, however, it does not change its original value (the next one).
– Mateusz
Nov 8 at 20:19
I also must ask are there any visible indications for the user of such process taking place in the background?
– Mateusz
Nov 8 at 20:30
Thanks. Just a side note, I am working with input fields instead of buttons. I would use
ExpectedConditions.elementToBeClickable
but I get an exception so I just check ExpectedConditions.visibilityOf
. The issue is that the onchange
events on fields seem to be clearing other fields if they haven't been validated so I wanted to see a way to determine when the event finishes– CamelCamelius
Nov 8 at 16:21
Thanks. Just a side note, I am working with input fields instead of buttons. I would use
ExpectedConditions.elementToBeClickable
but I get an exception so I just check ExpectedConditions.visibilityOf
. The issue is that the onchange
events on fields seem to be clearing other fields if they haven't been validated so I wanted to see a way to determine when the event finishes– CamelCamelius
Nov 8 at 16:21
That was just an idea, maybe I could give a better solution, but I'm still unclear about what You're trying to achieve. If the idea is to see if the fields are empty you could write some custom ExpectedCondition to wait for that specific situation.
– Mateusz
Nov 8 at 17:01
That was just an idea, maybe I could give a better solution, but I'm still unclear about what You're trying to achieve. If the idea is to see if the fields are empty you could write some custom ExpectedCondition to wait for that specific situation.
– Mateusz
Nov 8 at 17:01
Got it. Fields are empty and I setup a value. After that it runs some JavaScript. If I populate the next related field immediately, it loses it's content but if I give some time so the JavaScript finishes whatever it is doing, I can update the next related field without issues. Also they look the same before and after the
onchange
event...it's just the background processing what's giving issues and wanted to check if there was a way to do it without hardcoding a sleep time. Thanks– CamelCamelius
Nov 8 at 19:41
Got it. Fields are empty and I setup a value. After that it runs some JavaScript. If I populate the next related field immediately, it loses it's content but if I give some time so the JavaScript finishes whatever it is doing, I can update the next related field without issues. Also they look the same before and after the
onchange
event...it's just the background processing what's giving issues and wanted to check if there was a way to do it without hardcoding a sleep time. Thanks– CamelCamelius
Nov 8 at 19:41
Ok, I think I understand now. You have a set of connected input fields, that if you change one, the asynchronous task prohibits you from changing the next one, however, it does not change its original value (the next one).
– Mateusz
Nov 8 at 20:19
Ok, I think I understand now. You have a set of connected input fields, that if you change one, the asynchronous task prohibits you from changing the next one, however, it does not change its original value (the next one).
– Mateusz
Nov 8 at 20:19
I also must ask are there any visible indications for the user of such process taking place in the background?
– Mateusz
Nov 8 at 20:30
I also must ask are there any visible indications for the user of such process taking place in the background?
– Mateusz
Nov 8 at 20:30
|
show 3 more comments
up vote
0
down vote
accepted
After serious refactoring and some research, I finally made it. The onchange
event fires when the value of the input
field is changed and the element loses focus. Manipulating the fields with WebElement
(e.g. sendKeys()
) is not an option because you are not in control of the background processing, so using JavascriptExecutor
is the choice. First, I updated the value of the field using JavaScript (which does NOT trigger the event), and after that, I triggered the onchange
event, also using JavaScript:
//Setting implicit wait to 0 to avoid mess with explicit waits
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
//Use WebDriverWait instead of FluentWait (it's superclass)
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
String elementId = "field$" + i;
String javaScript = String.format("document.getElementById('%s').value='%s';", elementId , myValue);
Object jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
javaScript = String.format("return document.getElementById('%s').dispatchEvent(new Event('change'));", elementId);
jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
}
There are some key aspects here.
- Do NOT mix implicit and explicit waits (I learnt it the hard way), it will cause unexpected results.
- Use
WebDriverWait
(specialization ofFluentWait
) instead of its superclass, unless you have a very specific requirement. If you useFluentWait
make sure to ignore appropriate exceptions; otherwise you will start gettingNoSuchElementException
. - The
onchange
event fires when the value of theinput
field is changed and the element loses focus.
dispatchEvent()
dispatches an Event at the specified EventTarget, (synchronously) invoking the affected EventListeners in the appropriate order. This applies for custom events as well. This is a very nice post for events.
I used the following code to get a better grasp of ExpectedConditions.javaScriptThrowsNoExceptions
and ExpectedConditions.jsReturnsValue
. It is a JavaScript function call that just keeps the engine busy for few seconds. That way you can see the interaction of the explicit wait with JavaScript and inspect the return values. Notice that the JS code is slightly different for each ExpectedCondition
:
//ExpectedConditions.jsReturnsValue
String javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);return 'success';";
log.trace("javaScript={}", javaScript);
Object jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
log.trace("jsResult={}", jsResult);
//ExpectedConditions.javaScriptThrowsNoExceptions
javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);";
log.trace("javaScript={}", javaScript);
jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
log.trace("jsResult={}", jsResult);
add a comment |
up vote
0
down vote
accepted
After serious refactoring and some research, I finally made it. The onchange
event fires when the value of the input
field is changed and the element loses focus. Manipulating the fields with WebElement
(e.g. sendKeys()
) is not an option because you are not in control of the background processing, so using JavascriptExecutor
is the choice. First, I updated the value of the field using JavaScript (which does NOT trigger the event), and after that, I triggered the onchange
event, also using JavaScript:
//Setting implicit wait to 0 to avoid mess with explicit waits
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
//Use WebDriverWait instead of FluentWait (it's superclass)
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
String elementId = "field$" + i;
String javaScript = String.format("document.getElementById('%s').value='%s';", elementId , myValue);
Object jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
javaScript = String.format("return document.getElementById('%s').dispatchEvent(new Event('change'));", elementId);
jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
}
There are some key aspects here.
- Do NOT mix implicit and explicit waits (I learnt it the hard way), it will cause unexpected results.
- Use
WebDriverWait
(specialization ofFluentWait
) instead of its superclass, unless you have a very specific requirement. If you useFluentWait
make sure to ignore appropriate exceptions; otherwise you will start gettingNoSuchElementException
. - The
onchange
event fires when the value of theinput
field is changed and the element loses focus.
dispatchEvent()
dispatches an Event at the specified EventTarget, (synchronously) invoking the affected EventListeners in the appropriate order. This applies for custom events as well. This is a very nice post for events.
I used the following code to get a better grasp of ExpectedConditions.javaScriptThrowsNoExceptions
and ExpectedConditions.jsReturnsValue
. It is a JavaScript function call that just keeps the engine busy for few seconds. That way you can see the interaction of the explicit wait with JavaScript and inspect the return values. Notice that the JS code is slightly different for each ExpectedCondition
:
//ExpectedConditions.jsReturnsValue
String javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);return 'success';";
log.trace("javaScript={}", javaScript);
Object jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
log.trace("jsResult={}", jsResult);
//ExpectedConditions.javaScriptThrowsNoExceptions
javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);";
log.trace("javaScript={}", javaScript);
jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
log.trace("jsResult={}", jsResult);
add a comment |
up vote
0
down vote
accepted
up vote
0
down vote
accepted
After serious refactoring and some research, I finally made it. The onchange
event fires when the value of the input
field is changed and the element loses focus. Manipulating the fields with WebElement
(e.g. sendKeys()
) is not an option because you are not in control of the background processing, so using JavascriptExecutor
is the choice. First, I updated the value of the field using JavaScript (which does NOT trigger the event), and after that, I triggered the onchange
event, also using JavaScript:
//Setting implicit wait to 0 to avoid mess with explicit waits
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
//Use WebDriverWait instead of FluentWait (it's superclass)
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
String elementId = "field$" + i;
String javaScript = String.format("document.getElementById('%s').value='%s';", elementId , myValue);
Object jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
javaScript = String.format("return document.getElementById('%s').dispatchEvent(new Event('change'));", elementId);
jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
}
There are some key aspects here.
- Do NOT mix implicit and explicit waits (I learnt it the hard way), it will cause unexpected results.
- Use
WebDriverWait
(specialization ofFluentWait
) instead of its superclass, unless you have a very specific requirement. If you useFluentWait
make sure to ignore appropriate exceptions; otherwise you will start gettingNoSuchElementException
. - The
onchange
event fires when the value of theinput
field is changed and the element loses focus.
dispatchEvent()
dispatches an Event at the specified EventTarget, (synchronously) invoking the affected EventListeners in the appropriate order. This applies for custom events as well. This is a very nice post for events.
I used the following code to get a better grasp of ExpectedConditions.javaScriptThrowsNoExceptions
and ExpectedConditions.jsReturnsValue
. It is a JavaScript function call that just keeps the engine busy for few seconds. That way you can see the interaction of the explicit wait with JavaScript and inspect the return values. Notice that the JS code is slightly different for each ExpectedCondition
:
//ExpectedConditions.jsReturnsValue
String javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);return 'success';";
log.trace("javaScript={}", javaScript);
Object jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
log.trace("jsResult={}", jsResult);
//ExpectedConditions.javaScriptThrowsNoExceptions
javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);";
log.trace("javaScript={}", javaScript);
jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
log.trace("jsResult={}", jsResult);
After serious refactoring and some research, I finally made it. The onchange
event fires when the value of the input
field is changed and the element loses focus. Manipulating the fields with WebElement
(e.g. sendKeys()
) is not an option because you are not in control of the background processing, so using JavascriptExecutor
is the choice. First, I updated the value of the field using JavaScript (which does NOT trigger the event), and after that, I triggered the onchange
event, also using JavaScript:
//Setting implicit wait to 0 to avoid mess with explicit waits
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
//Use WebDriverWait instead of FluentWait (it's superclass)
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
String elementId = "field$" + i;
String javaScript = String.format("document.getElementById('%s').value='%s';", elementId , myValue);
Object jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
javaScript = String.format("return document.getElementById('%s').dispatchEvent(new Event('change'));", elementId);
jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
}
There are some key aspects here.
- Do NOT mix implicit and explicit waits (I learnt it the hard way), it will cause unexpected results.
- Use
WebDriverWait
(specialization ofFluentWait
) instead of its superclass, unless you have a very specific requirement. If you useFluentWait
make sure to ignore appropriate exceptions; otherwise you will start gettingNoSuchElementException
. - The
onchange
event fires when the value of theinput
field is changed and the element loses focus.
dispatchEvent()
dispatches an Event at the specified EventTarget, (synchronously) invoking the affected EventListeners in the appropriate order. This applies for custom events as well. This is a very nice post for events.
I used the following code to get a better grasp of ExpectedConditions.javaScriptThrowsNoExceptions
and ExpectedConditions.jsReturnsValue
. It is a JavaScript function call that just keeps the engine busy for few seconds. That way you can see the interaction of the explicit wait with JavaScript and inspect the return values. Notice that the JS code is slightly different for each ExpectedCondition
:
//ExpectedConditions.jsReturnsValue
String javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);return 'success';";
log.trace("javaScript={}", javaScript);
Object jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
log.trace("jsResult={}", jsResult);
//ExpectedConditions.javaScriptThrowsNoExceptions
javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);";
log.trace("javaScript={}", javaScript);
jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
log.trace("jsResult={}", jsResult);
answered Nov 11 at 10:21
CamelCamelius
439
439
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53199159%2fjava-wait-for-javascript-event-e-g-onchange-to-complete-using-selenium%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown