262 lines
7.3 KiB
CoffeeScript
262 lines
7.3 KiB
CoffeeScript
system = require 'system'
|
||
webpage = require 'webpage'
|
||
|
||
USAGE = """
|
||
Usage: phantomjs mocha-phantomjs.coffee URL REPORTER [CONFIG]
|
||
"""
|
||
|
||
class Reporter
|
||
|
||
constructor: (@reporter, @config) ->
|
||
@url = system.args[1]
|
||
@columns = parseInt(system.env.COLUMNS or 75) * .75 | 0
|
||
@mochaStarted = false
|
||
@mochaStartWait = @config.timeout || 6000
|
||
@startTime = Date.now()
|
||
@fail(USAGE) unless @url
|
||
|
||
run: ->
|
||
@initPage()
|
||
@loadPage()
|
||
|
||
# Subclass Hooks
|
||
|
||
customizeRunner: (options) ->
|
||
undefined
|
||
|
||
customizeProcessStdout: (options) ->
|
||
undefined
|
||
|
||
customizeConsole: (options) ->
|
||
undefined
|
||
|
||
customizeOptions: ->
|
||
columns: @columns
|
||
|
||
# Private
|
||
|
||
fail: (msg, errno) ->
|
||
console.log msg if msg
|
||
phantom.exit errno || 1
|
||
|
||
finish: ->
|
||
phantom.exit @page.evaluate -> mochaPhantomJS.failures
|
||
|
||
initPage: ->
|
||
@page = webpage.create
|
||
settings: @config.settings
|
||
@page.customHeaders = @config.headers if @config.headers
|
||
for name, value of @config.cookies
|
||
@page.addCookie
|
||
name: name
|
||
value: value
|
||
@page.viewportSize = @config.viewportSize if @config.viewportSize
|
||
@page.onConsoleMessage = (msg) -> console.log msg
|
||
@page.onInitialized = =>
|
||
@page.evaluate ->
|
||
window.mochaPhantomJS =
|
||
failures: 0
|
||
ended: false
|
||
started: false
|
||
run: ->
|
||
mochaPhantomJS.started = true
|
||
window.callPhantom 'mochaPhantomJS.run'
|
||
|
||
loadPage: ->
|
||
@page.open @url
|
||
@page.onLoadFinished = (status) =>
|
||
@page.onLoadFinished = ->
|
||
@onLoadFailed() if status isnt 'success'
|
||
@waitForInitMocha()
|
||
@page.onCallback = (data) =>
|
||
if data is 'mochaPhantomJS.run'
|
||
@waitForRunMocha() if @injectJS()
|
||
|
||
onLoadFailed: ->
|
||
@fail "Failed to load the page. Check the url: #{@url}"
|
||
|
||
injectJS: ->
|
||
if @page.evaluate(-> window.mocha?)
|
||
@page.injectJs 'mocha-phantomjs/core_extensions.js'
|
||
@page.evaluate @customizeProcessStdout, @customizeOptions()
|
||
@page.evaluate @customizeConsole, @customizeOptions()
|
||
true
|
||
else
|
||
@fail "Failed to find mocha on the page."
|
||
false
|
||
|
||
runMocha: ->
|
||
if @config.useColors is false then @page.evaluate -> Mocha.reporters.Base.useColors = false
|
||
@page.evaluate @runner, @reporter
|
||
@mochaStarted = @page.evaluate -> mochaPhantomJS.runner or false
|
||
if @mochaStarted
|
||
@mochaRunAt = new Date().getTime()
|
||
@page.evaluate @customizeRunner, @customizeOptions()
|
||
@waitForMocha()
|
||
else
|
||
@fail "Failed to start mocha."
|
||
|
||
waitForMocha: =>
|
||
ended = @page.evaluate -> mochaPhantomJS.ended
|
||
if ended then @finish() else setTimeout @waitForMocha, 100
|
||
|
||
waitForInitMocha: =>
|
||
setTimeout @waitForInitMocha, 100 unless @checkStarted()
|
||
|
||
waitForRunMocha: =>
|
||
if @checkStarted() then @runMocha() else setTimeout @waitForRunMocha, 100
|
||
|
||
checkStarted: =>
|
||
started = @page.evaluate -> mochaPhantomJS.started
|
||
if !started && @mochaStartWait && @startTime + @mochaStartWait < Date.now()
|
||
@fail "Failed to start mocha: Init timeout", 255
|
||
started
|
||
|
||
runner: (reporter) ->
|
||
try
|
||
mocha.setup reporter: reporter
|
||
mochaPhantomJS.runner = mocha.run()
|
||
if mochaPhantomJS.runner
|
||
cleanup = ->
|
||
mochaPhantomJS.failures = mochaPhantomJS.runner.failures
|
||
mochaPhantomJS.ended = true
|
||
if mochaPhantomJS.runner?.stats?.end
|
||
cleanup()
|
||
else
|
||
mochaPhantomJS.runner.on 'end', cleanup
|
||
catch error
|
||
false
|
||
|
||
class Spec extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'spec', config
|
||
|
||
customizeProcessStdout: (options) ->
|
||
process.stdout.write = (string) ->
|
||
return if string is process.cursor.deleteLine or string is process.cursor.beginningOfLine
|
||
console.log string
|
||
|
||
customizeConsole: (options) ->
|
||
process.cursor.CRMatcher = /\s+◦\s\S/
|
||
process.cursor.CRCleaner = process.cursor.up + process.cursor.deleteLine
|
||
origLog = console.log
|
||
console.log = ->
|
||
string = console.format.apply(console, arguments)
|
||
if string.match(process.cursor.CRMatcher)
|
||
process.cursor.CRCleanup = true
|
||
else if process.cursor.CRCleanup
|
||
string = process.cursor.CRCleaner + string
|
||
process.cursor.CRCleanup = false
|
||
origLog.call console, string
|
||
|
||
class Dot extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'dot', config
|
||
|
||
customizeProcessStdout: (options) ->
|
||
process.cursor.margin = 2
|
||
process.cursor.CRMatcher = /\u001b\[\d\dm\․\u001b\[0m/
|
||
process.stdout.columns = options.columns
|
||
process.stdout.allowedFirstNewLine = false
|
||
process.stdout.write = (string) ->
|
||
if string is '\n '
|
||
unless process.stdout.allowedFirstNewLine
|
||
process.stdout.allowedFirstNewLine = true
|
||
else
|
||
return
|
||
else if string.match(process.cursor.CRMatcher)
|
||
if process.cursor.count is process.stdout.columns
|
||
process.cursor.count = 0
|
||
forward = process.cursor.margin
|
||
string = process.cursor.forwardN(forward) + string
|
||
else
|
||
forward = process.cursor.margin + process.cursor.count
|
||
string = process.cursor.up + process.cursor.forwardN(forward) + string
|
||
++process.cursor.count
|
||
console.log string
|
||
|
||
class Tap extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'tap', config
|
||
|
||
class List extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'list', config
|
||
|
||
customizeProcessStdout: (options) ->
|
||
process.stdout.write = (string) ->
|
||
return if string is process.cursor.deleteLine or string is process.cursor.beginningOfLine
|
||
console.log string
|
||
|
||
customizeProcessStdout: (options) ->
|
||
process.cursor.CRMatcher = /\u001b\[90m.*:\s\u001b\[0m/
|
||
process.cursor.CRCleaner = (string) -> process.cursor.up + process.cursor.deleteLine + string + process.cursor.up + process.cursor.up
|
||
origLog = console.log
|
||
console.log = ->
|
||
string = console.format.apply(console, arguments)
|
||
if string.match /\u001b\[32m\s\s-\u001b\[0m/
|
||
string = string
|
||
process.cursor.CRCleanup = false
|
||
if string.match(process.cursor.CRMatcher)
|
||
process.cursor.CRCleanup = true
|
||
else if process.cursor.CRCleanup
|
||
string = process.cursor.CRCleaner(string)
|
||
process.cursor.CRCleanup = false
|
||
origLog.call console, string
|
||
|
||
class Min extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'min', config
|
||
|
||
class Doc extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'doc', config
|
||
|
||
class Teamcity extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'teamcity', config
|
||
|
||
class Xunit extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'xunit', config
|
||
|
||
class Json extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'json', config
|
||
|
||
class JsonCov extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'json-cov', config
|
||
|
||
class HtmlCov extends Reporter
|
||
|
||
constructor: (config) ->
|
||
super 'html-cov', config
|
||
|
||
|
||
reporterString = system.args[2] || 'spec'
|
||
reporterString = ("#{s.charAt(0).toUpperCase()}#{s.slice(1)}" for s in reporterString.split('-')).join('')
|
||
reporterKlass = try
|
||
eval(reporterString)
|
||
catch error
|
||
undefined
|
||
|
||
config = JSON.parse(system.args[3] || '{}')
|
||
|
||
if reporterKlass
|
||
reporter = new reporterKlass config
|
||
reporter.run()
|
||
else
|
||
console.log "Reporter class not implemented: #{reporterString}"
|
||
phantom.exit 1
|