UNPKG

10.8 kBJavaScriptView Raw
1'use strict';
2
3var tape = require('../');
4var tap = require('tap');
5var concat = require('concat-stream');
6var inspect = require('object-inspect');
7var assign = require('object.assign');
8
9var stripFullStack = require('./common').stripFullStack;
10
11var getter = function () { return 'message'; };
12var messageGetterError = Object.defineProperty(
13 { custom: 'error' },
14 'message',
15 {
16 configurable: true,
17 enumerable: true,
18 get: getter
19 }
20);
21var thrower = function () { throw messageGetterError; };
22
23tap.test('failures', function (tt) {
24 tt.plan(1);
25
26 var test = tape.createHarness();
27 test.createStream().pipe(concat(function (body) {
28 tt.same(stripFullStack(body.toString('utf8')), [
29 'TAP version 13',
30 '# non functions',
31 'ok 1 should throw',
32 'ok 2 should throw',
33 'ok 3 should throw',
34 'ok 4 should throw',
35 'ok 5 should throw',
36 'ok 6 should throw',
37 'ok 7 should throw',
38 'ok 8 should throw',
39 '# function',
40 'not ok 9 should throw',
41 ' ---',
42 ' operator: throws',
43 ' expected: undefined',
44 ' actual: undefined',
45 ' at: Test.<anonymous> ($TEST/throws.js:$LINE:$COL)',
46 ' stack: |-',
47 ' Error: should throw',
48 ' [... stack stripped ...]',
49 ' at Test.<anonymous> ($TEST/throws.js:$LINE:$COL)',
50 ' [... stack stripped ...]',
51 ' ...',
52 '# custom error messages',
53 'ok 10 "message" is enumerable',
54 "ok 11 { custom: 'error', message: 'message' }",
55 'ok 12 getter is still the same',
56 '# throws null',
57 'ok 13 throws null',
58 '# wrong type of error',
59 'not ok 14 throws actual',
60 ' ---',
61 ' operator: throws',
62 ' expected: |-',
63 ' [Function: TypeError]',
64 ' actual: |-',
65 ' { [RangeError: actual!] ' + ('cause' in Error.prototype ? '[cause]: undefined, ' : '') + "message: 'actual!' }",
66 ' at: Test.<anonymous> ($TEST/throws.js:$LINE:$COL)',
67 ' stack: |-',
68 ' RangeError: actual!',
69 ' at Test.<anonymous> ($TEST/throws.js:$LINE:$COL)',
70 ' [... stack stripped ...]',
71 ' ...',
72 '# object',
73 'ok 15 object properties are validated',
74 '# object with regexes',
75 'ok 16 object with regex values is validated',
76 '# similar error object',
77 'ok 17 throwing a similar error',
78 '# validate with regex',
79 'ok 18 regex against toString of error',
80 '# custom error validation',
81 'ok 19 error is SyntaxError',
82 'ok 20 error matches /value/',
83 'ok 21 unexpected error',
84 '# throwing primitives',
85 'ok 22 primitive: null',
86 'ok 23 primitive: undefined',
87 'ok 24 primitive: 0',
88 'ok 25 primitive: NaN',
89 'ok 26 primitive: 42',
90 'ok 27 primitive: Infinity',
91 'ok 28 primitive: \'\'',
92 'ok 29 primitive: \'foo\'',
93 'ok 30 primitive: true',
94 'ok 31 primitive: false',
95 '# ambiguous arguments',
96 'ok 32 Second',
97 'ok 33 Second',
98 'ok 34 Second',
99 'ok 35 should throw',
100 'not ok 36 should throw',
101 ' ---',
102 ' operator: throws',
103 ' expected: |-',
104 ' \'/Second$/\'',
105 ' actual: |-',
106 ' { [Error: First] ' + ('cause' in Error.prototype ? '[cause]: undefined, ' : '') + 'message: \'First\' }',
107 ' at: Test.<anonymous> ($TEST/throws.js:$LINE:$COL)',
108 ' stack: |-',
109 ' Error: First',
110 ' at throwingFirst ($TEST/throws.js:$LINE:$COL)',
111 ' [... stack stripped ...]',
112 ' at Test.<anonymous> ($TEST/throws.js:$LINE:$COL)',
113 ' [... stack stripped ...]',
114 ' ...',
115 '',
116 '1..36',
117 '# tests 36',
118 '# pass 33',
119 '# fail 3',
120 ''
121 ]);
122 }));
123
124 test('non functions', function (t) {
125 t.plan(8);
126 t.throws();
127 t.throws(null);
128 t.throws(true);
129 t.throws(false);
130 t.throws('abc');
131 t.throws(/a/g);
132 t.throws([]);
133 t.throws({});
134 });
135
136 test('function', function (t) {
137 t.plan(1);
138 t.throws(function () {});
139 });
140
141 test('custom error messages', function (t) {
142 t.plan(3);
143 t.equal(Object.prototype.propertyIsEnumerable.call(messageGetterError, 'message'), true, '"message" is enumerable');
144 t.throws(thrower, "{ custom: 'error', message: 'message' }");
145 t.equal(Object.getOwnPropertyDescriptor(messageGetterError, 'message').get, getter, 'getter is still the same');
146 });
147
148 test('throws null', function (t) {
149 t.plan(1);
150 t.throws(function () { throw null; }, 'throws null');
151 t.end();
152 });
153
154 test('wrong type of error', function (t) {
155 t.plan(1);
156 var actual = new RangeError('actual!');
157 t.throws(function () { throw actual; }, TypeError, 'throws actual');
158 t.end();
159 });
160
161 // taken from https://nodejs.org/api/assert.html#assert_assert_throws_fn_error_message
162 var err = new TypeError('Wrong value');
163 err.code = 404;
164 err.foo = 'bar';
165 err.info = {
166 nested: true,
167 baz: 'text'
168 };
169 err.reg = /abc/i;
170
171 test('object', function (t) {
172 t.plan(1);
173
174 t.throws(
175 function () { throw err; },
176 {
177 name: 'TypeError',
178 message: 'Wrong value',
179 info: {
180 nested: true,
181 baz: 'text'
182 }
183 // Only properties on the validation object will be tested for.
184 // Using nested objects requires all properties to be present. Otherwise
185 // the validation is going to fail.
186 },
187 'object properties are validated'
188 );
189
190 t.end();
191 });
192
193 test('object with regexes', function (t) {
194 t.plan(1);
195 t.throws(
196 function () { throw err; },
197 {
198 // The `name` and `message` properties are strings and using regular
199 // expressions on those will match against the string. If they fail, an
200 // error is thrown.
201 name: /^TypeError$/,
202 message: /Wrong/,
203 foo: 'bar',
204 info: {
205 nested: true,
206 // It is not possible to use regular expressions for nested properties!
207 baz: 'text'
208 },
209 // The `reg` property contains a regular expression and only if the
210 // validation object contains an identical regular expression, it is going
211 // to pass.
212 reg: /abc/i
213 },
214 'object with regex values is validated'
215 );
216 t.end();
217 });
218
219 test('similar error object', function (t) {
220 t.plan(1);
221 t.throws(
222 function () {
223 var otherErr = new TypeError('Not found');
224 // Copy all enumerable properties from `err` to `otherErr`.
225 assign(otherErr, err);
226 throw otherErr;
227 },
228 // The error's `message` and `name` properties will also be checked when using
229 // an error as validation object.
230 err,
231 'throwing a similar error'
232 );
233 t.end();
234 });
235
236 test('validate with regex', function (t) {
237 t.plan(1);
238 t.throws(
239 function () { throw new Error('Wrong value'); },
240 /^Error: Wrong value$/,
241 'regex against toString of error'
242 );
243 t.end();
244 });
245
246 test('custom error validation', function (t) {
247 t.plan(3);
248 t.throws(
249 function () { throw new SyntaxError('Wrong value'); },
250 function (error) {
251 t.ok(error instanceof SyntaxError, 'error is SyntaxError');
252 t.ok((/value/).test(error), 'error matches /value/');
253 // Avoid returning anything from validation functions besides `true`.
254 // Otherwise, it's not clear what part of the validation failed. Instead,
255 // throw an error about the specific validation that failed (as done in this
256 // example) and add as much helpful debugging information to that error as
257 // possible.
258 return true;
259 },
260 'unexpected error'
261 );
262 t.end();
263 });
264
265 test('throwing primitives', function (t) {
266 [null, undefined, 0, NaN, 42, Infinity, '', 'foo', true, false].forEach(function (primitive) {
267 t.throws(function () { throw primitive; }, 'primitive: ' + inspect(primitive));
268 });
269
270 t.end();
271 });
272
273 test('ambiguous arguments', function (t) {
274 function throwingFirst() {
275 throw new Error('First');
276 }
277
278 function throwingSecond() {
279 throw new Error('Second');
280 }
281
282 function notThrowing() {}
283
284 // The second argument is a string and the input function threw an Error.
285 // The first case will not throw as it does not match for the error message
286 // thrown by the input function!
287 t.throws(throwingFirst, 'Second');
288 // In the next example the message has no benefit over the message from the
289 // error and since it is not clear if the user intended to actually match
290 // against the error message, Node.js throws an `ERR_AMBIGUOUS_ARGUMENT` error.
291 t.throws(throwingSecond, 'Second');
292 // TypeError [ERR_AMBIGUOUS_ARGUMENT]
293
294 // The string is only used (as message) in case the function does not throw:
295 t.doesNotThrow(notThrowing, 'Second');
296 // AssertionError [ERR_ASSERTION]: Missing expected exception: Second
297
298 // If it was intended to match for the error message do this instead:
299 // It does not fail because the error messages match.
300 t.throws(throwingSecond, /Second$/);
301
302 // If the error message does not match, an AssertionError is thrown.
303 t.throws(throwingFirst, /Second$/);
304 // AssertionError [ERR_ASSERTION]
305 t.end();
306 });
307});