I have this method using implicit wait pre configured to return me some WebElement:
private WebElement findElement(By locator) {
WebElement element = null;
for (int i = 0; i <= numberOfTries && element == null; i++)
try {
waiter.until(ExpectedConditions.elementToBeClickable(locator));
element = driver.findElement(locator);
} catch (TimeoutException | NoSuchElementException e) {
System.out.println(specificStringAboutTimeOutAndNoSuchEl);
} catch (WebDriverException e) {
Throwable cause = null;
Throwable result = e;
while (null != (cause = result.getCause()) && (result != cause))
result = cause;
System.out.println(specificStringAboutException);
System.out.println(result);
}
return element;
}
And this to wait to some other element vanish, like spinners and loading screens:
private void waitsElementToVanish(By locator, String previousOp) {
List<Class<? extends Throwable>> ignorableExceptions = new ArrayList<>();
ignorableExceptions.add(StaleElementReferenceException.class);
ignorableExceptions.add(NoSuchElementException.class);
try {
if (driver.findElements(locator).size() > 0)
System.out.println(locator.toString() + " exists");
waiter.ignoreAll(ignorableExceptions).until(ExpectedConditions.visibilityOfElementLocated(locator));
waiter.ignoreAll(ignorableExceptions).until(ExpectedConditions.invisibilityOfElementLocated(locator));
} catch (TimeoutException e) {
System.out.println("timeout waiting " + locator.toString() + " to vanish");
System.out.println(previousOp);
}
}
And I use them like this:
waitsElementToVanish(By.className("spinnerLoading"), "someString");
findElement(By.xpath(otherElement)).click();
And I get this error at .click():
org.openqa.selenium.WebDriverException: unknown error: Element <div class="menuContent ellipsis flex flexGrow">...</div> is not clickable at point (136, 240). Other element would receive the click: <div class="spinnerLoading">...</div>
This is the website I'm using and this application is purely educational. This is the entire code.
Should I wrap Selenium click method into some WebDriverException like:
Tries to click, if WebDriverException is thrown with spinnerClass on it, goes to waitsElementToVanish and then tries again
If number of tries goes beyond limit, then it throws some ReallyMessedUpExceptionDoSomething! exception.
My question is: I want to avoid Thread.sleep() at all costs. I want optimize test time waiting only for what it's coming, not for some pre fixed period without checking anything on page. The 'good practice actions' are really wrap click on many tries? How I can assure I'm avoiding spinner even when the line before tried that too? And I don't see any problem wrapping click on a loop to try again and again with a limit. But, actually, what is the best solution here?
Related
This is an extract of a method in CDialog class:
void CDialog1::Method()
{
try
{
// Snip
}
catch (CException* e_)
{
const gsl::not_null<CException*> e{ e_ };
e->ReportError();
e->Delete();
}
catch (const _com_error& e)
{
AfxMessageBox(e.ErrorMessage(), MB_OK | MB_ICONERROR);
}
}
There is no issue with this function when it is ran from an instance of the modal dialog.
But, in another part of my application I load the same dialog as a hidden modeless dialog. And I call the same function. Eg:
void CDialog2::SomeTask()
{
if (m_pDialog1 != nullptr)
{
m_pDialog1->Method();
}
}
In this second scenario there is an issue with Method when an error is encountered. CDialog2 needs to handle the display of the errors from what I understand, because the hidden instance will appear if it shows a messagebox.
How do I get around this? Note that CDialog1 has a boolean method IsHiddenMode so we know if we are running it as a model or not.
What is the easy way to change my methods to cater for both scenarios:
When CDialog1 calls the method in it's modal dialog.
When CDialog2 calls the method using the modeless member variable of CDialog1.
I tend to overcomplicate my explanations so I hope it makes sense.
I adjusted the Method to detect if the dialog was in hidden mode and throw the exception, eg:
catch (const _com_error& e)
{
if (IsHiddenMode())
{
throw;
}
else
{
AfxMessageBox(e.ErrorMessage(), MB_OK | MB_ICONERROR);
}
}
That way, the calling dialog could catch and handle with a try / catch block.
I am building a (static) website in Blazor.wasm where the users upload some number of files. My intention is then (after all the files have passed some basic checks) to iteratively present a set of fields which the users are asked to complete. Only after they have submitted all the [Required] information and press submit will the next form show up.
I have included a minimal example below.
if (valid_files == numFiles)
{
for (int counter = 0; counter < num_files; counter++)
{
paramList.Add(new ParamsForm { });
<EditForm Model="#paramList[counter]" OnValidSubmit="#SingleSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<p>
Camera type <br>
<InputText id="cameratype" #bind-Value="#paramList[counter].CameraType" />
</p>
<button type="submit">Submit</button>
</EditForm>
}
<button #onclick="HandleValidSubmit">Upload Data </button>
}
The expected behaviour is that on each iteration, a frech instance of the onbject ParamsForm is added to the list. We then create a form based of that instance and wait for the user to complete the form. Once they press the Submit button the next stage of the for loop begins. Once all the data have been submitted and the for loop is completed, the Upload data button should appear and the users are invited to submit all their data to the server.
Instead, none of the code inside the EditForm ... section is being completed. I.e. - I see no popping up of text boxes, and any code that I put in there (for example #Console.WriteLine("This should show up) does not seem to be executed. The Upload data button does not appear and instead an error is thrown complaining that the index is out of range, which is weird because after the code at the top of the for loop there are no longer any elements being accessed by an index.
I am quite new to interacting between c# and HTML, so I think I can appreciate why what I have shouldn't work, but I don't know how I can go about writing something that will work.
Any advice would be gratefully recieved.
The ways of Blazor are a bit mysterious to me, too-- it takes a while to adjust! I do know that Blazor has an OnAfterRender event, which makes me think that it might not like to have user input in a loop like that. Or it may be that it's enumerating if (valid_files == numFiles) as false because those variables aren't initialized yet when the markup first renders.
I'd try two things:
(1) Throw StateHasChanged() at the end of your loop or after the code that sets valid_files and numFiles and see if that does anything you like.
(2) Probably this anyway: instead of looping in the markup, I'd build the entire List<ParamsForm> paramsList in the FileInput's event handler instead, move the counter to the code block, and add counter++ to the end of the SingleSubmit() method.
It's 5:00 am here, just got up to get a snack and going back to bed. Let me know if things still don't fly, and I'll try a more complete example tomorrow. :D
I don't have much information about your class, where you are getting your file list from, and so on. I recommend passing complete objects rather than individual properties. For example, I'd rather have IBrowserFile File {get; set;} in my ParamsForm class than say string FileName. That way, if I decide-- oh, I want to get this or that property-- it's already there.
Anyway, hope something in here might be useful:
#if (CurrentForm is not null)
{
<EditForm Model="CurrentForm" OnValidSubmit="#SingleSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<p>
Camera type <br>
<InputText id="cameratype" #bind-Value="CurrentForm.CameraType" />
</p>
<button type="submit">Submit</button>
</EditForm>
#if (IsComplete) // Don't show upload button until you're done
{
<button #onclick="DoUploads">Upload Data </button>
}
#DisplayMessage
}
#code {
class ParamsForm { public string FileName; public string CameraType; } // Just a placeholder
List<ParamsForm> ParamsList = new List<ParamsForm>();
ParamsForm CurrentForm { get; set; }
int counter = 0;
List<string> FileNames;
bool IsComplete = false;
string DisplayMessage = "";
void InitializeForms()
{
// I don't know your class, so just an example
foreach (var item in FileNames)
{
bool IsValid = false;
// check file validity
if (IsValid) ParamsList.Add(new ParamsForm() { FileName = item });
}
if(ParamsList.Count > 0)
CurrentForm = ParamsList[0];
}
void SingleSubmit()
{
// Do stuff with CurrentForm
if (++counter >= ParamsList.Count) IsComplete = true;
else CurrentForm = ParamsList[counter];
}
async Task DoUploads()
{
// Do stuff with your ParamsList
int UploadCounter = 0;
foreach (ParamsForm item in ParamsList){
DisplayMessage = "Uploading " + UploadCounter + " of " + ParamsList.Count;
StateHasChanged();
// Do the Upload;
}
DisplayMessage = "Finished.";
}
}
I am aware that doing Flow Control on a program using a try-catch block is bad practice, but I can't see how to do it in another way when the error caught needs a redirection of the code's execution.
For example, let's say I have screen where the user clicks a button, and on the buttons click I execute some code. After the code ends execution, I redirect the user to a new screen....
... But the code execution can failure, and in that case, I need to let the user know of the failure and redirect him to the previous screen.
The way I will do it is (C# example, but I think this is more of a not language related question, more like good practices on coding in general) :
try
{
ExcuteCode(); // Some code that can throw some Exception, it can be a .NET framework method or something else.
NavigateToNextScreen();
}
catch(Exception e)
{
Log.Write(e);
ShowErrorMessage();
GoBack();
}
Even if I change this to:
bool hasError = false;
try
{
ExecuteCode();
}
catch(Exception e)
{
Log.Write(e);
ShowErrorMessage();
hasError = true;
}
if(hasError) { GoBack(); }
else { NavigateToNextScreen();}
It's still doing Flow Control inside the try-catch block.
So, is it there a recommend way to handle this properly and avoid this bad practice?
Are you looking for the obvious?
try
{
if ( ExcuteCode() == false)
{
ShowErrorMessage();
GoBack();
}
else
{
NavigateToNextScreen();
}
}
catch(Exception e)
{
Log.Write(e);
ShowErrorMessage();
// handle critical error
}
Edit
If you have control over ExecuteCode() then instead of throwing an error return a null or false value and use that to check whether to go forward or backward.
Original
What if you always go back after the catch statement and put the go forward code at the end of the try, like this:
try
{
ExecuteCode();
NavigateToNextScreen();
}
catch(Exception e)
{
Log.Write(e);
ShowErrorMessage();
}
GoBack();
Now go back is the default and it only goes forward if everything executes correctly.
i am using eclipse juno and testing the application actitime,which has a check box in login page "keepLoggedInCheckBox"
The HTML source of it,
<input type="checkbox" title="Do not select if this computer is shared"
id="keepLoggedInCheckBox" value="on" name="remember">
I am trying locate the check box "keepLoggedInCheckBox" by using ,
WebElement check = driver.findElement(By.id("keepLoggedInCheckBox"));
But getting this error,
Exception in thread "main" org.openqa.selenium.NoSuchElementException:
Unable to locate element:
{"method":"id","selector":"keepLoggedInCheckBox"}
i tried with xpath (//input[#id='keepLoggedInCheckBox']) ,also getting same error.
please help me, to solve this.
I have faced the same problem. The DOM looses the reference to the element in question. It can either be StaleStateReferenceException or NoSuchElementException. There are two ways to deal with the situation.
(Though my solution is in Java. The underlying concept is the same. )
By using the the following method, you can try clicking an element. If exception is thrown then catch the exception and try to click again until the element is present:
public boolean retryingFindClick(By by) {
boolean result = false;
int attempts = 0;
while(attempts < 2) {
try {
Actions action = new Actions(driver);
WebElement userClick = wait.until(ExpectedConditions.presenceOfElementLocated(by));
action.moveToElement(userClick).click().build().perform();
driver.findElement(by).click();
result = true;
break;
} catch(StaleElementReferenceException e) {
System.out.println("StaleElementReferenceException");
}
catch(NoSuchElementException e) {
System.out.println("No Such Element Found");
}
attempts++;
}
return result;
}
Please try this. I have added implicitlyWait which will allow your DOM content to load.
Note: Please replace Your URL with the exact URL.
WebDriver driver=new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.get("Your URL");
driver.findElement(By.xpath("//*[#id='keepLoggedInCheckBox']")).click();
As with the title in Chrome (v.4.1) multiple tinyMce (v2.08) instances do not work. To be exact the first two instances are ok, the others not, and chrome gives this error:
Uncaught Error: INDEX_SIZE_ERR: DOM Exception 1
Has this happened before?
Unfortunately I can't show you any code because it's for an admin area, I just need some clue for the moment.
Yes, as user XP1 noted, at this link you can find resolution for a comprimed TinyMCE source:
http://my.opera.com/XP1/blog/2011/07/21/tinymce-javascript-error-in-opera-getrangeat-index-size-err
But if you want to work with original uncomprimed source (it's just a bit easier), here is the solution:
Look for code "setRng : function(r) {" (without quotes) and exchange the whole function with:
setRng : function(r) {
var s, t = this;
if (!t.tridentSel) {
s = t.getSel();
if (s) // this block fixed according to TinyMCE JavaScript error in Opera (getRangeAt, INDEX_SIZE_ERR); http://my.opera.com/XP1/blog/2011/07/21/tinymce-javascript-error-in-opera-getrangeat-index-size-err
{
if(s.anchorNode === null && s.focusNode === null)
{
t.explicitRange = r;
try {
s.removeAllRanges();
} catch (ex) {
// IE9 might throw errors here don't know why (NOW WE KNOW WHY DAMMIT!)
}
s.addRange(r);
}
if (s.rangeCount > 0)
t.selectedRange = s.getRangeAt(0);
}
} else {
// Is W3C Range
if (r.cloneRange) {
t.tridentSel.addRange(r);
return;
}
// Is IE specific range
try {
r.select();
} catch (ex) {
// Needed for some odd IE bug #1843306
}
}
},
ONE NOTE: please make sure variables match. I am not sure how it is between different TinyMCE versions BUT the variables are not the same between comprimed and src mutations of the script file.
Take Care and God Speed