Skip to content

Commit b64f4bb

Browse files
author
Olof Dahlbom
committed
Initial commit
1 parent 8d1835e commit b64f4bb

12 files changed

+1348
-30
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ tsconfig.tsbuildinfo
1111
tmp/
1212
dist/
1313
.vscode/
14+
.yarn/cache

package.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@
217217
"@babel/register": "^7.24.6",
218218
"@changesets/cli": "^2.27.8",
219219
"@chiragrupani/karma-chromium-edge-launcher": "^2.4.1",
220+
"@types/diff": "^5.2.2",
220221
"@types/jest": "^29.5.12",
221222
"@types/karma": "^6.3.8",
222223
"@types/node": "^22.5.4",
@@ -267,5 +268,9 @@
267268
"webpack": "^5.94.0",
268269
"whatwg-fetch": "^3.6.20"
269270
},
270-
"packageManager": "yarn@4.4.1"
271+
"packageManager": "yarn@4.4.1",
272+
"dependencies": {
273+
"diff": "^7.0.0",
274+
"tty-table": "^4.2.3"
275+
}
271276
}

src/mocks/mock-request.js

+97-22
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,30 @@ import MockAssert from './mock-assert'
22
import Response from '../response'
33
import { isPlainObject } from '../utils/index'
44
import { clone } from '../utils/clone'
5-
import { sortedUrl, toSortedQueryString, isSubset } from './mock-utils'
5+
import { sortedUrl, toSortedQueryString, filterKeys, diffString } from './mock-utils'
6+
import { mock } from 'node:test'
67

78
/**
89
* @param {number} id
910
* @param {object} props
1011
* @param {string} props.method
1112
* @param {string|function} props.url
1213
* @param {string|function} props.body - request body
14+
* @param {string} props.mockName
1315
* @param {object} props.response
1416
* @param {string} props.response.body
1517
* @param {object} props.response.headers
1618
* @param {integer} props.response.status
1719
*/
20+
21+
const MATCHED_AS_UNDEFINED_IN_MOCK = 'MATCHED_AS_UNDEFINED_IN_MOCK'
22+
const MATCHED_BY_FUNCTION = 'MATCHED_BY_FUNCTION'
23+
const MISMATCH_BY_FUNCTION = 'MISMATCHED_BY_FUNCTION'
24+
1825
function MockRequest(id, props) {
1926
this.id = id
2027

28+
this.mockName = props.mockName ? props.mockName : this.id
2129
this.method = props.method || 'get'
2230
this.urlFunction = typeof props.url === 'function'
2331
this.url = props.url
@@ -80,33 +88,100 @@ MockRequest.prototype = {
8088
return new MockAssert(this.calls)
8189
},
8290

83-
/**
84-
* Checks if the request matches with the mock HTTP method, URL, headers and body
85-
*
86-
* @return {boolean}
87-
*/
88-
isExactMatch(request) {
89-
const bodyMatch = () => {
90-
if (this.body === undefined) {
91-
return true
91+
bodyMatchRequest(request) {
92+
if (this.body === undefined) {
93+
return {
94+
match: true,
95+
mockValue: MATCHED_AS_UNDEFINED_IN_MOCK,
96+
value: MATCHED_AS_UNDEFINED_IN_MOCK,
9297
}
98+
}
99+
if (this.bodyFunction) {
100+
const match = this.body(request.body())
101+
const value = match ? MATCHED_BY_FUNCTION : MISMATCH_BY_FUNCTION
102+
return { match, mockValue: value, requestValue: value }
103+
}
104+
const requestBodyAsString = toSortedQueryString(request.body())
105+
const match = this.body === requestBodyAsString
106+
return {
107+
match,
108+
mockValue: decodeURIComponent(this.body),
109+
requestValue: decodeURIComponent(requestBodyAsString),
110+
}
111+
},
93112

94-
return this.bodyFunction
95-
? this.body(request.body())
96-
: this.body === toSortedQueryString(request.body())
113+
urlMatchRequest(request) {
114+
if (this.urlFunction) {
115+
const match = Boolean(this.url(request.url(), request.params()))
116+
const value = match ? MATCHED_BY_FUNCTION : MISMATCH_BY_FUNCTION
117+
return { match, mockValue: value, requestValue: value }
118+
}
119+
const requestUrlAsSortedString = sortedUrl(request.url())
120+
const mockRequestUrlAsSortedString = sortedUrl(this.url)
121+
const match = mockRequestUrlAsSortedString === requestUrlAsSortedString
122+
return {
123+
match,
124+
mockValue: decodeURIComponent(mockRequestUrlAsSortedString),
125+
requestValue: decodeURIComponent(requestUrlAsSortedString),
97126
}
127+
},
98128

99-
const urlMatch = this.urlFunction
100-
? this.url(request.url(), request.params())
101-
: sortedUrl(this.url) === sortedUrl(request.url())
129+
headersMatchRequest(request) {
130+
if (!this.headers)
131+
return {
132+
match: true,
133+
mockValue: MATCHED_AS_UNDEFINED_IN_MOCK,
134+
value: MATCHED_AS_UNDEFINED_IN_MOCK,
135+
}
136+
if (this.headersFunction) {
137+
const match = this.headers(request.headers())
138+
const value = match ? MATCHED_BY_FUNCTION : MISMATCH_BY_FUNCTION
139+
return { match, mockValue: value, requestValue: value }
140+
}
141+
const filteredRequestHeaders = filterKeys(this.headersObject, request.headers())
142+
const requestHeadersAsSortedString = toSortedQueryString(filteredRequestHeaders)
143+
const mockRequestHeadersAsSortedString = toSortedQueryString(this.headersObject)
144+
const match = requestHeadersAsSortedString === mockRequestHeadersAsSortedString
145+
146+
return {
147+
match,
148+
mockValue: mockRequestHeadersAsSortedString,
149+
requestValue: requestHeadersAsSortedString,
150+
}
151+
},
102152

103-
const headerMatch =
104-
!this.headers ||
105-
(this.headersFunction
106-
? this.headers(request.headers())
107-
: isSubset(this.headersObject, request.headers()))
153+
methodMatchRequest(request) {
154+
const requestMethod = request.method()
155+
const match = this.method === requestMethod
156+
return {
157+
match,
158+
mockValue: this.method,
159+
requestValue: requestMethod,
160+
}
161+
},
108162

109-
return this.method === request.method() && urlMatch && bodyMatch() && headerMatch
163+
getRequestMatching(request) {
164+
const method = this.methodMatchRequest(request)
165+
const url = this.urlMatchRequest(request)
166+
const body = this.bodyMatchRequest(request)
167+
const headers = this.headersMatchRequest(request)
168+
return {
169+
mockName: this.mockName,
170+
isExactMatch: method.match && url.match && body.match && headers.match,
171+
isPartialMatch: this.isPartialMatch(request),
172+
method,
173+
url,
174+
body,
175+
headers,
176+
}
177+
},
178+
/**
179+
* Checks if the request matches with the mock HTTP method, URL, headers and body
180+
*
181+
* @return {boolean}
182+
*/
183+
isExactMatch(request) {
184+
return this.getRequestMatching(request).isExactMatch
110185
},
111186

112187
/**

src/mocks/mock-resource.js

+28
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ function MockResource(id, client) {
1616
this.id = id
1717
this.manifest = client._manifest
1818
this.resourceName = null
19+
this.mockName = null
1920
this.methodName = null
2021
this.requestParams = {}
2122
this.responseData = null
@@ -37,6 +38,32 @@ MockResource.prototype = {
3738
return this
3839
},
3940

41+
/**
42+
* Names you mock instance for debugging purposes.
43+
* @return {MockResource}
44+
*/
45+
named(mockName) {
46+
this.mockName = mockName
47+
return this
48+
},
49+
50+
/**
51+
* Creates a name for the mock based on the client id and the resource name.
52+
* @returns {String}
53+
*/
54+
getName() {
55+
const { mockName, manifest, resourceName, id } = this
56+
const { clientId } = manifest || {}
57+
if (mockName) return mockName
58+
59+
const resourcePart = resourceName || id
60+
if (clientId) {
61+
return `${clientId} - ${resourcePart}`
62+
}
63+
64+
return resourceName ? `${resourceName} - ${id}` : id
65+
},
66+
4067
/**
4168
* @return {MockResource}
4269
*/
@@ -115,6 +142,7 @@ MockResource.prototype = {
115142

116143
if (!this.mockRequest) {
117144
this.mockRequest = new MockRequest(this.id, {
145+
mockName: this.getName(),
118146
method: finalRequest.method(),
119147
url: this.generateUrlMatcher(finalRequest),
120148
body: finalRequest.body(),

src/mocks/mock-utils.js

+7
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ export function isSubset(A, B) {
5151
return toSortedQueryString(A) === toSortedQueryString(filteredB)
5252
}
5353

54+
export function filterKeys(A, B) {
55+
// Make B only contain the non-nullish keys it has in in common with A
56+
const keysFromA = validKeys(A)
57+
const filteredB = filterByPredicate(B, (keyFromB) => keysFromA.includes(keyFromB))
58+
return filteredB
59+
}
60+
5461
/**
5562
* Sort the query params on a URL based on the 'key=value' string value.
5663
* E.g. /example?b=2&a=1 will become /example?a=1&b=2

0 commit comments

Comments
 (0)