class Cucumber::Formatter::ApiDocs

Constants

VERB_ORDER

Public Class Methods

new(step_mother, io, options) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 70
def initialize(step_mother, io, options)
  @endpoints = []
end

Public Instance Methods

after_feature(feature) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 83
def after_feature(feature)
  @endpoint.parameters.uniq! { |parameter| parameter.name }
  @endpoints << @endpoint
end
after_feature_element(feature_element) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 97
def after_feature_element(feature_element)
  @endpoint.examples << @example
end
after_features(features) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 74
def after_features(features)
  write_html
end
before_comment(comment) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 88
def before_comment(comment)
  @endpoint.description = comment.to_sexp[1].gsub(/#\s*/, "")
end
before_feature(feature) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 78
def before_feature(feature)
  verb, path = feature.name.split(" ")
  @endpoint = Endpoint.new(verb: verb, path: path)
end
before_feature_element(feature_element) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 92
def before_feature_element(feature_element)
  @example = Example.new(name: feature_element.title)
  @prior_keyword = nil
end
before_step(step) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 101
def before_step(step)
  keyword = if step.keyword.strip == "And" && @prior_keyword
    @prior_keyword
  else
    step.keyword.strip
  end

  case keyword
  when "Given"
    @example.prerequisites << step.name
    @prior_keyword = "Given"
  when "When"
    if step.multiline_arg
      step.multiline_arg.rows.each do |type, name, value|
        @example.parameters << Parameter.new(type: type, name: name, value: value)
        @endpoint.parameters << Parameter.new(type: type, name: name)
      end
    end

    @prior_keyword = "When"
  when "Then"
    if matches = step.name.match(/the status code is (\d+)/)
      @example.code = matches[1]
    elsif matches = step.name.match(/Content-Type is (.*)$/)
      @example.content_type = matches[1]
    elsif matches = step.name.match(/the JSON response at "(.*?)" should include( keys)*:/)
      key = matches[1]

      if step.multiline_arg.respond_to? :rows
        step.multiline_arg.rows.each do |nested_key, value|
          add_json("#{key}/#{nested_key}", json_value(value))
        end
      else
        add_json(key, JSON.parse(step.multiline_arg))
      end
    elsif matches = step.name.match(/the JSON response at "(.*?)" should be ([^ ]*?)$/)
      keys = matches[1]
      value = matches[2]
      add_json(keys, json_value(value))
    elsif matches = step.name.match(/the JSON response at "(.*?)" should be (variable )*"(.*?)"/)
      keys = matches[1]
      value = matches[3]
      add_json(keys, value)
    end

    @prior_keyword = "Then"
  end
end
tag_name(tag) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 150
def tag_name(tag)
  @endpoint.group = Group.for_tag(tag)
end

Private Instance Methods

add_json(path, terminal_value) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 161
def add_json(path, terminal_value)
  keys = path.split("/")

  return @example.json_response[path] = terminal_value if keys.size == 1

  keys.each_cons(2).reduce(@example.json_response) do |current, (key_one_string, key_two)|
    key_one = if key_one_string.match(/^\d+$/)
      key_one_string.to_i
    else
      key_one_string
    end

    value = case key_two
    when keys.last
      if current[key_one].is_a?(Hash)
        current[key_one].merge(key_two => terminal_value)
      else
        {key_two => terminal_value}
      end
    when /^\d+$/
      if current[key_one].is_a?(Array)
        current[key_one]
      else
        []
      end
    else
      current[key_one] || {}
    end

    current[key_one] = value

    value
  end
end
json_value(string) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 196
def json_value(string)
  JSON.parse('{"key":' + string + '}')["key"]
end
log(message) click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 156
def log(message)
  @log ||= File.open("doc-debug.log", "w")
  @log << message + "\n"
end
write_html() click to toggle source
# File lib/cucumber/formatter/api_docs.rb, line 200
def write_html
  endpoints_by_group = @endpoints.group_by(&:group).sort_by { |group, _| group.name }

  html = Arbre::Context.new do
    head do
      link rel: "stylesheet", href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"
      link rel: "stylesheet", href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css"
      style do
        "
          .summary, .panel-heading {
            cursor: pointer;
          }

          nav {
            margin-top: 38px;
            min-width: 255px;
            top: 0;
            bottom: 0;
            padding-right: 12px;
            padding-bottom: 12px;
            overflow-y: auto;
          }
        ".html_safe
      end
      script src: "https://code.jquery.com/jquery-2.1.1.min.js"
      script src: "https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"
    end

    body do
      div class: "container padded" do
        div class: "col-md-3" do
          nav class: "hidden-sm hidden-xs affix nav" do
            div class: "list-group" do
              endpoints_by_group.each do |group, _|
                a group.name, href: "##{group.key}", class: "list-group-item"
              end
            end
          end
        end

        div class: "col-md-9" do
          endpoints_by_group.each do |group, endpoints|
            a name: group.key
            h1 group.name

            endpoints.sort_by(&:sort_order).each do |endpoint|
              div class: "endpoint" do
                div class: "summary well well-sm" do
                  text_node endpoint.name
                  span class: "glyphicon glyphicon-plus pull-right"
                end

                div class: "detail panel panel-default", style: "display:none" do
                  div class: "panel-heading" do
                    h2 do
                      text_node endpoint.name
                      span class: "glyphicon glyphicon-minus pull-right"
                    end
                  end

                  div class: "panel-body" do
                    div do
                      endpoint.description.split(/\n/).each do |line|
                        div line
                      end
                    end

                    h3 "Parameters"

                    table class: "table table-striped table-bordered" do
                      thead do
                        tr do
                          th { "Name" }
                          th { "Type" }
                        end
                      end

                      tbody do
                        endpoint.parameters.each do |parameter|
                          tr do
                            td { parameter.type }
                            td { parameter.name }
                          end
                        end
                      end
                    end

                    h3 "Examples"
                    endpoint.examples.each do |example|
                      panel_type = case example.code
                      when 200..299
                        "success"
                      when 400..499
                        "warning"
                      when 500.599
                        "error"
                      end

                      div class: "panel panel-#{panel_type}" do
                        div example.name, class: "panel-heading"

                        div class: "panel-body" do
                          h4 "Request parameters"
                          table class: "table table-striped table-bordered" do
                            thead do
                              tr do
                                th { "Name" }
                                th { "Type" }
                                th { "Value" }
                              end
                            end

                            tbody do
                              example.parameters.each do |parameter|
                                tr do
                                  td { parameter.type }
                                  td { parameter.name }
                                  td { parameter.value }
                                end
                              end
                            end
                          end

                          h4 "Response"

                          div class: "panel panel-default" do
                            div class: "panel-heading" do
                              div "Code: #{example.code}"
                              div "Content-Type: #{example.content_type}"
                            end

                            div class: "panel-body" do
                              if example.json_response.empty?
                                "Empty body"
                              else
                                json = JSON.pretty_generate(example.json_response)
                                code json.gsub("\n", "<br>").gsub(" ", "&nbsp").html_safe
                              end
                            end
                          end
                        end
                      end
                    end
                  end
                end
              end
            end
          end
        end
      end

      script "
        $(document).ready(function(){
          $('.summary').click(function() {
            $(this).hide();
            $(this).closest('.endpoint').find('.detail').show();
          });

          $('.detail .panel-heading').click(function() {
            $(this).closest('.detail').hide();
            $(this).closest('.endpoint').find('.summary').show();
          });
        });
      ".html_safe
    end
  end

  file = File.open("docs.html", "w")
  file << html.to_s
  file.close
end