UNPKG

19.8 kBJavaScriptView Raw
1(function () {
2 /*global describe, it*/
3
4 'use strict';
5
6 var should = require('should'),
7 cors = require('../lib');
8
9 var fakeRequest = function (headers) {
10 return {
11 headers: headers || {
12 'origin': 'request.com',
13 'access-control-request-headers': 'requestedHeader1,requestedHeader2'
14 },
15 pause: function () {
16 // do nothing
17 return;
18 },
19 resume: function () {
20 // do nothing
21 return;
22 }
23 };
24 },
25 fakeResponse = function () {
26 var headers = {};
27 return {
28 allHeaders: function () {
29 return headers;
30 },
31 getHeader: function (key) {
32 return headers[key];
33 },
34 setHeader: function (key, value) {
35 headers[key] = value;
36 return;
37 },
38 get: function (key) {
39 return headers[key];
40 }
41 };
42 };
43
44 describe('cors', function () {
45 it('does not alter `options` configuration object', function () {
46 var options = Object.freeze({
47 origin: 'custom-origin'
48 });
49 (function () {
50 cors(options);
51 }).should.not.throw();
52 });
53
54 it('passes control to next middleware', function (done) {
55 // arrange
56 var req, res, next;
57 req = fakeRequest();
58 res = fakeResponse();
59 next = function () {
60 done();
61 };
62
63 // act
64 cors()(req, res, next);
65 });
66
67 it('shortcircuits preflight requests', function (done) {
68 // arrange
69 var req, res, next;
70 req = fakeRequest();
71 req.method = 'OPTIONS';
72 res = fakeResponse();
73 res.end = function () {
74 // assert
75 res.statusCode.should.equal(204);
76 done();
77 };
78 next = function () {
79 // assert
80 done('should not be called');
81 };
82
83 // act
84 cors()(req, res, next);
85 });
86
87 it('can configure preflight success response status code', function (done) {
88 // arrange
89 var req, res, next;
90 req = fakeRequest();
91 req.method = 'OPTIONS';
92 res = fakeResponse();
93 res.end = function () {
94 // assert
95 res.statusCode.should.equal(200);
96 done();
97 };
98 next = function () {
99 // assert
100 done('should not be called');
101 };
102
103 // act
104 cors({optionsSuccessStatus: 200})(req, res, next);
105 });
106
107 it('doesn\'t shortcircuit preflight requests with preflightContinue option', function (done) {
108 // arrange
109 var req, res, next;
110 req = fakeRequest();
111 req.method = 'OPTIONS';
112 res = fakeResponse();
113 res.end = function () {
114 // assert
115 done('should not be called');
116 };
117 next = function () {
118 // assert
119 done();
120 };
121
122 // act
123 cors({preflightContinue: true})(req, res, next);
124 });
125
126 it('normalizes method names', function (done) {
127 // arrange
128 var req, res, next;
129 req = fakeRequest();
130 req.method = 'options';
131 res = fakeResponse();
132 res.end = function () {
133 // assert
134 res.statusCode.should.equal(204);
135 done();
136 };
137 next = function () {
138 // assert
139 done('should not be called');
140 };
141
142 // act
143 cors()(req, res, next);
144 });
145
146 it('no options enables default CORS to all origins', function (done) {
147 // arrange
148 var req, res, next;
149 req = fakeRequest();
150 res = fakeResponse();
151 next = function () {
152 // assert
153 res.getHeader('Access-Control-Allow-Origin').should.equal('*');
154 should.not.exist(res.getHeader('Access-Control-Allow-Methods'));
155 done();
156 };
157
158 // act
159 cors()(req, res, next);
160 });
161
162 it('OPTION call with no options enables default CORS to all origins and methods', function (done) {
163 // arrange
164 var req, res, next;
165 req = fakeRequest();
166 req.method = 'OPTIONS';
167 res = fakeResponse();
168 res.end = function () {
169 // assert
170 res.statusCode.should.equal(204);
171 done();
172 };
173 next = function () {
174 // assert
175 res.getHeader('Access-Control-Allow-Origin').should.equal('*');
176 res.getHeader('Access-Control-Allow-Methods').should.equal('GET,PUT,PATCH,POST,DELETE');
177 done();
178 };
179
180 // act
181 cors()(req, res, next);
182 });
183
184 describe('passing static options', function () {
185 it('overrides defaults', function (done) {
186 // arrange
187 var req, res, next, options;
188 options = {
189 origin: 'example.com',
190 methods: ['FOO', 'bar'],
191 headers: ['FIZZ', 'buzz'],
192 credentials: true,
193 maxAge: 123
194 };
195 req = fakeRequest();
196 req.method = 'OPTIONS';
197 res = fakeResponse();
198 res.end = function () {
199 // assert
200 res.statusCode.should.equal(204);
201 done();
202 };
203 next = function () {
204 // assert
205 res.getHeader('Access-Control-Allow-Origin').should.equal('example.com');
206 res.getHeader('Access-Control-Allow-Methods').should.equal('FOO,bar');
207 res.getHeader('Access-Control-Allow-Headers').should.equal('FIZZ,buzz');
208 res.getHeader('Access-Control-Allow-Credentials').should.equal('true');
209 res.getHeader('Access-Control-Max-Age').should.equal('123');
210 done();
211 };
212
213 // act
214 cors(options)(req, res, next);
215 });
216
217 it('matches request origin against regexp', function(done) {
218 var req = fakeRequest();
219 var res = fakeResponse();
220 var options = { origin: /^(.+\.)?request.com$/ };
221 cors(options)(req, res, function(err) {
222 should.not.exist(err);
223 res.getHeader('Access-Control-Allow-Origin').should.equal(req.headers.origin);
224 should.exist(res.getHeader('Vary'));
225 res.getHeader('Vary').should.equal('Origin');
226 return done();
227 });
228 });
229
230 it('matches request origin against array of origin checks', function(done) {
231 var req = fakeRequest();
232 var res = fakeResponse();
233 var options = { origin: [ /foo\.com$/, 'request.com' ] };
234 cors(options)(req, res, function(err) {
235 should.not.exist(err);
236 res.getHeader('Access-Control-Allow-Origin').should.equal(req.headers.origin);
237 should.exist(res.getHeader('Vary'));
238 res.getHeader('Vary').should.equal('Origin');
239 return done();
240 });
241 });
242
243 it('doesn\'t match request origin against array of invalid origin checks', function(done) {
244 var req = fakeRequest();
245 var res = fakeResponse();
246 var options = { origin: [ /foo\.com$/, 'bar.com' ] };
247 cors(options)(req, res, function(err) {
248 should.not.exist(err);
249 should.not.exist(res.getHeader('Access-Control-Allow-Origin'));
250 should.exist(res.getHeader('Vary'));
251 res.getHeader('Vary').should.equal('Origin');
252 return done();
253 });
254 });
255
256 it('origin of false disables cors', function (done) {
257 // arrange
258 var req, res, next, options;
259 options = {
260 origin: false,
261 methods: ['FOO', 'bar'],
262 headers: ['FIZZ', 'buzz'],
263 credentials: true,
264 maxAge: 123
265 };
266 req = fakeRequest();
267 res = fakeResponse();
268 next = function () {
269 // assert
270 should.not.exist(res.getHeader('Access-Control-Allow-Origin'));
271 should.not.exist(res.getHeader('Access-Control-Allow-Methods'));
272 should.not.exist(res.getHeader('Access-Control-Allow-Headers'));
273 should.not.exist(res.getHeader('Access-Control-Allow-Credentials'));
274 should.not.exist(res.getHeader('Access-Control-Max-Age'));
275 done();
276 };
277
278 // act
279 cors(options)(req, res, next);
280 });
281
282 it('can override origin', function (done) {
283 // arrange
284 var req, res, next, options;
285 options = {
286 origin: 'example.com'
287 };
288 req = fakeRequest();
289 res = fakeResponse();
290 next = function () {
291 // assert
292 res.getHeader('Access-Control-Allow-Origin').should.equal('example.com');
293 done();
294 };
295
296 // act
297 cors(options)(req, res, next);
298 });
299
300 it('includes Vary header for specific origins', function (done) {
301 // arrange
302 var req, res, next, options;
303 options = {
304 origin: 'example.com'
305 };
306 req = fakeRequest();
307 res = fakeResponse();
308 next = function () {
309 // assert
310 should.exist(res.getHeader('Vary'));
311 res.getHeader('Vary').should.equal('Origin');
312 done();
313 };
314
315 // act
316 cors(options)(req, res, next);
317 });
318
319 it('appends to an existing Vary header', function (done) {
320 // arrange
321 var req, res, next, options;
322 options = {
323 origin: 'example.com'
324 };
325 req = fakeRequest();
326 res = fakeResponse();
327 res.setHeader('Vary', 'Foo');
328 next = function () {
329 // assert
330 res.getHeader('Vary').should.equal('Foo, Origin');
331 done();
332 };
333
334 // act
335 cors(options)(req, res, next);
336 });
337
338 it('origin defaults to *', function (done) {
339 // arrange
340 var req, res, next, options;
341 options = {
342 };
343 req = fakeRequest();
344 res = fakeResponse();
345 next = function () {
346 // assert
347 res.getHeader('Access-Control-Allow-Origin').should.equal('*');
348 done();
349 };
350
351 // act
352 cors(options)(req, res, next);
353 });
354
355 it('specifying true for origin reflects requesting origin', function (done) {
356 // arrange
357 var req, res, next, options;
358 options = {
359 origin: true
360 };
361 req = fakeRequest();
362 res = fakeResponse();
363 next = function () {
364 // assert
365 res.getHeader('Access-Control-Allow-Origin').should.equal('request.com');
366 done();
367 };
368
369 // act
370 cors(options)(req, res, next);
371 });
372
373 it('should allow origin when callback returns true', function (done) {
374 var req, res, next, options;
375 options = {
376 origin: function (sentOrigin, cb) {
377 sentOrigin.should.equal('request.com');
378 cb(null, true);
379 }
380 };
381 req = fakeRequest();
382 res = fakeResponse();
383 next = function () {
384 res.getHeader('Access-Control-Allow-Origin').should.equal('request.com');
385 done();
386 };
387
388 cors(options)(req, res, next);
389 });
390
391 it('should not allow origin when callback returns false', function (done) {
392 var req, res, next, options;
393 options = {
394 origin: function (sentOrigin, cb) {
395 sentOrigin.should.equal('request.com');
396 cb(null, false);
397 }
398 };
399 req = fakeRequest();
400 res = fakeResponse();
401 next = function () {
402 should.not.exist(res.getHeader('Access-Control-Allow-Origin'));
403 should.not.exist(res.getHeader('Access-Control-Allow-Methods'));
404 should.not.exist(res.getHeader('Access-Control-Allow-Headers'));
405 should.not.exist(res.getHeader('Access-Control-Allow-Credentials'));
406 should.not.exist(res.getHeader('Access-Control-Max-Age'));
407 done();
408 };
409
410 cors(options)(req, res, next);
411 });
412
413 it('should not override options.origin callback', function (done) {
414 var req, res, next, options;
415 options = {
416 origin: function (sentOrigin, cb) {
417 var isValid = sentOrigin === 'request.com';
418 cb(null, isValid);
419 }
420 };
421
422 req = fakeRequest();
423 res = fakeResponse();
424 next = function () {
425 res.getHeader('Access-Control-Allow-Origin').should.equal('request.com');
426 };
427
428 cors(options)(req, res, next);
429
430 req = fakeRequest({
431 'origin': 'invalid-request.com'
432 });
433 res = fakeResponse();
434
435 next = function () {
436 should.not.exist(res.getHeader('Access-Control-Allow-Origin'));
437 should.not.exist(res.getHeader('Access-Control-Allow-Methods'));
438 should.not.exist(res.getHeader('Access-Control-Allow-Headers'));
439 should.not.exist(res.getHeader('Access-Control-Allow-Credentials'));
440 should.not.exist(res.getHeader('Access-Control-Max-Age'));
441 done();
442 };
443
444 cors(options)(req, res, next);
445 });
446
447
448 it('can override methods', function (done) {
449 // arrange
450 var req, res, next, options;
451 options = {
452 methods: ['method1', 'method2']
453 };
454 req = fakeRequest();
455 req.method = 'OPTIONS';
456 res = fakeResponse();
457 res.end = function () {
458 // assert
459 res.statusCode.should.equal(204);
460 done();
461 };
462 next = function () {
463 // assert
464 res.getHeader('Access-Control-Allow-Methods').should.equal('method1,method2');
465 done();
466 };
467
468 // act
469 cors(options)(req, res, next);
470 });
471
472 it('methods defaults to GET, PUT, PATCH, POST, DELETE', function (done) {
473 // arrange
474 var req, res, next, options;
475 options = {
476 };
477 req = fakeRequest();
478 req.method = 'OPTIONS';
479 res = fakeResponse();
480 res.end = function () {
481 // assert
482 res.statusCode.should.equal(204);
483 done();
484 };
485 next = function () {
486 // assert
487 res.getHeader('Access-Control-Allow-Methods').should.equal('GET,PUT,PATCH,POST,DELETE');
488 done();
489 };
490
491 // act
492 cors(options)(req, res, next);
493 });
494
495 it('can specify allowed headers', function (done) {
496 // arrange
497 var req, res, options;
498 options = {
499 allowedHeaders: ['header1', 'header2']
500 };
501 req = fakeRequest();
502 req.method = 'OPTIONS';
503 res = fakeResponse();
504 res.end = function () {
505 // assert
506 res.getHeader('Access-Control-Allow-Headers').should.equal('header1,header2');
507 should.not.exist(res.getHeader('Vary'));
508 done();
509 };
510
511 // act
512 cors(options)(req, res, null);
513 });
514
515 it('specifying an empty list or string of allowed headers will result in no response header for allowed headers', function (done) {
516 // arrange
517 var req, res, next, options;
518 options = {
519 allowedHeaders: []
520 };
521 req = fakeRequest();
522 res = fakeResponse();
523 next = function () {
524 // assert
525 should.not.exist(res.getHeader('Access-Control-Allow-Headers'));
526 should.not.exist(res.getHeader('Vary'));
527 done();
528 };
529
530 // act
531 cors(options)(req, res, next);
532 });
533
534 it('if no allowed headers are specified, defaults to requested allowed headers', function (done) {
535 // arrange
536 var req, res, options;
537 options = {
538 };
539 req = fakeRequest();
540 req.method = 'OPTIONS';
541 res = fakeResponse();
542 res.end = function () {
543 // assert
544 res.getHeader('Access-Control-Allow-Headers').should.equal('requestedHeader1,requestedHeader2');
545 should.exist(res.getHeader('Vary'));
546 res.getHeader('Vary').should.equal('Access-Control-Request-Headers');
547 done();
548 };
549
550 // act
551 cors(options)(req, res, null);
552 });
553
554 it('can specify exposed headers', function (done) {
555 // arrange
556 var req, res, options, next;
557 options = {
558 exposedHeaders: ['custom-header1', 'custom-header2']
559 };
560 req = fakeRequest();
561 res = fakeResponse();
562 next = function () {
563 // assert
564 res.getHeader('Access-Control-Expose-Headers').should.equal('custom-header1,custom-header2');
565 done();
566 };
567
568 // act
569 cors(options)(req, res, next);
570 });
571
572 it('specifying an empty list or string of exposed headers will result in no response header for exposed headers', function (done) {
573 // arrange
574 var req, res, next, options;
575 options = {
576 exposedHeaders: []
577 };
578 req = fakeRequest();
579 res = fakeResponse();
580 next = function () {
581 // assert
582 should.not.exist(res.getHeader('Access-Control-Expose-Headers'));
583 done();
584 };
585
586 // act
587 cors(options)(req, res, next);
588 });
589
590 it('includes credentials if explicitly enabled', function (done) {
591 // arrange
592 var req, res, options;
593 options = {
594 credentials: true
595 };
596 req = fakeRequest();
597 req.method = 'OPTIONS';
598 res = fakeResponse();
599 res.end = function () {
600 // assert
601 res.getHeader('Access-Control-Allow-Credentials').should.equal('true');
602 done();
603 };
604
605 // act
606 cors(options)(req, res, null);
607 });
608
609 it('does not includes credentials unless explicitly enabled', function (done) {
610 // arrange
611 var req, res, next, options;
612 options = {
613 };
614 req = fakeRequest();
615 res = fakeResponse();
616 next = function () {
617 // assert
618 should.not.exist(res.getHeader('Access-Control-Allow-Credentials'));
619 done();
620 };
621
622 // act
623 cors(options)(req, res, next);
624 });
625
626 it('includes maxAge when specified', function (done) {
627 // arrange
628 var req, res, options;
629 options = {
630 maxAge: 456
631 };
632 req = fakeRequest();
633 req.method = 'OPTIONS';
634 res = fakeResponse();
635 res.end = function () {
636 // assert
637 res.getHeader('Access-Control-Max-Age').should.equal('456');
638 done();
639 };
640
641 // act
642 cors(options)(req, res, null);
643 });
644
645 it('does not includes maxAge unless specified', function (done) {
646 // arrange
647 var req, res, next, options;
648 options = {
649 };
650 req = fakeRequest();
651 res = fakeResponse();
652 next = function () {
653 // assert
654 should.not.exist(res.getHeader('Access-Control-Max-Age'));
655 done();
656 };
657
658 // act
659 cors(options)(req, res, next);
660 });
661 });
662
663 describe('passing a function to build options', function () {
664 it('handles options specified via callback', function (done) {
665 // arrange
666 var req, res, next, delegate;
667 delegate = function (req2, cb) {
668 cb(null, {
669 origin: 'delegate.com'
670 });
671 };
672 req = fakeRequest();
673 res = fakeResponse();
674 next = function () {
675 // assert
676 res.getHeader('Access-Control-Allow-Origin').should.equal('delegate.com');
677 done();
678 };
679
680 // act
681 cors(delegate)(req, res, next);
682 });
683
684 it('handles error specified via callback', function (done) {
685 // arrange
686 var req, res, next, delegate;
687 delegate = function (req2, cb) {
688 cb('some error');
689 };
690 req = fakeRequest();
691 res = fakeResponse();
692 next = function (err) {
693 // assert
694 err.should.equal('some error');
695 done();
696 };
697
698 // act
699 cors(delegate)(req, res, next);
700 });
701 });
702 });
703
704}());