@@ -8,9 +8,12 @@ import {
88import { interceptXMLHttpRequest } from '@mswjs/interceptors/lib/interceptors/XMLHttpRequest' ;
99import { interceptFetch } from '@mswjs/interceptors/lib/interceptors/fetch' ;
1010import { match } from 'node-match-path' ;
11+ import { Such } from 'suchjs/lib/core/such' ;
1112// !Use require to import the umd style library such
1213// eslint-disable-next-line @typescript-eslint/no-var-requires
13- const Such = require ( 'suchjs/lib/browser' ) ;
14+ const windowSuch = require ( 'suchjs/lib/browser' ) ;
15+ const globalSuchInstance = windowSuch . default as Such ;
16+ const protoOfSuch = Object . getPrototypeOf ( globalSuchInstance ) ;
1417/**
1518 * Interceptor Target
1619 * XHR -> XMLHttpRequest
@@ -21,7 +24,7 @@ enum InterceptorTarget {
2124 FETCH = 0b001 << 1 ,
2225}
2326/**
24- *
27+ * Request Methods
2528 */
2629enum RequestMethod {
2730 GET = 0b1 ,
@@ -38,21 +41,39 @@ type TReqMatchFunc = (request: IsomorphicRequest, params?: TObj) => boolean;
3841type TMockDataFunc = ( request : IsomorphicRequest , params ?: TObj ) => unknown ;
3942type TReqMatchParam = string | TReqMatchFunc | RequestMethod ;
4043type TMockDataParam = TMockDataFunc | unknown ;
41- type MockedResult = Partial < MockedResponse > | ( ( resp : MockedResponse ) => void ) ;
44+ type ResponseTransformer = Partial < MockedResponse > | ( ( resp : MockedResponse ) => void ) ;
4245type RouteItem < T extends string | RegExp = string | RegExp > = [
4346 T ,
4447 TReqMatchFunc ,
4548 TMockDataFunc ,
46- MockedResult ,
49+ ResponseOptions ,
4750] ;
48- type SuchExtended = typeof Such & {
51+ export type SuchWithMock = typeof globalSuchInstance & {
4952 mock : (
5053 route : string | RegExp ,
5154 match : TReqMatchParam ,
5255 data : TMockDataParam ,
53- resp ?: MockedResult ,
56+ responseOptions ?: ResponseOptions ,
5457 ) => unknown ;
5558} ;
59+ /**
60+ * ResponseTimeout
61+ */
62+ interface ResponseTimeout {
63+ timeout ?: number | [ number , number ]
64+ }
65+ /**
66+ * InterceptorOptions
67+ */
68+ type InterceptorOptions = ResponseTimeout
69+
70+ /**
71+ * ResponseOptions
72+ */
73+ interface ResponseOptions extends ResponseTimeout {
74+ transformer ?: ResponseTransformer
75+ }
76+
5677// Pick the value => key in ts enum.
5778const reqMethodPairs : [ number , string ] [ ] = ( ( ) => {
5879 const pairs : [ number , string ] [ ] = [ ] ;
@@ -69,15 +90,70 @@ let interceptor: InterceptorApi;
6990// routes
7091let stringRoutes : RouteItem < string > [ ] = [ ] ;
7192let regexRoutes : RouteItem < RegExp > [ ] = [ ] ;
93+ // interceptRequest
94+ const interceptRequest = ( request : IsomorphicRequest , options : InterceptorOptions = { } ) => {
95+ const url = request . url ;
96+ const location = document . location ;
97+ // first, check the host if is equal
98+ if ( location . host !== url . host ) {
99+ return ;
100+ }
101+ // then check the pathname
102+ const loop = ( ...args : RouteItem [ ] [ ] ) => {
103+ for ( let i = 0 , j = args . length ; i < j ; i ++ ) {
104+ const routes = args [ i ] ;
105+ for ( let l = 0 , m = routes . length ; l < m ; l ++ ) {
106+ const [ route , matchFn , dataFn , responseOptions ] = routes [ l ] ;
107+ const { matches, params } = match ( route , url . pathname ) ;
108+ if ( matches && matchFn ( request , params ) ) {
109+ const data = dataFn ( request , params ) ;
110+ const response = {
111+ status : 200 ,
112+ statusText : 'Ok' ,
113+ headers : {
114+ 'Content-Type' : 'application/json' ,
115+ } ,
116+ body : JSON . stringify ( data ) ,
117+ } ;
118+ // if resp parameter is a function
119+ // use the function to transform response
120+ const { timeout, transformer } = responseOptions || { } ;
121+ if ( typeof transformer === 'function' ) {
122+ transformer ( response ) ;
123+ } else if ( transformer ) {
124+ // if resp is a MockResponse
125+ // extend to override the response
126+ Object . assign ( response , transformer ) ;
127+ }
128+ return { response, timeout } ;
129+ }
130+ }
131+ }
132+ } ;
133+ // first check the string routes, then the regex routes
134+ const result = loop ( stringRoutes , regexRoutes ) ;
135+ if ( result ) {
136+ const { timeout, response } = result ;
137+ const useTimeout = timeout || options . timeout ;
138+ if ( useTimeout === undefined ) {
139+ return response ;
140+ } else {
141+ const delay = Array . isArray ( useTimeout ) ? globalSuchInstance . utils . makeRandom . apply ( null , useTimeout ) : useTimeout ;
142+ return new Promise ( ( resolve ) => {
143+ setTimeout ( ( ) => resolve ( response ) , delay ) ;
144+ } ) ;
145+ }
146+ }
147+ } ;
72148/**
73149 * Such.mock
74150 */
75- const ThisSuch : SuchExtended = Object . assign ( Such , {
151+ const SuchPrototype : SuchWithMock = Object . assign ( protoOfSuch , {
76152 mock (
77153 route : string | RegExp ,
78154 match : TReqMatchParam ,
79155 data : TMockDataParam ,
80- resp ?: MockedResult ,
156+ responseOptions ?: ResponseOptions ,
81157 ) {
82158 // handle the match function
83159 let matchFn : TReqMatchFunc ;
@@ -109,28 +185,29 @@ const ThisSuch: SuchExtended = Object.assign(Such, {
109185 dataFn = data as TMockDataFunc ;
110186 } else {
111187 dataFn = ( _req : IsomorphicRequest ) => {
112- return Such . as ( data ) ;
188+ return globalSuchInstance . as ( data ) ;
113189 } ;
114190 }
115191 // route
116192 if ( typeof route === 'string' ) {
117- stringRoutes . push ( [ route , matchFn , dataFn , resp ] ) ;
193+ stringRoutes . push ( [ route , matchFn , dataFn , responseOptions ] ) ;
194+ } else if ( route instanceof RegExp ) {
195+ regexRoutes . push ( [ route , matchFn , dataFn , responseOptions ] ) ;
118196 } else {
119- regexRoutes . push ( [ route , matchFn , dataFn , resp ] ) ;
197+ throw new Error ( `wrong route pattern of type " ${ typeof route } ", please use a string or a RegExp rule instead.` ) ;
120198 }
121199 } ,
122200} ) ;
123-
124201/**
125202 * Define properties inject into Such.mock
126203 */
127- Object . assign ( ThisSuch . mock , {
204+ Object . assign ( SuchPrototype . mock , {
128205 // the target need be interceptor, XHR or FETCH or both
129206 target : InterceptorTarget ,
130207 // method
131208 method : RequestMethod ,
132209 // intercept
133- intercept ( target : InterceptorTarget ) {
210+ intercept ( target : InterceptorTarget , options : InterceptorOptions = { } ) : InterceptorApi | never {
134211 const modules : Interceptor [ ] = [ ] ;
135212 if ( ( target & InterceptorTarget . XHR ) > 0 ) {
136213 modules . push ( interceptXMLHttpRequest ) ;
@@ -142,48 +219,11 @@ Object.assign(ThisSuch.mock, {
142219 interceptor = createInterceptor ( {
143220 modules,
144221 resolver : ( request : IsomorphicRequest ) => {
145- const url = request . url ;
146- const location = document . location ;
147- // first, check the host if is equal
148- if ( location . host !== url . host ) {
149- return ;
150- }
151- // then check the pathname
152- const loop = ( ...args : RouteItem [ ] [ ] ) => {
153- for ( let i = 0 , j = args . length ; i < j ; i ++ ) {
154- const routes = args [ i ] ;
155- for ( let l = 0 , m = routes . length ; l < m ; l ++ ) {
156- const [ route , matchFn , dataFn , resp ] = routes [ l ] ;
157- const { matches, params } = match ( route , url . pathname ) ;
158- if ( matches && matchFn ( request , params ) ) {
159- const data = dataFn ( request , params ) ;
160- const result = {
161- status : 200 ,
162- statusText : 'Ok' ,
163- headers : {
164- 'Content-Type' : 'application/json' ,
165- } ,
166- body : JSON . stringify ( data ) ,
167- } ;
168- // if resp parameter is a function
169- // use the function to transform result
170- if ( typeof resp === 'function' ) {
171- resp ( result ) ;
172- } else if ( resp ) {
173- // if resp is a MockResponse
174- // extend to override the result
175- Object . assign ( result , resp ) ;
176- }
177- return result ;
178- }
179- }
180- }
181- } ;
182- // first check the string routes, then the regex routes
183- return loop ( stringRoutes , regexRoutes ) ;
222+ return interceptRequest ( request , options ) ;
184223 } ,
185224 } ) ;
186225 interceptor . apply ( ) ;
226+ return interceptor ;
187227 } else {
188228 throw new Error (
189229 `Wrong interceptor target parameter when call the "Such.mock.intercept": expect at least one interceptor target.` ,
@@ -198,4 +238,4 @@ Object.assign(ThisSuch.mock, {
198238 regexRoutes = [ ] ;
199239 } ,
200240} ) ;
201- export default ThisSuch ;
241+ export default windowSuch ;
0 commit comments