@jaypie/testkit 0.1.2 → 1.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/.vscode/settings.json
CHANGED
package/README.md
CHANGED
|
@@ -12,16 +12,212 @@ npm install --save-dev @jaypie/testkit
|
|
|
12
12
|
|
|
13
13
|
### Example
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
#### Log Spying
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
import { restoreLog, spyLog } from "@jaypie/testkit";
|
|
19
|
+
import { log } from "@jaypie/core";
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
spyLog(log);
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
restoreLog(log);
|
|
26
|
+
vi.clearAllMocks();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("log", () => {
|
|
30
|
+
log.warn("Danger");
|
|
31
|
+
expect(log.warn).toHaveBeenCalled();
|
|
32
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
👺 Logging Conventions:
|
|
37
|
+
|
|
38
|
+
* Only use `log.trace` or `log.var` during "happy path"
|
|
39
|
+
* Use `log.debug` for edge cases
|
|
40
|
+
* Now you can add an "observability" test that will fail as soon as new code triggers an unexpected edge condition
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
describe("Observability", () => {
|
|
44
|
+
it("Does not log above trace", async () => {
|
|
45
|
+
// Arrange
|
|
46
|
+
// TODO: "happy path" setup
|
|
47
|
+
// Act
|
|
48
|
+
await myNewFunction(); // TODO: add any "happy path" parameters
|
|
49
|
+
// Assert
|
|
50
|
+
expect(log.debug).not.toHaveBeenCalled();
|
|
51
|
+
expect(log.info).not.toHaveBeenCalled();
|
|
52
|
+
expect(log.warn).not.toHaveBeenCalled();
|
|
53
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
54
|
+
expect(log.fatal).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
> 👺 Follow the "arrange, act, assert" pattern
|
|
60
|
+
|
|
61
|
+
#### Test Matchers
|
|
62
|
+
|
|
63
|
+
testSetup.js
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import { matchers as jaypieMatchers } from "@jaypie/testkit";
|
|
67
|
+
import * as extendedMatchers from "jest-extended";
|
|
68
|
+
import { expect } from "vitest";
|
|
69
|
+
|
|
70
|
+
expect.extend(extendedMatchers);
|
|
71
|
+
expect.extend(jaypieMatchers);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
test.spec.js
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
import { ConfigurationError } from "@jaypie/core";
|
|
78
|
+
|
|
79
|
+
const error = new ConfigurationError();
|
|
80
|
+
const json = error.json();
|
|
81
|
+
expect(error).toBeJaypieError();
|
|
82
|
+
expect(json).toBeJaypieError();
|
|
83
|
+
```
|
|
16
84
|
|
|
17
85
|
## 📖 Reference
|
|
18
86
|
|
|
19
|
-
|
|
87
|
+
```
|
|
88
|
+
import {
|
|
89
|
+
jsonApiErrorSchema,
|
|
90
|
+
jsonApiSchema,
|
|
91
|
+
matchers,
|
|
92
|
+
mockLogFactory,
|
|
93
|
+
restoreLog,
|
|
94
|
+
spyLog,
|
|
95
|
+
} from '@jaypie/testkit'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `jsonApiErrorSchema`
|
|
99
|
+
|
|
100
|
+
A [JSON Schema](https://json-schema.org/) validator for the [JSON:API](https://jsonapi.org/) error schema. Powers the `toBeJaypieError` matcher (via `toMatchSchema`).
|
|
101
|
+
|
|
102
|
+
### `jsonApiSchema`
|
|
103
|
+
|
|
104
|
+
A [JSON Schema](https://json-schema.org/) validator for the [JSON:API](https://jsonapi.org/) data schema.
|
|
105
|
+
|
|
106
|
+
### `matchers`
|
|
107
|
+
|
|
108
|
+
testSetup.js
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
import { matchers as jaypieMatchers } from "@jaypie/testkit";
|
|
112
|
+
import * as extendedMatchers from "jest-extended";
|
|
113
|
+
import { expect } from "vitest";
|
|
114
|
+
|
|
115
|
+
expect.extend(extendedMatchers);
|
|
116
|
+
expect.extend(jaypieMatchers);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `expect(subject).toBeJaypieError()`
|
|
120
|
+
|
|
121
|
+
Validates instance objects:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
try {
|
|
125
|
+
throw new Error("Sorpresa!");
|
|
126
|
+
} catch (error) {
|
|
127
|
+
expect(error).not.toBeJaypieError();
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Validates plain old JSON:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
expect({ errors: [ { status, title, detail } ] }).toBeJaypieError();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Jaypie errors, which are `ProjectErrors`, all have a `.json()` to convert
|
|
138
|
+
|
|
139
|
+
#### `expect(subject).toBeValidSchema()`
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
import { jsonApiErrorSchema, jsonApiSchema } from "@jaypie/testkit";
|
|
143
|
+
|
|
144
|
+
expect(jsonApiErrorSchema).toBeValidSchema();
|
|
145
|
+
expect(jsonApiSchema).toBeValidSchema();
|
|
146
|
+
expect({ project: "mayhem" }).not.toBeValidSchema();
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
From `jest-json-schema` [toBeValidSchema.js](https://github.com/americanexpress/jest-json-schema/blob/main/matchers/toBeValidSchema.js) (not documented in README)
|
|
150
|
+
|
|
151
|
+
#### `expect(subject).toMatchSchema(schema)`
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
import { jsonApiErrorSchema, jsonApiSchema } from "@jaypie/testkit";
|
|
155
|
+
import { ConfigurationError } from "@jaypie/core";
|
|
156
|
+
|
|
157
|
+
const error = new ConfigurationError();
|
|
158
|
+
const json = error.json();
|
|
159
|
+
expect(json).toMatchSchema(jsonApiErrorSchema);
|
|
160
|
+
expect(json).not.toMatchSchema(jsonApiSchema);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
From `jest-json-schema`; see [README](https://github.com/americanexpress/jest-json-schema?tab=readme-ov-file#tomatchschemaschema)
|
|
164
|
+
|
|
165
|
+
### `mockLogFactory()`
|
|
166
|
+
|
|
167
|
+
Creates a mock of the `log` provided by `@jaypie/core`.
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
import { mockLogFactory } from "@jaypie/testkit";
|
|
171
|
+
|
|
172
|
+
const log = mockLogFactory();
|
|
173
|
+
log.warn("Danger");
|
|
174
|
+
expect(log.warn).toHaveBeenCalled();
|
|
175
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### `restoreLog(log)`
|
|
179
|
+
|
|
180
|
+
Restores the `log` provided by `@jaypie/core`, commonly performed `afterEach` with `spyLog` in `beforeEach`. See example with `spyLog`.
|
|
181
|
+
|
|
182
|
+
### `spyLog(log)`
|
|
183
|
+
|
|
184
|
+
Spies on the `log` provided by `@jaypie/core`, commonly performed `beforeEach` with `restoreLog` in `afterEach`.
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
import { restoreLog, spyLog } from "@jaypie/testkit";
|
|
188
|
+
import { log } from "@jaypie/core";
|
|
189
|
+
|
|
190
|
+
beforeEach(() => {
|
|
191
|
+
spyLog(log);
|
|
192
|
+
});
|
|
193
|
+
afterEach(() => {
|
|
194
|
+
restoreLog(log);
|
|
195
|
+
vi.clearAllMocks();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("log", () => {
|
|
199
|
+
log.warn("Danger");
|
|
200
|
+
expect(log.warn).toHaveBeenCalled();
|
|
201
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## 🌠 Wishlist
|
|
206
|
+
|
|
207
|
+
* matcher toBeHttpStatus
|
|
208
|
+
* matcher toBeJaypieAny
|
|
209
|
+
* matcher toBeJaypieData
|
|
210
|
+
* matcher toBeJaypieDataObject
|
|
211
|
+
* matcher toBeJaypieDataArray
|
|
212
|
+
* matcher toThrowJaypieError
|
|
213
|
+
* ...@knowdev/jest
|
|
20
214
|
|
|
21
215
|
## 📝 Changelog
|
|
22
216
|
|
|
23
217
|
| Date | Version | Summary |
|
|
24
218
|
| ---------- | ------- | -------------- |
|
|
219
|
+
| 3/16/2024 | 1.0.0 | Artists ship |
|
|
220
|
+
| 3/15/2024 | 0.1.0 | Initial deploy |
|
|
25
221
|
| 3/15/2024 | 0.0.1 | Initial commit |
|
|
26
222
|
|
|
27
223
|
## 📜 License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaypie/testkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"author": "Finlayson Studio",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
"test:spec:index": "vitest run ./src/__tests__/index.spec.js",
|
|
16
16
|
"test:spec:jsonApiSchema.module": "vitest run ./src/__tests__/jsonApiSchema.module.spec.js",
|
|
17
17
|
"test:spec:matchers.module": "vitest run ./src/__tests__/matchers.module.spec.js",
|
|
18
|
-
"test:spec:mockLog.module": "vitest run ./src/__tests__/mockLog.module.spec.js"
|
|
18
|
+
"test:spec:mockLog.module": "vitest run ./src/__tests__/mockLog.module.spec.js",
|
|
19
|
+
"test:spec:toBeJaypieError.matcher": "vitest run ./src/matchers/__tests__/toBeJaypieError.matcher.spec.js"
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
21
22
|
"jest-json-schema": "^6.1.0"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
25
|
+
"@jaypie/core": "^0.3.7",
|
|
24
26
|
"eslint": "^8.57.0",
|
|
25
27
|
"eslint-config-prettier": "^9.1.0",
|
|
26
28
|
"eslint-plugin-import": "^2.29.1",
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// eslint-disable-next-line no-unused-vars
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { ConfigurationError } from "@jaypie/core";
|
|
5
|
+
|
|
6
|
+
// Subject
|
|
7
|
+
import toBeJaypieError from "../toBeJaypieError.matcher.js";
|
|
8
|
+
|
|
9
|
+
//
|
|
10
|
+
//
|
|
11
|
+
// Mock constants
|
|
12
|
+
//
|
|
13
|
+
|
|
14
|
+
//
|
|
15
|
+
//
|
|
16
|
+
// Mock modules
|
|
17
|
+
//
|
|
18
|
+
|
|
19
|
+
//
|
|
20
|
+
//
|
|
21
|
+
// Mock environment
|
|
22
|
+
//
|
|
23
|
+
|
|
24
|
+
const DEFAULT_ENV = process.env;
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
process.env = { ...process.env };
|
|
27
|
+
});
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
process.env = DEFAULT_ENV;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
//
|
|
33
|
+
//
|
|
34
|
+
// Run tests
|
|
35
|
+
//
|
|
36
|
+
|
|
37
|
+
describe("To Be Jaypie Error Matcher", () => {
|
|
38
|
+
it("Is a function", () => {
|
|
39
|
+
expect(toBeJaypieError).toBeFunction();
|
|
40
|
+
});
|
|
41
|
+
describe("Error Conditions", () => {
|
|
42
|
+
it("Rejects instances of plain error objects", () => {
|
|
43
|
+
const error = new Error(
|
|
44
|
+
"If this is your first night at Fight Club, you have to fight",
|
|
45
|
+
);
|
|
46
|
+
const result = toBeJaypieError(error);
|
|
47
|
+
expect(result.message).toBeFunction();
|
|
48
|
+
expect(result.message()).toBeString();
|
|
49
|
+
expect(result.pass).toBeFalse();
|
|
50
|
+
});
|
|
51
|
+
it("Rejects if nothing passed", () => {
|
|
52
|
+
const result = toBeJaypieError();
|
|
53
|
+
expect(result.message).toBeFunction();
|
|
54
|
+
expect(result.message()).toBeString();
|
|
55
|
+
expect(result.pass).toBeFalse();
|
|
56
|
+
});
|
|
57
|
+
it("Rejects if non-object passed", () => {
|
|
58
|
+
const result = toBeJaypieError(12);
|
|
59
|
+
expect(result.message).toBeFunction();
|
|
60
|
+
expect(result.message()).toBeString();
|
|
61
|
+
expect(result.pass).toBeFalse();
|
|
62
|
+
});
|
|
63
|
+
it("Rejects if no errors array", () => {
|
|
64
|
+
const result = toBeJaypieError({});
|
|
65
|
+
expect(result.message).toBeFunction();
|
|
66
|
+
expect(result.message()).toBeString();
|
|
67
|
+
expect(result.pass).toBeFalse();
|
|
68
|
+
});
|
|
69
|
+
it("Rejects if errors array is empty", () => {
|
|
70
|
+
const result = toBeJaypieError({ errors: [] });
|
|
71
|
+
expect(result.message).toBeFunction();
|
|
72
|
+
expect(result.message()).toBeString();
|
|
73
|
+
expect(result.pass).toBeFalse();
|
|
74
|
+
});
|
|
75
|
+
it("Must match the entire json:api error schema", () => {
|
|
76
|
+
const result = toBeJaypieError({ errors: ["taco"] });
|
|
77
|
+
expect(result.message).toBeFunction();
|
|
78
|
+
expect(result.message()).toBeString();
|
|
79
|
+
expect(result.pass).toBeFalse();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe("Happy Path", () => {
|
|
83
|
+
it("Matches instances of Jaypie error objects", () => {
|
|
84
|
+
const error = new ConfigurationError();
|
|
85
|
+
const result = toBeJaypieError(error);
|
|
86
|
+
expect(result.message).toBeFunction();
|
|
87
|
+
expect(result.message()).toBeString();
|
|
88
|
+
expect(result.pass).toBeTrue();
|
|
89
|
+
});
|
|
90
|
+
it("Matches plain old json errors", () => {
|
|
91
|
+
const error = new ConfigurationError(
|
|
92
|
+
"If this is your first night at Fight Club, you have to fight",
|
|
93
|
+
).json();
|
|
94
|
+
const result = toBeJaypieError(error);
|
|
95
|
+
expect(result.message).toBeFunction();
|
|
96
|
+
expect(result.message()).toBeString();
|
|
97
|
+
expect(result.pass).toBeTrue();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { matchers as jsonSchemaMatchers } from "jest-json-schema";
|
|
2
|
+
import { jsonApiErrorSchema } from "../jsonApiSchema.module.js";
|
|
3
|
+
|
|
4
|
+
//
|
|
5
|
+
//
|
|
6
|
+
// Constants
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
//
|
|
10
|
+
//
|
|
11
|
+
// Helper Functions
|
|
12
|
+
//
|
|
13
|
+
|
|
14
|
+
function isErrorObjectJaypieError(error) {
|
|
15
|
+
if (error.isProjectError) {
|
|
16
|
+
return {
|
|
17
|
+
message: () => `expected "${error}" not to be a Jaypie error`,
|
|
18
|
+
pass: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
message: () => `expected "${error}" to be a Jaypie error`,
|
|
23
|
+
pass: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//
|
|
28
|
+
//
|
|
29
|
+
// Main
|
|
30
|
+
//
|
|
31
|
+
|
|
32
|
+
const toBeJaypieError = (received) => {
|
|
33
|
+
// See if it is an instance of error:
|
|
34
|
+
if (received instanceof Error) {
|
|
35
|
+
return isErrorObjectJaypieError(received);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const result = jsonSchemaMatchers.toMatchSchema(received, jsonApiErrorSchema);
|
|
39
|
+
if (result.pass) {
|
|
40
|
+
return {
|
|
41
|
+
message: () => `expected ${received} not to be a Jaypie error`,
|
|
42
|
+
pass: true,
|
|
43
|
+
};
|
|
44
|
+
} else {
|
|
45
|
+
return {
|
|
46
|
+
message: () => `expected ${received} to be a Jaypie error`,
|
|
47
|
+
pass: false,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
//
|
|
53
|
+
//
|
|
54
|
+
// Export
|
|
55
|
+
//
|
|
56
|
+
|
|
57
|
+
export default toBeJaypieError;
|
package/src/matchers.module.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { matchers as jsonSchemaMatchers } from "jest-json-schema";
|
|
2
2
|
|
|
3
|
+
import toBeJaypieError from "./matchers/toBeJaypieError.matcher.js";
|
|
4
|
+
|
|
3
5
|
//
|
|
4
6
|
//
|
|
5
7
|
// Export
|
|
6
8
|
//
|
|
7
9
|
|
|
8
10
|
export default {
|
|
11
|
+
toBeJaypieError,
|
|
9
12
|
toBeValidSchema: jsonSchemaMatchers.toBeValidSchema,
|
|
10
13
|
toMatchSchema: jsonSchemaMatchers.toMatchSchema,
|
|
11
14
|
};
|