@bagaar/ember-graphql-mocking 0.7.0 → 0.9.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,33 @@
4
4
 
5
5
 
6
6
 
7
+
8
+
9
+ ## v0.9.0 (2023-11-27)
10
+
11
+ #### :boom: Breaking Change
12
+ * [#191](https://github.com/Bagaar/ember-graphql-mocking/pull/191) Support MSW v2 ([@bertdeblock](https://github.com/bertdeblock))
13
+ * [#189](https://github.com/Bagaar/ember-graphql-mocking/pull/189) Drop support for Ember v4.4 ([@bertdeblock](https://github.com/bertdeblock))
14
+ * [#177](https://github.com/Bagaar/ember-graphql-mocking/pull/177) Drop support for Node.js v16 ([@bertdeblock](https://github.com/bertdeblock))
15
+
16
+ #### :rocket: Enhancement
17
+ * [#191](https://github.com/Bagaar/ember-graphql-mocking/pull/191) Support MSW v2 ([@bertdeblock](https://github.com/bertdeblock))
18
+
19
+ #### :house: Internal
20
+ * [#176](https://github.com/Bagaar/ember-graphql-mocking/pull/176) Update dependencies ([@bertdeblock](https://github.com/bertdeblock))
21
+ * [#175](https://github.com/Bagaar/ember-graphql-mocking/pull/175) Update to Ember v5.4 ([@bertdeblock](https://github.com/bertdeblock))
22
+
23
+ #### Committers: 1
24
+ - Bert De Block ([@bertdeblock](https://github.com/bertdeblock))
25
+
26
+ ## v0.8.0 (2023-07-06)
27
+
28
+ #### :boom: Breaking Change
29
+ * [#143](https://github.com/Bagaar/ember-graphql-mocking/pull/143) Drop support for Ember versions below v4.4 and Node v14 ([@bertdeblock](https://github.com/bertdeblock))
30
+
31
+ #### Committers: 1
32
+ - Bert De Block ([@bertdeblock](https://github.com/bertdeblock))
33
+
7
34
  ## v0.7.0 (2023-02-22)
8
35
 
9
36
  #### :boom: Breaking Change
package/README.md CHANGED
@@ -1,17 +1,16 @@
1
1
  # Ember GraphQL Mocking
2
2
 
3
3
  [![CI](https://github.com/Bagaar/ember-graphql-mocking/workflows/CI/badge.svg)](https://github.com/Bagaar/ember-graphql-mocking/actions?query=workflow%3ACI)
4
- [![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
5
- [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)
4
+ [![NPM Version](https://badge.fury.io/js/%40bagaar%2Fember-graphql-mocking.svg)](https://badge.fury.io/js/%40bagaar%2Fember-graphql-mocking)
6
5
 
7
6
  Ember addon for mocking GraphQL requests using [Mock Service Worker (MSW)](https://github.com/mswjs/msw).
8
7
 
9
8
  ## Compatibility
10
9
 
11
- - Ember.js v3.28 or above
12
- - Ember CLI v3.28 or above
13
- - Node.js v14 or above
14
- - MSW v1 or above
10
+ - Ember.js v4.8 or above
11
+ - Ember CLI v4.8 or above
12
+ - Node.js v18 or above
13
+ - MSW v2 or above
15
14
 
16
15
  ## Installation
17
16
 
@@ -57,7 +56,8 @@ setup(QUnit.assert);
57
56
  start();
58
57
  ```
59
58
 
60
- > **NOTE:** Make sure to use `QUnit.begin`, as `setupEmberGraphqlMocking` returns a Promise.
59
+ > [!NOTE]
60
+ > Make sure to use `QUnit.begin`, as `setupEmberGraphqlMocking` returns a Promise.
61
61
 
62
62
  If you want to pass along additional [start options](https://mswjs.io/docs/api/setup-worker/start#options)
63
63
  to MSW's service worker, you can do so by defining an `mswStartOptions` object:
@@ -82,7 +82,8 @@ QUnit.begin(() =>
82
82
 
83
83
  `mockResolvers` accepts an object that consists of one or more resolvers. The key must be the name of the mocked operation, the value can either be a response object _or_ a function that returns a response object.
84
84
 
85
- > **NOTE:** Make sure that your operations are [named](https://graphql.org/learn/queries/#operation-name) in order for `@bagaar/ember-graphql-mocking` to function properly.
85
+ > [!NOTE]
86
+ > Make sure that your operations are [named](https://graphql.org/learn/queries/#operation-name) in order for `@bagaar/ember-graphql-mocking` to function properly.
86
87
 
87
88
  ```javascript
88
89
  // tests/acceptance/my-acceptance-test.js
@@ -116,9 +117,11 @@ module('Acceptance | ember graphql mocking', function (hooks) {
116
117
  });
117
118
  ```
118
119
 
119
- > **NOTE:** Calling `mockResolvers` multiple times within a single test, will simply merge all resolvers into a single root.
120
+ > [!NOTE]
121
+ > Calling `mockResolvers` multiple times within a single test, will simply merge all resolvers into a single root.
120
122
 
121
- 💡 Working versions of these code examples can be found in [this addon's `tests` folder](./tests/).
123
+ > [!NOTE]
124
+ > Working versions of these code examples can be found in [this addon's `tests` folder](./tests/).
122
125
 
123
126
  ## Contributing
124
127
 
@@ -1,7 +1,8 @@
1
1
  import { assert } from '@ember/debug';
2
2
  import { buildASTSchema, graphql } from 'graphql';
3
3
  import merge from 'lodash.merge';
4
- import { graphql as mswGraphql, setupWorker } from 'msw';
4
+ import { graphql as mswGraphql, HttpResponse } from 'msw';
5
+ import { setupWorker } from 'msw/browser';
5
6
 
6
7
  const IS_TESTEM = Boolean(window.Testem);
7
8
  const DEFAULT_OPTIONS = {
@@ -37,7 +38,7 @@ export function setupGraphqlTest(hooks) {
37
38
  export function mockResolvers(resolvers) {
38
39
  assert(
39
40
  'Cannot call `mockResolvers` before calling `setupGraphqlTest`. Please make sure to call `setupGraphqlTest(hooks);`.',
40
- isSetupGraphqlTestCalled
41
+ isSetupGraphqlTestCalled,
41
42
  );
42
43
 
43
44
  root = { ...root, ...resolvers };
@@ -59,15 +60,16 @@ function createWorker() {
59
60
 
60
61
  function createGraphqlOperationHandler(schemaDocument) {
61
62
  const schema = buildASTSchema(schemaDocument);
62
- const graphqlOperation = mswGraphql.operation(async (req, res, ctx) => {
63
- const queryResult = await graphql({
63
+ const graphqlOperation = mswGraphql.operation(async ({ request }) => {
64
+ const { query, variables } = await request.json();
65
+ const { data, errors } = await graphql({
64
66
  rootValue: root,
65
67
  schema,
66
- source: req.body.query,
67
- variableValues: req.variables,
68
+ source: query,
69
+ variableValues: variables,
68
70
  });
69
71
 
70
- return res(ctx.data(queryResult.data), ctx.errors(queryResult.errors));
72
+ return HttpResponse.json({ data, errors });
71
73
  });
72
74
 
73
75
  worker.use(graphqlOperation);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bagaar/ember-graphql-mocking",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Ember addon for mocking GraphQL requests using Mock Service Worker (MSW).",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -15,6 +15,8 @@
15
15
  "scripts": {
16
16
  "build": "ember build --environment=production",
17
17
  "lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
18
+ "lint:css": "stylelint \"**/*.css\"",
19
+ "lint:css:fix": "concurrently \"npm:lint:css -- --fix\"",
18
20
  "lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
19
21
  "lint:hbs": "ember-template-lint .",
20
22
  "lint:hbs:fix": "ember-template-lint . --fix",
@@ -31,64 +33,69 @@
31
33
  "@commitlint/config-conventional"
32
34
  ]
33
35
  },
34
- "peerDependencies": {
35
- "ember-source": "^3.28.0 || ^4.0.0",
36
- "msw": "^1.0.1"
37
- },
38
36
  "dependencies": {
37
+ "@babel/core": "^7.23.3",
39
38
  "broccoli-funnel": "^3.0.5",
40
39
  "broccoli-merge-trees": "^4.2.0",
41
- "ember-auto-import": "^2.5.0",
42
- "ember-cli-babel": "^7.26.11",
43
- "graphql": "^16.6.0",
40
+ "ember-auto-import": "^2.7.0",
41
+ "ember-cli-babel": "^8.2.0",
42
+ "graphql": "^16.8.1",
44
43
  "lodash.merge": "^4.6.2"
45
44
  },
46
45
  "devDependencies": {
47
- "@commitlint/cli": "^17.4.4",
48
- "@commitlint/config-conventional": "^17.4.4",
46
+ "@apollo/client": "^3.8.7",
47
+ "@babel/eslint-parser": "^7.23.3",
48
+ "@babel/plugin-proposal-decorators": "^7.23.3",
49
+ "@commitlint/cli": "^18.4.3",
50
+ "@commitlint/config-conventional": "^18.4.3",
49
51
  "@ember/optional-features": "^2.0.0",
50
- "@ember/string": "^3.0.1",
51
- "@ember/test-helpers": "^2.9.3",
52
- "@embroider/test-setup": "^2.0.2",
52
+ "@ember/test-helpers": "^3.2.1",
53
+ "@embroider/test-setup": "^3.0.3",
53
54
  "@glimmer/component": "^1.1.2",
54
55
  "@glimmer/tracking": "^1.1.2",
55
- "@release-it-plugins/lerna-changelog": "^5.0.0",
56
- "babel-eslint": "^10.1.0",
56
+ "@release-it-plugins/lerna-changelog": "^6.0.0",
57
57
  "broccoli-asset-rev": "^3.0.0",
58
- "concurrently": "^7.6.0",
59
- "ember-apollo-client": "^4.1.0",
60
- "ember-cli": "~4.10.0",
61
- "ember-cli-dependency-checker": "^3.3.1",
62
- "ember-cli-htmlbars": "^6.2.0",
58
+ "concurrently": "^8.2.2",
59
+ "ember-cli": "~5.4.1",
60
+ "ember-cli-clean-css": "^3.0.0",
61
+ "ember-cli-dependency-checker": "^3.3.2",
62
+ "ember-cli-htmlbars": "^6.3.0",
63
63
  "ember-cli-inject-live-reload": "^2.1.0",
64
64
  "ember-cli-sri": "^2.1.1",
65
65
  "ember-cli-terser": "^4.0.2",
66
66
  "ember-fetch": "^8.1.2",
67
67
  "ember-load-initializers": "^2.1.2",
68
- "ember-page-title": "^7.0.0",
69
- "ember-qunit": "^6.1.1",
70
- "ember-resolver": "^10.0.0",
71
- "ember-source": "~4.10.0",
68
+ "ember-page-title": "^8.0.0",
69
+ "ember-qunit": "^8.0.1",
70
+ "ember-resolver": "^11.0.1",
71
+ "ember-source": "~5.4.0",
72
72
  "ember-source-channel-url": "^3.0.0",
73
- "ember-template-lint": "^5.3.1",
74
- "ember-try": "^2.0.0",
75
- "eslint": "^7.32.0",
76
- "eslint-config-prettier": "^8.6.0",
77
- "eslint-plugin-ember": "^11.4.3",
78
- "eslint-plugin-n": "^15.6.1",
79
- "eslint-plugin-prettier": "^4.2.1",
80
- "eslint-plugin-qunit": "^7.3.4",
73
+ "ember-template-lint": "^5.13.0",
74
+ "ember-try": "^3.0.0",
75
+ "eslint": "^8.54.0",
76
+ "eslint-config-prettier": "^9.0.0",
77
+ "eslint-plugin-ember": "^11.11.1",
78
+ "eslint-plugin-n": "^16.3.1",
79
+ "eslint-plugin-prettier": "^5.0.1",
80
+ "eslint-plugin-qunit": "^8.0.1",
81
81
  "husky": "^8.0.3",
82
82
  "loader.js": "^4.7.0",
83
- "msw": "^1.0.1",
84
- "prettier": "^2.8.3",
85
- "qunit": "^2.19.3",
86
- "qunit-dom": "^2.0.0",
87
- "release-it": "^15.6.0",
88
- "webpack": "^5.75.0"
83
+ "msw": "^2.0.9",
84
+ "prettier": "^3.1.0",
85
+ "qunit": "^2.20.0",
86
+ "qunit-dom": "^3.0.0",
87
+ "release-it": "^16.2.1",
88
+ "stylelint": "^15.11.0",
89
+ "stylelint-config-standard": "^34.0.0",
90
+ "stylelint-prettier": "^4.1.0",
91
+ "webpack": "^5.89.0"
92
+ },
93
+ "peerDependencies": {
94
+ "ember-source": ">= 4.8.0",
95
+ "msw": "^2.0.0"
89
96
  },
90
97
  "engines": {
91
- "node": "14.* || 16.* || >= 18"
98
+ "node": ">= 18"
92
99
  },
93
100
  "publishConfig": {
94
101
  "access": "public",
@@ -2,13 +2,14 @@
2
2
  /* tslint:disable */
3
3
 
4
4
  /**
5
- * Mock Service Worker (1.0.1).
5
+ * Mock Service Worker (2.0.9).
6
6
  * @see https://github.com/mswjs/msw
7
7
  * - Please do NOT modify this file.
8
8
  * - Please do NOT serve this file on production.
9
9
  */
10
10
 
11
- const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70'
11
+ const INTEGRITY_CHECKSUM = '0877fcdc026242810f5bfde0d7178db4'
12
+ const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
12
13
  const activeClientIds = new Set()
13
14
 
14
15
  self.addEventListener('install', function () {
@@ -86,12 +87,6 @@ self.addEventListener('message', async function (event) {
86
87
 
87
88
  self.addEventListener('fetch', function (event) {
88
89
  const { request } = event
89
- const accept = request.headers.get('accept') || ''
90
-
91
- // Bypass server-sent events.
92
- if (accept.includes('text/event-stream')) {
93
- return
94
- }
95
90
 
96
91
  // Bypass navigation requests.
97
92
  if (request.mode === 'navigate') {
@@ -112,29 +107,8 @@ self.addEventListener('fetch', function (event) {
112
107
  }
113
108
 
114
109
  // Generate unique request ID.
115
- const requestId = Math.random().toString(16).slice(2)
116
-
117
- event.respondWith(
118
- handleRequest(event, requestId).catch((error) => {
119
- if (error.name === 'NetworkError') {
120
- console.warn(
121
- '[MSW] Successfully emulated a network error for the "%s %s" request.',
122
- request.method,
123
- request.url,
124
- )
125
- return
126
- }
127
-
128
- // At this point, any exception indicates an issue with the original request/response.
129
- console.error(
130
- `\
131
- [MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
132
- request.method,
133
- request.url,
134
- `${error.name}: ${error.message}`,
135
- )
136
- }),
137
- )
110
+ const requestId = crypto.randomUUID()
111
+ event.respondWith(handleRequest(event, requestId))
138
112
  })
139
113
 
140
114
  async function handleRequest(event, requestId) {
@@ -146,21 +120,29 @@ async function handleRequest(event, requestId) {
146
120
  // this message will pend indefinitely.
147
121
  if (client && activeClientIds.has(client.id)) {
148
122
  ;(async function () {
149
- const clonedResponse = response.clone()
150
- sendToClient(client, {
151
- type: 'RESPONSE',
152
- payload: {
153
- requestId,
154
- type: clonedResponse.type,
155
- ok: clonedResponse.ok,
156
- status: clonedResponse.status,
157
- statusText: clonedResponse.statusText,
158
- body:
159
- clonedResponse.body === null ? null : await clonedResponse.text(),
160
- headers: Object.fromEntries(clonedResponse.headers.entries()),
161
- redirected: clonedResponse.redirected,
123
+ const responseClone = response.clone()
124
+ // When performing original requests, response body will
125
+ // always be a ReadableStream, even for 204 responses.
126
+ // But when creating a new Response instance on the client,
127
+ // the body for a 204 response must be null.
128
+ const responseBody = response.status === 204 ? null : responseClone.body
129
+
130
+ sendToClient(
131
+ client,
132
+ {
133
+ type: 'RESPONSE',
134
+ payload: {
135
+ requestId,
136
+ isMockedResponse: IS_MOCKED_RESPONSE in response,
137
+ type: responseClone.type,
138
+ status: responseClone.status,
139
+ statusText: responseClone.statusText,
140
+ body: responseBody,
141
+ headers: Object.fromEntries(responseClone.headers.entries()),
142
+ },
162
143
  },
163
- })
144
+ [responseBody],
145
+ )
164
146
  })()
165
147
  }
166
148
 
@@ -196,20 +178,20 @@ async function resolveMainClient(event) {
196
178
 
197
179
  async function getResponse(event, client, requestId) {
198
180
  const { request } = event
199
- const clonedRequest = request.clone()
181
+
182
+ // Clone the request because it might've been already used
183
+ // (i.e. its body has been read and sent to the client).
184
+ const requestClone = request.clone()
200
185
 
201
186
  function passthrough() {
202
- // Clone the request because it might've been already used
203
- // (i.e. its body has been read and sent to the client).
204
- const headers = Object.fromEntries(clonedRequest.headers.entries())
187
+ const headers = Object.fromEntries(requestClone.headers.entries())
205
188
 
206
- // Remove MSW-specific request headers so the bypassed requests
207
- // comply with the server's CORS preflight check.
208
- // Operate with the headers as an object because request "Headers"
209
- // are immutable.
210
- delete headers['x-msw-bypass']
189
+ // Remove internal MSW request header so the passthrough request
190
+ // complies with any potential CORS preflight checks on the server.
191
+ // Some servers forbid unknown request headers.
192
+ delete headers['x-msw-intention']
211
193
 
212
- return fetch(clonedRequest, { headers })
194
+ return fetch(requestClone, { headers })
213
195
  }
214
196
 
215
197
  // Bypass mocking when the client is not active.
@@ -227,31 +209,36 @@ async function getResponse(event, client, requestId) {
227
209
 
228
210
  // Bypass requests with the explicit bypass header.
229
211
  // Such requests can be issued by "ctx.fetch()".
230
- if (request.headers.get('x-msw-bypass') === 'true') {
212
+ const mswIntention = request.headers.get('x-msw-intention')
213
+ if (['bypass', 'passthrough'].includes(mswIntention)) {
231
214
  return passthrough()
232
215
  }
233
216
 
234
217
  // Notify the client that a request has been intercepted.
235
- const clientMessage = await sendToClient(client, {
236
- type: 'REQUEST',
237
- payload: {
238
- id: requestId,
239
- url: request.url,
240
- method: request.method,
241
- headers: Object.fromEntries(request.headers.entries()),
242
- cache: request.cache,
243
- mode: request.mode,
244
- credentials: request.credentials,
245
- destination: request.destination,
246
- integrity: request.integrity,
247
- redirect: request.redirect,
248
- referrer: request.referrer,
249
- referrerPolicy: request.referrerPolicy,
250
- body: await request.text(),
251
- bodyUsed: request.bodyUsed,
252
- keepalive: request.keepalive,
218
+ const requestBuffer = await request.arrayBuffer()
219
+ const clientMessage = await sendToClient(
220
+ client,
221
+ {
222
+ type: 'REQUEST',
223
+ payload: {
224
+ id: requestId,
225
+ url: request.url,
226
+ mode: request.mode,
227
+ method: request.method,
228
+ headers: Object.fromEntries(request.headers.entries()),
229
+ cache: request.cache,
230
+ credentials: request.credentials,
231
+ destination: request.destination,
232
+ integrity: request.integrity,
233
+ redirect: request.redirect,
234
+ referrer: request.referrer,
235
+ referrerPolicy: request.referrerPolicy,
236
+ body: requestBuffer,
237
+ keepalive: request.keepalive,
238
+ },
253
239
  },
254
- })
240
+ [requestBuffer],
241
+ )
255
242
 
256
243
  switch (clientMessage.type) {
257
244
  case 'MOCK_RESPONSE': {
@@ -261,21 +248,12 @@ async function getResponse(event, client, requestId) {
261
248
  case 'MOCK_NOT_FOUND': {
262
249
  return passthrough()
263
250
  }
264
-
265
- case 'NETWORK_ERROR': {
266
- const { name, message } = clientMessage.data
267
- const networkError = new Error(message)
268
- networkError.name = name
269
-
270
- // Rejecting a "respondWith" promise emulates a network error.
271
- throw networkError
272
- }
273
251
  }
274
252
 
275
253
  return passthrough()
276
254
  }
277
255
 
278
- function sendToClient(client, message) {
256
+ function sendToClient(client, message, transferrables = []) {
279
257
  return new Promise((resolve, reject) => {
280
258
  const channel = new MessageChannel()
281
259
 
@@ -287,17 +265,28 @@ function sendToClient(client, message) {
287
265
  resolve(event.data)
288
266
  }
289
267
 
290
- client.postMessage(message, [channel.port2])
268
+ client.postMessage(
269
+ message,
270
+ [channel.port2].concat(transferrables.filter(Boolean)),
271
+ )
291
272
  })
292
273
  }
293
274
 
294
- function sleep(timeMs) {
295
- return new Promise((resolve) => {
296
- setTimeout(resolve, timeMs)
275
+ async function respondWithMock(response) {
276
+ // Setting response status code to 0 is a no-op.
277
+ // However, when responding with a "Response.error()", the produced Response
278
+ // instance will have status code set to 0. Since it's not possible to create
279
+ // a Response instance with status code 0, handle that use-case separately.
280
+ if (response.status === 0) {
281
+ return Response.error()
282
+ }
283
+
284
+ const mockedResponse = new Response(response.body, response)
285
+
286
+ Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
287
+ value: true,
288
+ enumerable: true,
297
289
  })
298
- }
299
290
 
300
- async function respondWithMock(response) {
301
- await sleep(response.delay)
302
- return new Response(response.body, response)
291
+ return mockedResponse
303
292
  }