Waiting for Angular

Friday, February 7, 2014 Posted by

Recently I’ve spent a fair bit of time working with Angular JS applications and as great as Angular is, it can be a pain when it comes to automating it.

The main problem you will probably see is due to the fact that Angular does everything asynchronously, so you’re never quite sure when the page has finished loading, if only there was a way to know Angular had finished before you started doing stuff on a page…

Here’s an ExpectedCondition that will wait for Angular to finish processing stuff on the page:

public static ExpectedCondition<Boolean> angularHasFinishedProcessing() {
        return new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                return Boolean.valueOf(((JavascriptExecutor) driver).executeScript("return angular.element(document).injector().get('$http').pendingRequests.length === 0").toString());
            }
        };
    }

JMeter Maven Plugin Version 1.9.0 Released

Wednesday, January 15, 2014 Posted by

It has been a while since the last release and there are quite a few fixes and improvements.

One notable exclusion is JMeter 2.11 support, this is due to some dependency problems with the 2.11 JMeter artifacts.  We plan to make another release to support 2.11 as soon as everything is in place.

So on to the release notes:

Version 1.9.0 Release Notes

  • JMeter version 2.10 support added.
  • Issue #56 – Now using a ProcessBuilder to isolate the JVM JMeter runs in.
  • Merge pull request #70 from Erik G. H. Meade – Add requiresDirectInvocation true to JMeterMojo.
  • Issue #71 – Fixed documentation errors.
  • Issue #63 – Fixed remote configuration documentation errors.
  • Merge pull request #73 from Zmicier Zaleznicenka – Added missed dependency causing file not found / error in NonGUIDriver error.
  • Issue #72 – Remove the maven site from the plugin.
  • Issue #73 – Add missing dependency for ApacheJMeter-native.
  • Issue #84 – Correctly place explicit dependencies in the /lib directory.
  • Issue #66 – Jmeter lib directory contains additional jars.
  • Issue #75 – Allow empty propertiesUser properties.
  • Issue #80 – Integration Tests Failing With Maven 2.
  • Issue #77 – JMeter plugins artifacts now placed in lib/ext directory. You can specify which artifacts are JMeter plugins using the new jmeterPlugins configuration setting:
  • <configuration>
        <jmeterPlugins>
            <plugin>
                <groupId>my.group</groupId>
                <artifactId>my.artifact</artifactId>
            </plugin>
        </jmeterPlugins>
    </configuration>
  • Added the ability to configure the JMeter JVM:
  • <configuration>
        <jMeterProcessJVMSettings>
            <xms>1024</xms>
            <xmx>1024</xmx>
            <arguments>
                <argument>-Xprof</argument>
                <argument>-Xfuture</argument>
            </arguments>
        </jMeterProcessJVMSettings>
    </configuration>
  • Issue #82 – Allow users to specify the resultsDir:
  • <configuration>
        <resultsDirectory>/tmp/jmeter</resultsDirectory>
    </configuration>
  • Issue #64 – Remote execution seems to be stopping before agent stops running the tests.
  • Merge pull request #78 from Mike Patel – Changes to allow system / global jmeter properties to be sent to remote clients.
  • Issue #89 – Add support for advanced log config. If you add a “logkit.xml” into the <testFilesDirectory> it will now be copied into the /bin folder. If one does not exist the default one supplied with JMeter will be used instead. If you don’t want to call your advanced log config file “logkit.xml”, you can specify the filename using:
  • <configuration>
        <logConfigFilename>myFile.xml</logConfigFilename>
    </configuration>
  • Issue #88 – ApacheJMeter_mongodb dependency is not in POM

The Driver Binary Downloader Maven Plugin for Selenium 1.0.0 Released

Wednesday, January 15, 2014 Posted by

The initial stable release of the driver-binary-downloader-maven-plugin has been released. This brings in the following changes:

  1. Improved the performance of the unzip code (things are much quicker now).
  2. Only download binaries for the current OS (note more pulling down windows binaries on your linux box).
  3. PhantomJS support so that you can get GhostDriver(PhantomJSDriver) up and running with minimal effort.

To use it is very simple, just add the following to your POM:

    <plugins>
        <plugin>
            <groupId>com.lazerycode.selenium</groupId>
            <artifactId>driver-binary-downloader-maven-plugin</artifactId>
            <version>1.0.0</version>
            <configuration>
                <!-- root directory that downloaded driver binaries will be stored in -->
                <rootStandaloneServerDirectory>/my/location/binaries</rootStandaloneServerDirectory>
                <!-- Where you want to store downloaded zip files -->
                <downloadedZipFileDirectory>/my/location/zips</downloadedZipFileDirectory>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>selenium</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>

For more information see the project on github:  https://github.com/Ardesco/selenium-standalone-server-plugin

If you want to see it in action have a look at https://github.com/Ardesco/Selenium-Maven-Template

Time for some common sense testing?

Thursday, August 29, 2013 Posted by

There has been quite a buzz around the software testing community this week about the shiny new ISST, my initial reaction was “Another one, why do we need that?”. I then started to see quite a lot of activity on twitter talking about this new society and it piqued my interest so I went over to the ISST site and had a look for myself.

So what’s it all about?

The first thing I hit was the homepage and it looks great. It’s clean well designed and has a great mission statement, unfortunately for me things then started to go wrong…

The first thing I did was click join and I was instantly shown a page asking for €80, OK fair enough I thought. So what do I actually get for my €80? Well there is no member benefits page, so I clicked around a bit more and found the conference page. Excellent I would get €100 off the Let’s Test conference, unfortunately it’s in Sweden and realistically my overseas junkets are limited to a maximum of one per year (if I’m lucky) and I intend to go to the Selenium conference if I can next year, so not useful for me.

So I clicked around a bit more and the only other thing I came across was a short post about ISST member initiatives, well that looks great but I still don’t really see what the ISST are going to do with the registration fee so I took to twitter. It seems I’m not the only person asking what’s going to happen with the funds, to quote what I’ve seen so far (Including answers to my questions):

‏@intsst 21 Aug
@autesting Hi Geoff, very good point. We will be fully transparent on our spending and where the money flows to.

‏@intsst 21 Aug
@autesting e.g. when we do webinars, we need to fund the provider, admin costs etc. As said, we’ll be fully transparent on it

‏@intsst 21 Aug
What happens with my membership fee? Read here: http://www.commonsensetesting.org/about/faq/

‏@intsst 21 Aug
@Ardesco Hi Mark, glad you ask. We have addressed your question on the last entry of our FAQ: http://www.commonsensetesting.org/about/faq/

‏@intsst 21 Aug
@Ardesco also, have a look here: http://www.commonsensetesting.org/events/

OK this looks better, so I head over to the FAQ:

Question: Where does the money go that I pay as a member?

Answer: Running a professional society costs money. Changing an industry costs money. We are a non-profit society that is dedicated to the delivery of our mission, and every cent, penny and paisa will be devoted to that cause, or to providing tangible benefits to our members. Many of our initiatives won’t come free of costs. Our commitment to the members of ISST is that we will be completely transparent when it comes to our income and expenditure. As stated in our by-laws, we will issue a financial report once a year stating all our income and spending.

Right… Well that doesn’t really tell me anything, I know that it costs money to run a professional society but I want to know what it’s going to do for me. Back to twitter:

‏@intsst 21 Aug
Ask not (just) what the @intsst can do for your, ask what you can do for your profession.

I’m not in a happy place right now…

‏@intsst 21 Aug
We are proud to announce the first set of members-only webinars. Details to follow:

http://www.commonsensetesting.org/events/

Aha here we go something tangible, OK it’s not clear what the topics are yet but at least I know something is happening. That being said, how much does it cost to run a Webinar? Google hangouts are free and you can stream a Google hangout direct to YouTube. I’m sure it’s more complicated than that if you want to limit the Webinar’s to members only, but I can’t help thinking that a society that wants to change the face of testing should be evangelising to the whole community.

So I get back on twitter and see this:

@intsst 27 Aug
.@mpkhosla Yes, @badbud65 has a point: there are those who are about “what’s in it for them”, rather than the mission @jbtestpilot

I throw in my tuppence:

‏@Ardesco 28 Aug
@intsst @mpkhosla @badbud65 @jbtestpilot sounds harsh.When you are asking for money people will ask what’s in it for them and how it’s spent

and this comes back:

‏@intsst 22h
@kinofrost @badbud65 @Ardesco guys maybe you are missing one thing. It is nice to be spoon feed but we don’t believe that is what…..
@kinofrost @badbud65 @Ardesco our community is looking for. You asking how we gonna spend money. We ask what do you want to do with it…
@kinofrost @badbud65 @Ardesco put yourself in the driving seat and make a suggestion on how we reinvest our cash. http://context-driven.org/news/

So I think I finally understand what’s going on:

‏@Ardesco
@intsst @kinofrost @badbud65 So basically you’re setting yourself up as a fund that will bankroll ideas that you think will promote testing?

Or maybe not:

‏@intsst 22h
@Ardesco that could be one way to put it @kinofrost @badbud65

So what is it dammit, I now start to get annoyed:

@Ardesco 20h
@intsst @kinofrost @badbud65 I wish you’d give straight answers. Do you really know what you stand for? Vague mission statements don’t count

‏@intsst 20h
@Ardesco Be specific. WHAT is not straight to you? WHAT is vague to you?

I’ve lost it now, the whole bloody concept is vague to me. I know the ISST wants to do something but I’m not sure what and I don’t think they really know what either. I have no idea what they spend their money on and to be honest they don’t seem to have thought past getting money rolling into the coffers. To top it off I then see this:


‏@intsst 22h
@badbud65 if you are a member you are more then welcome to apply to launch you initiative. http://www.commonsensetesting.org/news/ @kinofrost @Ardesco


‏@badbud65 21h
@intsst Sneaky, so if I’m not a member I can’t make suggestions about how to improve that would encourage me to join? @kinofrost @Ardesco


‏@intsst 21h
@badbud65 what is sneaky about that? @kinofrost @Ardesco

So to sum it up we have a society that want a membership fee for you to join, but they don’t know what they are going to do with that membership fee. You can make suggestions as to what they should do, but you can only do it if you are a fully paid up member (I’m in full on cynic mode now). So I then go and look at the bylaws and the following stand out to me (I have added emphasis):


6. Withdrawal or exemption

Withdrawal from the society is always possible by the end of each year. Any member can be exempt from the society without justification by the board.


8. General assembly

The highest body of the society is the general assembly. The general assembly takes place once per year, between April and June.

All members will be invited to the general assembly at least 30 days in advance, and an agenda list will be provided.
General assembly has the following duties:

a) Election of the board
b) Definition and change of bylaws
c) Approval of financial statements
d) Approval of budget for the coming year
e) Definition of the membership fees

At the General Assembly each active member has a vote. Decisions of a General Assembly require a simple majority of the votes of the active members present or represented.

Passive members will be invited to the general assembly but will not have voting rights.

Looking at the FAQ:

Question: What does “passive member” mean?

Answer: General membership is “passive”, i.e. it does not confer voting rights. This measure is to ensure that there is continuity amongst the board in the short term whilst the society is being established. We, the board, expect this to be revised at some point in the future. We strongly believe that the society remain accountable to its membership, and recognize that members have the ultimate right to “vote with their feet”. We also encourage feedback and are happy to consider suggestions that might help us in pursuing our mission.

I then go back to the bylaws…

12. Changes of the bylaws

The bylaws can be changed if all members of the board agree to the change.

13. Liquidation of the society

The board can decide to liquidate the society by majority vote at any time.



In case of liquidation the remaining financial means will go to Médecins Sans Frontières.

So basically by offering up €80 I become a passive member who has no voting rights at all. I am then subject to decisions made by the board who can rewrite the rulebook as and when they see fit. They can liquidate the society at any point and right now any remaining funds will go to Médecins Sans Frontières (a worthy cause). But since they can also rewrite the rulebook at any point they could also change that bylaw to redistribute the funds amongst the board just before they liquidate it and as passive members everybody who has paid a membership fee can’t do anything about it.

Realistic?

Now this scenario is very much over the top and I don’t really think it is likely to happen, there are a lot of names associated with the ISST that I respect and I wouldn’t expect them to play silly buggers.

However I do also remember the problems the PCG had when it started up. It had one clear aim (to get rid of IR35), but a slightly woolly strategy. In the end the original board members spent a large amount of the money that came in on things that made them very rich and didn’t actually do a huge amount for all the members (It’s changed quite a bit since then and is now back on track, but for a while there PCG was seen by many as a get rich quick scheme for the founding members).

So what would give me confidence to join?

The cynic in me wants some kind of guarantee that this isn’t going to happen with the ISST.

I want to know in advance what you are planning on spending the money on, for example.

  • A maximum of 5% on admin (or some hard figures for admin costs).
  • 50% Funds to help community initiatives.
  • 20% Promotional material to raise the profile of context driven testing.
  • 25% Sponsoring context driven testing events.

If this needs to change it should be agreed in a board meeting for the next years worth of funding so that as members we always know what our membership fee is going to be going towards for the next year. This then gives us the option of not renewing our membership if we don’t like the funding plans for the next year.

A non-profit is quite able to spend all of their money on admin costs and wages to people that work for the non-profit, I would like to know some remuneration figures up front. I’m aware that running something like this takes time and money and I have no problem with people being paid for their time, I do however want some sort of guarantee that they will not be milking the society for everything it has (You can still be totally transparent while you run something into the ground so transparency on it’s own doesn’t mean a huge amount).

I want all members (passive ones as well) to be consulted if the bylaws change, that shouldn’t get in the way of running the society but it gives all members adegree of control.

I want a page entitled “Member benefits” that clearly tells me what they are. Yes I’m one of those people who wants to know what I’m going to get right now if I give you my money, it may make me a horrible person but I’m sure I’m not the only one.

And finally…

I’ve been testing for a long time now and every so often something sets off an alarm bell in my head. I can’t quite vocalise it but I know something doesn’t seem quite right. I then go looking for that something that may have triggered that alarm bell in my head and I won’t be happy until I have an explanation that fits. Right now the ISST has set off that bell in my head.

Don’t get me wrong, I like the concept of the ISST. I like the mission statement and I think if done properly it could be something great for the testing community. However I think that so far they have failed the promotion test. People are going to have questions and people want real answers, not hand waving or more questions. I see some people saying there is a backlash against the ISST, I’m not sure there really is, you just have a large group of people who have learnt to ask awkward questions as part of their job asking a series of awkward questions. The trick is giving them an answer that doesn’t set off those testing alarm bells in peoples heads.

When they manage to do that is when they will have my money.

Waiting with jQuery

Monday, May 6, 2013 Posted by

Waiting can be hard, so here are a couple of useful tricks to use with jQuery:

First of all, have you ever tried to interact with something on the screen only for some background AJAX call to change what is on the screen at the last possible moment as if it was purposly trying to break your test? Well lets get rid of that problem by waiting until all AJAX calls have finished processing:

    public static ExpectedCondition<Boolean> jQueryAJAXCallsHaveCompleted() {
        return new ExpectedCondition<Boolean>() {
 
            @Override
            public Boolean apply(WebDriver driver) {
                return (Boolean) ((JavascriptExecutor) driver).executeScript("return (window.jQuery != null) && (jQuery.active === 0);");
            }
        };
    }

Bear in mind that this will wait until there are no outstanding AJAX calls, once this condition has been met something sneaky could then fire off another AJAX call just to be awkward so it’s not totally foolproof, it should help increase reliability however.

Secondly, have you ever tried to click on an element that is supposed to do something special (e.g. save a form, sort a table, make something magical happen on screen, make a drop down box appear on mouseover, etc) and once you have clicked on it found that nothing happened? The usual thing to do is blame Selenium because it didn’t click on your element, but have you ever thought that Selenium is so fast that it managed to click on the element before the JavaScript that is rendering the page managed to register a listener on the element that you are about to interact with? You may want to try this:

    public static ExpectedCondition<Boolean> listenerIsRegisteredOnElement(final String listenerType, final WebElement element) {
        return new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                Map<String, Object> registeredListeners = (Map<String, Object>) ((JavascriptExecutor) driver).executeScript("return jQuery._data(jQuery(arguments[0]).get(0), 'events')", element);
                for (Map.Entry<String, Object> listener : registeredListeners.entrySet()) {
                    if (listener.getKey().equals(listenerType)) {
                        return true;
                    }
                }
                return false;
            }
        };
    }

You would use it like this:

    WebElement myDropDownMenu = driver.findElement(By.id("menu"));
    wait.until(listenerIsRegisteredOnElement("mouseover", myDropDownMenu ))

This would make selenium wait until a mouseover listener has been applied to a dropdown menu element (obviously this example assumes that the menu dropdown is being performed using jQuery).

The above will only work if your site is using jQuery and jQuery is triggering the relevant actions so it is limited, however it is hopefully useful as well, enjoy.

JMeter Maven Plugin 1.8.1 Released

Saturday, April 13, 2013 Posted by

Version 1.8.1 of the JMeter Maven plugin has been released.

This is a minor update that fixes Issue #62 – testResultsTimestamp not working.

JMeter Maven Plugin 1.8.0 Released

Wednesday, March 13, 2013 Posted by

I’m a bit late adding this here (I’ve been distracted updating the wiki for the plugin and doing a bit of running around closing off issues) but thought it would be a good idea to start posting stuff about the JMeter Maven plugin here as well. Version 1.8.0 of the JMeter Maven Plugin is now available in maven central.

The source code is available on Github and there is now also an up to date Wiki as well.

Release Notes

  • Added support for JMeter version 2.9.
  • Fixed issue #61 – Added skipTests ability. You can now add a configuration option to skip tests, use it like this:

    <properties>
        <skipTests>false</skipTests>
    </properties>
     
    <plugin>
        <groupId>com.lazerycode.jmeter</groupId>
        <artifactId>jmeter-maven-plugin</artifactId>
        <version>1.8.0</version>
        <executions>
            <execution>
                <id>jmeter-tests</id>
                <phase>verify</phase>
                <goals>
                    <goal>jmeter</goal>
                </goals>
                <configuration>
                    <skipTests>${skipTests}</skipTests>
                </configuration>
            </execution>
        </executions>
    </plugin>

    If you now run:

    mvn verify –DskipTests=true

    The performance tests will be skipped.

  • #58,#59 – Add dependencies with custom function to /lib/ext folder (pull request made by dpishchukhin that has been merged in).
  • Removed jmx file sorting code as it was not sorting files into a deterministic order. Tests are run in the order the plugin discovers them on disk now.
  • Removed checks for <error>true</error> and <failure>true</failure> in .jtl files, these elements do not occur in JMeter 2.9.
  • Added ability to choose whether to Append or Prepend date to filename using the new “appendResultsTimestamp“ configuration option (Valid values are: TRUE,FALSE):

    <appendResultsTimestamp>false</appendResultsTimestamp>

  • Set default timestamp to an ISO_8601 timestamp. The formatter now used in the configuration option “resultsFileNameDateFormat“ is a JodaTime DateTimeFormatter (See http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html):

    <resultsFileNameDateFormat >MMMM, yyyy</resultsFileNameDateFormat >

  • Added the ability to override the root log level using the new “overrideRootLogLevel” configuration option (Valid log levels are FATAL_ERROR, ERROR, WARN, INFO and DEBUG):

    <overrideRootLogLevel>DEBUG</overrideRootLogLevel>

  • Failure scanner refactored to use a Boyer-Moore algorithm to increase performance on large results files, you should hopefully see some improvements in speed when the plugin is checking your results files for the presence of failures.
  • Added the ability to set the result file format using a new “resultsFileFormat” configuration option (Valid options are XML and CSV, it will default to XML):

    <resultsFileFormat>CSV</resultsFileFormat>

  • Modified remote configuration settings, configuration options are now:

    <remoteConfiguration>
    	<startAndStopServersForEachTest>false</startAndStopServersForEachTest>
    	<startServersBeforeTests>true</startServersBeforeTests>
    	<stopServersAfterTests>true</stopServersAfterTests>
    	<serverList>server1,server2</serverList>
    </remoteConfiguration>

    If you use “startAndStopServersForEachTest” it will override “startServersBeforeTests” and “stopServersAfterTests” if they have been configured as well.

Stop Moving So I Can Click You Dammit!

Sunday, February 24, 2013 Posted by

This is a little trick that some may find useful.

I re-factored some tests that were checking an accordion control on Friday to speed things up, unfortunately when I was done I started getting some intermittent failures.  It seemed that I was now sometimes unable to open up one of the accordion elements.  A bit of head scratching and some time in the debugger with nothing obvious jumping out at me I finally realised what it was.  I was sometimes clicking on an element to open up the next accordion whilst it was still moving (all because I made things run faster).  

The solution? Wait until the element has finished moving.

Hers is an Expected condition that you can use with WebDriverWait:

public static ExpectedCondition<Boolean> elementHasStoppedMoving(final WebElement element) {
    new ExpectedCondition<Boolean>() {
        @Override
        Boolean apply(WebDriver driver) {
            Point initialLocation = ((Locatable) element).getCoordinates().inViewPort();
            Thread.sleep(50);
            Point finalLocation = ((Locatable) element).getCoordinates().inViewPort();
            initialLocation.equals(finalLocation);
        }
    }
}

Please Let Manual Testers Be Manual Testers

Sunday, February 24, 2013 Posted by

The testing world seems to have entered a state of flux in the last couple of years where “Automated Testing” is the new nirvana, I suspect this in part due to more and more companies following Google’s lead and starting to hire developers in test. Now in my opinion having people performing these roles is not a bad thing. When you have somebody writing your test framework you want somebody with some experience writing code and making architectural decisions. A developer in test is a very useful and powerful resource in this situation. The problem is that people have seen how useful a developer in test is, and they have decided that every tester should now become a developer in test, even though the majority of testers are probably not going to be performing automated testing, or writing test frameworks.

I regularly frequent the Selenium User’s mailing list and day by day, I see more and more people coming to the list who just don’t seem to have a basic clue about programming. These people invariably want you to ‘urgently’ help them because they have to write an automated test/test framework and they have no idea how to do it. Now people wanting to learn Selenium and become automated testers is not a bad thing, but most of these requests seem to be people who have testing jobs and have suddenly had the job of automated tester thrust upon them, this is a bad thing!

The other thing I see more and more regularly now is test frameworks that have been written so that manual testers can easily use them without learning how to program. These products seem to come in two forms:

  1. Something that scans a page finding all the elements of interest to abstract away the logic of finding them.Let manual testers be manual testers
  2. An Excel spread sheet driven framework where you have to manually populate the spread sheet with locators/expected text to run the tests.

Now I can see some value in option 1, that could be useful for automated testers who don’t want to spend lots of time locating things that may be interesting on a page and just want to spend time interacting with elements. Personally however I would not want to use something like this, I prefer to locate elements of interest myself to keep my tests lean and mean.

Option 2 is something that should be killed right now in my opinion. It has exclusively been written to take manual testers and trick them with the promise that they will now become automated testers and eventually developers in test. It will not do this; they will spend all of their day filling in Excel spread sheets (Why do we still have such an obsession with Excel spread sheets anyway?) and then the rest of their time updating them as things go wrong.

This process really turns manual testers into data entry clerks. These frameworks are invariably brittle as hell and require a lot of manual effort to keep them up to date. The spread sheets that are used end up being very complex, because automated testing is a complex thing to do, and as you add more functionality they get worse. They are useless, soulless and worst of all, take up all the time that manual testers have so that they stop doing the thing that manual testers do best, manual testing!

In our eagerness to move testing forward we are actually forgetting what the point of automated testing was in the first place. Automated testing was designed to make boring and monotonous regression tests a thing of the past. If the machine can rerun a series of known tests by itself and check that nothing has broken in the current build that frees the manual testers up to do the thing they do best; manual exploratory testing which is where you find all the bugs.

Automated testing checks that the functionality you have written works as you expect it to. Manual testing starts to push the envelope and use the program in ways it wasn’t intended to be used. Manual testing has no barriers, it isn’t constrained, and it is what will find holes in your application.

Some of the best manual testers I have worked with knew nothing about programming; they could not write automated tests and quite frankly had no interest in it. If I was building a new test team and could pick either two of them, or a team of forty automated testers, I would pick the manual testers any day of the week. They will find bugs, they will exercise the system properly and they will be of much more benefit to the project. I would still want automated testers as well to write the automated test framework and write regression tests, the point is that they would be doing this to free up time for the manual testers to go in and break the system in new and inventive ways.

To finish off I would like to make a plea:

Please don’t try to turn manual testers into data entry clerks, and don’t try and force them to become programmers; all you are doing is destroying the testing profession.

Manual testers rock and are the heart and soul of testing, make sure you appreciate them!

Hacking Mouse Move Events Into Safari Driver The Nasty Way

Sunday, December 2, 2012 Posted by

I thought long and hard before posting this entry because while it works, it’s not the real solution (Which is adding the code to enable the Actions classes into Safari driver, something I’m looking at right now to see if I can contribute something useful back to the Selenium community). I follow the Selenium/WebDriver mailing lists quite closley and regularly see people overcomplicating and hacking things, usually due to the fact that they did not get instant gratification when trying to do things the right way because they didn’t quite get it working the first time. I really hope people will not use this example to “try and get things working” in other browsers because they don’t understand how to use the Actions class, or they can’t be bothered to learn how to do things the right way.

Let me be very clear, this will work but it’s a hack and there are consequences:

  • You will no longer be able to run multiple safari instances on your local machine because there is only one mouse cursor and we will be using it (bye bye threading to speed your tests up in safari)
  • You will have messy code because you are going to have to put in code branches specifically for Safari mouse events.
  • You can’t run the tests in the background, Safari will need to be in the foreground and have focus while tests are running

If you can live with these issues and you really need to get Safari working with mouse events in the short term, this may just work for you.

Now that the warnings are out of the way let’s have a look at the problem…

Lot of modern websites are using hover events to make customised tooltips (well they aren’t tooltips in the HTML sense, but everybody still calls them tooltips) that appear when hovering your mouse over something like a chart point, or maybe there is some drag and drop functionality you need to test. The problem with Safari is that the Actions class hasn’t yet been implemented and the Safari driver object does not have an underlying mouse object. Game over? Not quite there is a way to get around the current limitations of the Safari Driver that will enable you to start clicking and hovering away like a mad man, the Java AWT Robot class (I’ll just call it the robot for the rest of this post).

The Robot is cross platform compliant so this solution could potentially work on any OS, but since Safari 6 is only available on OSX that is all we are interested in at the moment. The solution I have still uses Selenium for the majority of the heavy lifting, it is simply using the robot in place of the actions class to manipulate the mouse.

For all of these examples I have created a class called RobotPowered with the following private variables and constructor:

private final Robot mouseObject;
private final WebDriver driver;
private final JavascriptExecutor executor;
 
public RobotPowered(WebDriver driver) throws AWTException {
  this.mouseObject = new Robot();
  this.driver = driver;
  this.executor = (JavascriptExecutor) driver;
}

Now we know what is available to us let’s start creating the code to move the mouse in safari. First of all we have a basic robot implementation that will allow you to move the mouse to a specific X/Y coordinate on the screen

public void robotPoweredMoveMouseToAbsoluteCoordinates(int xCoordinates, int yCoordinates) {
  mouseObject.mouseMove(xCoordinates, yCoordinates);
  mouseObject.waitForIdle();
}

It really doesn’t get much easier than this, the code is self-explanatory and it will just work. We do have a problem however; we don’t know where the browser was loaded on the screen so if we tried to click on some coordinates we would effectively be clicking blind. On to part two:

public void robotPoweredMoveMouseToCoordinatesOnPage(int xCoordinates, int yCoordinates) {
  //Get Browser dimensions
  int browserWidth = driver.manage().window().getSize().width;
  int browserHeight = driver.manage().window().getSize().height;
 
  //Get dimensions of the window displaying the web page
  int pageWidth = Integer.parseInt(executor.executeScript("return document.documentElement.clientWidth").toString());
  int pageHeight = Integer.parseInt(executor.executeScript("return document.documentElement.clientHeight").toString());
 
  //Calculate the space the browser is using for toolbars
  int browserFurnitureOffsetX = browserWidth - pageWidth;
  int browserFurnitureOffsetY = browserHeight - pageHeight;
 
  //Calculate the correct X/Y coordinates based upon the browser furniture offset and the position of the browser on the desktop
  int xPosition = driver.manage().window().getPosition().x + browserFurnitureOffsetX + xCoordinates;
  int yPosition = driver.manage().window().getPosition().y + browserFurnitureOffsetY + yCoordinates;
 
  //Move the mouse to the calculated X/Y coordinates
  mouseObject.mouseMove(xPosition, yPosition);
  mouseObject.waitForIdle();
}

This now calculates where the browser is on the screen, the size of the browser and how much space is used up by browser toolbars. There is one caveat to the above code; you will need to disable the status bar. I haven’t found an easy way to work out how much space is used by the status bar, and how much is used by the rest of the browser so the simple solution is to just remove it.

This code is getting better, we now know that when we pass X/Y coordinates into the function it will click on the page, but that still leaves us guessing where on the page a specific WebElement is, well it’s not a problem Selenium actually knows the coordinates of the elements on the page and it can tell you where they are:

public void robotPoweredMoveMouseToWebElementCoordinates(WebElement element) {
  //Get Browser dimensions
  int browserWidth = driver.manage().window().getSize().width;
  int browserHeight = driver.manage().window().getSize().height;
 
  //Get dimensions of the window displaying the web page
  int pageWidth = Integer.parseInt(executor.executeScript("return document.documentElement.clientWidth").toString());
  int pageHeight = Integer.parseInt(executor.executeScript("return document.documentElement.clientHeight").toString());
 
  //Calculate the space the browser is using for toolbars
  int browserFurnitureOffsetX = browserWidth - pageWidth;
  int browserFurnitureOffsetY = browserHeight - pageHeight;
 
  //Get the coordinates of the WebElement on the page and calculate the centre point
  int webElementX = ((Locatable) element).getCoordinates().getLocationOnScreen().x + Math.round(element.getSize().width / 2);
  int webElementY = ((Locatable) element).getCoordinates().getLocationOnScreen().y + Math.round(element.getSize().height / 2);
 
  //Calculate the correct X/Y coordinates based upon the browser furniture offset and the position of the browser on the desktop
  int xPosition = driver.manage().window().getPosition().x + browserFurnitureOffsetX + webElementX;
  int yPosition = driver.manage().window().getPosition().y + browserFurnitureOffsetY + webElementY;
 
  //Move the mouse to the calculated X/Y coordinates
  mouseObject.mouseMove(xPosition, yPosition);
  mouseObject.waitForIdle();
}

We are now taking in a WebElement and getting the coordinates of its top left point. We then use the size and height of the element to work out its centre point which is where we move the mouse.

What about drag and drop? Well that’s easy we can use the robot to hold and release the mouse button as well.

public void robotPoweredMouseDown() {
  mouseObject.mousePress(InputEvent.BUTTON1_DOWN_MASK);
  mouseObject.waitForIdle();
}
public void robotPoweredMouseUp() {
  mouseObject.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
  mouseObject.waitForIdle();
}

You now have everything you need to perform mouse actions with the Safari driver, unfortunately there is another caveat. I have found that when safari initially loads it doesn’t always have focus and when it doesn’t have focus it ignores the mouse events, the solution is to make the robot click on the window to set focus at the start of your test, here’s a quick click function to let you do just that:

public void robotPoweredClick() {
  mouseObject.mousePress(InputEvent.BUTTON1_DOWN_MASK);
  mouseObject.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
  mouseObject.waitForIdle();
}

Finally here’s a little trick to find out what browser the current driver object is driving, you can use this to add in some specific code branches for safari:

((RemoteWebDriver) driver).getCapabilities().getBrowserName();

Once again I must reiterate that the above code is a hack and has its limitations, it does however get you out of a tight spot if you need to run your hover/drag and drop automated tests against Safari.

All of the above code is available on github at https://github.com/Ardesco/Powder-Monkey/blob/master/src/main/java/com/lazerycode/selenium/tools/RobotPowered.java