Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm using CasperJS and CoffeeScript for integration testing of a hobby project. Page Objects seem to be a useful abstraction. I had two major requirements:

  • Support for node.js style callbacks - mainly because tests can then be written using async.waterfall
  • Support extending (subclassing) to encapsulate common functionality, like a sidebar or a navbar

Here's the code, followed by a brief example of how I use it. It's a bit long, but that's about the shortest meaningful example I can give.

class PO # Base PO
  @casper: null

  # name: The name of the page object. Used to access it as PO[name], which is kind of like a forward declaration
  # selector: The page represented by this PO is considered to be ready for usage once this is visible
  # mixin: An object; properties of this will be mixed into the new instance
  constructor: (@name, @selector, mixin) ->
    if PO[@name] isnt undefined
      throw "Failed creating PO #{@name}: a PO instance or class method with this name already exists"
    PO[@name] = this
    @__defineGetter__ 'casper', -> PO.casper
    this[key] = val for key, val of mixin

  waitUntilVisible: (cb) -> @casper.waitUntilVisible @selector, cb

  # Switch to po, and call the callback cb with err and the po as parameters
  next: (cb, po, err=null) ->
    t0 = Date.now()
    msg = "Switching to PO #{po.name}"
    @casper.log msg, 'debug'
    po.waitUntilVisible =>
      @casper.log "#{msg}: done in #{Date.now() - t0}ms", 'debug'
      cb err, po
    po

new PO 'navbar', '',
  tabs: {}
  logout: (cb) ->
    @casper.click '#logout'
    @casper.waitFor (=> not @haveLogin()), (=> cb null, PO['login'])

  haveLogin: (cb) -> 
    result = nick == @casper.evaluate -> $('#current-user-nick').text()
    cb? result
    result

  addTab: ({name, switchSelector, readySelector, po}) ->
    @tabs[name] = arguments[0]

  toTab: (name, cb) ->
    cb "Navbar tab #{name} not known", null unless name of @tabs
    tab = @tabs[name]
    @casper.click tab.switchSelector
    @casper.waitUntilVisible tab.readySelector, => cb null, PO[tab.po]


class PON extends PO # PO with navbar
  constructor: (name, selector, mixin) ->
    super name, selector, mixin
    @navbar = PO['navbar']


new PON 'login', '.login-form',
  # ...

module.exports = (casper) ->
  PO.casper = casper
  PO['login']
share|improve this question

Know someone who can answer? Share a link to this question via email, Google+, Twitter, or Facebook.

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.