msw 0.22.2 → 0.24.1

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.
@@ -1,5 +1,5 @@
1
1
  import { Headers } from 'headers-utils';
2
- import { ResponseWithSerializedHeaders } from '../../setupWorker/glossary';
2
+ import { Mask, ResponseWithSerializedHeaders } from '../../setupWorker/glossary';
3
3
  import { ResponseComposition, MockedResponse } from '../../response';
4
4
  import { set } from '../../context/set';
5
5
  export declare const defaultContext: {
@@ -36,6 +36,13 @@ export declare type RequestParams = {
36
36
  export declare type ResponseResolverReturnType<R> = R | undefined | void;
37
37
  export declare type AsyncResponseResolverReturnType<R> = Promise<ResponseResolverReturnType<R>> | ResponseResolverReturnType<R>;
38
38
  export declare type ResponseResolver<RequestType = MockedRequest, ContextType = typeof defaultContext, BodyType = any> = (req: RequestType, res: ResponseComposition<BodyType>, context: ContextType) => AsyncResponseResolverReturnType<MockedResponse<BodyType>>;
39
+ declare type RequestHandlerType = 'rest' | 'graphql';
40
+ export interface RequestHandlerMetaInfo<Type = RequestHandlerType> {
41
+ type: Type;
42
+ header: string;
43
+ mask: Mask;
44
+ callFrame: string | undefined;
45
+ }
39
46
  export interface RequestHandler<RequestType = MockedRequest, ContextType = typeof defaultContext, ParsedRequest = any, PublicRequest = RequestType, ResponseBodyType = any> {
40
47
  /**
41
48
  * Parses a captured request to retrieve additional
@@ -69,6 +76,11 @@ export interface RequestHandler<RequestType = MockedRequest, ContextType = typeo
69
76
  * when dealing with any subsequent matching requests.
70
77
  */
71
78
  shouldSkip?: boolean;
79
+ /**
80
+ * Returns request handler's meta information used
81
+ * when listing each current request handler.
82
+ */
83
+ getMetaInfo: () => RequestHandlerMetaInfo;
72
84
  }
73
85
  declare const _default: null;
74
86
  export default _default;
@@ -1,5 +1,13 @@
1
+ declare type ArityOneFn = (arg: any) => any;
2
+ declare type PickLastInTuple<T extends any[]> = T extends [
3
+ ...rest: infer U,
4
+ argn: infer L
5
+ ] ? L : never;
6
+ declare type FirstFnParameterType<T extends any[]> = Parameters<PickLastInTuple<T>>[any];
7
+ declare type LastFnParameterType<T extends any[]> = ReturnType<T[0]>;
1
8
  /**
2
9
  * Composes a given list of functions into a new function that
3
10
  * executes from right to left.
4
11
  */
5
- export declare function compose(...funcs: Array<(...args: any[]) => any>): (...args: any[]) => any;
12
+ export declare function compose<T extends ArityOneFn[], LeftReturnType extends FirstFnParameterType<T>, RightReturnType extends LastFnParameterType<T>>(...fns: T): (...args: LeftReturnType extends never ? never[] : [LeftReturnType]) => RightReturnType;
13
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Return the stack trace frame of a function's invocation.
3
+ */
4
+ export declare function getCallFrame(): string | undefined;
package/lib/umd/index.js CHANGED
@@ -70,6 +70,13 @@
70
70
  "511": "Network Authentication Required"
71
71
  };
72
72
 
73
+ /**
74
+ * Sets a response status code and text.
75
+ * @example
76
+ * res(ctx.status(301))
77
+ * res(ctx.status(400, 'Custom status text'))
78
+ * @see {@link https://mswjs.io/docs/api/context/status `ctx.status()`}
79
+ */
73
80
  const status = (statusCode, statusText) => {
74
81
  return (res) => {
75
82
  res.status = statusCode;
@@ -333,6 +340,10 @@
333
340
  exports.flattenHeadersObject = flattenHeadersObject_1.flattenHeadersObject;
334
341
  });
335
342
 
343
+ /**
344
+ * Sets one or multiple response headers.
345
+ * @see {@link https://mswjs.io/docs/api/context/set `ctx.set()`}
346
+ */
336
347
  function set(...args) {
337
348
  return (res) => {
338
349
  const [name, value] = args;
@@ -551,9 +562,8 @@
551
562
  }
552
563
 
553
564
  /**
554
- * Sets a given cookie on the response.
555
- * @example
556
- * res(cookie('name', 'value'))
565
+ * Sets a given cookie on the mocked response.
566
+ * @example res(ctx.cookie('name', 'value'))
557
567
  */
558
568
  const cookie = (name, value, options) => {
559
569
  return (res) => {
@@ -567,9 +577,11 @@
567
577
  };
568
578
 
569
579
  /**
570
- * Sets the body of the response without any `Content-Type` header.
580
+ * Sets a raw response body. Does not append any `Content-Type` headers.
571
581
  * @example
572
- * res(body('message'))
582
+ * res(ctx.body('Successful response'))
583
+ * res(ctx.body(JSON.stringify({ key: 'value' })))
584
+ * @see {@link https://mswjs.io/docs/api/context/body `ctx.body()`}
573
585
  */
574
586
  const body = (value) => {
575
587
  return (res) => {
@@ -578,6 +590,19 @@
578
590
  };
579
591
  };
580
592
 
593
+ /**
594
+ * Parses a given string into a JSON.
595
+ * Does not throw an exception on an invalid JSON string.
596
+ */
597
+ function jsonParse(str) {
598
+ try {
599
+ return JSON.parse(str);
600
+ }
601
+ catch (error) {
602
+ return undefined;
603
+ }
604
+ }
605
+
581
606
  /**
582
607
  * Determines if the given value is an object.
583
608
  */
@@ -607,24 +632,34 @@
607
632
 
608
633
  /**
609
634
  * Sets the given value as the JSON body of the response.
635
+ * Appends a `Content-Type: application/json` header on the
636
+ * mocked response.
610
637
  * @example
611
- * res(json({ key: 'value' }))
612
- * res(json('Some string'))
613
- * res(json([1, '2', false, { ok: true }]))
638
+ * res(ctx.json('Some string'))
639
+ * res(ctx.json({ key: 'value' }))
640
+ * res(ctx.json([1, '2', false, { ok: true }]))
641
+ * @see {@link https://mswjs.io/docs/api/context/json `ctx.json()`}
614
642
  */
615
- const json = (body, { merge = false } = {}) => {
643
+ const json = (body) => {
616
644
  return (res) => {
617
645
  res.headers.set('Content-Type', 'application/json');
618
- res.body = merge ? mergeRight(res.body || {}, body) : body;
646
+ res.body = JSON.stringify(body);
619
647
  return res;
620
648
  };
621
649
  };
622
650
 
623
651
  /**
624
- * Returns a GraphQL body payload.
652
+ * Sets a given payload as a GraphQL response body.
653
+ * @example
654
+ * res(ctx.data({ user: { firstName: 'John' }}))
655
+ * @see {@link https://mswjs.io/docs/api/context/data `ctx.data()`}
625
656
  */
626
657
  const data = (payload) => {
627
- return json({ data: payload }, { merge: true });
658
+ return (res) => {
659
+ const prevBody = jsonParse(res.body) || {};
660
+ const nextBody = mergeRight(prevBody, { data: payload });
661
+ return json(nextBody)(res);
662
+ };
628
663
  };
629
664
 
630
665
  /**
@@ -654,10 +689,11 @@
654
689
  MIN_SERVER_RESPONSE_TIME);
655
690
  };
656
691
  /**
657
- * Delays the current response for the given duration (in ms)
692
+ * Delays the response by the given duration (ms).
658
693
  * @example
659
- * res(delay()) // realistic server response time
660
- * res(delay(1500)) // explicit response delay duration
694
+ * res(ctx.delay()) // realistic server response time
695
+ * res(ctx.delay(1200))
696
+ * @see {@link https://mswjs.io/docs/api/context/delay `ctx.delay()`}
661
697
  */
662
698
  const delay = (durationMs) => {
663
699
  return (res) => {
@@ -668,12 +704,18 @@
668
704
 
669
705
  /**
670
706
  * Sets a given list of GraphQL errors on the mocked response.
707
+ * @example res(ctx.errors([{ message: 'Unauthorized' }]))
708
+ * @see {@link https://mswjs.io/docs/api/context/errors}
671
709
  */
672
710
  const errors = (errorsList) => {
673
- if (errorsList == null) {
674
- return (res) => res;
675
- }
676
- return json({ errors: errorsList }, { merge: true });
711
+ return (res) => {
712
+ if (errorsList == null) {
713
+ return res;
714
+ }
715
+ const prevBody = jsonParse(res.body) || {};
716
+ const nextBody = mergeRight(prevBody, { errors: errorsList });
717
+ return json(nextBody)(res);
718
+ };
677
719
  };
678
720
 
679
721
  const useFetch = isNodeProcess() ? require('node-fetch') : window.fetch;
@@ -693,9 +735,10 @@
693
735
  return requestParameters;
694
736
  };
695
737
  /**
696
- * Wrapper around the native `window.fetch()` function that performs
697
- * a request bypassing MSW. Requests performed using
698
- * this function will never be mocked.
738
+ * Performs a bypassed request inside a request handler.
739
+ * @example
740
+ * const originalResponse = await ctx.fetch(req)
741
+ * @see {@link https://mswjs.io/docs/api/context/fetch `ctx.fetch()`}
699
742
  */
700
743
  const fetch = (input, requestInit = {}) => {
701
744
  // Keep the default `window.fetch()` call signature
@@ -708,9 +751,10 @@
708
751
  };
709
752
 
710
753
  /**
711
- * Sets a given text as a "Cotent-Type: text/plain" body of the response.
712
- * @example
713
- * res(text('message'))
754
+ * Sets a textual response body. Appends a `Content-Type: text/plain`
755
+ * header on the mocked response.
756
+ * @example res(ctx.text('Successful response'))
757
+ * @see {@link https://mswjs.io/docs/api/context/text `ctx.text()`}
714
758
  */
715
759
  const text = (body) => {
716
760
  return (res) => {
@@ -721,9 +765,11 @@
721
765
  };
722
766
 
723
767
  /**
724
- * Sets the given XML as the body of the response.
768
+ * Sets an XML response body. Appends a `Content-Type: text/xml` header
769
+ * on the mocked response.
725
770
  * @example
726
- * res(xml('<key>value</key>'))
771
+ * res(ctx.xml('<node key="value">Content</node>'))
772
+ * @see {@link https://mswjs.io/docs/api/context/xml `ctx.xml()`}
727
773
  */
728
774
  const xml = (body) => {
729
775
  return (res) => {
@@ -930,8 +976,14 @@ Learn more about creating the Service Worker script: https://mswjs.io/docs/cli/i
930
976
  * Composes a given list of functions into a new function that
931
977
  * executes from right to left.
932
978
  */
933
- function compose(...funcs) {
934
- return funcs.reduce((f, g) => (...args) => f(g(...args)));
979
+ function compose(...fns) {
980
+ return (...args) => {
981
+ return fns.reduceRight((leftFn, rightFn) => {
982
+ return leftFn instanceof Promise
983
+ ? Promise.resolve(leftFn).then(rightFn)
984
+ : rightFn(leftFn);
985
+ }, args[0]);
986
+ };
935
987
  }
936
988
 
937
989
  class NetworkError extends Error {
@@ -941,17 +993,6 @@ Learn more about creating the Service Worker script: https://mswjs.io/docs/cli/i
941
993
  }
942
994
  }
943
995
 
944
- /**
945
- * Internal response transformer to ensure response JSON body
946
- * is always stringified.
947
- */
948
- const stringifyJsonBody = (res) => {
949
- var _a, _b;
950
- if (res.body && ((_b = (_a = res.headers) === null || _a === void 0 ? void 0 : _a.get('content-type')) === null || _b === void 0 ? void 0 : _b.endsWith('json'))) {
951
- res.body = JSON.stringify(res.body);
952
- }
953
- return res;
954
- };
955
996
  const defaultResponse = {
956
997
  status: 200,
957
998
  statusText: 'OK',
@@ -959,11 +1000,9 @@ Learn more about creating the Service Worker script: https://mswjs.io/docs/cli/i
959
1000
  delay: 0,
960
1001
  once: false,
961
1002
  };
962
- const defaultResponseTransformers = [
963
- stringifyJsonBody,
964
- ];
1003
+ const defaultResponseTransformers = [];
965
1004
  function createResponseComposition(responseOverrides, defaultTransformers = defaultResponseTransformers) {
966
- return (...transformers) => {
1005
+ return (...transformers) => __awaiter(this, void 0, void 0, function* () {
967
1006
  const initialResponse = Object.assign({}, defaultResponse, {
968
1007
  headers: new lib.Headers({
969
1008
  'x-powered-by': 'msw',
@@ -977,7 +1016,7 @@ Learn more about creating the Service Worker script: https://mswjs.io/docs/cli/i
977
1016
  ? compose(...resolvedTransformers)(initialResponse)
978
1017
  : initialResponse;
979
1018
  return resolvedResponse;
980
- };
1019
+ });
981
1020
  }
982
1021
  const response = Object.assign(createResponseComposition(), {
983
1022
  once: createResponseComposition({ once: true }),
@@ -2442,19 +2481,6 @@ Learn more about creating the Service Worker script: https://mswjs.io/docs/cli/i
2442
2481
  }
2443
2482
  }
2444
2483
 
2445
- /**
2446
- * Parses a given string into a JSON.
2447
- * Does not throw an exception on an invalid JSON string.
2448
- */
2449
- function jsonParse(str) {
2450
- try {
2451
- return JSON.parse(str);
2452
- }
2453
- catch (error) {
2454
- return undefined;
2455
- }
2456
- }
2457
-
2458
2484
  /**
2459
2485
  * Parses a given request/response body based on the `Content-Type` header.
2460
2486
  */
@@ -2758,6 +2784,12 @@ If this message still persists after updating, please report an issue: https://g
2758
2784
  // Declare the list of event handlers on the module's scope
2759
2785
  // so it persists between Fash refreshes of the application's code.
2760
2786
  let listeners = [];
2787
+ /**
2788
+ * Creates a new mock Service Worker registration
2789
+ * with the given request handlers.
2790
+ * @param {RequestHandler[]} requestHandlers List of request handlers
2791
+ * @see {@link https://mswjs.io/docs/api/setup-worker `setupWorker`}
2792
+ */
2761
2793
  function setupWorker(...requestHandlers) {
2762
2794
  requestHandlers.forEach((handler) => {
2763
2795
  if (Array.isArray(handler))
@@ -2818,6 +2850,18 @@ If this message still persists after updating, please report an issue: https://g
2818
2850
  resetHandlers(...nextHandlers) {
2819
2851
  context.requestHandlers = resetHandlers(requestHandlers, ...nextHandlers);
2820
2852
  },
2853
+ printHandlers() {
2854
+ context.requestHandlers.forEach((handler) => {
2855
+ const meta = handler.getMetaInfo();
2856
+ console.groupCollapsed(meta.header);
2857
+ console.log(`Declaration: ${meta.callFrame}`);
2858
+ console.log('Resolver: %s', handler.resolver);
2859
+ if (['rest'].includes(meta.type)) {
2860
+ console.log('Match:', `https://mswjs.io/repl?path=${meta.mask}`);
2861
+ }
2862
+ console.groupEnd();
2863
+ });
2864
+ },
2821
2865
  };
2822
2866
  }
2823
2867
 
@@ -2969,6 +3013,31 @@ If this message still persists after updating, please report an issue: https://g
2969
3013
  return match(cleanMask, cleanRequestUrl);
2970
3014
  }
2971
3015
 
3016
+ /**
3017
+ * Return the stack trace frame of a function's invocation.
3018
+ */
3019
+ function getCallFrame() {
3020
+ try {
3021
+ const inspectionError = new Error();
3022
+ inspectionError.name = 'Inspection Error';
3023
+ throw inspectionError;
3024
+ }
3025
+ catch (error) {
3026
+ const frames = error.stack.split('\n');
3027
+ // Get the first frame that doesn't reference the library's internal trace.
3028
+ // Assume that frame is the invocation frame.
3029
+ const declarationFrame = frames.slice(1).find((frame) => {
3030
+ return !/(node_modules)?\/lib\/(umd|esm)\//.test(frame);
3031
+ });
3032
+ if (!declarationFrame) {
3033
+ return;
3034
+ }
3035
+ // Extract file reference from the stack frame.
3036
+ const [, declarationPath] = declarationFrame.match(/\((.+?)\)$/) || [];
3037
+ return declarationPath;
3038
+ }
3039
+ }
3040
+
2972
3041
  (function (RESTMethods) {
2973
3042
  RESTMethods["HEAD"] = "HEAD";
2974
3043
  RESTMethods["GET"] = "GET";
@@ -2992,6 +3061,7 @@ If this message still persists after updating, please report an issue: https://g
2992
3061
  const createRestHandler = (method) => {
2993
3062
  return (mask, resolver) => {
2994
3063
  const resolvedMask = getUrlByMask(mask);
3064
+ const callFrame = getCallFrame();
2995
3065
  return {
2996
3066
  parse(req) {
2997
3067
  // Match the request during parsing to prevent matching it twice
@@ -3042,16 +3112,85 @@ ${queryParams
3042
3112
  console.log('Response', loggedResponse);
3043
3113
  console.groupEnd();
3044
3114
  },
3115
+ getMetaInfo() {
3116
+ return {
3117
+ type: 'rest',
3118
+ header: `[rest] ${method} ${mask.toString()}`,
3119
+ mask,
3120
+ callFrame,
3121
+ };
3122
+ },
3045
3123
  };
3046
3124
  };
3047
3125
  };
3048
3126
  const rest = {
3127
+ /**
3128
+ * Captures a HEAD request by a given path.
3129
+ * @example
3130
+ * rest.head('/numbers', (req, res, ctx) => {
3131
+ * return res(ctx.status(302))
3132
+ * })
3133
+ * @see {@link https://mswjs.io/docs/api/rest `rest`}
3134
+ */
3049
3135
  head: createRestHandler(exports.RESTMethods.HEAD),
3136
+ /**
3137
+ * Captures a GET request by a given path.
3138
+ * @example
3139
+ * rest.get('/numbers', (req, res, ctx) => {
3140
+ * return res(ctx.json([1, 2, 3]))
3141
+ * })
3142
+ * @see {@link https://mswjs.io/docs/api/rest `rest`}
3143
+ */
3050
3144
  get: createRestHandler(exports.RESTMethods.GET),
3145
+ /**
3146
+ * Captures a POST request by a given path.
3147
+ * @example
3148
+ * rest.post('/numbers', (req, res, ctx) => {
3149
+ * return res(ctx.text('success'))
3150
+ * })
3151
+ * @see {@link https://mswjs.io/docs/api/rest `rest`}
3152
+ */
3051
3153
  post: createRestHandler(exports.RESTMethods.POST),
3154
+ /**
3155
+ * Captures a PUT request by a given path.
3156
+ * @example
3157
+ * rest.put('/numbers', (req, res, ctx) => {
3158
+ * const { numbers } = req.body
3159
+ * return res(ctx.json(numbers))
3160
+ * })
3161
+ * @see {@link https://mswjs.io/docs/api/rest `rest`}
3162
+ */
3052
3163
  put: createRestHandler(exports.RESTMethods.PUT),
3164
+ /**
3165
+ * Captures a DELETE request by a given path.
3166
+ * @example
3167
+ * rest.delete('/numbers', (req, res, ctx) => {
3168
+ * const index = req.url.searchParams.get('index')
3169
+ * prevNumbers.splice(index, 1)
3170
+ * return res(ctx.json(nextNumbers))
3171
+ * })
3172
+ * @see {@link https://mswjs.io/docs/api/rest `rest`}
3173
+ */
3053
3174
  delete: createRestHandler(exports.RESTMethods.DELETE),
3175
+ /**
3176
+ * Captures a PATCH request by a given path.
3177
+ * @example
3178
+ * rest.patch('/numbers', (req, res, ctx) => {
3179
+ * const { numbers } = req.body
3180
+ * const nextNumbers = prevNumbers.concat(number)
3181
+ * return res(ctx.json(nextNumbers))
3182
+ * })
3183
+ * @see {@link https://mswjs.io/docs/api/rest `rest`}
3184
+ */
3054
3185
  patch: createRestHandler(exports.RESTMethods.PATCH),
3186
+ /**
3187
+ * Captures an OPTIONS request by a given path.
3188
+ * @example
3189
+ * rest.options('/numbers', (req, res, ctx) => {
3190
+ * return res(ctx.set('Allow', 'GET,HEAD,POST'))
3191
+ * })
3192
+ * @see {@link https://mswjs.io/docs/api/rest `rest`}
3193
+ */
3055
3194
  options: createRestHandler(exports.RESTMethods.OPTIONS),
3056
3195
  };
3057
3196
 
@@ -6196,6 +6335,7 @@ ${queryParams
6196
6335
  };
6197
6336
  }
6198
6337
  function graphQLRequestHandler(expectedOperationType, expectedOperationName, mask, resolver) {
6338
+ const callFrame = getCallFrame();
6199
6339
  return {
6200
6340
  resolver,
6201
6341
  parse(req) {
@@ -6267,6 +6407,17 @@ ${queryParams
6267
6407
  console.log('Response:', loggedResponse);
6268
6408
  console.groupEnd();
6269
6409
  },
6410
+ getMetaInfo() {
6411
+ const header = expectedOperationType === 'all'
6412
+ ? `[graphql] ${expectedOperationType} (origin: ${mask.toString()})`
6413
+ : `[graphql] ${expectedOperationType} ${expectedOperationName} (origin: ${mask.toString()})`;
6414
+ return {
6415
+ type: 'graphql',
6416
+ header,
6417
+ mask,
6418
+ callFrame,
6419
+ };
6420
+ },
6270
6421
  };
6271
6422
  }
6272
6423
  const createGraphQLScopedHandler = (expectedOperationType, mask) => {
@@ -6280,10 +6431,42 @@ ${queryParams
6280
6431
  };
6281
6432
  };
6282
6433
  const graphqlStandardHandlers = {
6434
+ /**
6435
+ * Captures any GraphQL operation, regardless of its name, under the current scope.
6436
+ * @example
6437
+ * graphql.operation((req, res, ctx) => {
6438
+ * return res(ctx.data({ name: 'John' }))
6439
+ * })
6440
+ * @see {@link https://mswjs.io/docs/api/graphql/operation `graphql.operation()`}
6441
+ */
6283
6442
  operation: createGraphQLOperationHandler('*'),
6443
+ /**
6444
+ * Captures a GraphQL query by a given name.
6445
+ * @example
6446
+ * graphql.query('GetUser', (req, res, ctx) => {
6447
+ * return res(ctx.data({ user: { name: 'John' } }))
6448
+ * })
6449
+ * @see {@link https://mswjs.io/docs/api/graphql/query `graphql.query()`}
6450
+ */
6284
6451
  query: createGraphQLScopedHandler('query', '*'),
6452
+ /**
6453
+ * Captures a GraphQL mutation by a given name.
6454
+ * @example
6455
+ * graphql.mutation('SavePost', (req, res, ctx) => {
6456
+ * return res(ctx.data({ post: { id: 'abc-123' } }))
6457
+ * })
6458
+ * @see {@link https://mswjs.io/docs/api/graphql/mutation `graphql.mutation()`}
6459
+ */
6285
6460
  mutation: createGraphQLScopedHandler('mutation', '*'),
6286
6461
  };
6462
+ /**
6463
+ * Creates a GraphQL mocking API scoped to the given endpoint.
6464
+ * @param uri Endpoint URL, or path.
6465
+ * @example
6466
+ * const api = graphql.link('https://api.site.com/graphql)
6467
+ * api.query('GetUser', resolver)
6468
+ * @see {@link https://mswjs.io/docs/api/graphql/link `graphql.link()`}
6469
+ */
6287
6470
  function createGraphQLLink(uri) {
6288
6471
  return {
6289
6472
  operation: createGraphQLOperationHandler(uri),
@@ -6293,6 +6476,7 @@ ${queryParams
6293
6476
  }
6294
6477
  const graphql = Object.assign(Object.assign({}, graphqlStandardHandlers), { link: createGraphQLLink });
6295
6478
 
6479
+ exports.compose = compose;
6296
6480
  exports.context = index;
6297
6481
  exports.createResponseComposition = createResponseComposition;
6298
6482
  exports.defaultContext = defaultContext;