UNPKG

5.39 kBJavaScriptView Raw
1/// Module runner
2//
3// Runs a series of tests.
4//
5//
6// Copyright (c) 2013 Quildreen Motta
7//
8// Permission is hereby granted, free of charge, to any person
9// obtaining a copy of this software and associated documentation files
10// (the "Software"), to deal in the Software without restriction,
11// including without limitation the rights to use, copy, modify, merge,
12// publish, distribute, sublicense, and/or sell copies of the Software,
13// and to permit persons to whom the Software is furnished to do so,
14// subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be
17// included in all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26//
27
28//// -- Dependencies ---------------------------------------------------
29var Eventful = require('ekho').Eventful
30var pinky = require('pinky')
31var pipeline = require('pinky-combinators').pipeline
32var utils = require('./utils')
33
34var flatten = utils.flatten
35var sequentially = utils.sequentially
36
37
38//// -- Core implementation --------------------------------------------
39
40///// {} Report
41//
42// Represents the result of running a collection of tests.
43//
44// :: Eventful <| Report
45var Report = Eventful.derive({
46
47 ////// λ init
48 //
49 // Initialises a Report instance.
50 //
51 // :: @Report => Eventful -> ()
52 init:
53 function _init(parent) {
54 Eventful.init.call(this, parent)
55
56 this.passed = []
57 this.failed = []
58 this.ignored = []
59 this.all = []
60 this.started = new Date
61
62 return this
63 }
64
65 ////// λ done
66 //
67 // Signals we've finished processing the Report.
68 //
69 // :: @Report => Report
70, done:
71 function _done() {
72 this.finished = new Date
73 this.trigger('done', this)
74
75 return this
76 }
77
78 ////// λ add
79 //
80 // Adds a test result to the Report.
81 //
82 // :: @Report => Result -> Report
83, add:
84 function _add(result) {
85 this.all.push(result)
86 this.trigger('result', result)
87
88 var verdict = result.verdict
89 return verdict == 'success'? this.addSuccess(result)
90 : verdict == 'failure'? this.addFailure(result)
91 : verdict == 'ignored'? this.addIgnored(result)
92 : /* otherwise */ this
93 }
94
95 ////// λ addFailure
96 // :internal:
97 // Adds a failure to the Report.
98 //
99 // :: @Report => Result -> Report
100, addFailure:
101 function _addFailure(result) {
102 this.failed.push(result)
103 this.trigger('failure', result)
104
105 return this
106 }
107
108 ////// λ addSuccess
109 // :internal:
110 // Adds a success to the Report.
111 //
112 // :: @Report => Result -> Report
113, addSuccess:
114 function _addSuccess(result) {
115 this.passed.push(result)
116 this.trigger('success', result)
117
118 return this
119 }
120
121 ////// λ addIgnored
122 // :internal:
123 // Adds a ignored result to the Report.
124 //
125 // :: @Report => Result -> Report
126, addIgnored:
127 function _addIgnored(result) {
128 this.ignored.push(result)
129 this.trigger('ignored', result)
130
131 return this
132 }
133})
134
135
136///// λ run
137//
138// Runs a series of test cases.
139//
140// Optionally we take a reporter, which will be used to bind listeners
141// to the eventful reporter. This is used for interfaces that need to
142// update frequently to provide feedback for the user.
143//
144// The `report` argument is used for passing around the top-level
145// eventful object when recursing through the nested suites.
146//
147// :: [Runnable], (Reporter -> ()), Report? -> Promise [Result]
148function run(tests, reporter, report) {
149 var reportDone
150 if (!report) {
151 reportDone = true
152 report = Report.make(null)
153 report.on('test:finished', function(ev, result) {
154 report.add(result)
155 })
156 }
157 if (reporter) reporter(report)
158
159 var promise = sequentially(tests.map(runnerForThing))
160 if (reportDone) promise = promise.then(function(results){
161 report.done()
162 return report.all
163 })
164
165 return promise
166
167 function runnerForThing(test) {
168 return test.tests? runnerForSuite(test)
169 : /* otherwise */ runnerForTest(test)
170 }
171
172 function runnerForSuite(suite) { return function() {
173 report.trigger('suite:started', suite)
174 return suite.run(report)
175 .then(function(xs) {
176 var results = flatten(xs)
177 report.trigger('suite:finished', results, suite)
178 return results
179 })
180 }}
181
182 function runnerForTest(test) { return function() {
183 report.trigger('test:started', test)
184 return test.run(reporter)
185 .then(function(result) {
186 report.trigger('test:finished', result, test)
187 return result
188 })
189 }}
190}
191
192//// -- Exports --------------------------------------------------------
193module.exports = { run: run
194 , Report: Report }
\No newline at end of file