Monday, April 28, 2014

Another day, another Selenium gotcha or two

1. Entering text into rich text editors (iframes with content editable).

Basic:

$this->frame('frame-id');
$this->keys('the text');
$this->frame(null);

Works in Firefox.

In Chrome, you also need:

$this->frame('frame-id');
$this->one('body')->click();
$this->keys('the text');
$this->frame(null);

Note that because we're in the 'scope' of the iframe, $this->one('body') is the body element of the iframe.  Clicking on it gives it the focus so that the keys() get to the iframe.

Doesn't work in IE (10 on Win 7, at least).  Win 7 can't find the body element (although putting in an echo $this->source()) confirms it's finding the iframe OK.

Couldn't work this one out.  The only thing I could do with this is a browser sniff for IE, and if it is inject some javascript to set the value manually.  Because the text editor is empty, I can't click() on any other elements in the iframe.  Horribly hacky.


2. Auto correct

I was putting the text 'i have a comment' into a normal text editor.  Turns out IE autocorrects that to 'I have a comment' which means the test fails when it compares the strings after saving.



Grrr.....

Friday, April 25, 2014

On why writing Selenium tests is like writing raw js 5 years ago

I really thought js libs meant we didn't need to worry quite so much about cross browser quirks so much any more.

Writing some Selenium functional tests, it turns out that's not true.

I have a table. A simple table. With a header, footer, and body content.  With the html in the correct order, as it should be, thead then tfoot then tbody.

In my Selenium tests I generate a lot of tables.  I don't want to compare them cell by cell, so I'd taken the (what I thought to be) neat approach of grabbing the text the first time the test runs, saving it in a file, and then comparing subsequent runs with this known good.

Except.

It turns out that when running the test on Firefox, a call to $element->text() returns the text, without any html, as it appears on screen - that is, thead content, followed by tbody content, followed by tfoot content.

In Chrome, it returns the text, without any html, as the html is - that is, thead content, followed by tfoot, followed by tbody.

For the record, on IE it does the same as Firefox. (This all on a Win7 VM, IE10, Chrome/FF latest).


Which means the tests ALL FAIL when they shouldn't.


Which feels like trying to write cross-browser js all over again.  Painful.


UPDATE 28/4/14

I was wrong.  I jumped the gun.

The tables I was looking at are javascript generated, and inject a tfoot to hold some summary data.  It turns out that Chrome injects the tfoot between the thead and tbody, and so that's what it shows when you get the text content.  Firefox and IE append it to the markup, so it goes after the tbody.  So that's what shows up when you get the text content in FF/IE.

I've yet to look into why the html ends up different in the different browsers; it's possible that's where the bug is.

Anway, what I have learned: don't file bugs on a Friday afternoon.  Make sure your error report actually produces the error.

Set the time date on Virtualbox Windows VM for Selenium testing

When I initialize my Selenium setup before running the tests, two things happen.  First, the database is restored to a known state.

Secondly, the date and time is set to a known, fixed value.

This happens on the Linux VMs that run the application by calling a bash script over ssh on the servers.  That works fine.

However, I also need the Windows VM that's running as a Selenium node to be set at the same time/date.

The reason for this is this.  About the first thing you see when you log in is a diary.  I need that diary to be showing a particular date so that I know what records should be visible, and my tests verify that those records are there.  If I write the tests one week, and run them the next, without resetting the date the diary will be showing the current week - where the data displayed is different.

There are lots of situations in the application where this is the case.  The alternatives (for example, running an sql update on all date columns) seem more wrong to me - my fixtures (the database) are no longer fixed.  I'm pretty sure that trying that will bite me in other ways (for example, the tests will be filled up with $expected = date('d/m/Y'); instead of $expected = '13/12/2013';).

Anyway, there's also some situations where the client uses the local date to calculate things (in particular, there's some javascript that does 'number of days since x'.  So when I run the tests, the date that javascript uses as the starting point to do that calculation needs to be consistent, else the expected value one day is different on the following day.

Now, Windows has a date-time setting to get the date from the internet.  We can turn that off easily in the date settings on the Windows VM.

However, that's only half the story: even with that off, the VM is getting the date/time from the host bios.  So I change the date manually, and a second or two later it switches back to the current date.

I don't want to fix the date on the host machine.  Other bad things will start happening.  For example, all my Jenkins builds will apparently happen at the same time on the same day - the history will be meaningless.  And other things, I'm sure.

So the solution in this situation is obviously to google and try things you don't fully understand until one of them works.  One of these solutions:

host$ vboxmanage setextradata "win7box" "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled" "1"

Didn't work.  This is possibly because of Guest Additions (which I need for various reasons).  The next one did.  You need to change the parameters passed to the VirtualBox Guest Additions Service (this is on a Windows VM).  The details are at https://forums/virtualbox.org/viewtopic.php?t=24057#p149903 but you need to add --disable-timesync to the VBoxService.exe call.

Next step is then to run a little batch script to set the date/time when the machine starts.  Obviously this is made more complicated by the fact that you can't change the date as a normal user, you need to be Administrator.  So I can't just bung the .bat file into the startup.

Instead, it seems it needs to be a scheduled task.  To add these, click Start, right-click Computer, click 'Manage'.  Click Task Scheduler on the left, Create Basic Task on the right, follow the wizard.

For reasons that I don't understand, I can't start the selenium node in this way - a shortcut to a batch file to do that stays in the Startup folder.