path = require 'path' _ = require 'underscore' _str = require 'underscore.string' {convertStackTrace} = require 'coffeestack' React = require 'react' ReactDOM = require 'react-dom' grim = require 'grim' marked = require 'marked' sourceMaps = {} formatStackTrace = (spec, message='', stackTrace, indent="") -> 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) 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() for line, index in lines # Remove prefix of lines matching: at [object Object]. (path:1:2) prefixMatch = line.match(/at \[object Object\]\. \(([^)]+)\)/) line = "at #{prefixMatch[1]}" if prefixMatch # Relativize locations to spec directory lines[index] = line.replace("at #{spec.specDirectory}#{path.sep}", 'at ') lines = lines.map (line) -> indent + line.trim() lines.join('\n') indentationString: (suite, plus=0) -> rootSuite = suite indentLevel = 0 + plus while rootSuite.parentSuite rootSuite = rootSuite.parentSuite indentLevel += 1 return [0...indentLevel].map(-> " ").join("") suiteString: (spec) -> descriptions = [spec.suite.description] rootSuite = spec.suite while rootSuite.parentSuite indent = indentationString(rootSuite) descriptions.unshift(indent + rootSuite.description) rootSuite = rootSuite.parentSuite descriptions.join("\n") class N1SpecReporter extends React.Component constructor: (@props) -> render: ->
Core
{@_renderSpecsOfType('core')}
Bundled
{@_renderSpecsOfType('bundled')}
User
{@_renderSpecsOfType('user')}
{@_renderStatus()}
{@_renderFailures()}
{@_renderDeprecations()}
{@props.plainTextOutput}
_renderSpecsOfType: (type) => items = [] @props.specs.forEach (spec, idx) => return unless spec.specType is type statusClass = "pending" title = undefined results = spec.results() if results if results.skipped statusClass = "skipped" else if results.failedCount > 0 statusClass = "failed" title = spec.getFullName() else if spec.endedAt statusClass = "passed" items.push
  • items _renderFailures: => # We have an array of specs with `suite` and potentially N `parentSuite` from there. # Create a tree instead. topLevelSuites = [] failedSpecs = @props.specs.filter (spec) -> spec.endedAt and spec.results().failedCount > 0 for spec in failedSpecs suite = spec.suite suite = suite.parentSuite while suite.parentSuite if topLevelSuites.indexOf(suite) is -1 topLevelSuites.push(suite) topLevelSuites.map (suite, idx) => _renderDeprecations: => return if @props.deprecations.length is 0 if @props.deprecations.length is 1 label = "1 deprecation" else label = "#{@props.deprecations.length} deprecations"
    {label}
    {@_renderDeprecationList()}
    _renderDeprecationList: => @props.deprecations.map (deprecation) =>
    {deprecation.message} { deprecation.getStacks().map (stack) => fullStack = stack.map ({functionName, location}) -> if functionName is '' " at #{location}" else " at #{functionName} (#{location})"
                    {formatStackTrace(deprecation.spec, deprecation.message, fullStack.join('\n'))}
                  
    }
    _renderStatus: => failedCount = 0 skippedCount = 0 completeCount = 0 for spec in @props.specs results = spec.results() continue unless spec.endedAt failedCount += 1 if results.failedCount > 0 skippedCount += 1 if results.skipped completeCount += 1 if results.passedCount > 0 and results.failedCount is 0 if failedCount is 1 message = "#{failedCount} failure" else message = "#{failedCount} failures" if skippedCount specCount = "#{completeCount - skippedCount}/#{@props.specs.length - skippedCount} (#{skippedCount} skipped)" else specCount = "#{completeCount}/#{@props.specs.length}"
    {specCount}
    {message}
    onReloadSpecs: => require('electron').ipcRenderer.send('call-webcontents-method', 'reload') class SuiteResultView extends React.Component @propTypes: -> suite: React.PropTypes.object allSpecs: React.PropTypes.array render: -> items = [] subsuites = [] @props.allSpecs.forEach (spec) => if spec.suite is @props.suite items.push(spec) else suite = spec.suite while suite.parentSuite if suite.parentSuite is @props.suite subsuites.push(suite) return suite = suite.parentSuite items = items.map (spec, idx) => subsuites = subsuites.map (suite, idx) =>
    {@props.suite.description}
    {items} {subsuites}
    class SpecResultView extends React.Component @propTypes: -> spec: React.PropTypes.object render: -> description = @props.spec.description resultItems = @props.spec.results().getItems() description = "it #{description}" if description.indexOf('it ') isnt 0 failures = [] for result, idx in resultItems continue if result.passed() stackTrace = formatStackTrace(@props.spec, result.message, result.trace.stack) failures.push(
    {result.message}
    {stackTrace}
    )
    {description}
    {failures}
    el = document.createElement('div') document.body.appendChild(el) startedAt = null specs = [] deprecations = [] plainTextOutput = "" update = => component = ReactDOM.render(component, el) updateSoon = _.debounce(update, 125) module.exports = reportRunnerStarting: (runner) -> specs = runner.specs() startedAt = Date.now() updateSoon() reportRunnerResults: (runner) -> updateSoon() reportSuiteResults: (suite) -> reportSpecResults: (spec) -> spec.endedAt = Date.now() specDeprecations = grim.getDeprecations() d.spec = spec for d in specDeprecations deprecations = deprecations.concat(specDeprecations) grim.clearDeprecations() updateSoon() reportPlainTextSpecResult: (spec) -> str = "" if spec.results().failedCount > 0 str += suiteString(spec) + "\n" indent = indentationString(spec.suite, 1) stackIndent = indentationString(spec.suite, 2) description = spec.description description = "it #{description}" if description.indexOf('it ') isnt 0 str += indent + description + "\n" for result in spec.results().getItems() continue if result.passed() str += indent + result.message + "\n" stackTrace = formatStackTrace(spec, result.message, result.trace.stack, stackIndent) str += stackTrace + "\n" str += "\n\n" plainTextOutput = plainTextOutput + str updateSoon() reportSpecStarting: (spec) -> updateSoon()