Skip to content

pratoservices/extjswebdriver

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build status

extjswebdriver

What's this?

ExtJS 4 & Selenium WebDriver C# (.NET 4) framework for easily writing js-heavy scenario tests.

Features

There are a couple of baseline element classes you can work with while writing WebDriver tests. Featuers:

  • Async waiting: don't just wait x seconds when an Extjs view is loaded to start clicking, but use a clever mechanism.
  • DOM-to-C# class mapping using WebDriver attributes - initialize them via the WebDriver PageFactory.
  • (Extjs) Javascript utility methods (close all views, tooltips, evaluate JS files, ...) provided.
  • Handy WebElement extensions (DoubleClick, Get/Set values, date specific stuff, fill in random values, HTML contents, ...)
  • Exception handling: save a screenshot to a file, save extra info (stacktrace, serverside logging, ...) to a file, and wrap these in an exception.

Requirements

DLL requirements are handled through Nuget.

  • Extjs 4.
  • NUnit >= 2.6.4
  • Selenium WebDriver >= 3.5.0 including Selenium Support
  • RestSharp (why? see below) >= 105.0.1

How should you use this?

Start by extending from BaseScenarioTestCase and provide methods for logging in. Every test case should extend from your base class. Then extend from ScenarioFixture to provide an implementation for ResolveHostAndPort() - so WebDriver knows where to surf to to test your application.

We use NUnit's [SetupFixture] attribute to create a new instance of our concrete scenario fixture. The constructor automatically starts Google Chrome by default:

   [SetUpFixture]
    public class NUnitFixture
    {
        [SetUp]
        public void SetUp()
        {
            MyScenarioFixture.SetUpFixture();
        }

        [TearDown]
        public void TearDown()
        {
            MyScenarioFixture.TearDownFixture();
        }
    }

The SetUpFixture method is implemented by creating a new instance of our concrete scenario fixture.

After that's done, you can start creating page objects in C# and using them to walk through the flow of your web application. Every page object should extend from BaseContainerComponent. In the constructor, it automatically waits until the "component is loaded". This is abstract and thus should be implemented. Our default implementation looks like this:

    public IWebElement Window
    {
        get { return Driver.FindElement(By.CssSelector(".wd-window-" + CssClassname)); }
    }

    public override void WaitUntilComponentLoaded()
    {
        WaitUntil(x => Window.Displayed);
        this.WaitUntilExtLoadingDone();
    }

The CSS class is provided via the constructor. All Extjs panels, views, ... are identified through CSS classes set in the definition in the javascript files. This is very important, since we need a way to bind an extjs object to a webdriver WebElement to start working with it. Since Extjs generates it's own IDs on every element in the DOM tree, we used classes. Every marker class for webdriver starts with '.wd-'.

Example unit tests

    [Test]
    public void CreateSomeClientViaUI()
    {
        OpenClientsWindow()
            .ClickMenuActionAdd()
            .SetName(_CLIENT_NAME)
            .SetStreet("TestStreet")
            .SetCountry("BE")
            .SetZip("3500")
            .SetLanguage("1")
            .ClickSave()
            .ExpectOpened<ClientDetailPanel>();
    }

What did we have to do in order to achieve this?

  • Implement OpenClientsWindow: return a new instance of ClientsWindow, which inherits BaseContainerComponent.
  • Implement methods on the clients window, like SetName and SetStreet. Use InputField or simply IWebElement from WebDriver itself.

Mapping web elements can be done like this:

    [FindsBy(How = How.CssSelector, Using = ".wd-menu-actions"), UsedImplicitly]
    private IWebElement _ActionsMenu;

Using exception handling

You can use PostSharp like we do to catch any exception and redirect to the provided handler, which in turn saves metadata like the screenshot. Wrap it in another exception to bubble up to the actual NUnit unit test.

    [ScenarioExceptionAspect(AttributeExclude = true)]
    public class ScenarioExceptionAspect : OnMethodBoundaryAspect
    {
        public override void OnException(MethodExecutionArgs args)
        {
            var exceptionFileName = Directory.GetCurrentDirectory() + @"/" +
                WebDriverExceptionHandler.Handle(args.Exception, new ServiceLogInfoResolver("log/serviceLog.txt"));
            throw new Exception("Scenario test failed"
                + Environment.NewLine
                + " -- Screenshot: " + exceptionFileName + ".png"
                + Environment.NewLine
                + " -- Log: " + exceptionFileName + ".txt", args.Exception);
        }
    }

More info on how this works can be found in this blog post.

Service logging

ServiceLogInfoResolver uses REST to retrieve the logfile from the webserver, since the scenario tests don't have access to those files and they are usually not run on the same machine, so there's no way to retrieve them by simply reading a file. If you have some other way to provide extra information when something goes wrong, implement another IExceptionLogInfoResolver.

Using async waiting in page objects

Since javascript (and therefore Extjs) uses async callbacks for Ajax etc, one cannot simply wait 3 or 4 seconds until "stuff" is done. There are two important methods to help avoid this: WaitUntilAjaxLoadingDone and WaitUntilExtjsLoadingDone.

Ajax loading

We use a hook to count the number of ajax calls currently "busy" on the client - see jsoverrides_mainpage.js. When a call returns, the number is decreased. The method wait until ajax loading is done simply waits until the number is back set to zero, by evaluating window.ajaxRequests.

Extjs loading

Extjs sometimes is busy rendering it's views (which is is obviously not an ajax call, so can't use the previous) and WebDriver needs to wait until all rendering is done, otherwise a bunch of random ElementNotFound exceptions keep appearing (it works on my machine!).

So, when rendering still is busy, we use load masks, and simply check on the presence of the mask CSS class:

Wait(30).UntilNotDisplayed(By.CssSelector(".x-mask-msg"));

This is exactly what I need, but x or y could be made like z...

Great, dig in! Fork and submit a merge request, we might be delighted to accept it. Thanks for your interest!

About

ExtJS & Selenium WebDriver C# framework for easily writing js-heavy scenario tests

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages