The Browser the Programmer & the Dalek

A modern Who novel by @asciidisco

                                   __
                        __  __    /\ \  __
   __      ____    ___ /\_\/\_\   \_\ \/\_\    ____    ___    ___
 /'__`\   /',__\  /'___\/\ \/\ \  /'_` \/\ \  /',__\  /'___\ / __`\
/\ \L\.\_/\__, `\/\ \__/\ \ \ \ \/\ \L\ \ \ \/\__, `\/\ \__//\ \L\ \
\ \__/.\_\/\____/\ \____\\ \_\ \_\ \___,_\ \_\/\____/\ \____\ \____/
 \/__/\/_/\/___/  \/____/ \/_/\/_/\/__,_ /\/_/\/___/  \/____/\/___/
                                                                    
  • Sebastian Golasch
  • Cologne/germany
  • asciidisco on the internetz
  • JavaScript developer
  • Coffee maniac
  • Passionate Whovian
  • Ready 8.1

Whovian?

Dr. Who Facts in a hurry

  • A (british) TV show
  • About a 1000?! year old alien time traveler
  • Longest running TV show ever (Since 1963)
  • The Doctor can cheat death by regenerating (So clever show runners!)
  • Rule #1: The doctor lies
  • Daleks are the Doctors Number one enemies

Enough Background Information...

Lets talk about testing! Yeah!

The problem

How can we assure that clicking that link does what we expect?

"Phew! I'm a JS developer, i can write a unit test for this!"

the theory of:
IFRAME script injection & synthetic events

Prepare a testpage

<!doctype html>
<script>
function testme() {
	var myIframe = document.getElementById("myframe");
	var script = myIframe.contentWindow.document.createElement("script");
	script.src = 'ourDirtyLittleHelperScript.js'
	myIframe.contentWindow.document.body.appendChild(script);
}
</script>
<iframe src="http://2013.front-trends.com/" onload="testme()" id="myframe"/>

Prepare a script that will be injected

function clickLink(link) {
  var cancelled = false;
  if (document.createEvent) {
    var event = document.createEvent('MouseEvents');
    event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false,0, null);
    cancelled = !link.dispatchEvent(event);
  } else if (link.fireEvent) {
    cancelled = !link.fireEvent('onclick');
  }

  if (!cancelled) {
    window.location = link.href;
  }
};

if (window.location.href !== 'http://2013.front-trends.com/register/') {
  clickLink(document.querySelectorAll('.register-link')[0]);
} else {
  alert('Click on .regsiter-link leads to the register page');
}

Disable the same origin restriction

open -a '/Applications/Safari.app' --args --disable-web-security
open -a Google\ Chrome --args --disable-web-security

Cool. Lets build a testing framework & end this talk, I need a coffee anyway!

Sorry. Not that easy.

A click is a click is a click is a click...

Like the doctor would have said it

Remember Rule #1:
The doctor lies!

Lets change the situation

$('a').on('click', iWillBeFired);
$('a').on('mousedown', iWillNeverBeTriggered);
$('a').click();

A click is JUST a click
that is MORE than JUST a click...

Anatomy of a click

  1. mouseenter
  2. mouseover
  3. mousemove (n:times)
  4. mousedown
  5. mouseup
  6. click
  7. mousemove (n:times)
  8. mouseleave
  9. mouseout

Faking this is easy. But clicks are an easy interaction...

...try to do this with an select element...

...and cross browser consistency is a damn lie by the way!

Also

Doesn't work in IE & Firefox

No way to automate this

False negatives (dis. same-origin)

...

Not the way you wan't to write your tests!

The crux with time travel

We've been here before

...I mean others have been here before...

...Java developers have been here before...

And then it started to head in the wrong direction...

First, a bit of history...

Back in the old days, there were only desktop applications

First GUI testing solutions basically recorded a user interaction, replayed it & checked if app looked the same everytime

Based on that...

Selenium I, quick & dirty

  • Around since 2004
  • Build in Java
  • Has adapters for different other Programming languages (PHP, Ruby, Python)
  • Uses so called drivers to hook into the browsers
  • Allows you to remote control a browser
  • Can exchange data with the browser
  • Is deprecated!
package org.openqa.selenium.example;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

public class GoogleSuggest {
    public static void main(String[] args) throws Exception {
        WebDriver driver = new FirefoxDriver();
        driver.get("http://www.google.com/webhp?complete=1&hl=en");
        WebElement query = driver.findElement(By.name("q"));
        query.sendKeys("Cheese");
        long end = System.currentTimeMillis() + 5000;
        while (System.currentTimeMillis() < end) {
            WebElement resultsDiv = driver.findElement(By.className("gssb_e"));
            if (resultsDiv.isDisplayed()) {
              break;
            }
        }
        List s = driver.findElements(By.xpath("//td[@class='gssb_a gbqfsf']"));
        for (WebElement suggestion : s) {
            System.out.println(suggestion.getText());
        }
     }
}

Gnah, don't force Frontend devs to learn a new language

Complicated...

Too Complicated...

We need a simpler way...

Reboot!

Webdriver

Developed as Selenium2

Webdriver defined a RESTful way to access the browser from outside

It basically turns your browser into an remote controllable web service

Based on the JSONWire Protocol

IS now a w3c spec

We use...

...PhantomJS

Basically a browser that runs in your terminal

Headless is the Keyword

Based on WebKit

Now lets use Webdriver to control PhantomJS with a Node.js script

PhantomJS & Webdriver are awesome...

...But...

...it's just 1 browser

Right direction...

...Very fast...

..but Not enough!

What do we need?

A tool that allows us to write our tests in javascript

An easy abstraction that tackles the average frontend developer

Should be able to test real Browsers

Should be able to (re)use selenium infrastructure

In browser runner

CMD runner

As easy as jQuery

Do you know such a tool?

I don't!

So i decided to write my own

Say hello to Dalek JS

My approach to bring UI testing back to the frontend devs

Is currently in an early development stage

Expect the first release within the next 2 months

Spoilers!

THE END

BY Sebastian Golasch / @asciidisco