mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Merge pull request #1528 from atom/ks-spec-runner-ui
Tweak spec reporter styles
This commit is contained in:
@@ -1,34 +1,49 @@
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
{convertStackTrace} = require 'coffeestack'
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
sourceMaps = {}
|
||||
formatStackTrace = (stackTrace) ->
|
||||
formatStackTrace = (message='', stackTrace) ->
|
||||
return stackTrace unless stackTrace
|
||||
|
||||
jasminePattern = /^\s*at\s+.*\(?.*\/jasmine(-[^\/]*)?\.js:\d+:\d+\)?\s*$/
|
||||
firstJasmineLinePattern = /^\s*at \/.*\/jasmine(-[^\/]*)?\.js:\d+:\d+\)?\s*$/
|
||||
convertedLines = []
|
||||
for line in stackTrace.split('\n')
|
||||
convertedLines.push(line) unless jasminePattern.test(line)
|
||||
break if firstJasmineLinePattern.test(line)
|
||||
|
||||
convertStackTrace(convertedLines.join('\n'), sourceMaps)
|
||||
stackTrace = convertStackTrace(convertedLines.join('\n'), sourceMaps)
|
||||
lines = stackTrace.split('\n')
|
||||
|
||||
# Remove first line of stack when it is the same as the error message
|
||||
errorMatch = lines[0]?.match(/^Error: (.*)/)
|
||||
lines.shift() if message.trim() is errorMatch?[1]?.trim()
|
||||
|
||||
# Remove prefix of lines matching: at [object Object].<anonymous> (path:1:2)
|
||||
for line, index in lines
|
||||
prefixMatch = line.match(/at \[object Object\]\.<anonymous> \(([^\)]+)\)/)
|
||||
lines[index] = "at #{prefixMatch[1]}" if prefixMatch
|
||||
|
||||
lines = lines.map (line) -> line.trim()
|
||||
lines.join('\n')
|
||||
|
||||
module.exports =
|
||||
class AtomReporter extends View
|
||||
@content: ->
|
||||
@div id: 'HTMLReporter', class: 'jasmine_reporter', =>
|
||||
@div outlet: 'specPopup', class: "spec-popup"
|
||||
@div class: 'spec-reporter', =>
|
||||
@div outlet: "suites"
|
||||
@div outlet: 'coreArea', =>
|
||||
@div outlet: 'coreHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'coreSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'bundledArea', =>
|
||||
@div outlet: 'bundledHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'bundledSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'userArea', =>
|
||||
@div outlet: 'userHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'userSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: "status", class: 'status', =>
|
||||
@div outlet: 'coreArea', class: 'symbol-area', =>
|
||||
@div outlet: 'coreHeader', class: 'symbol-header'
|
||||
@ul outlet: 'coreSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: 'bundledArea', class: 'symbol-area', =>
|
||||
@div outlet: 'bundledHeader', class: 'symbol-header'
|
||||
@ul outlet: 'bundledSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: 'userArea', class: 'symbol-area', =>
|
||||
@div outlet: 'userHeader', class: 'symbol-header'
|
||||
@ul outlet: 'userSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: "status", class: 'status alert alert-success', =>
|
||||
@div outlet: "time", class: 'time'
|
||||
@div outlet: "specCount", class: 'spec-count'
|
||||
@div outlet: "message", class: 'message'
|
||||
@@ -45,7 +60,7 @@ class AtomReporter extends View
|
||||
|
||||
reportRunnerStarting: (runner) ->
|
||||
@handleEvents()
|
||||
@startedAt = new Date()
|
||||
@startedAt = Date.now()
|
||||
specs = runner.specs()
|
||||
@totalSpecCount = specs.length
|
||||
@addSpecs(specs)
|
||||
@@ -53,57 +68,28 @@ class AtomReporter extends View
|
||||
|
||||
reportRunnerResults: (runner) ->
|
||||
@updateSpecCounts()
|
||||
if @failedCount == 0
|
||||
@message.text "Success!"
|
||||
if @failedCount is 1
|
||||
@message.text "#{@failedCount} failure"
|
||||
else
|
||||
@message.text "Game Over"
|
||||
@message.text "#{@failedCount} failures"
|
||||
|
||||
reportSuiteResults: (suite) ->
|
||||
|
||||
reportSpecResults: (spec) ->
|
||||
@completeSpecCount++
|
||||
spec.endedAt = new Date().getTime()
|
||||
spec.endedAt = Date.now()
|
||||
@specComplete(spec)
|
||||
@updateStatusView(spec)
|
||||
|
||||
reportSpecStarting: (spec) ->
|
||||
@specStarted(spec)
|
||||
|
||||
specFilter: (spec) ->
|
||||
globalFocusPriority = jasmine.getEnv().focusPriority
|
||||
parent = spec.parentSuite ? spec.suite
|
||||
|
||||
if !globalFocusPriority
|
||||
true
|
||||
else if spec.focusPriority >= globalFocusPriority
|
||||
true
|
||||
else if not parent
|
||||
false
|
||||
else
|
||||
@specFilter(parent)
|
||||
|
||||
handleEvents: ->
|
||||
$(document).on "mouseover", ".spec-summary", ({currentTarget}) =>
|
||||
element = $(currentTarget)
|
||||
description = element.data("description")
|
||||
return unless description
|
||||
|
||||
clearTimeout @timeoutId if @timeoutId?
|
||||
@specPopup.show()
|
||||
spec = _.find(window.timedSpecs, ({fullName}) -> description is fullName)
|
||||
description = "#{description} #{spec.time}ms" if spec
|
||||
@specPopup.text description
|
||||
{left, top} = element.offset()
|
||||
left += 20
|
||||
top += 20
|
||||
@specPopup.offset({left, top})
|
||||
@timeoutId = setTimeout((=> @specPopup.hide()), 3000)
|
||||
|
||||
$(document).on "click", ".spec-toggle", ({currentTarget}) =>
|
||||
element = $(currentTarget)
|
||||
specFailures = element.parent().find('.spec-failures')
|
||||
specFailures.toggle()
|
||||
if specFailures.is(":visible") then element.text "\uf03d" else element.html "\uf03f"
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
|
||||
updateSpecCounts: ->
|
||||
@@ -115,7 +101,7 @@ class AtomReporter extends View
|
||||
|
||||
updateStatusView: (spec) ->
|
||||
if @failedCount > 0
|
||||
@status.addClass('failed') unless @status.hasClass('failed')
|
||||
@status.addClass('alert-danger').removeClass('alert-success')
|
||||
|
||||
@updateSpecCounts()
|
||||
|
||||
@@ -123,7 +109,7 @@ class AtomReporter extends View
|
||||
rootSuite = rootSuite.parentSuite while rootSuite.parentSuite
|
||||
@message.text rootSuite.description
|
||||
|
||||
time = "#{Math.round((spec.endedAt - @startedAt.getTime()) / 10)}"
|
||||
time = "#{Math.round((spec.endedAt - @startedAt) / 10)}"
|
||||
time = "0#{time}" if time.length < 3
|
||||
@time[0].textContent = "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
@@ -145,15 +131,22 @@ class AtomReporter extends View
|
||||
@userSummary.append symbol
|
||||
|
||||
if coreSpecs > 0
|
||||
@coreHeader.text("Core Specs (#{coreSpecs}):")
|
||||
@coreHeader.text("Core Specs (#{coreSpecs})")
|
||||
else
|
||||
@coreArea.hide()
|
||||
if bundledPackageSpecs > 0
|
||||
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs}):")
|
||||
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs})")
|
||||
else
|
||||
@bundledArea.hide()
|
||||
if userPackageSpecs > 0
|
||||
@userHeader.text("User Package Specs (#{userPackageSpecs}):")
|
||||
if coreSpecs is 0 and bundledPackageSpecs is 0
|
||||
# Package specs being run, show a more descriptive label
|
||||
{specDirectory} = specs[0]
|
||||
packageFolderName = path.basename(path.dirname(specDirectory))
|
||||
packageName = _.undasherize(_.uncamelcase(packageFolderName))
|
||||
@userHeader.text("#{packageName} Specs")
|
||||
else
|
||||
@userHeader.text("User Package Specs (#{userPackageSpecs})")
|
||||
else
|
||||
@userArea.hide()
|
||||
|
||||
@@ -163,7 +156,7 @@ class AtomReporter extends View
|
||||
specComplete: (spec) ->
|
||||
specSummaryElement = $("#spec-summary-#{spec.id}")
|
||||
specSummaryElement.removeClass('pending')
|
||||
specSummaryElement.data("description", spec.getFullName())
|
||||
specSummaryElement.setTooltip(title: spec.getFullName(), container: '.spec-reporter')
|
||||
|
||||
results = spec.results()
|
||||
if results.skipped
|
||||
@@ -184,11 +177,9 @@ class SuiteResultView extends View
|
||||
@div class: 'suite', =>
|
||||
@div outlet: 'description', class: 'description'
|
||||
|
||||
suite: null
|
||||
|
||||
initialize: (@suite) ->
|
||||
@attr('id', "suite-view-#{@suite.id}")
|
||||
@description.html @suite.description
|
||||
@description.text(@suite.description)
|
||||
|
||||
attach: ->
|
||||
(@parentSuiteView() or $('.results')).append this
|
||||
@@ -205,20 +196,22 @@ class SuiteResultView extends View
|
||||
class SpecResultView extends View
|
||||
@content: ->
|
||||
@div class: 'spec', =>
|
||||
@div "\uf03d", class: 'spec-toggle'
|
||||
@div class: 'spec-toggle'
|
||||
@div outlet: 'description', class: 'description'
|
||||
@div outlet: 'specFailures', class: 'spec-failures'
|
||||
spec: null
|
||||
|
||||
initialize: (@spec) ->
|
||||
@addClass("spec-view-#{@spec.id}")
|
||||
@description.text @spec.description
|
||||
|
||||
description = @spec.description
|
||||
description = "it #{description}" if description.indexOf('it ') isnt 0
|
||||
@description.text(description)
|
||||
|
||||
for result in @spec.results().getItems() when not result.passed()
|
||||
stackTrace = formatStackTrace(result.trace.stack)
|
||||
stackTrace = formatStackTrace(result.message, result.trace.stack)
|
||||
@specFailures.append $$ ->
|
||||
@div result.message, class: 'resultMessage fail'
|
||||
@div stackTrace, class: 'stackTrace' if stackTrace
|
||||
@div result.message, class: 'result-message fail'
|
||||
@pre stackTrace, class: 'stack-trace padded' if stackTrace
|
||||
|
||||
attach: ->
|
||||
@parentSuiteView().append this
|
||||
|
||||
@@ -1,171 +1,164 @@
|
||||
@import "octicon-mixins.less";
|
||||
@import "octicon-mixins";
|
||||
|
||||
@font-face { .octicon-font(); }
|
||||
#jasmine_content {
|
||||
position: fixed;
|
||||
right: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #ddd;
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.spec-popup {
|
||||
position: absolute;
|
||||
background-color: #ddd;
|
||||
border: 2px solid black;
|
||||
padding: 5px;
|
||||
font-size: 13px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.list-unstyled {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#HTMLReporter { font-size: 11px; font-family: Monaco, Consolas, monospace; line-height: 1.6em; color: #333333; }
|
||||
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
|
||||
#HTMLReporter .symbolHeader {
|
||||
background-color: #222;
|
||||
color: #c2c2c2;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
padding: 5px 2px 2px 2px;
|
||||
}
|
||||
|
||||
#HTMLReporter .symbolSummary {
|
||||
background-color: #222;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#HTMLReporter .symbolSummary li {
|
||||
float: left;
|
||||
line-height: 10px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
#HTMLReporter .symbolSummary li.passed { color: #63AD75 }
|
||||
#HTMLReporter .symbolSummary li.failed { color: #FF3F05 }
|
||||
#HTMLReporter .symbolSummary li.skipped { color: #444 }
|
||||
#HTMLReporter .symbolSummary li.pending { color: #111 }
|
||||
|
||||
#HTMLReporter .symbolSummary li:before { content: "\02022"; }
|
||||
|
||||
#HTMLReporter .symbolSummary li:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#HTMLReporter .status {
|
||||
font-size: 20px;
|
||||
color: #222;
|
||||
background-color: #E5FFC0;
|
||||
line-height: 2em;
|
||||
padding: 2px;
|
||||
border: 2px solid #222;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#HTMLReporter .status.failed {
|
||||
color: white;
|
||||
background-color: rgba(204,51,63,1.0);
|
||||
}
|
||||
|
||||
#HTMLReporter .status .spec-count {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#HTMLReporter .status .message {
|
||||
}
|
||||
|
||||
#HTMLReporter .status .time {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#HTMLReporter .results .suite + .suite, #HTMLReporter .results .spec + .spec {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#HTMLReporter .results .suite, #HTMLReporter .results .spec {
|
||||
border: 2px solid #222;
|
||||
border-radius: 7px 0 0 0;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
padding: 5px;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
#HTMLReporter .results .suite:first-child {
|
||||
border-radius: 0;
|
||||
border: 2px solid #6A4A3C;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
#HTMLReporter .results .suite {
|
||||
border: 2px solid #6A4A3C;
|
||||
border-radius: 7px 0 0 0;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
padding: 5px;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
background-color:rgba(204,51,63,0.33);
|
||||
}
|
||||
|
||||
#HTMLReporter .results .spec {
|
||||
padding: 10px;
|
||||
background-color:rgba(204,51,63,1.0);
|
||||
}
|
||||
|
||||
#HTMLReporter .results .suite > .suite, #HTMLReporter .results .suite > .spec {
|
||||
margin-left: 5px
|
||||
}
|
||||
|
||||
#HTMLReporter .results .description {
|
||||
color: white;
|
||||
font-size: 15px;
|
||||
padding-bottom: 10px
|
||||
}
|
||||
|
||||
#HTMLReporter .results .spec .spec-toggle {
|
||||
font-family: Octicons Regular;
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
text-shadow: 3px 3px #222;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#HTMLReporter .results .spec .spec-toggle:hover {
|
||||
text-shadow: none;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
#HTMLReporter .results .spec:hover .spec-toggle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#HTMLReporter .resultMessage {
|
||||
padding-top: 5px;
|
||||
color: #fff;
|
||||
font-size: 15px
|
||||
}
|
||||
|
||||
#HTMLReporter .stackTrace {
|
||||
font-size: 12px;
|
||||
padding: 5px;
|
||||
margin: 5px 0 0 0;
|
||||
border-radius: 2px;
|
||||
line-height: 18px;
|
||||
color: #666666;
|
||||
border: 1px solid #ddd;
|
||||
background: white;
|
||||
white-space: pre;
|
||||
overflow: auto;
|
||||
.spec-reporter {
|
||||
font-size: 11px;
|
||||
line-height: 1.6em;
|
||||
color: #333;
|
||||
|
||||
.list-unstyled {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.symbol-header {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.symbol-area {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.symbol-summary {
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
font-family: Monaco, Consolas, monospace;
|
||||
float: left;
|
||||
line-height: 10px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
font-size: 10px;
|
||||
|
||||
&.passed {
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
&.failed {
|
||||
color: #d9534f;
|
||||
}
|
||||
|
||||
&.skipped {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
&.pending {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: "\02022";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 20px;
|
||||
line-height: 2em;
|
||||
padding: 5px;
|
||||
border-radius: 0;
|
||||
text-align: center;
|
||||
|
||||
.spec-count {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.time {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.results {
|
||||
padding: 10px;
|
||||
|
||||
.description {
|
||||
font-size: 16px;
|
||||
padding: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
> .suite {
|
||||
> .description {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.spec {
|
||||
margin-top: 5px;
|
||||
padding: 0 10px 10px 10px;
|
||||
border-left: 3px solid #d9534f;
|
||||
|
||||
.spec-toggle {
|
||||
.octicon(fold);
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
color: #999;
|
||||
|
||||
&.folded {
|
||||
.octicon(unfold);
|
||||
}
|
||||
}
|
||||
|
||||
.spec-toggle:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
&:hover .spec-toggle {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.suite > .suite,
|
||||
.suite > .spec {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.result-message {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #d9534f;
|
||||
padding: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
.stack-trace {
|
||||
font-size: 12px;
|
||||
margin: 5px 0 0 0;
|
||||
border-radius: 2px;
|
||||
line-height: 18px;
|
||||
color: #666;
|
||||
border: 1px solid #ddd;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
.tooltip-inner {
|
||||
border: 1px solid #ccc;
|
||||
background: #fff;
|
||||
color: #666;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
&.in {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user