Explicit Waits in page object model - c#

I am trying to use the Explicit Waits in page object model to test a single page app , The problem i am facing is that the Explicit wait starts checking for the element as soon the page is called up ,since the page completely dynamic some element only appear after a some action . Since the element is checked even before its created it always troughs a error "No such Element exception"
Sample Code:
public class example
{
private IWebElement ExampleElement = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id("ExampleElement ID"));
});
private IWebElement ExampleElement2 = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id("ExampleElement ID"));
});
public void example1()
{
ExampleElement.click();
ExampleElement2.click();
}
}
public class exampleTest
{
[Test]
public void SomeTest()
{
example.example1();
}
}
The ExampleElement2 only loads after the ExampleElement click but what happens is it keeps checking for the ExampleElement2 even before the click action so it is not able to find it and it gives a exception . The thing is i dont want the element to checked for exsistence even before its called for .
I am completely new , thanks in advance

It seems that you have a pattern issue here. Your WebElements are at class level, so when the instance of the class is initiated webdriver is trying to initialize everything and therefore waiting even for Element2. If Element2 is dynamic then you can't wait for it at class level. Wait for it after the condition you expect it to appear.
I would say just have at class level,
private IWebElement ExampleElement2;
and then, do
public void example1()
{
ExampleElement.click();
ExampleElement2 = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id("ExampleElement ID"));
});
ExampleElement2.click();
}
Also you might want to look at PageFactory. You don't want to repeat driver.findElement all over your code and other obvious advantages which are off this topic :-).

As nilesh mentioned in his answer, it's required to defer the loading of the ExampleElement2. However it should be an explicit wait and you need to wait for relevant expected condition for example you can say wait until ElementIsVisible (in Java API you can put specific conditions like elementToBeClickable based on the element type). Then it makes sure the element satisfies the particular condition you passed.
P.S - I am sorry I couldn't help you through a code snippet. Since I am a Java guy I haven't done any Selenium tests using C#. But the principle is same.

Related

Can't verify a modal is showing with Selenium in C#

I'm making some automated tests with Selenium in C#. It's an ASP.NET MVC Core 3 application, using xUnit for the Unit Tests and we are using Bootstrap as the UI framework. I have a problem with verifying that a modal is showing on screen.
When I run this test:
// HomePageAutomatedUITests.cs
[Fact]
public void Try_Open_Arkivskaber_Modal()
{
_page.ClickArkivskaberNyButton();
string modalValue = _page.GetArkivskaberModalAttribute("class");
Assert.Contains("show", modalValue);
}
It always fails, telling me that the Assert.Contains() is never true. The Class string is normally modal fade but when the button is pressed its supposed to add the class "show" so it becomes modal fade show. This works when a user does it on the website, but for some reason I can't make it work in this test. The test tells me that the "modalValue" reads modal fade so it is at least reading the correct place.
The _page variable is a Page Object Model and the following two properties and methods are used in the test:
// HomePage.cs
private IWebElement ArkivskaberNyButton => _driver.FindElement(By.Id("arkivskaber-ny"));
public void ClickArkivskaberNyButton() => ArkivskaberNyButton.Click();
private IWebElement ArkivskaberModalWindow => _driver.FindElement(By.Id("opretArkivskaber"));
public string GetArkivskaberModalAttribute(string attrName) => ArkivskaberModalWindow.GetAttribute(attrName);
I have checked the id arkivskaber-ny I am passing is correct.
I'm running all the tests I do Headless using the Chrome Driver. What might be wrong here?
If anyone else runs into this issue then here is the solution:
Use the WebDriverWait class from the OpenQA.Selenium.Support.UI namespace. You'll also need to get the DotNetSeleniumExtras.WaitHelpers package from NPM or Github.
Then in your code you can do this:
// HomePageAutomatedUITests.cs
public void Arkivskaber_Open_NyArkivskaber_Modal()
{
WebDriverWait waitDriver = new WebDriverWait(_driver, TimeSpan.FromSeconds(2));
_page.ClickArkivskaberNyButton();
waitDriver.Until(_page.GetArkivskaberModalWaitFunc());
string modalValue = _page.GetArkivskaberModalAttribute("class");
Assert.Contains("show", modalValue);
}
In your POM (if you use those) you do this:
// HomePage.cs
using ExpectedConditions = SeleniumExtras.WaitHelpers.ExpectedConditions;
...
public Func<IWebDriver, IWebElement> GetArkivskaberModalWaitFunc() => ExpectedConditions.ElementIsVisible(By.XPath("//div[#id='modal-window-id-here']"));
If you don't use a POM, then you can just make the ExpectedConditions.ElementIsVisible() call straight in your Test function. The key here is how long you wait for the modal to appear after clicking. If you set this too low, it might not have time to appear after you click.
Hope this helps others.

Xamarin Forms Navigation PopAsync isn't working

I'm working on an app in Xamarin.Forms, and things have been going pretty steadily, until I ran into a navigation error. The thing that mystifies me about it is that I've already been successfully using the same code calls on other pages, but suddenly with this page, it isn't working.
I have designed a bit of a unique navigation flow because of the visual result that I'm trying to accomplish, using a combination of master-detail that has two tiers of navigation pages using the normal push / pop code. I was following suggestions from this article on medium.com.
The app initializes a main page called "Root Navigation" that initializes the master and detail pages.
public App()
{
InitializeComponent();
MainPage = new RootNavigation();
}
Root Navigation page:
public partial class RootNavigation : MasterDetailPage
{
private MenuPage menuPage;
private NavigationPage OuterPage;
private NavigationPage InnerPage;
public RootNavigation()
{
this.Master = this.menuPage = new MenuPage();
this.menuPage.MenuItemsListView.ItemSelected += Menu_ItemSelected;
var viewModel = new SelectEmployeeViewModel();
var page = new SelectEmployeePage(viewModel);
SetAsDetailPage(page);
}
To navigate forward in the app, I'm using a method called "set as detail page," that bridges the gap between master-detail behavior and navigation push / pop behavior.
private void SetAsDetailPage(ContentPage page)
{
NavigationPage.SetHasNavigationBar(newPage, false);
if (newPage.GetType() == typeof(JobDetailPage))
{
newPage.ToolbarItems.Add(
new ToolbarItem()
{
Text = "Back",
Command = new Command(() => BackButton_Clicked())
});
}
this.InnerPage = this.InnerPage ?? new NavigationPage();
this.InnerPage.Navigation.PushAsync(page);
this.OuterPage = this.OuterPage ?? new NavigationPage(this.InnerPage);
this.Detail = this.Detail ?? this.OuterPage;
}
Then, navigating backward calls one of two methods: "BackButton_Clicked()" or "ReturnToJobList()".
private void ReturnToJobList()
{
while (InnerPage.CurrentPage.GetType() != typeof(JobsPage))
{
var current = InnerPage.CurrentPage.ToString();
System.Diagnostics.Debug.WriteLine($"'{current}' attempting Navigation.Pop()");
InnerPage.PopAsync();
}
}
private void BackButton_Clicked()
{
this.InnerPage.PopAsync();
}
All of the pages that display read-only data have navigated without issue. When I'm done with the page, I have it raise a call to the MessagingCenter, and the root navigation receives the message and performs the desired navigation. For example, the "MenuPage_ItemSelected" event handler fires the code:
if (e.SelectedItem.ToString().ToLower() == "log out")
{
Device.InvokeOnMainThreadAsync(() =>
{
InnerPage.PopToRootAsync();
this.IsPresented = false;
});
}
This seems to be working fine, after I spent a while researching that when a secondary page calls 'pop to root' on a background thread, I have to invoke it on the main thread.
Ok, so finally to the problem page: The "update job notes" page. The page raises a call to the Messaging Center, and the Root Navigation page picks that up and executes the following code:
private async void SaveJobNotes(ContentPage sender)
{
if (sender is UpdateJobNotesPage notesPage)
{
bool result = await SaveNewJobNote(notesPage);
var message = result ? "Saved changes" : "An error occurred; changes not saved";
await DisplayAlert("Save", message, "OK");
}
ReturnToJobList();
}
Stepping through the code, it correctly executes SaveNewJobNote() and returns true. Then, it awaits displaying the alert "Saved Changes". Then, the code then gets stuck in an infinite while loop in the "ReturnToJobList()," forever printing out into the debug output [0:] 'Application.Views.UpdateJobNotesPage' attempting Navigation.Pop(). After about a million cycles I get tired of waiting and quit the debugger. I can't seem to do anything to make the page go away!
I've tried a bunch of stuff with investigating the differences between PopAsync and PopModalAsync. After checking what's on the navigation stacks for the different pages in question, everything looks exactly like what I'd expect -- there's nothing on the modal stacks for anything (because I never called PushModalAsync on anything), there's 0 on the RootNavigation stack, 1 on the OuterPage stack, and 4 on the InnerPage stack. That all makes perfect sense to me, but it still doesn't pop the Update Job Notes page. I also tried code with Navigation.RemovePage(page) with no success. The only difference there was that the debugger included printing a warning about my code and suggesting I use PopAsync() instead.
I also tried some different things with making the PopAsync() call from this.Navigation, this.Outer, this.Outer.Navigation, this.Inner, this.Inner.Navigation, all with no success.
I have already looked at a bunch of other questions on Stack Overflow including this question and this question but none of them seem to apply in this case. Any assistance would be greatly appreciated!!
I remember this was happening to me.
I forget exactly what was the cause, but I had some funky navigation going on as well. My problem was around popups and at one point, they were creating a new stack. So when I popped, I was getting unexpected results.
I would suspect you are also creating another stack somewhere, especially if you are at 0 in the debugger.
The culprit is most likely lurking around that InvokeOnMainThread().
I haven't really figured out what the problem is with the code block that I created that was supposed to call InnerNavigation.PopAsync() until the Job List page was visible. It seems like all the variables in that code block evaluate to values that I'd expect, but somehow it doesn't seem to be able to pop anything off the stack.
However, I did change my code block that handles saving Job Notes, and it does now pop the Save Job Notes page off the stack.
private async void SaveJobNotes(ContentPage sender)
{
this.InnerPage.PopAsync(); //I don't understand why this works and the
//other didn't, but it correctly pops the page
if (sender is UpdateJobNotesPage notesPage)
{
bool noteSaved = await SaveNewJobNote(notesPage);
bool progressSaved = await SaveJobProgress(notesPage);
var message = noteSaved && progressSaved ?
"Changes were save successfully" :
"An error occurred; changes not saved";
await DisplayAlert("Save", message, "OK");
}
}

How to fix 'The current thread is not associated with the renderer's synchronization context'?

I am trying to change a string, being used for a title, in my blazor-server-side application. But I am having trouble getting the UI to update.
I tried using StateHasChanged(), but that didn't work so I looked around and found that on the FlightFinder Demo that was made, it has an OnChange event Action, so I am trying to implement that.
It works until I try to refresh the browser, then I am hit with this error
System.InvalidOperationException: 'The current thread is not associated with the renderer's synchronization context. Use Invoke() or InvokeAsync() to switch execution to the renderer's synchronization context when triggering rendering or modifying any state accessed during rendering.'
This is what I have:
private string _title = "TestSite";
public string Title => _title;
public event Action OnChange;
public void ChangePage(string pageName)
{
_title = pageName;
NotifyStateChanged();
}
private void NotifyStateChanged(int navigationType = 0)
{
OnChange?.Invoke();
}
All I have to do is call ChangePage("some Page Title") and it works, unless as I mentioned I try to refresh.
I am just trying to change a string on one component that through another component, which doesn't sound all that crazy. If there is a better way to do titles or change things from other components, I would love to hear about it.
So, what can I do to make sure that m invoke method is on the correct thread?
Or is there a different way to change the title that would be more effective?
Thank you in advance!
I have just implemented a State Container like this and ran into the same error - but my service needs to be a singleton.
So I found an example on the aspnetcore git that does exactly what the error message says to do.
Call InvokeAsync -- not from your state container but when you try to change the state of your razor component.
https://github.com/dotnet/aspnetcore/blob/321db9d99f84cf7a67d453384292d9339de748d1/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor
So your state container doesn't need to change, just your component event handler does.
#code{
protected override void OnInitialized()
{
_YourService.OnChange += OnMyChangeHandler;
}
public void Dispose()
{
_YourService.OnChange -= OnMyChangeHandler;
}
private async void OnMyChangeHandler(object sender, EventArgs e)
{
// InvokeAsync is inherited, it syncs the call back to the render thread
await InvokeAsync(() => {
DoStuff();
StateHasChanged();
});
}
}
Now your service (if it's a singleton) can notify ALL your users at once! Think about all hoops we had to jump through in past to do this.
I posted this first thing in the morning thinking that I wouldn't have the time to look into and thinking that by time someone was able to help me out, I would have found the time to look into it more. Though I have spent a couple of days going back and forth on this already.
I finally found this article that explains that what I am trying to do is called a State Container.
What they said is that I could inject the class as a singleton, which is what I was doing or a scoped service. Turns out all I needed to do was change it to a scoped service and it works great!
no need sophisticated solution, Blazor working perfectly if you will update GUI in your event handler by
this.InvokeAsync(() => this.StateHasChanged());
it worked using await InvokeAsync(stateHasChanged); in my case

Passing a condition for click in Selenium

I saw this line of code someone wrote once that essentially resolves a condition for a selenium click by passing in the webdriver, some soft of boolean condition, and a Timespan to wait for the condition to be fulfilled. It looks like this:
_driver.WaitForClick(By.XPath("xpath value"), () =>
{
return _driver.IsElementVisible(By.XPath("//div[#class='some value']"));
}, TimeSpan.FromMilliseconds(250));
What i want to know is how to build the same method. How can i build a custom driver click method like this? Please help. New to C# Can someone share some sample code?
In order to implement such a method you need to understand several concepts first. I won't be explaining all of them and would request you to search and read.
Explicit waits in selenium - Explicit Waits . Toggle C# as language of choice.
Extension methods in C#
Func delegate in C# Func delegate
Here is a sample implementation with a slightly modified signature i.e. this implementation doesn't require By.Xpath/By.Id etc as first parameter:
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;
namespace SeleniumWebAutomation
{
public class WebElementWait : DefaultWait<IWebElement>
{
public WebElementWait(IWebElement element, TimeSpan timeout) : base(element, new SystemClock())
{
this.Timeout = timeout;
this.IgnoreExceptionTypes(typeof(NotFoundException));
}
}
public static class WebDriverExtension
{
public static void WaitAndClick(this IWebDriver driver, Func<IWebDriver,IWebElement> condition,TimeSpan timeSpan)
{
IWebElement webElement = new WebDriverWait(driver, timeSpan).Until(condition);
webElement.Click();
}
}
}
You can invoke it as shown below now :
//Wait for element to exist for up to 10 second before performing click operation
driver.WaitAndClick(ExpectedConditions.ElementExists(By.XPath("xpath value")),TimeSpan.FromMilliseconds(10000));
//Wait for element to be visible for up to 5 second before performing click operation
driver.WaitAndClick(ExpectedConditions.ElementIsVisible(By.Id("Id")),TimeSpan.FromMilliseconds(5000));
Please note that i have used the inbuilt helper class ExpectedConditions
which has several methods such as ElementExists(),ElementIsVisible(),etc. which returns a Func given a locator. If element is not found /not visible respectively in provided examples within specified timeout, exception will be thrown.
Regards,
Nish26

C# Webdriver - Page Title assert fails before page loads

This issue began when I switched from testing on the www website to my localhost version of it. Working in VS 2012, I will begin debugging so the localhost is active, detach the process so I can test on it, then run any test I like. For a very basic example:
[Test]
public void CanGoToHomePage()
{
Pages.HomePage.Goto();
Assert.IsTrue(Pages.HomePage.IsAt());
}
And the functions it references are here:
public class HomePage
{
const string Url = "http://localhost:3738";
const string HomepageTitle = "FunnelFire - Home Page";
public void Goto()
{
Browser.Goto(Url);
}
public bool IsAt()
{
return Browser.Title == HomepageTitle;
}
}
And the actual selenium code here:
public static class Browser
{
static IWebDriver webDriver = new FirefoxDriver();
public static void Goto(string url)
{
webDriver.Manage().Window.Maximize();
webDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
webDriver.Url = url;
}
}
Now the issue. The 10 second implicit wait that I added in Browser does successfully wait at most 10 seconds after loading a page to see if it can locate whatever information I want it to find, that is not the problem.
As I said earlier, after I switched to testing on localhost, suddenly I ran into a strange issue where a page would begin to load (i.e. screen still totally white, nothing finished) or even sometimes the next page would JUST barely finish loading and suddenly the test would just up and fail, pointing to the Assert of IsAt returning false even though the page it was loading was the correct one. I could run that test immediately once more and it would pass without a problem. Run it a third time and it could randomly fail again. I'm honestly not sure what is causing the issue and any help would be appreciated!
Implicit waits work only for finding elements. For waiting on the title of the page to be a certain value, you'll want to use an explicit wait. You can write your own version of this pattern, but in the .NET bindings, the WebDriver.Support.dll assembly has a WebDriverWait class to help with this. Its use would look something like this:
// WARNING! Untested code written from memory below. It has not
// been tested or even compiled in an IDE, so may be syntactically
// incorrect. The concept, however, should still be valid.
public void WaitForTitle(IWebDriver driver, string title, TimeSpan timeout)
{
WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.Until((d) => { return d.Title == title; });
}
You could even modify your IsAt method to use this pattern, catching the WebDriverTimeoutException and returning false if the wait function times out.

Categories