1 | /// Module test
|
2 | //
|
3 | // Represents a single test.
|
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 ---------------------------------------------------
|
29 | var boo = require('boo')
|
30 | var pinky = require('pinky')
|
31 | var timeout = require('./utils').timeout
|
32 |
|
33 |
|
34 | //// -- Interfaces -----------------------------------------------------
|
35 |
|
36 | ///// type Verdict
|
37 | // The verdict of running a test.
|
38 | //
|
39 | // :: String ('success' | 'failure' | 'ignored')
|
40 |
|
41 |
|
42 | ///// type Result
|
43 | // The result of running a test.
|
44 | //
|
45 | // :: verdict :: Verdict --^ The verdict about the test
|
46 | // .. started :: Date --^ When it started
|
47 | // .. finished :: Date --^ When it finished
|
48 | // .. exception :: Error --^ The exception thrown (if any)
|
49 | // .. slow :: Boolean --^ Was it slower than the threshold?
|
50 | // .. test :: Test --^ The test that we ran
|
51 |
|
52 |
|
53 | ///// type TestMeta
|
54 | // Meta information about a Test.
|
55 | //
|
56 | // :: title :: String --^ The name of the test
|
57 | // .. test :: () -> Promise a --^ The test code
|
58 | // .. timeout :: Number --^ How long to wait for async (ms)
|
59 | // .. slow :: Number --^ What to consider slow (ms)
|
60 | // .. enabled :: Test -> Boolean --^ Should we run this at all?
|
61 |
|
62 |
|
63 | ///// type Test
|
64 | // Represents a single test.
|
65 | //
|
66 | // :: Test -: TestMeta
|
67 | // .. -- | Yields all the components of the Test's title.
|
68 | // .. full-title :: @TestMeta => () -> [String]
|
69 |
|
70 |
|
71 | ///// type Runnable a
|
72 | // The type of all the things we can run.
|
73 | //
|
74 | // :: run :: Reporter, Report -> Promise a
|
75 |
|
76 |
|
77 | //// -- Results --------------------------------------------------------
|
78 |
|
79 | ///// λ isSlow
|
80 | // :internal:
|
81 | // Checks if a test should be considered slow.
|
82 | //
|
83 | // :: Test, Date, Date -> Boolean
|
84 | function isSlow(test, started, finished) {
|
85 | return (finished - started) >= test.slow
|
86 | }
|
87 |
|
88 | ///// λ makeResult
|
89 | // :internal:
|
90 | // Constructs an object of the Result type.
|
91 | //
|
92 | // :: Test, Date, Date -> Result
|
93 | function makeResult(test, started, finished) {
|
94 | return { started : started
|
95 | , finished : finished
|
96 | , slow : isSlow(test, started, finished)
|
97 | , test : test
|
98 | }
|
99 | }
|
100 |
|
101 | ///// λ success
|
102 | // :internal:
|
103 | // Constructs a Result for successful tests.
|
104 | //
|
105 | // :: Test, Date, Date -> Result
|
106 | function success(test, started, finished) {
|
107 | return boo.merge( makeResult(test, started, finished)
|
108 | , { verdict: 'success' })
|
109 |
|
110 | }
|
111 |
|
112 | ///// λ fail
|
113 | // :internal:
|
114 | // Constructs a Result for failed tests.
|
115 | //
|
116 | // :: Test, Date, Date, Error -> Result
|
117 | function fail(test, started, finished, error) {
|
118 | return boo.merge( makeResult(test, started, finished)
|
119 | , { verdict : 'failure'
|
120 | , exception : error })
|
121 | }
|
122 |
|
123 | ///// λ ignore
|
124 | // :internal:
|
125 | // Constructs a Result for a test that didn't ran.
|
126 | //
|
127 | // :: Test -> Result
|
128 | function ignore(test) {
|
129 | return boo.merge( makeResult(test, new Date, new Date)
|
130 | , { verdict: 'ignored' })
|
131 | }
|
132 |
|
133 | //// -- Base test class ------------------------------------------------
|
134 |
|
135 | ///// {} Test
|
136 | //
|
137 | // Represents a single test.
|
138 | //
|
139 | // :: boo.Base <| Test -: ((Runnable Result) + Test)
|
140 | var Test = boo.Base.derive({
|
141 |
|
142 | ///// -- Interface: TestMeta -----------------------------------------
|
143 |
|
144 | ////// data slow
|
145 | // The slow threshold for this test.
|
146 | // :: Number
|
147 | slow: 500
|
148 |
|
149 | ////// data timeout
|
150 | // The timeout threshold for this test.
|
151 | // :: Number
|
152 | , timeout : 2000
|
153 |
|
154 | ////// λ enabled
|
155 | // Should we run this test at all?
|
156 | // :: Test -> Bool
|
157 | , enabled:
|
158 | function _enabled() {
|
159 | return true
|
160 | }
|
161 |
|
162 | ///// -- Interface: Instantiable -------------------------------------
|
163 |
|
164 | ////// λ init
|
165 | // Initialises a instance of Test.
|
166 | //
|
167 | // :: @Test => Suite, String, (() -> Promise a), TestOptions -> ()
|
168 | , init:
|
169 | function _init(suite, title, fn, options) {
|
170 | this.parent = suite
|
171 | this.title = title
|
172 | this.test = fn
|
173 |
|
174 | boo.extend(this, options || {})
|
175 | if (suite) suite.add(this)
|
176 | }
|
177 |
|
178 | ///// -- Interface: Test ---------------------------------------------
|
179 |
|
180 | ////// λ fullTitle
|
181 | // Returns all the components of the fully qualified title for this
|
182 | // test.
|
183 | //
|
184 | // :: () -> [String]
|
185 | , fullTitle:
|
186 | function _fullTitle() {
|
187 | var parentTitle = this.parent? this.fullTitle.call(this.parent)
|
188 | : /* otherwise */ []
|
189 |
|
190 | return parentTitle.concat(this.title).filter(Boolean)
|
191 | }
|
192 |
|
193 | ////// λ setEnabled
|
194 | // Sets the enabled property of this test.
|
195 | //
|
196 | // :: @Test => (Test -> Bool) -> Test
|
197 | , setEnabled:
|
198 | function _setEnabled(f) {
|
199 | this.enabled = f
|
200 | return this
|
201 | }
|
202 |
|
203 | ////// λ setTimeout
|
204 | // Defines the timeout of this test.
|
205 | //
|
206 | // :: @Test => Number -> Test
|
207 | , setTimeout:
|
208 | function _setTimeout(n) {
|
209 | this.timeout = n
|
210 | return this
|
211 | }
|
212 |
|
213 | ////// λ setSlow
|
214 | // Defines the slow threshold for this test.
|
215 | //
|
216 | // :: @Test => Number -> Test
|
217 | , setSlow:
|
218 | function _setSlow(n) {
|
219 | this.slow = n
|
220 | return this
|
221 | }
|
222 |
|
223 |
|
224 | ///// -- Interface: Runnable -----------------------------------------
|
225 |
|
226 | ////// λ run
|
227 | // Runs the test.
|
228 | //
|
229 | // :: Reporter, Report -> Promise Result
|
230 | , run:
|
231 | function _run() {
|
232 | if (!this.enabled()) return pinky(ignore(this))
|
233 |
|
234 | var test = this
|
235 | var start = new Date
|
236 | var p = pinky(undefined)
|
237 |
|
238 | return timeout(this.timeout, p.then(this.test))
|
239 | .then(ok, failure)
|
240 |
|
241 | function ok() { return success(test, start, new Date) }
|
242 | function failure(e) { return fail(test, start, new Date, e) }
|
243 | }
|
244 | })
|
245 |
|
246 | //// -- Exports --------------------------------------------------------
|
247 | module.exports = { Test: Test } |
\ | No newline at end of file |