@khanacademy/wonder-blocks-testing 12.0.0 → 13.0.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
@@ -1,5 +1,30 @@
1
1
  # @khanacademy/wonder-blocks-testing
2
2
 
3
+ ## 13.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - eb807af8: When mocking GraphQL, consider explicit undefined values in a request to be equivalent to missing keys in a mock
8
+
9
+ ### Minor Changes
10
+
11
+ - 16565a85: Add support for hard fails to the request mocking features
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [16565a85]
16
+ - @khanacademy/wonder-blocks-testing-core@1.1.0
17
+
18
+ ## 12.0.1
19
+
20
+ ### Patch Changes
21
+
22
+ - 02a1b298: Make sure we don't package tsconfig and tsbuildinfo files
23
+ - Updated dependencies [02a1b298]
24
+ - @khanacademy/wonder-blocks-core@7.0.1
25
+ - @khanacademy/wonder-blocks-data@13.0.12
26
+ - @khanacademy/wonder-blocks-testing-core@1.0.2
27
+
3
28
  ## 12.0.0
4
29
 
5
30
  ### Major Changes
package/dist/es/index.js CHANGED
@@ -6,8 +6,7 @@ import { InterceptRequests } from '@khanacademy/wonder-blocks-data';
6
6
  import { KindError, Errors } from '@khanacademy/wonder-stuff-core';
7
7
  import { RenderStateRoot } from '@khanacademy/wonder-blocks-core';
8
8
 
9
- const safeHasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
10
- const areObjectsEqual = (a, b) => {
9
+ const areObjectsEquivalent = (a, b) => {
11
10
  if (a === b) {
12
11
  return true;
13
12
  }
@@ -19,12 +18,9 @@ const areObjectsEqual = (a, b) => {
19
18
  }
20
19
  const aKeys = Object.keys(a);
21
20
  const bKeys = Object.keys(b);
22
- if (aKeys.length !== bKeys.length) {
23
- return false;
24
- }
25
- for (let i = 0; i < aKeys.length; i++) {
26
- const key = aKeys[i];
27
- if (!safeHasOwnProperty(b, key) || !areObjectsEqual(a[key], b[key])) {
21
+ const allKeys = new Set([...aKeys, ...bKeys]);
22
+ for (const key of allKeys) {
23
+ if (!areObjectsEquivalent(a[key], b[key])) {
28
24
  return false;
29
25
  }
30
26
  }
@@ -35,12 +31,12 @@ const gqlRequestMatchesMock = (mock, operation, variables, context) => {
35
31
  return false;
36
32
  }
37
33
  if (mock.variables != null) {
38
- if (!areObjectsEqual(mock.variables, variables)) {
34
+ if (!areObjectsEquivalent(mock.variables, variables)) {
39
35
  return false;
40
36
  }
41
37
  }
42
38
  if (mock.context != null) {
43
- if (!areObjectsEqual(mock.context, context)) {
39
+ if (!areObjectsEquivalent(mock.context, context)) {
44
40
  return false;
45
41
  }
46
42
  }
@@ -1,14 +1,74 @@
1
1
  import type { GqlOperation, GqlContext } from "@khanacademy/wonder-blocks-data";
2
- import type { GraphQLJson, MockResponse } from "@khanacademy/wonder-blocks-testing-core";
2
+ import type { ConfigureFn, GraphQLJson, MockResponse } from "@khanacademy/wonder-blocks-testing-core";
3
+ /**
4
+ * A GraphQL operation to be mocked.
5
+ *
6
+ * This is used to specify what a request must match in order for a mock to
7
+ * be used.
8
+ */
3
9
  export type GqlMockOperation<TData extends Record<any, any>, TVariables extends Record<any, any>, TContext extends GqlContext> = {
4
10
  operation: GqlOperation<TData, TVariables>;
5
11
  variables?: TVariables;
6
12
  context?: TContext;
7
13
  };
8
- type GqlMockOperationFn = <TData extends Record<any, any>, TVariables extends Record<any, any>, TContext extends GqlContext, TResponseData extends GraphQLJson<TData>>(operation: GqlMockOperation<TData, TVariables, TContext>, response: MockResponse<TResponseData>) => GqlFetchMockFn;
9
- export type GqlFetchMockFn = {
14
+ interface GqlMockOperationFn {
15
+ <TData extends Record<any, any>, TVariables extends Record<any, any>, TContext extends GqlContext, TResponseData extends GraphQLJson<TData>>(
16
+ /**
17
+ * The operation to match.
18
+ */
19
+ operation: GqlMockOperation<TData, TVariables, TContext>,
20
+ /**
21
+ * The response to return when the operation is matched.
22
+ */
23
+ response: MockResponse<TResponseData>): GqlFetchMockFn;
24
+ }
25
+ export interface GqlFetchMockFn {
26
+ /**
27
+ * The mock fetch function.
28
+ *
29
+ * This function is a drop-in replacement for the gqlFetch function used
30
+ * by Wonder Blocks Data. You should not need to call this function
31
+ * directly. Just pass this in places where you would pass a gqlFetch
32
+ * function, as provided by the GqlRouter.
33
+ */
10
34
  (operation: GqlOperation<any, any>, variables: Record<any, any> | null | undefined, context: GqlContext): Promise<Response>;
35
+ /**
36
+ * Mock a fetch operation.
37
+ *
38
+ * This adds a response for a given mocked operation. Operations are
39
+ * matched greedily, so if only the GraphQL operation is provided, then
40
+ * all requests for that operation will be matched, regardless of
41
+ * variables or context.
42
+ *
43
+ * Regardless of how many times this mock is matched, it will be used.
44
+ *
45
+ * @returns The mock fetch function for chaining.
46
+ */
11
47
  mockOperation: GqlMockOperationFn;
48
+ /**
49
+ * Mock a fetch operation once.
50
+ *
51
+ * This adds a response for a given mocked operation. Operations are
52
+ * matched greedily, so if only the GraphQL operation is provided, then
53
+ * all requests for that operation will be matched, regardless of
54
+ * variables or context.
55
+ *
56
+ * Once the added mock is used, it will be discarded and no longer match
57
+ * any requests.
58
+ *
59
+ * @returns The mock fetch function for chaining.
60
+ */
12
61
  mockOperationOnce: GqlMockOperationFn;
13
- };
62
+ /**
63
+ * Configure the mock fetch function with the given configuration.
64
+ *
65
+ * This function is provided as a convenience to allow for configuring the
66
+ * mock fetch function in a fluent manner. The configuration is applied
67
+ * to all mocks for a given fetch function; the last configuration applied
68
+ * will be the one that is used for all mocked operations.
69
+ *
70
+ * @returns The mock fetch function for chaining.
71
+ */
72
+ configure: ConfigureFn<GqlMockOperation<any, any, any>, GraphQLJson<any>>;
73
+ }
14
74
  export {};
package/dist/index.js CHANGED
@@ -32,8 +32,7 @@ function _interopNamespace(e) {
32
32
  var _extends__default = /*#__PURE__*/_interopDefaultLegacy(_extends);
33
33
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
34
34
 
35
- const safeHasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
36
- const areObjectsEqual = (a, b) => {
35
+ const areObjectsEquivalent = (a, b) => {
37
36
  if (a === b) {
38
37
  return true;
39
38
  }
@@ -45,12 +44,9 @@ const areObjectsEqual = (a, b) => {
45
44
  }
46
45
  const aKeys = Object.keys(a);
47
46
  const bKeys = Object.keys(b);
48
- if (aKeys.length !== bKeys.length) {
49
- return false;
50
- }
51
- for (let i = 0; i < aKeys.length; i++) {
52
- const key = aKeys[i];
53
- if (!safeHasOwnProperty(b, key) || !areObjectsEqual(a[key], b[key])) {
47
+ const allKeys = new Set([...aKeys, ...bKeys]);
48
+ for (const key of allKeys) {
49
+ if (!areObjectsEquivalent(a[key], b[key])) {
54
50
  return false;
55
51
  }
56
52
  }
@@ -61,12 +57,12 @@ const gqlRequestMatchesMock = (mock, operation, variables, context) => {
61
57
  return false;
62
58
  }
63
59
  if (mock.variables != null) {
64
- if (!areObjectsEqual(mock.variables, variables)) {
60
+ if (!areObjectsEquivalent(mock.variables, variables)) {
65
61
  return false;
66
62
  }
67
63
  }
68
64
  if (mock.context != null) {
69
- if (!areObjectsEqual(mock.context, context)) {
65
+ if (!areObjectsEquivalent(mock.context, context)) {
70
66
  return false;
71
67
  }
72
68
  }
package/package.json CHANGED
@@ -1,36 +1,36 @@
1
1
  {
2
- "name": "@khanacademy/wonder-blocks-testing",
3
- "version": "12.0.0",
4
- "design": "v1",
5
- "publishConfig": {
6
- "access": "public"
7
- },
8
- "description": "",
9
- "main": "dist/index.js",
10
- "module": "dist/es/index.js",
11
- "types": "dist/index.d.ts",
12
- "scripts": {
13
- "test": "echo \"Error: no test specified\" && exit 1"
14
- },
15
- "dependencies": {
16
- "@babel/runtime": "^7.18.6",
17
- "@khanacademy/wonder-blocks-core": "^7.0.0",
18
- "@khanacademy/wonder-blocks-data": "^13.0.11",
19
- "@khanacademy/wonder-blocks-testing-core": "^1.0.1"
20
- },
21
- "peerDependencies": {
22
- "@khanacademy/wonder-stuff-core": "^1.2.2",
23
- "@storybook/addon-actions": "^7.0.0",
24
- "aphrodite": "^1.2.5",
25
- "node-fetch": "^2.6.7",
26
- "react": "16.14.0",
27
- "react-dom": "16.14.0",
28
- "react-router-dom": "5.3.0"
29
- },
30
- "devDependencies": {
31
- "@khanacademy/wb-dev-build-settings": "^1.0.1",
32
- "@khanacademy/wonder-stuff-testing": "^3.0.1"
33
- },
34
- "author": "",
35
- "license": "MIT"
36
- }
2
+ "name": "@khanacademy/wonder-blocks-testing",
3
+ "version": "13.0.0",
4
+ "design": "v1",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "description": "",
9
+ "main": "dist/index.js",
10
+ "module": "dist/es/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "scripts": {
13
+ "test": "echo \"Error: no test specified\" && exit 1"
14
+ },
15
+ "dependencies": {
16
+ "@babel/runtime": "^7.18.6",
17
+ "@khanacademy/wonder-blocks-core": "^7.0.1",
18
+ "@khanacademy/wonder-blocks-data": "^13.0.12",
19
+ "@khanacademy/wonder-blocks-testing-core": "^1.1.0"
20
+ },
21
+ "peerDependencies": {
22
+ "@khanacademy/wonder-stuff-core": "^1.2.2",
23
+ "@storybook/addon-actions": "^7.0.0",
24
+ "aphrodite": "^1.2.5",
25
+ "node-fetch": "^2.6.7",
26
+ "react": "16.14.0",
27
+ "react-dom": "16.14.0",
28
+ "react-router-dom": "5.3.0"
29
+ },
30
+ "devDependencies": {
31
+ "@khanacademy/wb-dev-build-settings": "^1.0.1",
32
+ "@khanacademy/wonder-stuff-testing": "^3.0.1"
33
+ },
34
+ "author": "",
35
+ "license": "MIT"
36
+ }
@@ -1,234 +0,0 @@
1
- import {gqlRequestMatchesMock} from "../gql-request-matches-mock";
2
-
3
- describe("#gqlRequestMatchesMock", () => {
4
- it("should return false if operation types don't match", () => {
5
- // Arrange
6
- const mock = {
7
- operation: {
8
- id: "foo",
9
- type: "query",
10
- },
11
- } as const;
12
- const operation = {
13
- id: "foo",
14
- type: "mutation",
15
- } as const;
16
-
17
- // Act
18
- const result = gqlRequestMatchesMock(mock, operation, null, {});
19
-
20
- // Assert
21
- expect(result).toBe(false);
22
- });
23
-
24
- it("should return false if operation ids don't match", () => {
25
- // Arrange
26
- const mock = {
27
- operation: {
28
- id: "foo",
29
- type: "query",
30
- },
31
- } as const;
32
-
33
- // Act
34
- const result = gqlRequestMatchesMock(
35
- mock,
36
- {
37
- id: "bar",
38
- type: "query",
39
- },
40
- null,
41
- {},
42
- );
43
-
44
- // Assert
45
- expect(result).toBe(false);
46
- });
47
-
48
- it.each([{foo: "bar"}, {foo: "baz", anExtra: "property"}, null])(
49
- "should return false if variables don't match",
50
- (variables: any) => {
51
- // Arrange
52
- const mock = {
53
- operation: {
54
- id: "foo",
55
- type: "query",
56
- },
57
- variables: {
58
- foo: "baz",
59
- },
60
- } as const;
61
- const operation = {
62
- id: "foo",
63
- type: "query",
64
- } as const;
65
-
66
- // Act
67
- const result = gqlRequestMatchesMock(
68
- mock,
69
- operation,
70
- variables,
71
- {},
72
- );
73
-
74
- // Assert
75
- expect(result).toBe(false);
76
- },
77
- );
78
-
79
- it.each([{a: "context"}, null])(
80
- "should return false if contexts don't match",
81
- (context: any) => {
82
- // Arrange
83
- const mock = {
84
- operation: {
85
- id: "foo",
86
- type: "query",
87
- },
88
- variables: {
89
- foo: "bar",
90
- },
91
- context: {
92
- mock: "context",
93
- },
94
- } as const;
95
- const operation = {
96
- id: "foo",
97
- type: "query",
98
- } as const;
99
- const variables = {
100
- foo: "bar",
101
- } as const;
102
-
103
- // Act
104
- const result = gqlRequestMatchesMock(
105
- mock,
106
- operation,
107
- variables,
108
- context,
109
- );
110
-
111
- // Assert
112
- expect(result).toBe(false);
113
- },
114
- );
115
-
116
- it("should return true if operation matches and mock does not include context nor variables, regardless of comparison operation variables and context", () => {
117
- // Arrange
118
- const mock = {
119
- operation: {
120
- id: "foo",
121
- type: "query",
122
- },
123
- } as const;
124
-
125
- // Act
126
- const result = gqlRequestMatchesMock(
127
- mock,
128
- {
129
- id: "foo",
130
- type: "query",
131
- },
132
- {a: "variable"},
133
- {my: "context"},
134
- );
135
-
136
- // Assert
137
- expect(result).toBe(true);
138
- });
139
-
140
- it("should return true if operation and variables match and there is no mock context", () => {
141
- // Arrange
142
- const mock = {
143
- operation: {
144
- id: "foo",
145
- type: "query",
146
- },
147
- variables: {
148
- foo: "bar",
149
- },
150
- } as const;
151
-
152
- // Act
153
- const result = gqlRequestMatchesMock(
154
- mock,
155
- {
156
- id: "foo",
157
- type: "query",
158
- },
159
- {
160
- foo: "bar",
161
- },
162
- {my: "context"},
163
- );
164
-
165
- // Assert
166
- expect(result).toBe(true);
167
- });
168
-
169
- it("should return true if operation and context match and there are no mock variables", () => {
170
- // Arrange
171
- const mock = {
172
- operation: {
173
- id: "foo",
174
- type: "query",
175
- },
176
- context: {
177
- foo: "bar",
178
- },
179
- } as const;
180
-
181
- // Act
182
- const result = gqlRequestMatchesMock(
183
- mock,
184
- {
185
- id: "foo",
186
- type: "query",
187
- },
188
- {a: "variable"},
189
- {
190
- foo: "bar",
191
- },
192
- );
193
-
194
- // Assert
195
- expect(result).toBe(true);
196
- });
197
-
198
- it("should return true if everything matches", () => {
199
- // Arrange
200
- const mock = {
201
- operation: {
202
- id: "foo",
203
- type: "query",
204
- },
205
- variables: {
206
- foo: "bar",
207
- },
208
- context: {
209
- foo: "bar",
210
- },
211
- } as const;
212
- const operation = {
213
- id: "foo",
214
- type: "query",
215
- } as const;
216
- const variables = {
217
- foo: "bar",
218
- } as const;
219
- const context = {
220
- foo: "bar",
221
- } as const;
222
-
223
- // Act
224
- const result = gqlRequestMatchesMock(
225
- mock,
226
- operation,
227
- {...variables},
228
- {...context},
229
- );
230
-
231
- // Assert
232
- expect(result).toBe(true);
233
- });
234
- });