@tryghost/express-test 0.3.0 → 0.5.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/lib/expect-request.js +93 -10
- package/lib/request.js +1 -1
- package/lib/utils.js +6 -0
- package/package.json +6 -6
package/lib/expect-request.js
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
const assert = require('assert');
|
|
2
2
|
const {Request, RequestOptions} = require('./request');
|
|
3
3
|
const {snapshotManager} = require('@tryghost/jest-snapshot');
|
|
4
|
+
const {makeMessageFromMatchMessage} = require('./utils');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {object} ExpressTestAssertion
|
|
8
|
+
* @prop {function} fn - Wrapped assertion with response context
|
|
9
|
+
* @prop {string} [message] - The message to display if the assertion fails
|
|
10
|
+
* @prop {string} [method] - The method to call on the assertion object
|
|
11
|
+
* @prop {'header' | 'status'} [type] - The type of assertion
|
|
12
|
+
* @prop {import('assert').AssertionError} [error] - The error to throw if the assertion fails
|
|
13
|
+
* @prop {any} [expected]
|
|
14
|
+
* @prop {any} [expectedValue]
|
|
15
|
+
* @prop {string} [expectedField]
|
|
16
|
+
* @prop {object} [properties]
|
|
17
|
+
*/
|
|
4
18
|
|
|
5
19
|
class ExpectRequest extends Request {
|
|
6
20
|
constructor(...args) {
|
|
7
21
|
super(...args);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @property {ExpressTestAssertion[]} assertions
|
|
25
|
+
*/
|
|
26
|
+
this.assertions = [];
|
|
8
27
|
}
|
|
9
28
|
|
|
10
29
|
expect(callback) {
|
|
@@ -35,7 +54,8 @@ class ExpectRequest extends Request {
|
|
|
35
54
|
expectStatus(expected) {
|
|
36
55
|
const assertion = {
|
|
37
56
|
fn: this._assertStatus,
|
|
38
|
-
expected
|
|
57
|
+
expected,
|
|
58
|
+
type: 'status'
|
|
39
59
|
};
|
|
40
60
|
|
|
41
61
|
this._addAssertion(assertion);
|
|
@@ -47,7 +67,8 @@ class ExpectRequest extends Request {
|
|
|
47
67
|
const assertion = {
|
|
48
68
|
fn: this._assertHeader,
|
|
49
69
|
expectedField: expectedField.toLowerCase(),
|
|
50
|
-
expectedValue
|
|
70
|
+
expectedValue,
|
|
71
|
+
type: 'header'
|
|
51
72
|
};
|
|
52
73
|
|
|
53
74
|
this._addAssertion(assertion);
|
|
@@ -55,7 +76,7 @@ class ExpectRequest extends Request {
|
|
|
55
76
|
return this;
|
|
56
77
|
}
|
|
57
78
|
|
|
58
|
-
matchBodySnapshot(properties) {
|
|
79
|
+
matchBodySnapshot(properties = {}) {
|
|
59
80
|
let assertion = {
|
|
60
81
|
fn: this._assertSnapshot,
|
|
61
82
|
properties: properties,
|
|
@@ -67,11 +88,12 @@ class ExpectRequest extends Request {
|
|
|
67
88
|
return this;
|
|
68
89
|
}
|
|
69
90
|
|
|
70
|
-
matchHeaderSnapshot(properties) {
|
|
91
|
+
matchHeaderSnapshot(properties = {}) {
|
|
71
92
|
let assertion = {
|
|
72
93
|
fn: this._assertSnapshot,
|
|
73
94
|
properties: properties,
|
|
74
|
-
field: 'headers'
|
|
95
|
+
field: 'headers',
|
|
96
|
+
type: 'header'
|
|
75
97
|
};
|
|
76
98
|
|
|
77
99
|
this._addAssertion(assertion);
|
|
@@ -86,7 +108,7 @@ class ExpectRequest extends Request {
|
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
try {
|
|
89
|
-
if (this.assertions) {
|
|
111
|
+
if (this.assertions && this.assertions.length) {
|
|
90
112
|
this._assertAll(response);
|
|
91
113
|
}
|
|
92
114
|
return callback(null, response);
|
|
@@ -96,13 +118,51 @@ class ExpectRequest extends Request {
|
|
|
96
118
|
});
|
|
97
119
|
}
|
|
98
120
|
|
|
121
|
+
/**
|
|
122
|
+
* @description Runs through the assertions queue and executes them
|
|
123
|
+
* in following order:
|
|
124
|
+
* 1. Assertions with no type, e.g: body snapshot match, generic assertions
|
|
125
|
+
* 2. Assertions of "header" type
|
|
126
|
+
* 3. Assertions of "status" type
|
|
127
|
+
*
|
|
128
|
+
* The order is here to assert the most informative assertions first and the
|
|
129
|
+
* least informative ones last. For example status code assertions, usually
|
|
130
|
+
* are setup first but give the least amount of information needed to
|
|
131
|
+
* investigate the reason of failure. Same problem with header assertions.
|
|
132
|
+
* By changing the default order of assertions it's expected to improve
|
|
133
|
+
* the developer experience creating/debugging tests.
|
|
134
|
+
*
|
|
135
|
+
* @param {Object} response - HTTP response object
|
|
136
|
+
*/
|
|
99
137
|
_assertAll(response) {
|
|
138
|
+
const statusCodeAssertions = [];
|
|
139
|
+
const headerAssertions = [];
|
|
140
|
+
|
|
100
141
|
for (const assertion of this.assertions) {
|
|
142
|
+
if (!['status', 'header'].includes(assertion.type)) {
|
|
143
|
+
assertion.fn(response, assertion);
|
|
144
|
+
} else if (assertion.type === 'status') {
|
|
145
|
+
statusCodeAssertions.push(assertion);
|
|
146
|
+
} else if (assertion.type === 'header') {
|
|
147
|
+
headerAssertions.push(assertion);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const assertion of headerAssertions) {
|
|
152
|
+
assertion.fn(response, assertion);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (const assertion of statusCodeAssertions) {
|
|
101
156
|
assertion.fn(response, assertion);
|
|
102
157
|
}
|
|
103
158
|
}
|
|
104
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Adds assertion to the queue of assertions to be performed
|
|
162
|
+
* @param {ExpressTestAssertion} assertion
|
|
163
|
+
*/
|
|
105
164
|
_addAssertion(assertion) {
|
|
165
|
+
// We create the error here so that we get a useful stack trace
|
|
106
166
|
let error = new assert.AssertionError({
|
|
107
167
|
message: 'Unexpected assertion error',
|
|
108
168
|
expected: assertion.expected,
|
|
@@ -112,7 +172,6 @@ class ExpectRequest extends Request {
|
|
|
112
172
|
error.contextString = this.reqOptions.toString();
|
|
113
173
|
assertion.error = error;
|
|
114
174
|
|
|
115
|
-
this.assertions = this.assertions || [];
|
|
116
175
|
this.assertions.push(assertion);
|
|
117
176
|
}
|
|
118
177
|
|
|
@@ -120,6 +179,11 @@ class ExpectRequest extends Request {
|
|
|
120
179
|
const {error} = assertion;
|
|
121
180
|
|
|
122
181
|
error.message = `Expected statusCode ${assertion.expected}, got statusCode ${response.statusCode} ${error.contextString}`;
|
|
182
|
+
|
|
183
|
+
if (response.body && response.body.errors && response.body.errors[0].message) {
|
|
184
|
+
error.message += `\n${response.body.errors[0].message}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
123
187
|
error.actual = response.statusCode;
|
|
124
188
|
|
|
125
189
|
assert.equal(response.statusCode, assertion.expected, error);
|
|
@@ -152,13 +216,32 @@ class ExpectRequest extends Request {
|
|
|
152
216
|
_assertSnapshot(result, assertion) {
|
|
153
217
|
const {properties, field, error} = assertion;
|
|
154
218
|
|
|
155
|
-
|
|
219
|
+
if (!result[field]) {
|
|
220
|
+
error.message = `Unable to match snapshot on undefined field ${field} ${error.contextString}`;
|
|
221
|
+
error.expected = field;
|
|
222
|
+
error.actual = 'undefined';
|
|
223
|
+
assert.notEqual(result[field], undefined, error);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const hint = `[${field}]`;
|
|
227
|
+
const match = snapshotManager.match(result[field], properties, hint);
|
|
228
|
+
|
|
229
|
+
Object.keys(properties).forEach((prop) => {
|
|
230
|
+
const errorMessage = `"response.${field}" is missing the expected property "${prop}"`;
|
|
231
|
+
error.message = makeMessageFromMatchMessage(match.message(), errorMessage);
|
|
232
|
+
error.expected = prop;
|
|
233
|
+
error.actual = 'undefined';
|
|
234
|
+
error.showDiff = false; // Disable mocha's diff output as it's already present in match.message()
|
|
235
|
+
|
|
236
|
+
assert.notEqual(result[field][prop], undefined, error);
|
|
237
|
+
});
|
|
156
238
|
|
|
157
239
|
if (match.pass !== true) {
|
|
158
|
-
const
|
|
240
|
+
const errorMessage = `"response.${field}" does not match snapshot.`;
|
|
241
|
+
error.message = makeMessageFromMatchMessage(match.message(), errorMessage);
|
|
159
242
|
error.expected = match.expected;
|
|
160
243
|
error.actual = match.actual;
|
|
161
|
-
error.
|
|
244
|
+
error.showDiff = false; // Disable mocha's diff output as it's already present in match.message()
|
|
162
245
|
}
|
|
163
246
|
|
|
164
247
|
assert.equal(match.pass, true, error);
|
package/lib/request.js
CHANGED
|
@@ -67,7 +67,7 @@ class Request {
|
|
|
67
67
|
const {req, res} = reqresnext(Object.assign({}, reqOptions, {app}), {app});
|
|
68
68
|
|
|
69
69
|
// This is needed to make error handling work
|
|
70
|
-
// @TODO:
|
|
70
|
+
// @TODO: remove once https://github.com/antongolub/reqresnext/issues/31 is solved
|
|
71
71
|
req.socket.destroy = () => { };
|
|
72
72
|
return {req, res};
|
|
73
73
|
}
|
package/lib/utils.js
CHANGED
|
@@ -3,3 +3,9 @@ module.exports.isJSON = function isJSON(mime) {
|
|
|
3
3
|
// but not /json-seq
|
|
4
4
|
return /[/+]json($|[^-\w])/i.test(mime);
|
|
5
5
|
};
|
|
6
|
+
|
|
7
|
+
module.exports.makeMessageFromMatchMessage = function makeMessageFromMatchMessage(message, errorMessage) {
|
|
8
|
+
const messageLines = message.split('\n');
|
|
9
|
+
messageLines.splice(0, 1, errorMessage);
|
|
10
|
+
return messageLines.join('\n');
|
|
11
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryghost/express-test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"repository": "https://github.com/TryGhost/framework/tree/main/packages/express-test",
|
|
5
5
|
"author": "Ghost Foundation",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,15 +20,15 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"c8": "7.11.0",
|
|
23
|
-
"express": "
|
|
24
|
-
"express-session": "
|
|
23
|
+
"express": "4.17.2",
|
|
24
|
+
"express-session": "1.17.2",
|
|
25
25
|
"mocha": "9.2.0",
|
|
26
26
|
"sinon": "13.0.1"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@tryghost/jest-snapshot": "^0.2
|
|
29
|
+
"@tryghost/jest-snapshot": "^0.3.2",
|
|
30
30
|
"cookiejar": "^2.1.3",
|
|
31
|
-
"reqresnext": "^1.
|
|
31
|
+
"reqresnext": "^1.7.0"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "e954893434fda3f8c8de1970aaaa6f6a8b297f5d"
|
|
34
34
|
}
|