Customizing Cucumber Formatters

Note: example code can be found at the following github repo:https://github.com/callahat/cucumber_custom_print

Sometimes you come across a client that adamantly thinks they need more of a record from running tests than the output the standard formatters supply. In terms of the HTML formatter the client can see in the report the usual stats about how many scenarios ran, how long they took, and a listing of each scenario, each scenario step, and any error messages for failing scenarios. The “puts” method can allow for adding more of an audit trail by telling the formatter to include the text in the report. You can even pass in HTML tags to puts (and javascript) and it will wind up in your HTML report. And pretty report. And any other formatters you tell cucumber to use that listen to the puts method.

If you don’t want HTML markup getting dumped into your Pretty report you can add your own print methods to cucumber by monkey patching a few classes as well as your formatters. For example, consider the method “put_collapsible_list” that will take an array we want to display in the HTML report. Since this array might have a lot of information that we may or may not immediately care about we can make it hidden initially. To let cucumber know about this method, a few classes and modules need patched:

module Cucumber
  module RbSupport
    module RbWorld
      def put_collapsible_list(list_title, list)
        @__cucumber_runtime.put_collapsible_list(list_title, list)
      end
    end
  end
end

module Cucumber
  class Runtime
    module UserInterface
      def put_collapsible_list(list_title, list)
        @visitor.put_collapsible_list(list_title, list)
      end
    end
  end
end

module Cucumber
  class Runtime
    class ForProgrammingLanguages
      def_delegators :@user_interface,
                     :put_collapsible_list
    end
  end
end

module Cucumber
  module Ast
    # Walks the AST, executing steps and notifying listeners
    class TreeWalker
      def put_collapsible_list(list_title, list)
        broadcast(list_title, list)
      end
    end
  end
end

(I came about this solution by a combination of tracing many debug points, and a bit of trial and error).
Once the method has been added to the world, delegator created for it, added to the tree walker, and runtime, the formatter also needs this method defined. Formatters without this method will do nothing when the “put_collapsible_list” method is invoked in your step. From the example here is one way the method could look:

module Cucumber
  module Formatter
    class Html
      def put_collapsible_list(list_title, list)
        @delayed_messages << <<-HTML.strip
          <a onclick="ol=document.getElementById('#{list_title}'); ol.style.display = (ol.style.display == 'none' ? 'block' : 'none');return false">#{list_title} (click to expand/hide)</a>
          <ol id="#{list_title}" style="display:none;">
          #{list_helper(list)}
          </ol>
        HTML
      end

      def list_helper(list)
        list.to_a.flatten.inject("") do |a, item|
          a + <<-HTML
            <li>#{item}</li>
          HTML
        end
      end
    end
  end
end

For the HTML report, this markup and JS will end up in a blue box, and there will be a link you can click to show/hide the contents of the array in an ordered list.