I am doing a test based mocha. node v8.2.1, selenium-webdriver: ^3.5.0.
test.it('demoClass', () => {
driver.classes[0].findElement(By.css('.class-avatar')).click();
driver.wait(until.elementIsVisible(driver.findElement(By.css('.anticon.anticon-plus'))));
//driver.sleep(2000);
driver.findElement(By.css('.anticon.anticon-plus')).click();
})
I am getting two different types of errors, either its NoSuchElementError: no such element: Unable to locate element: or StaleElementReferenceError: stale element reference: element is not attached to the page document
But whichever error, its refer to line:
driver.findElement(By.css('.anticon.anticon-plus')).click();
When I use driver.sleep(2000), its getting resolved. In my opinion, It's the question of animation. I can get the element(.anticon.ancicon-plus) only at the time, the page's animation is completed.
what I am confused is that I use driver.wait(until.elementIsVisible()) without an error, It's obvious that I got the element. but at the next line, I can't use it. Or NoSuchElementError, or StaleElementReferenceError.
I find some answer like http://www.seleniumhq.org/exceptions/stale_element_reference.jsp,https://stackoverflow.com/questions/18225997/stale-element-reference-element-is-not-attached-to-the-page-document. But It can't help me.
when use driver.findElement, something terrible will be triggered. use javascript instead it.
driver.executeScript(function() {
while(true) {
if(!document.querySelector('.anticon.anticon-plus')){}
else {
document.querySelector('.anticon.anticon-plus').click();
break;
}
}
return true; // not neccessary
})
Related
When I run this code I get push is not a function. I have gone over the code so many times and can't figure out where I went wrong. i have also read many of post and I still can't figure it out. I am new to programming and could use the help.
const fs= require('fs')
const getNotes = function() {
return 'This just returns get notes'
enter code here
};
const addNote = function (title, body) {
const notes = loadNotes()
notes.push({
title: title,
boby: body
})
saveNotes(notes)
};
const saveNotes = function (notes) {
const dataJSON = JSON.stringify(notes)
fs.writeFileSync('notes.json',dataJSON)
}
// Code below loads the notes. Above, addNote adds the note.
const loadNotes = function () {
try {
const dataBuffer = fs.readFileSync('notes.json')
const dataJSON= dataBuffer.toString()
return JSON.parse(dataJSON)
} catch (error) {
return('Note such file')
}
}
module.exports ={
getNotes: getNotes,
addNote: addNote
}
So, you have this:
const notes = loadNotes()
notes.push({
title: title,
boby: body
});
If you're getting an error that notes.push is not a function, then that is because loadNotes() is not return an array. That could be for a couple reasons:
JSON.parse(dataJson) successfully parses your json, but its top level object is not an array.
JSON.parse(dataJson) throws and you end up returning a string instead of an array.
You can fairly easily diagnose this by adding a console.log() statement like this:
const notes = loadNotes();
console.log(notes); // see what this shows
notes.push({
title: title,
boby: body
});
FYI, returning a string fromloadNotes()as an error really doesn't make much sense unless you're going to check for a string after calling that function. IMO, it would make more sense to either return null for an error or just let it throw. Both would be simpler and easier to check after calling loadNotes().
And, in either case, you must check for an error return value after calling loadNotes() unless you want loadNotes() to throw upon error like it is.
I'm trying to work with shadow roots in my Testcafe project. It's a little bit complicated to deal with it. I create a custom function that behaves the same as Selector().find() but I struggle with this error :
The "boundTestRun" option value is expected to be a test controller.
when I'm doing as documented here :
import { Selector, t } from 'testcafe'
getInShadowRoot = Selector(
// More code here
)
const boundedGetInShadowRoot = this.getInShadowRoot.with({ boundTestRun: t })
I create a gist to illustrate my problem: https://gist.github.com/HugoDel/a600f3e120674e3f255884f3dc84fee3
Thanks for your help!
Edit:
I finally get rid of it since I don't need to add .with({ boundTestRun: t }) to make it work.
I have the same exact question that has already been posted for angularjs (Angular - Watch for ngShow/ngHide Changes in Ancestors that Affect Child DOM Element's Visibility) except in my case we are using angulardart. I cannot seem to watch a function in angulardart. When I try to, I get an error from the expression parser stating that "{" is unexpected. Is there a better way to do this in angulardart? If not, how can I watch a function similar to angularjs?
I've tried the following code with no success:
scope.watch("() { return _element.hidden; }", (value, _) {
print(value);
});
Parser Error: Unexpected token ) at column 2 in [() { return _element.hidden; }]
scope.watch("(scope) { return _element.hidden; }", (value, _) {
print(value);
});
Parser Error: '{' is an unexpected token at column 9 in [(scope) { return _element.hidden; }]
We are using angular.dart 1.1.2
You could use MutationObserver, it notifies about DOM changes. See for example In Dart why the code below (about MutationObserver) dose not work? (there should be more examples on SO)
Really what I am after is a way to check if the control exists without throwing an error.
The code should look something like this:
Control myControl = UIMap.MyMainWindow;
if (!myControl.Exists)
{
//Do something here
}
The problem is that the control throws an error because it is invalid if it doesn't exist, essentially making the exists property useless.
What is the solution?
In this case I am using the tryfind method.
Like this:
HtmlDiv list = new HtmlDiv(Window.GetWebtop());
list.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
list.SearchProperties.Add(HtmlDiv.PropertyNames.InnerText, "Processing search", PropertyExpressionOperator.Contains);
if (list.TryFind())
{
//DO Something
}
I am re-posting the comment kida gave as a answer, because I think its the best solution.
Control myControl = UIMap.MyMainWindow;
if (!myControl.FindMatchingControls().Count == 0)
{
//Do something here
}
The FindMatchingControls().Count is much faster then the Try Catch or the TryFind. Since it does not wait for SearchTimeoutto check if the element is now there. Default it waits 30 seconds for the element to not be there, but I like my tests to fail fast.
Alternatively its possible to lower the Playback.PlaybackSettings.SearchTimeout before the Catch or TryFind and restore it afterwards, but this is unnecessary code if you ask me.
You can do one of two things: Wrap your code in a try-catch block so the exception will be swallowed:
try
{
if (!myControl.Exists)
{
// Do something here.
}
}
catch (System.Exception ex)
{
}
Or, you could add more conditions:
if (!myControl.Exists)
{
// Do something here.
}
else if (myControlExists)
{
// Do something else.
}
else
{
// If the others don't qualify
// (for example, if the object is null), this will be executed.
}
Personally, I like the catch block, because if I expect the control to be there as part of my test, I can Assert.Fail(ex.ToString()); to stop the test right there and log the error message for use in bug reporting.
If you are sure that control will exist or enabled after some time you can use WaitForControlExist() or WaitForControlEnabled() methods with a default timeout or specified timeout.
I have a situation like this and I am looping until the control is available :
bool isSaveButtonExist = uISaveButton.WaitForControlEnabled();
while (!isSaveButtonExist )
{
try
{
uISaveButton.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
uISaveButton.SetFocus(); // setting focus for the save button if found
isSaveButtonExist = uISaveButton.WaitForControlExist(100);
}
catch (Exception ex)
{
//Console.WriteLine(ex.Message); // exception for every set focus message if the control not exist
}
}
// do something with found save button
// Click 'Save' button
Mouse.Click(uISaveButton, new Point(31, 37));
please refer to this link for more about these Methods:
Make playback wait methods
According to the "Book of Geb" I started to map our portal's web pages. I prefer to use variables defined within static content closure block and accessing them afterwards in page methods:
static content = {
buttonSend { $("input", type: "submit", nicetitle: "Senden") }
}
def sendLetter() {
waitFor { buttonSend.isDisplayed() }
buttonSend.click()
}
Unfortunately, sometimes I get an Geb waiting timeout exception (after 60 secs) or even worse I receive the well known "StaleElementReferenceException".
I could avoid the wait timeout when using "isEnabled" instead of "isDisplayed" but for the "StaleElementReferenceException" I could only apply the below solution:
def sendLetter() {
waitFor { buttonSend.isEnabled() }
try {
buttonSend.click()
} catch (StaleElementReferenceException e) {
log.info(e.getMessage())
buttonSend.click()
}
}
I guess, this solution is not really nice but I could not apply an explicitly wait as described in another article. Thus, I have some general questions:
Should I avoid to use static content definitions when pages are dynamically?
At what time or event Geb is refreshing its DOM? How can I trigger the DOM refreshment?
Why I still get a "StaleElementReferenceException" when using CSS selectors?
I would appreciate every hint which helps to understand or to solve this issue. The best would be to have a simple code example since I'm still a beginner. Thank you!
If you defined an at check on your page class the page would first verify that condition and wait for the first n seconds. Which is assigned in your gebConfig file. The default is 30 seconds.
static at = {
waitFor { buttonSend.isDisplayed() }
}
Thus once you call your pages 'to' method with a test or whatever you are using it for the page will wait and then perform your page manipulations.
to MyPage
buttonSend.click()
Should I avoid to use static content definitions when pages are dynamically?
No. Actually, the static definitions are of closures. So what is
actually happening is each time you make use of that Pages static
components you are calling a closure which is run dynamically on the
current page(collection of webElements). Understanding this is key to
using Geb and discovering the problems you will run into.
At what time or event Geb is refreshing its DOM? How can I trigger the DOM refreshment?
When you call: to, go, at, click ,withFrame(frame, page), withWindow
and browser drive methods it will refresh the current set of
WebElements. Geb has a nice collection of utiliities to make switching
between pages and waiting for page manipulations easy. Note: Geb is
actually built on WebDriver WebElements.
Why I still get a "StaleElementReferenceException" when using CSS selectors?
It is possible the page hasn't finished loading, has been manipulated
with ajax calls or has been refreshed in some other way. Sometimes an
'at' PAGE method call can fix these issues. They are for me most
common when using frames as Geb seems to become confused between pages
and frames a little. There are workarounds.
In short if you use the page pattern you can easily switch expected pages using the Page class you have defined with a static content, at, and url closure using the below:
to(Page)
at(Page)
Navigator.click(Page)
withFrame(frame, Page) { }
In addition to twinj's answer, I would like to point out a couple of other workarounds in case you encounter a StaleElementReferenceException.
Often times I find it is better to write out your selector manually rather than rely on the contents as defined in the page. Even though your page contents should not be cached by default, they still manage to slip away from me at times. This is particularly prevalent when dealing with dynamic content or iterations.
Ex: Let's say we want to click an element from a dynamically created dropdown.
Typically you might want to do something like...
static content = {
dropdown { $("#parentDiv").find("ul") }
}
void clickDesiredElement(String elementName) {
dropdown.click()
def desiredElement = dropdown.find("li", text:elementName)
waitFor { desiredElement.displayed }
desiredElement.click()
}
If this doesn't work, try getting rid of the contents altogether, and writing out the selector manually...
void clickDesiredElement(String elementName) {
$("#parentDiv").find("ul").click()
def desiredElement = $("#parentDiv").find("ul").find("li", text:elementName)
waitFor { desiredElement.displayed }
desiredElement.click()
}
In really nasty cases, you may have to use a manual timer, as pointed out in this answer, and your code may look like this...
void clickDesiredElement(String elementName) {
$("#parentDiv").find("ul").click()
sleepForNSeconds(2)
def desiredElement = $("#parentDiv").find("ul").find("li", text:elementName)
waitFor { desiredElement.displayed }
desiredElement.click()
}
Keep in mind this is a workaround :)
For large iterations and convenient closure methods, such as each{} or collect{}, you may want to add a waitFor{} in each iteration.
Ex: Let's say we want to get all rows of a large table
Typically you might want to do something like...
def rows = $("#table1").find("tr").collect {
[
name: it.find("td",0),
email: it.find("td",1)
]
}
Sometimes I find myself having to do this iteratively, along with a waitFor{} between each iteration in order to avoid a StaleElementReferentException. It might look something like this...
def rows = []
int numRows = $("#table1").find("tr").size()
int i
for(i=0; i < numRows; i++) {
waitFor {
def row = $("#table1").find("tr",i)
rows << [
name: row.find("td",0),
email: row.find("td",1)
]
}
}
I have figured that it is the navigator which get lost when you load dynamically.
I've solve the issue locally by reinit the page or module with below code:
void waitForDynamically(Double timeout = 20, Closure closure) {
closure.resolveStrategy = Closure.DELEGATE_FIRST
switch (this) {
case Module:
init(browser, browser.navigatorFactory)
break
case Page:
init(browser)
break
default:
throw new UnsupportedOperationException()
}
waitFor {
closure()
}
}