Skip to content

Commit 4006374

Browse files
committed
Setup mocks service worker
Setup new mocks
1 parent fdfa405 commit 4006374

18 files changed

+673
-158
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"eslint-plugin-prettier": "^3.1.1",
5858
"fork-ts-checker-webpack-plugin": "^3.1.1",
5959
"gh-pages": "^2.2.0",
60+
"msw": "^0.15.4",
6061
"prettier": "1.18.2",
6162
"react-app-rewired": "^2.1.5",
6263
"react-docgen-typescript-loader": "^3.6.0",

public/mockServiceWorker.js

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/**
2+
* Mock Service Worker.
3+
* @see https://github.com/open-draft/msw
4+
* - Please do NOT modify this file.
5+
* - Please do NOT serve this file on production.
6+
*/
7+
/* eslint-disable */
8+
/* tslint:disable */
9+
10+
const INTEGRITY_CHECKSUM = 'dcff59cce1b1cf75d9d30c0b6a63fdba'
11+
const bypassHeaderName = 'x-msw-bypass'
12+
13+
let clients = {}
14+
15+
self.addEventListener('install', function () {
16+
return self.skipWaiting()
17+
})
18+
19+
self.addEventListener('activate', async function (event) {
20+
return self.clients.claim()
21+
})
22+
23+
self.addEventListener('message', async function (event) {
24+
const clientId = event.source.id
25+
const client = await event.currentTarget.clients.get(clientId)
26+
const allClients = await self.clients.matchAll()
27+
const allClientIds = allClients.map((client) => client.id)
28+
29+
switch (event.data) {
30+
case 'INTEGRITY_CHECK_REQUEST': {
31+
sendToClient(client, {
32+
type: 'INTEGRITY_CHECK_RESPONSE',
33+
payload: INTEGRITY_CHECKSUM,
34+
})
35+
break
36+
}
37+
38+
case 'MOCK_ACTIVATE': {
39+
clients = ensureKeys(allClientIds, clients)
40+
clients[clientId] = true
41+
42+
sendToClient(client, {
43+
type: 'MOCKING_ENABLED',
44+
payload: true,
45+
})
46+
break
47+
}
48+
49+
case 'MOCK_DEACTIVATE': {
50+
clients = ensureKeys(allClientIds, clients)
51+
clients[clientId] = false
52+
break
53+
}
54+
55+
case 'CLIENT_CLOSED': {
56+
delete clients[clientId]
57+
58+
// Unregister itself when there are no more clients
59+
if (Object.keys(clients).length === 0) {
60+
self.registration.unregister()
61+
}
62+
63+
break
64+
}
65+
}
66+
})
67+
68+
self.addEventListener('fetch', async function (event) {
69+
const { clientId, request } = event
70+
const requestClone = request.clone()
71+
const getOriginalResponse = () => fetch(requestClone)
72+
73+
// Opening the DevTools triggers the "only-if-cached" request
74+
// that cannot be handled by the worker. Bypass such requests.
75+
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
76+
return
77+
}
78+
79+
event.respondWith(
80+
new Promise(async (resolve) => {
81+
const client = await event.target.clients.get(clientId)
82+
83+
if (
84+
// Bypass mocking when no clients active
85+
!client ||
86+
// Bypass mocking if the current client has mocking disabled
87+
!clients[clientId] ||
88+
// Bypass mocking for navigation requests
89+
request.mode === 'navigate'
90+
) {
91+
return resolve(getOriginalResponse())
92+
}
93+
94+
// Bypass requests with the explicit bypass header
95+
if (requestClone.headers.get(bypassHeaderName) === 'true') {
96+
const modifiedHeaders = serializeHeaders(requestClone.headers)
97+
// Remove the bypass header to comply with the CORS preflight check
98+
delete modifiedHeaders[bypassHeaderName]
99+
100+
const originalRequest = new Request(requestClone, {
101+
headers: new Headers(modifiedHeaders),
102+
})
103+
104+
return resolve(fetch(originalRequest))
105+
}
106+
107+
const reqHeaders = serializeHeaders(request.headers)
108+
const body = await request
109+
.json()
110+
.catch(() => request.text())
111+
.catch(() => null)
112+
113+
const rawClientMessage = await sendToClient(client, {
114+
type: 'REQUEST',
115+
payload: {
116+
url: request.url,
117+
method: request.method,
118+
headers: reqHeaders,
119+
cache: request.cache,
120+
mode: request.mode,
121+
credentials: request.credentials,
122+
destination: request.destination,
123+
integrity: request.integrity,
124+
redirect: request.redirect,
125+
referrer: request.referrer,
126+
referrerPolicy: request.referrerPolicy,
127+
body,
128+
bodyUsed: request.bodyUsed,
129+
keepalive: request.keepalive,
130+
},
131+
})
132+
133+
const clientMessage = JSON.parse(rawClientMessage)
134+
135+
switch (clientMessage.type) {
136+
case 'MOCK_SUCCESS': {
137+
setTimeout(
138+
resolve.bind(this, createResponse(clientMessage)),
139+
clientMessage.delay,
140+
)
141+
break
142+
}
143+
144+
case 'MOCK_NOT_FOUND': {
145+
return resolve(getOriginalResponse())
146+
}
147+
148+
case 'INTERNAL_ERROR': {
149+
const parsedBody = JSON.parse(clientMessage.payload.body)
150+
151+
console.error(
152+
`\
153+
[MSW] Request handler function for "%s %s" has thrown the following exception:
154+
155+
${parsedBody.errorType}: ${parsedBody.message}
156+
(see more detailed error stack trace in the mocked response body)
157+
158+
This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error.
159+
If you wish to mock an error response, please refer to this guide: https://redd.gitbook.io/msw/recipes/mocking-error-responses\
160+
`,
161+
request.method,
162+
request.url,
163+
)
164+
165+
return resolve(createResponse(clientMessage))
166+
}
167+
}
168+
}).catch((error) => {
169+
console.error(
170+
'[MSW] Failed to mock a "%s" request to "%s": %s',
171+
request.method,
172+
request.url,
173+
error,
174+
)
175+
}),
176+
)
177+
})
178+
179+
function serializeHeaders(headers) {
180+
const reqHeaders = {}
181+
headers.forEach((value, name) => {
182+
reqHeaders[name] = value
183+
})
184+
return reqHeaders
185+
}
186+
187+
function sendToClient(client, message) {
188+
return new Promise((resolve, reject) => {
189+
const channel = new MessageChannel()
190+
191+
channel.port1.onmessage = (event) => {
192+
if (event.data && event.data.error) {
193+
reject(event.data.error)
194+
} else {
195+
resolve(event.data)
196+
}
197+
}
198+
199+
client.postMessage(JSON.stringify(message), [channel.port2])
200+
})
201+
}
202+
203+
function createResponse(clientMessage) {
204+
return new Response(clientMessage.payload.body, {
205+
...clientMessage.payload,
206+
headers: clientMessage.payload.headers,
207+
})
208+
}
209+
210+
function ensureKeys(keys, obj) {
211+
return Object.keys(obj).reduce((acc, key) => {
212+
if (keys.includes(key)) {
213+
acc[key] = obj[key]
214+
}
215+
216+
return acc
217+
}, {})
218+
}

src/_api/_data/organizationsData.ts renamed to src/_api/_mocks/_data/organizationsData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import _keyBy from 'lodash/keyBy'
2-
import Organization from '../_types/Organization'
2+
import Organization from '../../_types/Organization'
33
// import organizationsToUsersData from './organizationsToUsersData'
44

55
const list: Organization[] = [

src/_api/_data/organizationsToUsersData.ts renamed to src/_api/_mocks/_data/organizationsToUsersData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import _groupBy from 'lodash/groupBy'
2-
import OrganizationToUser from '../_types/OrganizationToUser'
2+
import OrganizationToUser from '../../_types/OrganizationToUser'
33

44
// import organizationsData from './organizationsData'
55
// import usersData from './usersData'

src/_api/_data/usersData.ts renamed to src/_api/_mocks/_data/usersData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import _keyBy from 'lodash/keyBy'
22
import moment from 'moment'
3-
import User from '../_types/User'
3+
import User from '../../_types/User'
44
import organizationsToUsersData from './organizationsToUsersData'
55

66
const list: User[] = [

src/_api/_mocks/_ref/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
console.log('hello mocks!')
2+
3+
export default {}
4+
5+
// import { AxiosInstance } from 'axios'
6+
// import MockAdapter from 'axios-mock-adapter'
7+
8+
// import usersMocks from './usersMocks'
9+
// import organizationsMocks from './organizationsMocks'
10+
11+
// const init = (instance: AxiosInstance) => {
12+
// const mockAdapter = new MockAdapter(instance, { delayResponse: 200 })
13+
14+
// usersMocks.init(mockAdapter, instance)
15+
// organizationsMocks.init(mockAdapter, instance)
16+
17+
// return mockAdapter
18+
// }
19+
20+
// export default {
21+
// init,
22+
// }
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { AxiosInstance } from 'axios'
2+
import MockAdapter from 'axios-mock-adapter'
3+
import organizationsData from './../_data/organizationsData'
4+
5+
export default {
6+
init(mock: MockAdapter, instance: AxiosInstance) {
7+
mock.onGet('/organizations').reply(200, {
8+
organizations: {
9+
...organizationsData.list,
10+
},
11+
count: organizationsData.list.length,
12+
})
13+
14+
//
15+
mock.onGet(/\/organizations\/\d+/).reply((config: any) => {
16+
// console.log(config)
17+
const urlPaths = config.url.split('/')
18+
const organizationId = urlPaths[urlPaths.length - 1]
19+
const organization = organizationsData.byId[organizationId]
20+
21+
if (organization) {
22+
return [200, { ...organization }]
23+
} else {
24+
return [404, { message: 'Organization not found ' }]
25+
}
26+
})
27+
28+
mock.onPut(/\/organizations\/\d+/).reply((config: any) => {
29+
const urlPaths = config.url.split('/')
30+
const organizationId = urlPaths[urlPaths.length - 1]
31+
const organizationData = JSON.parse(config.data)
32+
const organization = organizationsData.byId[organizationId]
33+
34+
if (organization) {
35+
return [200, { ...organization, ...organizationData }]
36+
} else {
37+
return [403, { message: 'Update not permitted' }]
38+
}
39+
})
40+
41+
mock.onPost(/\/organizations/).reply((config: any) => {
42+
const organizationData = JSON.parse(config.data)
43+
44+
return [200, { id: 100, ...organizationData }]
45+
})
46+
47+
mock.onDelete(/\/organizations\/\d+/).reply((config: any) => {
48+
return [200, { message: 'Organization deleted' }]
49+
})
50+
},
51+
}

0 commit comments

Comments
 (0)