@jaypie/testkit 1.0.20 → 1.0.22

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/README.md CHANGED
@@ -12,21 +12,33 @@ npm install --save-dev @jaypie/testkit
12
12
 
13
13
  ### Example
14
14
 
15
+ #### Mocking Jaypie
16
+
17
+ The testkit provides a complete mock for Jaypie including:
18
+
19
+ * Log spying (`expect(log.warn).toHaveBeenCalled()`)
20
+ * Default responses for runtime-only functions (`connect`, `sendMessage`, `submitMetric`)
21
+ * No automatic error handling for handlers (which is good in production but obfuscates tests)
22
+ * Most non-utility functions are mocked to allow simple testing
23
+
24
+ ```javascript
25
+ vi.mock("jaypie", vi.importActual("@jaypie/testkit"));
26
+ ```
27
+
15
28
  #### Log Spying
16
29
 
17
30
  ```javascript
18
- import { restoreLog, spyLog } from "@jaypie/testkit";
19
- import { log } from "@jaypie/core";
31
+ import { log } from "jaypie";
32
+
33
+ vi.mock("jaypie", vi.importActual("@jaypie/testkit"));
20
34
 
21
- beforeEach(() => {
22
- spyLog(log);
23
- });
24
35
  afterEach(() => {
25
- restoreLog(log);
26
36
  vi.clearAllMocks();
27
37
  });
28
38
 
29
39
  test("log", () => {
40
+ expect(vi.isMockFunction(log.warn)).toBe(true);
41
+ expect(log.warn).not.toHaveBeenCalled();
30
42
  log.warn("Danger");
31
43
  expect(log.warn).toHaveBeenCalled();
32
44
  expect(log.error).not.toHaveBeenCalled();
@@ -256,7 +268,7 @@ Restores the `log` provided by `@jaypie/core`, commonly performed `afterEach` wi
256
268
 
257
269
  ### `spyLog(log)`
258
270
 
259
- Spies on the `log` provided by `@jaypie/core`, commonly performed `beforeEach` with `restoreLog` in `afterEach`.
271
+ Spies on the `log` provided by `@jaypie/core`, commonly performed `beforeEach` with `restoreLog` in `afterEach`. Not necessary when mocking the entire Jaypie module.
260
272
 
261
273
  ```javascript
262
274
  import { restoreLog, spyLog } from "@jaypie/testkit";
@@ -303,6 +315,7 @@ const event = sqsTestRecords(
303
315
 
304
316
  | Date | Version | Summary |
305
317
  | ---------- | ------- | -------------- |
318
+ | 7/16/2024 | 1.0.21 | Export Jaypie mock as default |
306
319
  | 3/20/2024 | 1.0.2 | Export `LOG` |
307
320
  | 3/16/2024 | 1.0.0 | Artists ship |
308
321
  | 3/15/2024 | 0.1.0 | Initial deploy |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/testkit",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
4
4
  "author": "Finlayson Studio",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -21,6 +21,7 @@
21
21
  "test": "vitest",
22
22
  "test:spec:constants": "vitest run ./src/__tests__/constants.spec.js",
23
23
  "test:spec:index": "vitest run ./src/__tests__/index.spec.js",
24
+ "test:spec:jaypie.mock": "vitest run ./src/__tests__/jaypie.mock.spec.js",
24
25
  "test:spec:jsonApiSchema.module": "vitest run ./src/__tests__/jsonApiSchema.module.spec.js",
25
26
  "test:spec:matchers.module": "vitest run ./src/__tests__/matchers.module.spec.js",
26
27
  "test:spec:mockLog.module": "vitest run ./src/__tests__/mockLog.module.spec.js",
@@ -33,6 +34,7 @@
33
34
  },
34
35
  "dependencies": {
35
36
  "@jaypie/core": "^1.0.33",
37
+ "jaypie": "^1.0.44",
36
38
  "jest-json-schema": "^6.1.0",
37
39
  "lodash.isequal": "^4.5.0",
38
40
  "uuid": "^9.0.1"
package/src/index.js CHANGED
@@ -1,9 +1,12 @@
1
+ import * as mockingJay from "./jaypie.mock.js";
2
+
1
3
  //
2
4
  //
3
5
  // Constants
4
6
  //
5
7
 
6
8
  export { LOG } from "./constants.js";
9
+
7
10
  //
8
11
  //
9
12
  // Export
@@ -13,3 +16,5 @@ export { jsonApiErrorSchema, jsonApiSchema } from "./jsonApiSchema.module.js";
13
16
  export { default as matchers } from "./matchers.module.js";
14
17
  export { mockLogFactory, restoreLog, spyLog } from "./mockLog.module.js";
15
18
  export { default as sqsTestRecords } from "./sqsTestRecords.function.js";
19
+
20
+ export default mockingJay;
@@ -0,0 +1,259 @@
1
+ import { getMessages as originalGetMessages } from "@jaypie/aws";
2
+ import { force, uuid as originalUuid } from "@jaypie/core";
3
+ import {
4
+ BadRequestError,
5
+ HTTP,
6
+ JAYPIE,
7
+ log,
8
+ UnavailableError,
9
+ } from "@jaypie/core";
10
+ import { beforeAll, vi } from "vitest";
11
+
12
+ import { spyLog } from "./mockLog.module.js";
13
+
14
+ //
15
+ //
16
+ // Setup
17
+ //
18
+
19
+ const TAG = JAYPIE.LIB.TESTKIT;
20
+
21
+ // Export all the modules from Jaypie packages:
22
+
23
+ export * from "@jaypie/aws";
24
+ export * from "@jaypie/core";
25
+ export * from "@jaypie/express";
26
+ export * from "@jaypie/datadog";
27
+ export * from "@jaypie/lambda";
28
+ export * from "@jaypie/mongoose";
29
+
30
+ // Spy on log:
31
+
32
+ beforeAll(() => {
33
+ spyLog(log);
34
+ });
35
+
36
+ // afterEach(() => {
37
+ // This is not necessary because the log isn't being used outside tests:
38
+ // restoreLog(log);
39
+ // The is the client's responsibility:
40
+ // vi.clearAllMocks();
41
+ // });
42
+
43
+ //
44
+ //
45
+ // Mock Functions
46
+ //
47
+
48
+ // @jaypie/aws
49
+
50
+ export const getMessages = vi.fn((...params) => originalGetMessages(...params));
51
+
52
+ export const getSecret = vi.fn(() => {
53
+ return `_MOCK_SECRET_[${TAG}]`;
54
+ });
55
+
56
+ export const sendBatchMessages = vi.fn(() => {
57
+ // TODO: better default value
58
+ return { value: `_MOCK_BATCH_MESSAGES_[${TAG}]` };
59
+ });
60
+
61
+ export const sendMessage = vi.fn(() => {
62
+ // TODO: better default value
63
+ return { value: `_MOCK_MESSAGE_[${TAG}]` };
64
+ });
65
+
66
+ // @jaypie/core Functions
67
+
68
+ export const envBoolean = vi.fn(() => {
69
+ return true;
70
+ });
71
+
72
+ export const jaypieHandler = vi.fn(
73
+ (
74
+ handler,
75
+ {
76
+ setup = [],
77
+ teardown = [],
78
+ unavailable = force.boolean(process.env.PROJECT_UNAVAILABLE),
79
+ validate = [],
80
+ } = {},
81
+ ) => {
82
+ return async (...args) => {
83
+ let result;
84
+ let thrownError;
85
+ if (unavailable) throw new UnavailableError();
86
+ for (const validator of validate) {
87
+ if (typeof validator === "function") {
88
+ const valid = await validator(...args);
89
+ if (valid === false) {
90
+ throw new BadRequestError();
91
+ }
92
+ }
93
+ }
94
+ try {
95
+ for (const setupFunction of setup) {
96
+ if (typeof setupFunction === "function") {
97
+ await setupFunction(...args);
98
+ }
99
+ }
100
+ result = handler(...args);
101
+ } catch (error) {
102
+ thrownError = error;
103
+ }
104
+ for (const teardownFunction of teardown) {
105
+ if (typeof teardownFunction === "function") {
106
+ try {
107
+ await teardownFunction(...args);
108
+ } catch (error) {
109
+ // eslint-disable-next-line no-console
110
+ console.error(error);
111
+ }
112
+ }
113
+ }
114
+ if (thrownError) {
115
+ throw thrownError;
116
+ }
117
+ return result;
118
+ };
119
+ },
120
+ );
121
+
122
+ export const sleep = vi.fn(() => {
123
+ return true;
124
+ });
125
+
126
+ export const uuid = vi.fn(originalUuid);
127
+
128
+ // @jaypie/datadog
129
+
130
+ export const submitMetric = vi.fn(() => {
131
+ return true;
132
+ });
133
+
134
+ export const submitMetricSet = vi.fn(() => {
135
+ return true;
136
+ });
137
+
138
+ // @jaypie/express
139
+
140
+ export const expressHandler = vi.fn((handler, props = {}) => {
141
+ if (typeof handler !== "function") {
142
+ throw new BadRequestError("handler must be a function");
143
+ }
144
+ if (!props) {
145
+ props = {};
146
+ }
147
+ props.setup = force.array(props.setup); // allows a single item
148
+ props.teardown = force.array(props.teardown); // allows a single item
149
+ if (!Array.isArray(props.setup)) {
150
+ props.setup = [];
151
+ }
152
+ if (props.locals === null) {
153
+ throw new BadRequestError("locals cannot be null");
154
+ }
155
+ if (props.locals) {
156
+ if (typeof props.locals !== "object" || Array.isArray(props.locals)) {
157
+ throw new BadRequestError("locals must be an object");
158
+ }
159
+ // Locals
160
+ const keys = Object.keys(props.locals);
161
+ if (keys.length > 0) {
162
+ props.setup.push((req = {}) => {
163
+ if (typeof req !== "object") {
164
+ throw new BadRequestError("req must be an object");
165
+ }
166
+ // Set req.locals if it doesn't exist
167
+ if (!req.locals) req.locals = {};
168
+ if (typeof req.locals !== "object" || Array.isArray(req.locals)) {
169
+ throw new BadRequestError("req.locals must be an object");
170
+ }
171
+ if (!req.locals._jaypie) req.locals._jaypie = {};
172
+ });
173
+ const localsSetup = async (localsReq, localsRes) => {
174
+ for (let i = 0; i < keys.length; i += 1) {
175
+ const key = keys[i];
176
+ if (typeof props.locals[key] === "function") {
177
+ // eslint-disable-next-line no-await-in-loop
178
+ localsReq.locals[key] = await props.locals[key](
179
+ localsReq,
180
+ localsRes,
181
+ );
182
+ } else {
183
+ localsReq.locals[key] = props.locals[key];
184
+ }
185
+ }
186
+ };
187
+ props.setup.push(localsSetup);
188
+ }
189
+ }
190
+ const jaypieFunction = jaypieHandler(handler, props);
191
+ return async (req, res = {}, ...extra) => {
192
+ // For mocking, let's make sure res json, send, and status are functions
193
+ const status = HTTP.CODE.OK;
194
+ if (res && typeof res.status === "function") {
195
+ res.status(200);
196
+ }
197
+ const response = jaypieFunction(req, res, ...extra);
198
+ if (response) {
199
+ if (typeof response === "object") {
200
+ if (typeof response.json === "function") {
201
+ if (res && typeof res.json === "function") {
202
+ res.json(response.json());
203
+ }
204
+ } else {
205
+ if (res && typeof res.status === "function") {
206
+ res.status(status).json(response);
207
+ }
208
+ }
209
+ } else if (typeof response === "string") {
210
+ try {
211
+ if (res && typeof res.status === "function") {
212
+ res.status(status).json(JSON.parse(response));
213
+ }
214
+ } catch (error) {
215
+ if (res && typeof res.status === "function") {
216
+ res.status(status).send(response);
217
+ }
218
+ }
219
+ } else if (response === true) {
220
+ if (res && typeof res.status === "function") {
221
+ res.status(HTTP.CODE.CREATED).send();
222
+ }
223
+ } else {
224
+ if (res && typeof res.status === "function") {
225
+ res.status(status).send(response);
226
+ }
227
+ }
228
+ } else {
229
+ // No response
230
+ if (res && typeof res.status === "function") {
231
+ res.status(HTTP.CODE.NO_CONTENT).send();
232
+ }
233
+ }
234
+ return response;
235
+ };
236
+ });
237
+
238
+ // @jaypie/lambda
239
+
240
+ // For testing, this is the same as the jaypieHandler
241
+ export const lambdaHandler = vi.fn((handler, props = {}) => {
242
+ return async (event, context, ...extra) => {
243
+ return jaypieHandler(handler, props)(event, context, ...extra);
244
+ };
245
+ });
246
+
247
+ // @jaypie/mongoose
248
+
249
+ export const connect = vi.fn(() => {
250
+ return true;
251
+ });
252
+
253
+ export const connectFromSecretEnv = vi.fn(() => {
254
+ return true;
255
+ });
256
+
257
+ export const disconnect = vi.fn(() => {
258
+ return true;
259
+ });