Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Automating 130 browser, platform and language combinations without going insane

Automating 130 browser, platform and language combinations without going insane

Slides from my talk at Web Rebels 2012

jarib

May 25, 2012
Tweet

More Decks by jarib

Other Decks in Programming

Transcript

  1. Future • Browser automation standard • Make browser vendors care

    • Delete implementations from the Selenium tree
  2. Architectural themes • Emulate the user • Prove that the

    drivers work • You shouldn't need to understand everything • Every API call is an RPC
  3. Architectural themes • Designed to accurately emulate user interaction with

    a web application • Use native events where possible... • ...but make it easy to do cross-browser • API should match user interactions • e.g. no fireEvent() Emulate the user
  4. Architectural themes • Extensive automated test suite • Mostly integration

    tests • Strong culture for adding tests when fixing bugs • Challening CI setup • 6 browsers on 3 OSes with 4 languages • 72 builds per commit Prove the drivers work
  5. Architectural themes • Lots of languages / technologies in use

    • Architecture should allow developers to focus their talents where they'll be most productive You shouldn't need to understand how everything works
  6. Architectural themes • Need to communicate with external browser process

    • Performance at the mercy of network latency • Introduces tension in the API design • Coarseness (== improved performance) • Expressiveness, ease of use Every API call is a RPC
  7. API command –> { name: "findElement", parameters: { using: "name",

    value: "q" } } response <– { status: 0, value : <opaque element identifier> } command –> { name: "sendKeys", parameters: { element: <opaque element identifier>, value: ["webdriver"] } } response <– { status: 0 } SPI driver.find_element(name: 'q').send_keys('webdriver')
  8. API command –> { name: "findElement", parameters: { using: "name",

    value: "q" } } response <– { status: 0, value : <opaque element identifier> } command –> { name: "sendKeys", parameters: { element: <opaque element identifier>, value: ["webdriver"] } } response <– { status: 0 } SPI driver.find_element(name: 'q').send_keys('webdriver')
  9. driver.find_element(name: 'q').send_keys('webdriver') API command –> { name: "findElement", parameters: {

    using: "name", value: "q" } } response <– { status: 0, value : <opaque element identifier> } command –> { name: "sendKeys", parameters: { element: <opaque element identifier>, value: ["webdriver"] } } response <– { status: 0 } SPI
  10. API object oriented SPI procedural newSession(browser: 'firefox') findElement(using: 'name', value:

    'q') sendKeysToElement(id: '00cbe31e', value: ['webdriver']) getElementAttribute(id: '00cbe31e', name: 'value') driver = Selenium::WebDriver.for :firefox input = driver.find_element(name: 'q') input.send_keys('webdriver') puts input.attribute('value')
  11. Wire protocol • Reference implementation of the Service Provider Interface

    (SPI) • In spec terms, non-normative (not a requirement for implementors) • Though comes with some benefits • e.g. existing clients in multiple languages
  12. Wire protocol server client JSON HTTP Language binding Java Ruby

    C# Python JS Browser Firefox Opera Chrome IE
  13. Wire protocol Machine A JSON HTTP Language binding Java Ruby

    C# Python JS Browser Firefox Opera Chrome IE
  14. Wire protocol JSON HTTP Machine B Remote WebDriver Server JSON

    HTTP Browser Firefox Opera Chrome IE Machine A Language bindings Java Ruby Python C# JS
  15. Wire protocol Selenium 2 Grid Hub JSON HTTP Grid Node

    JSON HTTP Language bindings Java Ruby Python C# JS Grid Node Grid Node CI server
  16. Wire protocol Maps SPI commands to RESTish HTTP resources POST

    /session POST /session/:sessionId/element POST /session/:sessionId/element/:id/value GET /session/:sessionId/element/:id/attribute/:name newSession(browser: 'firefox') findElement(sessionId: 'c688f8e4', using: 'name', value: 'q') sendKeysToElement(id: '00cbe31e', value: ['webdriver']) getElementAttribute(id: '00cbe31e', name: 'value')
  17. Wire protocol Maps SPI commands to RESTish HTTP resources POST

    /session POST /session/c688f8e4/element POST /session/c688f8e4/element/00cbe31e/value GET /session/c688f8e4/element/00cbe31e/attribute/value newSession(browser: 'firefox') findElement(sessionId: 'c688f8e4', using: 'name', value: 'q') sendKeysToElement(id: '00cbe31e', value: ['webdriver']) getElementAttribute(id: '00cbe31e', name: 'value')
  18. Automation Atoms JavaScript Atoms (built on Google Closure Library) Google

    Closure Compiler Firefox extension (atoms.js) ChromeDriver (atoms.h) IE driver (atoms.h) Opera driver (OperaAtoms.java)
  19. Automation Atoms /** * @param {!Element} elem The element to

    consider. * @return {string} visible text. */ bot.dom.getVisibleText = function(elem) { var lines = []; bot.dom.appendVisibleTextLinesFromElement_(elem, lines); lines = goog.array.map( lines, bot.dom.trimExcludingNonBreakingSpaceCharacters_); var joined = lines.join('\n'); var trimmed = bot.dom.trimExcludingNonBreakingSpaceCharacters_(joined); // Replace non-breakable spaces with regular ones. return trimmed.replace(/\xa0/g, ' '); };
  20. Deep dive Goal: Implement the ability to maximize the browser

    window Ruby client Firefox extension Automation atoms 1 2 3