Nikolay Advolodkin
Nikolay Advolodkin

Reputation: 2230

How to automate testing of Angular JS using Selenium Webdriver

So I am facing a problem interacting with this HTML below. I am unable to open the pop up and close it using different Selenium Webdriver commands. Although I am looking for a specific solution here, any general tips on dealing with Angular JS would also be appreciated. I believe that the root cause of the problem is that I do not know a good way to automate Angular using Selenium Webdriver.

I'm using C#, but I will take any helpful information from any programming language as I can always retrofit the solution.

I am trying to interact with this pop up button, unsuccessfully: uib-popover-template="'/app/student/assessment/directives/templates/shortTextPopupTemplate.html'"

<div class="line ttsBorder">“<span style="font-style:italic;">And be it further enacted</span><span style="display: inline;">, That in all that territory ceded</span><span short-text-popup="" accession-number="VH209006" class="ng-isolate-scope" ng-non-bindable=""><a uib-popover-template="'/app/student/assessment/directives/templates/shortTextPopupTemplate.html'" popover-is-open="ctrl.isVisible" popover-trigger="none" popover-placement="auto top" tabindex="0" title="Shows more information." ng-click="buttonOnClick($event)" ng-keydown="buttonOnKeydown($event)" ng-class="['stpu-button', {disabled: ctrl.isDisabled, active: ctrl.isVisible}]" class="ng-scope stpu-button" style="z-index: 3;"></a></span> by France to the United States&nbsp;.&nbsp;.&nbsp;.&nbsp;which lies north of thirty‑six degrees and thirty minutes north latitude&nbsp;.&nbsp;.&nbsp;.&nbsp;slavery&nbsp;.&nbsp;.&nbsp;.&nbsp;shall be, and is hereby, forever prohibited.”</div>

Here is what I tried unsuccessfully:

//attempt 1
var elements = Driver.FindElements(By.XPath("//*[@class='line ttsBorder']"));

//attempt 2 - UserInteractions = new Actions(Driver);
UserInteractions.MoveToElement(PopUpButton).Click().Perform();


//attempt 3    
    Driver.FindElement(By.XPath("//[@title='Shows more information.']")).Click();

//attempt 4
//Driver.FindElement(By.XPath("//a[@uib-popover-template]"));
    PopUpButton.Click();

//attempt 5
//Working, but seems dirty - JavaExecutor.ExecuteScript("arguments[0].click();", PopUpButton);

I had to result to tabbing through the UI to find the element that I want. I'm really unhappy with such a brittle solution and was hoping that you can offer some advice.

Thanks in advance!

Upvotes: 7

Views: 5004

Answers (1)

Amir Shirazi
Amir Shirazi

Reputation: 836

The quick fix is to create a CssSelector to access your element like this: Driver.FindElement(By.CssSelector("a[ng-click='buttonOnClick($event)']")); A good fix is to create a class for each page you are testing and reach the elements of your page like so:

class LoginPageObject
    {
        public LoginPageObject()
        {
            PageFactory.InitElements(TestBase.driver, this);
        }

        [FindsBy(How = How.Id, Using = "UserName")]
        public IWebElement TxtUsername { get; set; }

        [FindsBy(How = How.Id, Using = "Password")]
        public IWebElement TxtPassword { get; set; }

        [FindsBy(How = How.Id, Using = "submit")]
        public IWebElement BtnLogin { get; set; }
    }

For accessing Angular elements using ng properties, It is better to use Protractor-Net which exposes NgBy class to explore angular elements in the DOM like this:

        var ngDriver = new NgWebDriver(driver);
        ngDriver.Navigate().GoToUrl("http://www.angularjs.org");
        var elements = ngDriver.FindElements(NgBy.Repeater("todo in todoList.todos"));

The complete source code for the code snippet above can be found in here. Also, you can create your own custom decorators for angular elements from protractor API like this:

public class NgByRepeaterFinder : By
    {
        public NgByRepeaterFinder(string locator)
        {
            FindElementsMethod = context => context.FindElements(NgBy.Repeater(locator));
        }
    }

    internal class NgByModelFinder : By
    {
        public NgByModelFinder(string locator)
        {
            FindElementMethod = context => context.FindElement(NgBy.Model(locator));
        }
    }

And then use them in your page class like this:

class YourPageObject
{
    public YourPageObject()
    {
        PageFactory.InitElements(TestBase.ngWebDriver, this);
    }

    [FindsBy(How = How.CssSelector, Using = "a[ng-click='addNewTrack()']")]
    public IWebElement BtnAddNewTrack { get; set; }

    [FindsBy(How = How.Custom, CustomFinderType = typeof(NgByModelFinder), Using = "trackSearch")]
    public IWebElement TxtSearchTrack { get; set; }

    [FindsBy(How = How.Custom, CustomFinderType = typeof(NgByRepeaterFinder), Using = "track in models.tracks | filter: trackSearch")]
    public IList<IWebElement> BtnListTracks { get; set; }
} 

the complete guide for how to create and you custom finder annotators for angularjs can be found in here.

Upvotes: 3

Related Questions