Module: Teaspoon::Formatter::RspecHtml::Templates

Defined in:
lib/teaspoon/formatter/rspec_html.rb

Constant Summary collapse

CSS =
<<-CSS.strip_heredoc
body {
  margin: 0;
  padding: 0;
  background: #fff;
  font-size: 80%;
}

#teaspoon-header {
  background: #65C400;
  color: #fff;
  height: 4em;
}

.teaspoon-report h1 {
  margin: 0px 10px 0px 10px;
  padding: 10px;
  font-family: "Lucida Grande", Helvetica, sans-serif;
  font-size: 1.8em;
  position: absolute;
}

#label {
  float: left;
}

#display-filters {
  float: left;
  padding: 28px 0 0 40%;
  font-family: "Lucida Grande", Helvetica, sans-serif;
}

#summary {
  float: right;
  padding: 5px 10px;
  font-family: "Lucida Grande", Helvetica, sans-serif;
  text-align: right;
}

#summary p {
  margin: 0 0 0 2px;
}

#summary #totals {
  font-size: 1.2em;
}

.example_group {
  background: #fff;
}

.results > .example_group {
  margin: 0 10px 5px;
}

dl {
  font: normal 11px "Lucida Grande", Helvetica, sans-serif;
}

.results > .example_group > dl {
  margin: 0;
  padding: 0 0 5px;
}

.results > .example_group > dl dl {
  margin-left: 15px;
}

dt {
  padding: 3px;
  background: #65C400;
  color: #fff;
  font-weight: bold;
}

dd {
  margin: 5px 0 5px 5px;
  padding: 3px 3px 3px 18px;
}

dd .duration {
  padding-left: 5px;
  text-align: right;
  right: 0px;
  float: right;
}

dd.example.passed {
  border-left: 5px solid #65C400;
  border-bottom: 1px solid #65C400;
  background: #DBFFB4; color: #3D7700;
}

dd.example.pending {
  border-left: 5px solid #FAF834;
  border-bottom: 1px solid #FAF834;
  background: #FCFB98; color: #131313;
}

dd.example.failed {
  border-left: 5px solid #C20000;
  border-bottom: 1px solid #C20000;
  color: #C20000; background: #FFFBD3;
}

dt.pending {
  color: #000000; background: #FAF834;
}

dt.failed {
  color: #FFFFFF; background: #C40D0D;
}

#teaspoon-header.pending {
  color: #000000; background: #FAF834;
}

#teaspoon-header.failed {
  color: #FFFFFF; background: #C40D0D;
}
CSS
JAVASCRIPT =
<<-JAVASCRIPT.strip_heredoc
(function() {
  "use strict";

  if (!document.querySelectorAll) {
    alert("Warning: Your browser does not support document.querySelectorAll. Your report may not work properly.");
    return;
  }

  function get(id) {
    return document.getElementById(id);
  }

  function getAll(scope, selector) {
    if (arguments.length === 1) {
      selector = scope;
      scope = document;
    }

    return scope.querySelectorAll(selector);
  }

  function show(element) {
    if (element.oldDisplayValue) {
      element.style.display = element.oldDisplayValue;
    } else {
      element.style.display = "";
    }
  }

  function hide(element) {
    if (element.style.display === "none") {
      return;
    }

    if (element.oldDisplayValue === undefined) {
      element.oldDisplayValue = element.style.display;
    }

    element.style.display = "none";
  }

  function showAll(elements) {
    for (var i = 0; i < elements.length; i++) {
      show(elements[i]);
    }
  }

  function hideAll(elements) {
    for (var i = 0; i < elements.length; i++) {
      hide(elements[i]);
    }
  }

  function toggleAll(elements, display) {
    if (display) {
      showAll(elements);
    } else {
      hideAll(elements);
    }
  }

  function isHidden(element) {
    return element.style.display === "none";
  }

  function isAllHidden(elements) {
    var allHidden = true;

    for (var i = 0; i < elements.length; i++) {
      if (!isHidden(elements[i])) {
        allHidden = false;
        break;
      }
    }

    return allHidden;
  }

  function setText(element, text) {
    while (element.firstChild !== null) {
      element.removeChild(element.firstChild);
    }

    element.appendChild(document.createTextNode(text));
  }

  function addClass(element, className) {
    element.className += " " + className;
  }

  function addClassAll(elements, className) {
    for (var i = 0; i < elements.length; i++) {
      addClass(elements[i], className);
    }
  }

  function hasClass(element, className) {
    return (" " + element.className + " ").replace(/[\\t\\r\\n\\f]/g, " ").indexOf(" " + className + " ") >= 0;
  }

  function isTag(element, tagName) {
    return element.tagName && element.tagName.toLowerCase() === tagName;
  }

  function parents(elements, predicate) {
    var results = [];

    for (var i = 0; i < elements.length; i++) {
      var parent = elements[i].parentNode;

      while (parent) {
        if (predicate(parent)) {
          results.push(parent);
        }

        parent = parent.parentNode;
      }
    }

    return results;
  }

  function children(elements, predicate) {
    var results = [];

    for (var i = 0; i < elements.length; i++) {
      if (!elements[i].hasChildNodes()) {
        continue;
      }

      for (var j = 0; j < elements[i].childNodes.length; j++) {
        if (predicate(elements[i].childNodes[j])) {
          results.push(elements[i].childNodes[j]);
        }
      }
    }

    return results;
  }

  var elements = getAll("input[data-class-filter]");

  function handleClassFilterChange(e) {
    var element = e.target || e.srcElement;
    showAll(getAll(".example_group"));
    toggleAll(getAll(".example." + element.getAttribute("data-type")), element.checked);

    var groups = getAll(".example_group");

    for (var i = 0; i < groups.length; i++) {
      if (isAllHidden(getAll(groups[i], ".example"))) {
        hide(groups[i]);
      }
    }
  }

  for (var i = 0; i < elements.length; i++) {
    elements[i].onchange = handleClassFilterChange;
  }

  get("duration").innerHTML = "Finished in <strong></strong>";
  setText(getAll("#duration strong")[0], get("duration-value").value + " seconds");
  get("totals").innerHTML = '<span class="total-amount"></span> examples, <span class="failure-amount"></span> failures, <span class="pending-amount"></span> pending';
  var failureAmount = getAll(".example.failed").length;
  var pendingAmount = getAll(".example.pending").length;
  setText(getAll("#totals .total-amount")[0], getAll(".example").length);
  setText(getAll("#totals .failure-amount")[0], failureAmount);
  setText(getAll("#totals .pending-amount")[0], pendingAmount);

  if (failureAmount > 0) {
    addClass(get("teaspoon-header"), "failed");
  } else if (pendingAmount > 0) {
    addClass(get("teaspoon-header"), "pending");
  }

  function propagateClass(exampleSelector, groupPredicate, classToAdd) {
    var exampleElements = getAll(exampleSelector);

    var groupElements = parents(exampleElements, function(p) {
      return hasClass(p, "example_group") && groupPredicate(p);
    });

    addClassAll(groupElements, classToAdd);
    var dlChildren = children(groupElements, function(c) { return isTag(c, "dl"); });
    var dtChildren = children(dlChildren, function(c) { return isTag(c, "dt"); });
    addClassAll(dtChildren, classToAdd);
  }

  propagateClass(".example.failed", function() { return true; }, "failed");
  propagateClass(".example.pending", function(p) { return !hasClass(p, "failed"); }, "pending");
  propagateClass(".example", function(p) { return !hasClass(p, "failed") && !hasClass(p, "pending"); }, "passed");
})();
JAVASCRIPT
HEADER =
<<-HTML.strip_heredoc
<!DOCTYPE html>
<html>
  <head>
    <title>Teaspoon results</title>

    <style type="text/css">
      #{CSS}
    </style>
  </head>

  <body>
    <div class="teaspoon-report">
      <div id="teaspoon-header">
        <div id="label">
          <h1>Teaspoon Code Examples</h1>
        </div>

        <div id="display-filters">
          <input id="passed-checkbox" data-class-filter data-type="passed" type="checkbox" checked="checked">
          <label for="passed-checkbox">Passed</label>
          <input id="failed-checkbox" data-class-filter data-type="failed" type="checkbox" checked="checked">
          <label for="failed-checkbox">Failed</label>
          <input id="pending-checkbox" data-class-filter data-type="pending" type="checkbox" checked="checked">
          <label for="pending-checkbox">Pending</label>
        </div>

        <div id="summary">
          <p id="totals">&nbsp;</p>
          <p id="duration">&nbsp;</p>
        </div>
      </div>

      <div class="results">
HTML
SUITE_START =
<<-HTML.strip_heredoc
<div class="example_group">
  <dl>
    <dt><%= h @o.label %></dt>
HTML
SPEC =
<<-HTML.strip_heredoc
<dd class="example <%= h @o.status %>">
  <span class="spec-name"><%= h @o.label %></span>
  <span class="duration"><%= h "\#{@o.elapsed}s" if @o.elapsed %></span>

  <% if @o.failing? %>
    <div class="message">
      <pre><%= h @o.trace %></pre>
    </div>
  <% end %>
</dd>
HTML
SUITE_END =
<<-HTML.strip_heredoc
  </dl>
</div>
HTML
<<-HTML.strip_heredoc
      </div>
    </div>

    <input type="hidden" id="duration-value" value="<%= h @o.elapsed %>" />

    <script type="text/javascript">
      #{JAVASCRIPT}
    </script>
  </body>
</html>
HTML