@e22m4u/js-trie-router 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +177 -56
- package/package.json +1 -1
- package/src/parsers/request-parser.spec.js +2 -2
- package/src/parsers/request-query-parser.spec.js +2 -2
- package/src/request-context.spec.js +10 -0
- package/src/route/route-registry.spec.js +1 -1
- package/src/trie-router.spec.js +7 -7
- package/src/utils/create-cookie-string.js +1 -1
- package/src/utils/create-cookie-string.spec.js +1 -1
- package/src/utils/create-request-mock.d.ts +3 -0
- package/src/utils/create-request-mock.js +162 -11
- package/src/utils/create-request-mock.spec.js +205 -21
package/dist/cjs/index.cjs
CHANGED
|
@@ -427,13 +427,43 @@ __name(parseCookieString, "parseCookieString");
|
|
|
427
427
|
// src/utils/create-request-mock.js
|
|
428
428
|
var import_net = require("net");
|
|
429
429
|
var import_tls = require("tls");
|
|
430
|
+
var import_querystring = __toESM(require("querystring"), 1);
|
|
430
431
|
var import_http2 = require("http");
|
|
432
|
+
var import_js_format9 = require("@e22m4u/js-format");
|
|
433
|
+
|
|
434
|
+
// src/utils/create-cookie-string.js
|
|
431
435
|
var import_js_format8 = require("@e22m4u/js-format");
|
|
436
|
+
function createCookieString(data) {
|
|
437
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
438
|
+
throw new import_js_format8.InvalidArgumentError(
|
|
439
|
+
"Cookie data must be an Object, but %v was given.",
|
|
440
|
+
data
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
let cookies = "";
|
|
444
|
+
for (const key in data) {
|
|
445
|
+
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const val = data[key];
|
|
449
|
+
if (val == null) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
cookies += `${key}=${val}; `;
|
|
453
|
+
}
|
|
454
|
+
return cookies.trim().replace(/;$/, "");
|
|
455
|
+
}
|
|
456
|
+
__name(createCookieString, "createCookieString");
|
|
457
|
+
|
|
458
|
+
// src/utils/create-request-mock.js
|
|
432
459
|
var SUPPORTED_OPTIONS = [
|
|
433
460
|
"host",
|
|
434
461
|
"method",
|
|
435
462
|
"secure",
|
|
436
463
|
"url",
|
|
464
|
+
"path",
|
|
465
|
+
"query",
|
|
466
|
+
"cookies",
|
|
437
467
|
"headers",
|
|
438
468
|
"body",
|
|
439
469
|
"stream",
|
|
@@ -442,54 +472,106 @@ var SUPPORTED_OPTIONS = [
|
|
|
442
472
|
function createRequestMock(options) {
|
|
443
473
|
if (options !== void 0) {
|
|
444
474
|
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
445
|
-
throw new
|
|
475
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
446
476
|
'Parameter "options" must be an Object, but %v was given.',
|
|
447
477
|
options
|
|
448
478
|
);
|
|
449
479
|
}
|
|
450
480
|
Object.keys(options).forEach((optionName) => {
|
|
451
481
|
if (!SUPPORTED_OPTIONS.includes(optionName)) {
|
|
452
|
-
throw new
|
|
482
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
453
483
|
"Option %v is not supported.",
|
|
454
484
|
optionName
|
|
455
485
|
);
|
|
456
486
|
}
|
|
457
487
|
});
|
|
458
488
|
if (options.host !== void 0 && typeof options.host !== "string") {
|
|
459
|
-
throw new
|
|
489
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
460
490
|
'Option "host" must be a String, but %v was given.',
|
|
461
491
|
options.host
|
|
462
492
|
);
|
|
463
493
|
}
|
|
464
494
|
if (options.method !== void 0 && typeof options.method !== "string") {
|
|
465
|
-
throw new
|
|
495
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
466
496
|
'Option "method" must be a String, but %v was given.',
|
|
467
497
|
options.method
|
|
468
498
|
);
|
|
469
499
|
}
|
|
470
500
|
if (options.secure !== void 0 && typeof options.secure !== "boolean") {
|
|
471
|
-
throw new
|
|
501
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
472
502
|
'Option "secure" must be a Boolean, but %v was given.',
|
|
473
503
|
options.secure
|
|
474
504
|
);
|
|
475
505
|
}
|
|
476
506
|
if (options.url !== void 0) {
|
|
477
507
|
if (typeof options.url !== "string") {
|
|
478
|
-
throw new
|
|
508
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
479
509
|
'Option "url" must be a String, but %v was given.',
|
|
480
510
|
options.url
|
|
481
511
|
);
|
|
482
512
|
}
|
|
483
513
|
if (options.url.indexOf("#") !== -1) {
|
|
484
|
-
throw new
|
|
514
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
485
515
|
'Option "url" must not contain "#", but %v was given.',
|
|
486
516
|
options.url
|
|
487
517
|
);
|
|
488
518
|
}
|
|
489
519
|
}
|
|
520
|
+
if (options.path !== void 0) {
|
|
521
|
+
if (typeof options.path !== "string") {
|
|
522
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
523
|
+
'Option "path" must be a String, but %v was given.',
|
|
524
|
+
options.path
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
if (options.path.indexOf("#") !== -1) {
|
|
528
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
529
|
+
'Option "path" must not contain "#", but %v was given.',
|
|
530
|
+
options.path
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
if (options.path.indexOf("?") !== -1) {
|
|
534
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
535
|
+
'Option "path" must not contain "?", but %v was given.',
|
|
536
|
+
options.path
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
if (!options.path.startsWith("/")) {
|
|
540
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
541
|
+
'Option "path" must start with "/", but %v was given.',
|
|
542
|
+
options.path
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (options.query !== void 0) {
|
|
547
|
+
if (options.query === null || typeof options.query !== "string" && typeof options.query !== "object" || Array.isArray(options.query)) {
|
|
548
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
549
|
+
'Option "query" must be a String or an Object, but %v was given.',
|
|
550
|
+
options.query
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (options.cookies !== void 0) {
|
|
555
|
+
if (!options.cookies || typeof options.cookies !== "object" || Array.isArray(options.cookies)) {
|
|
556
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
557
|
+
'Option "cookies" must be an Object, but %v was given.',
|
|
558
|
+
options.cookies
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
Object.keys(options.cookies).forEach((cookieName) => {
|
|
562
|
+
const cookieValue = options.cookies[cookieName];
|
|
563
|
+
if (cookieValue !== void 0 && typeof cookieValue !== "string") {
|
|
564
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
565
|
+
"Cookie %v must be a String, but %v was given.",
|
|
566
|
+
cookieName,
|
|
567
|
+
cookieValue
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
}
|
|
490
572
|
if (options.headers !== void 0) {
|
|
491
573
|
if (!options.headers || typeof options.headers !== "object" || Array.isArray(options.headers)) {
|
|
492
|
-
throw new
|
|
574
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
493
575
|
'Option "headers" must be an Object, but %v was given.',
|
|
494
576
|
options.headers
|
|
495
577
|
);
|
|
@@ -498,7 +580,7 @@ function createRequestMock(options) {
|
|
|
498
580
|
const headerValue = options.headers[headerName];
|
|
499
581
|
if (headerValue !== void 0) {
|
|
500
582
|
if (typeof headerValue !== "string" && !Array.isArray(headerValue)) {
|
|
501
|
-
throw new
|
|
583
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
502
584
|
"Header %v must be a String or an Array, but %v was given.",
|
|
503
585
|
headerName,
|
|
504
586
|
headerValue
|
|
@@ -507,7 +589,7 @@ function createRequestMock(options) {
|
|
|
507
589
|
if (Array.isArray(headerValue)) {
|
|
508
590
|
headerValue.forEach((headerEl, index) => {
|
|
509
591
|
if (typeof headerEl !== "string") {
|
|
510
|
-
throw new
|
|
592
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
511
593
|
"Element %d of the header %v must be a String, but %v was given.",
|
|
512
594
|
index,
|
|
513
595
|
headerName,
|
|
@@ -520,38 +602,50 @@ function createRequestMock(options) {
|
|
|
520
602
|
});
|
|
521
603
|
}
|
|
522
604
|
if (options.stream !== void 0 && !isReadableStream(options.stream)) {
|
|
523
|
-
throw new
|
|
605
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
524
606
|
'Option "stream" must be a Stream, but %v was given.',
|
|
525
607
|
options.stream
|
|
526
608
|
);
|
|
527
609
|
}
|
|
528
610
|
if (options.encoding !== void 0) {
|
|
529
611
|
if (typeof options.encoding !== "string") {
|
|
530
|
-
throw new
|
|
612
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
531
613
|
'Option "encoding" must be a String, but %v was given.',
|
|
532
614
|
options.encoding
|
|
533
615
|
);
|
|
534
616
|
}
|
|
535
617
|
if (!CHARACTER_ENCODING_LIST.includes(options.encoding)) {
|
|
536
|
-
throw new
|
|
618
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
537
619
|
"Character encoding %v is not supported.",
|
|
538
620
|
options.encoding
|
|
539
621
|
);
|
|
540
622
|
}
|
|
541
623
|
}
|
|
542
|
-
if (options.
|
|
624
|
+
if (options.url !== void 0) {
|
|
625
|
+
if (options.path !== void 0) {
|
|
626
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
627
|
+
'The "url" and "path" options cannot be used together.'
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
if (options.query !== void 0) {
|
|
631
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
632
|
+
'The "url" and "query" options cannot be used together.'
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (options.stream !== void 0) {
|
|
543
637
|
if (options.secure !== void 0) {
|
|
544
|
-
throw new
|
|
638
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
545
639
|
'The "stream" and "secure" options cannot be used together.'
|
|
546
640
|
);
|
|
547
641
|
}
|
|
548
642
|
if (options.body !== void 0) {
|
|
549
|
-
throw new
|
|
643
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
550
644
|
'The "stream" and "body" options cannot be used together.'
|
|
551
645
|
);
|
|
552
646
|
}
|
|
553
647
|
if (options.encoding !== void 0) {
|
|
554
|
-
throw new
|
|
648
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
555
649
|
'The "stream" and "encoding" options cannot be used together.'
|
|
556
650
|
);
|
|
557
651
|
}
|
|
@@ -575,11 +669,17 @@ function createRequestMock(options) {
|
|
|
575
669
|
Object.defineProperty(request.socket, "remoteAddress", { value: "127.0.0.1" });
|
|
576
670
|
Object.defineProperty(request.socket, "localAddress", { value: "127.0.0.1" });
|
|
577
671
|
request.httpVersion = "1.1";
|
|
578
|
-
request.url =
|
|
672
|
+
request.url = "/";
|
|
673
|
+
if (options.url !== void 0) {
|
|
674
|
+
request.url = options.url;
|
|
675
|
+
} else if (options.path !== void 0 || options.query !== void 0) {
|
|
676
|
+
request.url = createRequestUrl(options.path, options.query);
|
|
677
|
+
}
|
|
579
678
|
request.headers = createRequestHeaders(
|
|
580
679
|
options.host,
|
|
581
680
|
options.secure,
|
|
582
681
|
options.body,
|
|
682
|
+
options.cookies,
|
|
583
683
|
options.encoding,
|
|
584
684
|
options.headers
|
|
585
685
|
);
|
|
@@ -589,7 +689,7 @@ function createRequestMock(options) {
|
|
|
589
689
|
__name(createRequestMock, "createRequestMock");
|
|
590
690
|
function createRequestStream(secure, body, encoding) {
|
|
591
691
|
if (encoding !== void 0 && typeof encoding !== "string") {
|
|
592
|
-
throw new
|
|
692
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
593
693
|
'Parameter "encoding" must be a String, but %v was given.',
|
|
594
694
|
encoding
|
|
595
695
|
);
|
|
@@ -613,30 +713,67 @@ function createRequestStream(secure, body, encoding) {
|
|
|
613
713
|
return request;
|
|
614
714
|
}
|
|
615
715
|
__name(createRequestStream, "createRequestStream");
|
|
616
|
-
function
|
|
716
|
+
function createRequestUrl(path, query) {
|
|
717
|
+
if (path !== void 0 && typeof path !== "string") {
|
|
718
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
719
|
+
'Parameter "path" must be a String, but %v was given.',
|
|
720
|
+
path
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
if (query !== void 0) {
|
|
724
|
+
if (query === null || typeof query !== "string" && typeof query !== "object" || Array.isArray(query)) {
|
|
725
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
726
|
+
'Parameter "query" must be a String or an Object, but %v was given.',
|
|
727
|
+
query
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
let res = path !== void 0 ? path : "/";
|
|
732
|
+
if (typeof query === "object") {
|
|
733
|
+
const qs = import_querystring.default.stringify(query);
|
|
734
|
+
if (qs) {
|
|
735
|
+
res += `?${qs}`;
|
|
736
|
+
}
|
|
737
|
+
} else if (typeof query === "string" && query !== "" && query !== "?") {
|
|
738
|
+
res += `?${query.replace(/^\?/, "")}`;
|
|
739
|
+
}
|
|
740
|
+
return res;
|
|
741
|
+
}
|
|
742
|
+
__name(createRequestUrl, "createRequestUrl");
|
|
743
|
+
function createRequestHeaders(host, secure, body, cookies, encoding, headers) {
|
|
617
744
|
if (host !== void 0 && typeof host !== "string") {
|
|
618
|
-
throw new
|
|
745
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
619
746
|
'Parameter "host" must be a non-empty String, but %v was given.',
|
|
620
747
|
host
|
|
621
748
|
);
|
|
622
749
|
}
|
|
623
750
|
host = host || "localhost";
|
|
624
751
|
if (secure !== void 0 && typeof secure !== "boolean") {
|
|
625
|
-
throw new
|
|
752
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
626
753
|
'Parameter "secure" must be a Boolean, but %v was given.',
|
|
627
754
|
secure
|
|
628
755
|
);
|
|
629
756
|
}
|
|
630
757
|
secure = Boolean(secure);
|
|
631
|
-
if (
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
758
|
+
if (cookies !== void 0) {
|
|
759
|
+
if (!cookies || typeof cookies !== "object" || Array.isArray(cookies)) {
|
|
760
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
761
|
+
'Parameter "cookies" must be an Object, but %v was given.',
|
|
762
|
+
cookies
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (headers !== void 0) {
|
|
767
|
+
if (!headers || typeof headers !== "object" || Array.isArray(headers)) {
|
|
768
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
769
|
+
'Parameter "headers" must be an Object, but %v was given.',
|
|
770
|
+
headers
|
|
771
|
+
);
|
|
772
|
+
}
|
|
636
773
|
}
|
|
637
774
|
headers = headers || {};
|
|
638
775
|
if (encoding !== void 0 && typeof encoding !== "string") {
|
|
639
|
-
throw new
|
|
776
|
+
throw new import_js_format9.InvalidArgumentError(
|
|
640
777
|
'Parameter "encoding" must be a String, but %v was given.',
|
|
641
778
|
encoding
|
|
642
779
|
);
|
|
@@ -652,6 +789,14 @@ function createRequestHeaders(host, secure, body, encoding, headers) {
|
|
|
652
789
|
if (secure) {
|
|
653
790
|
res["x-forwarded-proto"] = "https";
|
|
654
791
|
}
|
|
792
|
+
if (typeof cookies === "object" && Object.keys(cookies).length) {
|
|
793
|
+
if (res["cookie"]) {
|
|
794
|
+
const existedCookies = parseCookieString(res["cookie"]);
|
|
795
|
+
res["cookie"] = createCookieString({ ...existedCookies, ...cookies });
|
|
796
|
+
} else {
|
|
797
|
+
res["cookie"] = createCookieString(cookies);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
655
800
|
if (body != null && !("content-type" in res)) {
|
|
656
801
|
if (typeof body === "string") {
|
|
657
802
|
res["content-type"] = "text/plain";
|
|
@@ -785,12 +930,12 @@ function patchBody(response) {
|
|
|
785
930
|
__name(patchBody, "patchBody");
|
|
786
931
|
|
|
787
932
|
// src/utils/get-request-pathname.js
|
|
788
|
-
var
|
|
933
|
+
var import_js_format10 = require("@e22m4u/js-format");
|
|
789
934
|
var HOST_RE2 = /^https?:\/\/[^/]+/;
|
|
790
935
|
var QUERY_STRING_RE = /\?.*$/;
|
|
791
936
|
function getRequestPathname(request) {
|
|
792
937
|
if (!request || typeof request !== "object" || Array.isArray(request) || typeof request.url !== "string") {
|
|
793
|
-
throw new
|
|
938
|
+
throw new import_js_format10.InvalidArgumentError(
|
|
794
939
|
'Parameter "request" must be an instance of IncomingMessage, but %v was given.',
|
|
795
940
|
request
|
|
796
941
|
);
|
|
@@ -802,30 +947,6 @@ function getRequestPathname(request) {
|
|
|
802
947
|
}
|
|
803
948
|
__name(getRequestPathname, "getRequestPathname");
|
|
804
949
|
|
|
805
|
-
// src/utils/create-cookie-string.js
|
|
806
|
-
var import_js_format10 = require("@e22m4u/js-format");
|
|
807
|
-
function createCookieString(data) {
|
|
808
|
-
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
809
|
-
throw new import_js_format10.InvalidArgumentError(
|
|
810
|
-
"Cookie data must be an Object, but %v was given.",
|
|
811
|
-
data
|
|
812
|
-
);
|
|
813
|
-
}
|
|
814
|
-
let cookies = "";
|
|
815
|
-
for (const key in data) {
|
|
816
|
-
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
|
817
|
-
continue;
|
|
818
|
-
}
|
|
819
|
-
const val = data[key];
|
|
820
|
-
if (val == null) {
|
|
821
|
-
continue;
|
|
822
|
-
}
|
|
823
|
-
cookies += `${key}=${val}; `;
|
|
824
|
-
}
|
|
825
|
-
return cookies.trim();
|
|
826
|
-
}
|
|
827
|
-
__name(createCookieString, "createCookieString");
|
|
828
|
-
|
|
829
950
|
// src/request-context.js
|
|
830
951
|
var import_js_format11 = require("@e22m4u/js-format");
|
|
831
952
|
var import_js_service2 = require("@e22m4u/js-service");
|
|
@@ -2227,7 +2348,7 @@ function parseJsonBody(input) {
|
|
|
2227
2348
|
__name(parseJsonBody, "parseJsonBody");
|
|
2228
2349
|
|
|
2229
2350
|
// src/parsers/request-query-parser.js
|
|
2230
|
-
var
|
|
2351
|
+
var import_querystring2 = __toESM(require("querystring"), 1);
|
|
2231
2352
|
var _RequestQueryParser = class _RequestQueryParser extends DebuggableService {
|
|
2232
2353
|
/**
|
|
2233
2354
|
* Parse
|
|
@@ -2238,7 +2359,7 @@ var _RequestQueryParser = class _RequestQueryParser extends DebuggableService {
|
|
|
2238
2359
|
parse(request) {
|
|
2239
2360
|
const debug = this.getDebuggerFor(this.parse);
|
|
2240
2361
|
const queryStr = request.url.replace(/^[^?]*\??/, "");
|
|
2241
|
-
const query = queryStr ?
|
|
2362
|
+
const query = queryStr ? import_querystring2.default.parse(queryStr) : {};
|
|
2242
2363
|
const queryKeys = Object.keys(query);
|
|
2243
2364
|
if (queryKeys.length) {
|
|
2244
2365
|
queryKeys.forEach((key) => {
|
package/package.json
CHANGED
|
@@ -78,7 +78,7 @@ describe('RequestParser', function () {
|
|
|
78
78
|
|
|
79
79
|
it('should return the result object with parsed cookies', function () {
|
|
80
80
|
const S = new RequestParser();
|
|
81
|
-
const req = createRequestMock({headers: {cookie: 'p1=foo; p2=bar
|
|
81
|
+
const req = createRequestMock({headers: {cookie: 'p1=foo; p2=bar'}});
|
|
82
82
|
const res = S.parse(req);
|
|
83
83
|
expect(res).to.be.eql({
|
|
84
84
|
query: {},
|
|
@@ -86,7 +86,7 @@ describe('RequestParser', function () {
|
|
|
86
86
|
body: undefined,
|
|
87
87
|
headers: {
|
|
88
88
|
host: 'localhost',
|
|
89
|
-
cookie: 'p1=foo; p2=bar
|
|
89
|
+
cookie: 'p1=foo; p2=bar',
|
|
90
90
|
},
|
|
91
91
|
});
|
|
92
92
|
});
|
|
@@ -12,13 +12,13 @@ describe('RequestQueryParser', function () {
|
|
|
12
12
|
|
|
13
13
|
it('should return an empty object when the url does not have a query string', function () {
|
|
14
14
|
const parser = new RequestQueryParser();
|
|
15
|
-
const result = parser.parse({url:
|
|
15
|
+
const result = parser.parse({url: '/test'});
|
|
16
16
|
expect(result).to.be.eql({});
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
it('should return an empty object when the url has an empty query string', function () {
|
|
20
20
|
const parser = new RequestQueryParser();
|
|
21
|
-
const result = parser.parse({url:
|
|
21
|
+
const result = parser.parse({url: '/test?'});
|
|
22
22
|
expect(result).to.be.eql({});
|
|
23
23
|
});
|
|
24
24
|
});
|
|
@@ -139,6 +139,16 @@ describe('RequestContext', function () {
|
|
|
139
139
|
expect(req.url).to.be.eq('/pathname?foo=bar');
|
|
140
140
|
expect(ctx.path).to.be.eq('/pathname?foo=bar');
|
|
141
141
|
});
|
|
142
|
+
|
|
143
|
+
it('should ignore a protocol and host in the request url', function () {
|
|
144
|
+
const req = createRequestMock({url: 'http://example.com:80/pathname'});
|
|
145
|
+
const res = createResponseMock();
|
|
146
|
+
const route = createRouteMock();
|
|
147
|
+
const cont = new ServiceContainer();
|
|
148
|
+
const ctx = new RequestContext(cont, req, res, route);
|
|
149
|
+
expect(req.url).to.be.eq('http://example.com:80/pathname');
|
|
150
|
+
expect(ctx.path).to.be.eq('/pathname');
|
|
151
|
+
});
|
|
142
152
|
});
|
|
143
153
|
|
|
144
154
|
describe('pathname', function () {
|
|
@@ -142,8 +142,8 @@ describe('RouteRegistry', function () {
|
|
|
142
142
|
handler,
|
|
143
143
|
});
|
|
144
144
|
const res = S.matchRouteByRequest({
|
|
145
|
-
url: '/foo/baz/bar/qux',
|
|
146
145
|
method: HttpMethod.GET,
|
|
146
|
+
url: '/foo/baz/bar/qux',
|
|
147
147
|
});
|
|
148
148
|
expect(typeof res).to.be.eq('object');
|
|
149
149
|
expect(res.route).to.be.instanceof(Route);
|
package/src/trie-router.spec.js
CHANGED
|
@@ -88,7 +88,7 @@ describe('TrieRouter', function () {
|
|
|
88
88
|
done();
|
|
89
89
|
},
|
|
90
90
|
});
|
|
91
|
-
const req = createRequestMock({
|
|
91
|
+
const req = createRequestMock({path: '/test'});
|
|
92
92
|
const res = createResponseMock();
|
|
93
93
|
router.handleRequest(req, res);
|
|
94
94
|
});
|
|
@@ -103,7 +103,7 @@ describe('TrieRouter', function () {
|
|
|
103
103
|
done();
|
|
104
104
|
},
|
|
105
105
|
});
|
|
106
|
-
const req = createRequestMock({
|
|
106
|
+
const req = createRequestMock({path: '/foo-bar'});
|
|
107
107
|
const res = createResponseMock();
|
|
108
108
|
router.handleRequest(req, res);
|
|
109
109
|
});
|
|
@@ -118,7 +118,7 @@ describe('TrieRouter', function () {
|
|
|
118
118
|
done();
|
|
119
119
|
},
|
|
120
120
|
});
|
|
121
|
-
const req = createRequestMock({
|
|
121
|
+
const req = createRequestMock({query: {p1: 'foo', p2: 'bar'}});
|
|
122
122
|
const res = createResponseMock();
|
|
123
123
|
router.handleRequest(req, res);
|
|
124
124
|
});
|
|
@@ -385,7 +385,7 @@ describe('TrieRouter', function () {
|
|
|
385
385
|
router.addHook(RouterHookType.ON_REQUEST, () => {
|
|
386
386
|
hookCalled = true;
|
|
387
387
|
});
|
|
388
|
-
const req = createRequestMock({
|
|
388
|
+
const req = createRequestMock({path: '/does-not-exist'});
|
|
389
389
|
const res = createResponseMock();
|
|
390
390
|
await router.handleRequest(req, res);
|
|
391
391
|
expect(hookCalled).to.be.true;
|
|
@@ -1641,7 +1641,7 @@ describe('TrieRouter', function () {
|
|
|
1641
1641
|
});
|
|
1642
1642
|
const req = createRequestMock({
|
|
1643
1643
|
method: HttpMethod.OPTIONS,
|
|
1644
|
-
|
|
1644
|
+
path: '/api/resource',
|
|
1645
1645
|
});
|
|
1646
1646
|
const res = createResponseMock();
|
|
1647
1647
|
await router.handleRequest(req, res);
|
|
@@ -1662,7 +1662,7 @@ describe('TrieRouter', function () {
|
|
|
1662
1662
|
});
|
|
1663
1663
|
const req = createRequestMock({
|
|
1664
1664
|
method: HttpMethod.OPTIONS,
|
|
1665
|
-
|
|
1665
|
+
path: '/api/resource',
|
|
1666
1666
|
});
|
|
1667
1667
|
const res = createResponseMock();
|
|
1668
1668
|
await router.handleRequest(req, res);
|
|
@@ -1676,7 +1676,7 @@ describe('TrieRouter', function () {
|
|
|
1676
1676
|
const router = new TrieRouter();
|
|
1677
1677
|
const req = createRequestMock({
|
|
1678
1678
|
method: HttpMethod.OPTIONS,
|
|
1679
|
-
|
|
1679
|
+
path: '/unknown',
|
|
1680
1680
|
});
|
|
1681
1681
|
const res = createResponseMock();
|
|
1682
1682
|
await router.handleRequest(req, res);
|
|
@@ -27,6 +27,6 @@ describe('createCookieString', function () {
|
|
|
27
27
|
it('should return the cookie string for the given object', function () {
|
|
28
28
|
const data = {foo: 'bar', baz: 'quz'};
|
|
29
29
|
const result = createCookieString(data);
|
|
30
|
-
expect(result).to.be.eq('foo=bar; baz=quz
|
|
30
|
+
expect(result).to.be.eq('foo=bar; baz=quz');
|
|
31
31
|
});
|
|
32
32
|
});
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import {Socket} from 'net';
|
|
2
2
|
import {TLSSocket} from 'tls';
|
|
3
|
+
import queryString from 'querystring';
|
|
3
4
|
import {IncomingMessage} from 'http';
|
|
4
5
|
import {InvalidArgumentError} from '@e22m4u/js-format';
|
|
5
6
|
import {isReadableStream} from './is-readable-stream.js';
|
|
7
|
+
import {parseCookieString} from './parse-cookie-string.js';
|
|
8
|
+
import {createCookieString} from './create-cookie-string.js';
|
|
6
9
|
import {CHARACTER_ENCODING_LIST} from './fetch-request-body.js';
|
|
7
10
|
|
|
8
11
|
/**
|
|
@@ -13,6 +16,9 @@ const SUPPORTED_OPTIONS = [
|
|
|
13
16
|
'method',
|
|
14
17
|
'secure',
|
|
15
18
|
'url',
|
|
19
|
+
'path',
|
|
20
|
+
'query',
|
|
21
|
+
'cookies',
|
|
16
22
|
'headers',
|
|
17
23
|
'body',
|
|
18
24
|
'stream',
|
|
@@ -77,6 +83,74 @@ export function createRequestMock(options) {
|
|
|
77
83
|
);
|
|
78
84
|
}
|
|
79
85
|
}
|
|
86
|
+
// options.path
|
|
87
|
+
if (options.path !== undefined) {
|
|
88
|
+
if (typeof options.path !== 'string') {
|
|
89
|
+
throw new InvalidArgumentError(
|
|
90
|
+
'Option "path" must be a String, but %v was given.',
|
|
91
|
+
options.path,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
// contain #
|
|
95
|
+
if (options.path.indexOf('#') !== -1) {
|
|
96
|
+
throw new InvalidArgumentError(
|
|
97
|
+
'Option "path" must not contain "#", but %v was given.',
|
|
98
|
+
options.path,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
// contain ?
|
|
102
|
+
if (options.path.indexOf('?') !== -1) {
|
|
103
|
+
throw new InvalidArgumentError(
|
|
104
|
+
'Option "path" must not contain "?", but %v was given.',
|
|
105
|
+
options.path,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
// not starting with /
|
|
109
|
+
if (!options.path.startsWith('/')) {
|
|
110
|
+
throw new InvalidArgumentError(
|
|
111
|
+
'Option "path" must start with "/", but %v was given.',
|
|
112
|
+
options.path,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// options.query
|
|
117
|
+
if (options.query !== undefined) {
|
|
118
|
+
if (
|
|
119
|
+
options.query === null ||
|
|
120
|
+
(typeof options.query !== 'string' &&
|
|
121
|
+
typeof options.query !== 'object') ||
|
|
122
|
+
Array.isArray(options.query)
|
|
123
|
+
) {
|
|
124
|
+
throw new InvalidArgumentError(
|
|
125
|
+
'Option "query" must be a String or an Object, but %v was given.',
|
|
126
|
+
options.query,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// options.cookies
|
|
131
|
+
if (options.cookies !== undefined) {
|
|
132
|
+
if (
|
|
133
|
+
!options.cookies ||
|
|
134
|
+
typeof options.cookies !== 'object' ||
|
|
135
|
+
Array.isArray(options.cookies)
|
|
136
|
+
) {
|
|
137
|
+
throw new InvalidArgumentError(
|
|
138
|
+
'Option "cookies" must be an Object, but %v was given.',
|
|
139
|
+
options.cookies,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
// options.cookies[k]
|
|
143
|
+
Object.keys(options.cookies).forEach(cookieName => {
|
|
144
|
+
const cookieValue = options.cookies[cookieName];
|
|
145
|
+
if (cookieValue !== undefined && typeof cookieValue !== 'string') {
|
|
146
|
+
throw new InvalidArgumentError(
|
|
147
|
+
'Cookie %v must be a String, but %v was given.',
|
|
148
|
+
cookieName,
|
|
149
|
+
cookieValue,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
80
154
|
// options.headers
|
|
81
155
|
if (options.headers !== undefined) {
|
|
82
156
|
if (
|
|
@@ -139,9 +213,23 @@ export function createRequestMock(options) {
|
|
|
139
213
|
);
|
|
140
214
|
}
|
|
141
215
|
}
|
|
216
|
+
// если определен url, выполняется
|
|
217
|
+
// проверка на несовместимые опции
|
|
218
|
+
if (options.url !== undefined) {
|
|
219
|
+
if (options.path !== undefined) {
|
|
220
|
+
throw new InvalidArgumentError(
|
|
221
|
+
'The "url" and "path" options cannot be used together.',
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (options.query !== undefined) {
|
|
225
|
+
throw new InvalidArgumentError(
|
|
226
|
+
'The "url" and "query" options cannot be used together.',
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
142
230
|
// если передан поток, выполняется
|
|
143
231
|
// проверка на несовместимые опции
|
|
144
|
-
if (options.stream) {
|
|
232
|
+
if (options.stream !== undefined) {
|
|
145
233
|
if (options.secure !== undefined) {
|
|
146
234
|
throw new InvalidArgumentError(
|
|
147
235
|
'The "stream" and "secure" options cannot be used together.',
|
|
@@ -184,11 +272,17 @@ export function createRequestMock(options) {
|
|
|
184
272
|
// определение остальных свойств
|
|
185
273
|
// экземпляра IncomingMessage
|
|
186
274
|
request.httpVersion = '1.1';
|
|
187
|
-
request.url =
|
|
275
|
+
request.url = '/';
|
|
276
|
+
if (options.url !== undefined) {
|
|
277
|
+
request.url = options.url;
|
|
278
|
+
} else if (options.path !== undefined || options.query !== undefined) {
|
|
279
|
+
request.url = createRequestUrl(options.path, options.query);
|
|
280
|
+
}
|
|
188
281
|
request.headers = createRequestHeaders(
|
|
189
282
|
options.host,
|
|
190
283
|
options.secure,
|
|
191
284
|
options.body,
|
|
285
|
+
options.cookies,
|
|
192
286
|
options.encoding,
|
|
193
287
|
options.headers,
|
|
194
288
|
);
|
|
@@ -238,17 +332,56 @@ function createRequestStream(secure, body, encoding) {
|
|
|
238
332
|
return request;
|
|
239
333
|
}
|
|
240
334
|
|
|
335
|
+
/**
|
|
336
|
+
* Create request url.
|
|
337
|
+
*
|
|
338
|
+
* @param {string|undefined} path
|
|
339
|
+
* @param {string|object|undefined} query
|
|
340
|
+
* @returns {string}
|
|
341
|
+
*/
|
|
342
|
+
function createRequestUrl(path, query) {
|
|
343
|
+
if (path !== undefined && typeof path !== 'string') {
|
|
344
|
+
throw new InvalidArgumentError(
|
|
345
|
+
'Parameter "path" must be a String, but %v was given.',
|
|
346
|
+
path,
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (query !== undefined) {
|
|
350
|
+
if (
|
|
351
|
+
query === null ||
|
|
352
|
+
(typeof query !== 'string' && typeof query !== 'object') ||
|
|
353
|
+
Array.isArray(query)
|
|
354
|
+
) {
|
|
355
|
+
throw new InvalidArgumentError(
|
|
356
|
+
'Parameter "query" must be a String or an Object, but %v was given.',
|
|
357
|
+
query,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
let res = path !== undefined ? path : '/';
|
|
362
|
+
if (typeof query === 'object') {
|
|
363
|
+
const qs = queryString.stringify(query);
|
|
364
|
+
if (qs) {
|
|
365
|
+
res += `?${qs}`;
|
|
366
|
+
}
|
|
367
|
+
} else if (typeof query === 'string' && query !== '' && query !== '?') {
|
|
368
|
+
res += `?${query.replace(/^\?/, '')}`;
|
|
369
|
+
}
|
|
370
|
+
return res;
|
|
371
|
+
}
|
|
372
|
+
|
|
241
373
|
/**
|
|
242
374
|
* Create request headers.
|
|
243
375
|
*
|
|
244
376
|
* @param {string|undefined} host
|
|
245
377
|
* @param {boolean|undefined} secure
|
|
246
378
|
* @param {*} body
|
|
379
|
+
* @param {object|undefined} cookies
|
|
247
380
|
* @param {string|undefined} encoding
|
|
248
381
|
* @param {object|undefined} headers
|
|
249
382
|
* @returns {object}
|
|
250
383
|
*/
|
|
251
|
-
function createRequestHeaders(host, secure, body, encoding, headers) {
|
|
384
|
+
function createRequestHeaders(host, secure, body, cookies, encoding, headers) {
|
|
252
385
|
if (host !== undefined && typeof host !== 'string') {
|
|
253
386
|
throw new InvalidArgumentError(
|
|
254
387
|
'Parameter "host" must be a non-empty String, but %v was given.',
|
|
@@ -263,14 +396,21 @@ function createRequestHeaders(host, secure, body, encoding, headers) {
|
|
|
263
396
|
);
|
|
264
397
|
}
|
|
265
398
|
secure = Boolean(secure);
|
|
266
|
-
if (
|
|
267
|
-
(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
399
|
+
if (cookies !== undefined) {
|
|
400
|
+
if (!cookies || typeof cookies !== 'object' || Array.isArray(cookies)) {
|
|
401
|
+
throw new InvalidArgumentError(
|
|
402
|
+
'Parameter "cookies" must be an Object, but %v was given.',
|
|
403
|
+
cookies,
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (headers !== undefined) {
|
|
408
|
+
if (!headers || typeof headers !== 'object' || Array.isArray(headers)) {
|
|
409
|
+
throw new InvalidArgumentError(
|
|
410
|
+
'Parameter "headers" must be an Object, but %v was given.',
|
|
411
|
+
headers,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
274
414
|
}
|
|
275
415
|
headers = headers || {};
|
|
276
416
|
if (encoding !== undefined && typeof encoding !== 'string') {
|
|
@@ -290,6 +430,17 @@ function createRequestHeaders(host, secure, body, encoding, headers) {
|
|
|
290
430
|
if (secure) {
|
|
291
431
|
res['x-forwarded-proto'] = 'https';
|
|
292
432
|
}
|
|
433
|
+
// формирование заголовка Cookie используя
|
|
434
|
+
// существующие данные заголовка и объекта,
|
|
435
|
+
// переданного в параметр данной функции
|
|
436
|
+
if (typeof cookies === 'object' && Object.keys(cookies).length) {
|
|
437
|
+
if (res['cookie']) {
|
|
438
|
+
const existedCookies = parseCookieString(res['cookie']);
|
|
439
|
+
res['cookie'] = createCookieString({...existedCookies, ...cookies});
|
|
440
|
+
} else {
|
|
441
|
+
res['cookie'] = createCookieString(cookies);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
293
444
|
// установка заголовка "content-type"
|
|
294
445
|
// в зависимости от тела запроса
|
|
295
446
|
if (body != null && !('content-type' in res)) {
|
|
@@ -88,19 +88,121 @@ describe('createRequestMock', function () {
|
|
|
88
88
|
throwable(undefined)();
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
it('should require the option "url" to not contain "#"
|
|
91
|
+
it('should require the option "url" to not contain "#"', function () {
|
|
92
92
|
const throwable = v => () => createRequestMock({url: v});
|
|
93
|
-
const
|
|
93
|
+
const mustThrowWith = v => {
|
|
94
94
|
expect(throwable(v)).to.throw(
|
|
95
95
|
format('Option "url" must not contain "#", but %v was given.', v),
|
|
96
96
|
);
|
|
97
97
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
mustThrowWith('#');
|
|
99
|
+
mustThrowWith('pathname#');
|
|
100
|
+
mustThrowWith('/pathname#');
|
|
101
|
+
mustThrowWith('http://example.com/#');
|
|
102
|
+
mustThrowWith('http://example.com/pathname#');
|
|
103
|
+
mustThrowWith('http://example.com/pathname?foo=bar#');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should require the option "path" to be a String', function () {
|
|
107
|
+
const throwable = v => () => createRequestMock({path: v});
|
|
108
|
+
const error = v =>
|
|
109
|
+
format('Option "path" must be a String, but %s was given.', v);
|
|
110
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
111
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
112
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
113
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
114
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
115
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
116
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
117
|
+
throwable('/path')();
|
|
118
|
+
throwable('/')();
|
|
119
|
+
throwable(undefined)();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should require the option "path" to not contain "#"', function () {
|
|
123
|
+
const throwable = v => () => createRequestMock({path: v});
|
|
124
|
+
const mustThrowWith = v => {
|
|
125
|
+
expect(throwable(v)).to.throw(
|
|
126
|
+
format('Option "path" must not contain "#", but %v was given.', v),
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
mustThrowWith('/#');
|
|
130
|
+
mustThrowWith('/path#');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should require the option "path" to not contain "?"', function () {
|
|
134
|
+
const throwable = v => () => createRequestMock({path: v});
|
|
135
|
+
const mustThrowWith = v => {
|
|
136
|
+
expect(throwable(v)).to.throw(
|
|
137
|
+
format('Option "path" must not contain "?", but %v was given.', v),
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
mustThrowWith('/?');
|
|
141
|
+
mustThrowWith('/path?');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should require the option "path" to start with "/"', function () {
|
|
145
|
+
const throwable = v => () => createRequestMock({path: v});
|
|
146
|
+
const mustThrowWith = v => {
|
|
147
|
+
expect(throwable(v)).to.throw(
|
|
148
|
+
format('Option "path" must start with "/", but %v was given.', v),
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
mustThrowWith('path');
|
|
152
|
+
mustThrowWith('');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should require the option "query" to be a String or an Object', function () {
|
|
156
|
+
const throwable = v => () => createRequestMock({query: v});
|
|
157
|
+
const error = v =>
|
|
158
|
+
format(
|
|
159
|
+
'Option "query" must be a String or an Object, but %s was given.',
|
|
160
|
+
v,
|
|
161
|
+
);
|
|
162
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
163
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
164
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
165
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
166
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
167
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
168
|
+
throwable({foo: 'bar'})();
|
|
169
|
+
throwable({})();
|
|
170
|
+
throwable('foo=bar')();
|
|
171
|
+
throwable('')();
|
|
172
|
+
throwable(undefined)();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should require the option "cookies" to be an Object', function () {
|
|
176
|
+
const throwable = v => () => createRequestMock({cookies: v});
|
|
177
|
+
const error = v =>
|
|
178
|
+
format('Option "cookies" must be an Object, but %s was given.', v);
|
|
179
|
+
expect(throwable('str')).to.throw(error('"str"'));
|
|
180
|
+
expect(throwable('')).to.throw(error('""'));
|
|
181
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
182
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
183
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
184
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
185
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
186
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
187
|
+
throwable({foo: 'bar'})();
|
|
188
|
+
throwable({})();
|
|
189
|
+
throwable(undefined)();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should require values in the option "cookies" to be a String', function () {
|
|
193
|
+
const throwable = v => () => createRequestMock({cookies: {test: v}});
|
|
194
|
+
const error = v =>
|
|
195
|
+
format('Cookie "test" must be a String, but %s was given.', v);
|
|
196
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
197
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
198
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
199
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
200
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
201
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
202
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
203
|
+
throwable('str')();
|
|
204
|
+
throwable('')();
|
|
205
|
+
throwable(undefined)();
|
|
104
206
|
});
|
|
105
207
|
|
|
106
208
|
it('should require the option "headers" to be an Object', function () {
|
|
@@ -120,7 +222,7 @@ describe('createRequestMock', function () {
|
|
|
120
222
|
throwable(undefined)();
|
|
121
223
|
});
|
|
122
224
|
|
|
123
|
-
it('should require values
|
|
225
|
+
it('should require values in the option "headers" to be a String or an Array', function () {
|
|
124
226
|
const throwable = v => () => createRequestMock({headers: {Test: v}});
|
|
125
227
|
const error = v =>
|
|
126
228
|
format(
|
|
@@ -196,6 +298,18 @@ describe('createRequestMock', function () {
|
|
|
196
298
|
expect(throwable).to.throw('Option "unknownOption" is not supported.');
|
|
197
299
|
});
|
|
198
300
|
|
|
301
|
+
it('should not allow using the "url" and "path" options together', function () {
|
|
302
|
+
const throwable = () => createRequestMock({url: 'url', path: '/path'});
|
|
303
|
+
const error = 'The "url" and "path" options cannot be used together.';
|
|
304
|
+
expect(throwable).to.throw(error);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should not allow using the "url" and "query" options together', function () {
|
|
308
|
+
const throwable = () => createRequestMock({url: 'url', query: {p: 1}});
|
|
309
|
+
const error = 'The "url" and "query" options cannot be used together.';
|
|
310
|
+
expect(throwable).to.throw(error);
|
|
311
|
+
});
|
|
312
|
+
|
|
199
313
|
it('should require the option "encoding" to be a correct value', function () {
|
|
200
314
|
const throwable = v => () => createRequestMock({encoding: v});
|
|
201
315
|
const error = v => format('Character encoding %s is not supported.', v);
|
|
@@ -232,11 +346,6 @@ describe('createRequestMock', function () {
|
|
|
232
346
|
throwable(undefined)();
|
|
233
347
|
});
|
|
234
348
|
|
|
235
|
-
it('should use "localhost" as the default host', function () {
|
|
236
|
-
const req = createRequestMock();
|
|
237
|
-
expect(req.headers['host']).to.be.eq('localhost');
|
|
238
|
-
});
|
|
239
|
-
|
|
240
349
|
it('should use "GET" as the default method', function () {
|
|
241
350
|
const req = createRequestMock();
|
|
242
351
|
expect(req.method).to.be.eq('GET');
|
|
@@ -247,17 +356,17 @@ describe('createRequestMock', function () {
|
|
|
247
356
|
expect(req.socket).to.be.instanceof(Socket);
|
|
248
357
|
});
|
|
249
358
|
|
|
250
|
-
it('should use the default
|
|
359
|
+
it('should use "/" as the default value of the request url', function () {
|
|
251
360
|
const req = createRequestMock();
|
|
252
361
|
expect(req.url).to.be.eq('/');
|
|
253
362
|
});
|
|
254
363
|
|
|
255
|
-
it('should use "localhost" as the default value
|
|
364
|
+
it('should use "localhost" as the default value of the "host" header', function () {
|
|
256
365
|
const req = createRequestMock();
|
|
257
366
|
expect(req.headers).to.be.eql({host: 'localhost'});
|
|
258
367
|
});
|
|
259
368
|
|
|
260
|
-
it('should use "utf-8"
|
|
369
|
+
it('should use "utf-8" as the default value of the data encoding', async function () {
|
|
261
370
|
const body = 'test';
|
|
262
371
|
const req = createRequestMock({body: Buffer.from(body)});
|
|
263
372
|
const chunks = [];
|
|
@@ -327,7 +436,43 @@ describe('createRequestMock', function () {
|
|
|
327
436
|
expect(data).to.be.eq(body);
|
|
328
437
|
});
|
|
329
438
|
|
|
330
|
-
it('should
|
|
439
|
+
it('should pass a number from the "body" option to the stream as a string', async function () {
|
|
440
|
+
const body = 10;
|
|
441
|
+
const req = createRequestMock({body});
|
|
442
|
+
const chunks = [];
|
|
443
|
+
const data = await new Promise((resolve, reject) => {
|
|
444
|
+
req.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
|
445
|
+
req.on('error', err => reject(err));
|
|
446
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
447
|
+
});
|
|
448
|
+
expect(data).to.be.eq('10');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should pass a boolean from the "body" option to the stream as a string', async function () {
|
|
452
|
+
const body = true;
|
|
453
|
+
const req = createRequestMock({body});
|
|
454
|
+
const chunks = [];
|
|
455
|
+
const data = await new Promise((resolve, reject) => {
|
|
456
|
+
req.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
|
457
|
+
req.on('error', err => reject(err));
|
|
458
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
459
|
+
});
|
|
460
|
+
expect(data).to.be.eq('true');
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('should pass an array from the "body" option to the stream as JSON', async function () {
|
|
464
|
+
const body = [1, 2];
|
|
465
|
+
const req = createRequestMock({body});
|
|
466
|
+
const chunks = [];
|
|
467
|
+
const data = await new Promise((resolve, reject) => {
|
|
468
|
+
req.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
|
469
|
+
req.on('error', err => reject(err));
|
|
470
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
471
|
+
});
|
|
472
|
+
expect(data).to.be.eq(JSON.stringify(body));
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should pass an object from the "body" option to the stream as JSON', async function () {
|
|
331
476
|
const body = {foo: 'bar'};
|
|
332
477
|
const req = createRequestMock({body});
|
|
333
478
|
const chunks = [];
|
|
@@ -339,7 +484,7 @@ describe('createRequestMock', function () {
|
|
|
339
484
|
expect(data).to.be.eq(JSON.stringify(body));
|
|
340
485
|
});
|
|
341
486
|
|
|
342
|
-
it('should pass a
|
|
487
|
+
it('should pass a Buffer from the "body" option to the stream', async function () {
|
|
343
488
|
const body = Buffer.from('test');
|
|
344
489
|
const req = createRequestMock({body});
|
|
345
490
|
const chunks = [];
|
|
@@ -351,12 +496,38 @@ describe('createRequestMock', function () {
|
|
|
351
496
|
expect(data).to.be.eql(body);
|
|
352
497
|
});
|
|
353
498
|
|
|
354
|
-
it('should
|
|
499
|
+
it('should pass a value form the "url" option to the request url', function () {
|
|
355
500
|
const req = createRequestMock({url: '/test'});
|
|
356
501
|
expect(req.url).to.be.eq('/test');
|
|
357
502
|
});
|
|
358
503
|
|
|
359
|
-
it('should
|
|
504
|
+
it('should pass a value form the "path" option to the request url', function () {
|
|
505
|
+
const req = createRequestMock({path: '/test'});
|
|
506
|
+
expect(req.url).to.be.eq('/test');
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('should pass a string form the "query" option to the request url', async function () {
|
|
510
|
+
const req1 = createRequestMock({query: 'p1=foo&p2=bar'});
|
|
511
|
+
const req2 = createRequestMock({query: '?p1=foo&p2=bar'});
|
|
512
|
+
expect(req1.url).to.be.eq('/?p1=foo&p2=bar');
|
|
513
|
+
expect(req2.url).to.be.eq('/?p1=foo&p2=bar');
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('should pass an object form the "query" option to the request url', async function () {
|
|
517
|
+
const req = createRequestMock({query: {foo: 'bar', baz: 'qux'}});
|
|
518
|
+
expect(req.url).to.be.eq('/?foo=bar&baz=qux');
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it('should combine the "path" and "query" options in the request url', function () {
|
|
522
|
+
const req1 = createRequestMock({path: '/test', query: 'foo=bar'});
|
|
523
|
+
const req2 = createRequestMock({path: '/test', query: '?foo=bar'});
|
|
524
|
+
const req3 = createRequestMock({path: '/test', query: {foo: 'bar'}});
|
|
525
|
+
expect(req1.url).to.be.eq('/test?foo=bar');
|
|
526
|
+
expect(req2.url).to.be.eq('/test?foo=bar');
|
|
527
|
+
expect(req3.url).to.be.eq('/test?foo=bar');
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('should set a value from the "method" option to the request method in upper case', async function () {
|
|
360
531
|
const req1 = createRequestMock({method: 'get'});
|
|
361
532
|
const req2 = createRequestMock({method: 'post'});
|
|
362
533
|
expect(req1.method).to.be.eq('GET');
|
|
@@ -374,6 +545,19 @@ describe('createRequestMock', function () {
|
|
|
374
545
|
expect(req.headers['x-forwarded-proto']).to.be.eq('https');
|
|
375
546
|
});
|
|
376
547
|
|
|
548
|
+
it('should serialize and set a value from the "cookies" option to the "cookie" header', function () {
|
|
549
|
+
const req = createRequestMock({cookies: {p1: 'foo', p2: 'bar'}});
|
|
550
|
+
expect(req.headers['cookie']).to.be.eq('p1=foo; p2=bar');
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it('should merge the "cookie" header with the "cookies" option', function () {
|
|
554
|
+
const req = createRequestMock({
|
|
555
|
+
headers: {cookie: 'p1=foo; p2=bar'},
|
|
556
|
+
cookies: {p2: 'baz', p3: 'qux'},
|
|
557
|
+
});
|
|
558
|
+
expect(req.headers['cookie']).to.be.eq('p1=foo; p2=baz; p3=qux');
|
|
559
|
+
});
|
|
560
|
+
|
|
377
561
|
it('should set the "content-type" header for a String body', function () {
|
|
378
562
|
const req = createRequestMock({body: 'test'});
|
|
379
563
|
expect(req.headers['content-type']).to.be.eq('text/plain');
|