UNPKG

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