@tryghost/express-test 0.4.2 → 0.6.1

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
@@ -40,6 +40,16 @@ return await agent
40
40
  This is an initial version for review. More docs coming if it works :)
41
41
 
42
42
 
43
+ ### Assertion execution order
44
+ The order of *chained* assertion execution is NOT the order they were declared in. The framework follows the priority order of the assertion types:
45
+ 1. `expect`
46
+ 2. `expectHeader`
47
+ 3. `expectStatus`
48
+
49
+ Meaning, any `expect` declarations would be asserted first following by `expectHeader` and `expectStatus` declarations.
50
+
51
+ The custom order is here to prioritize assertions with the most context first. The feature was added based on frustrations with frameworks like [superagent](https://github.com/visionmedia/superagent), which didn't provide enough information when the status code assertions failed.
52
+
43
53
  ## Develop
44
54
 
45
55
  This is a mono repository, managed with [lerna](https://lernajs.io/).
package/index.js CHANGED
@@ -1 +1,9 @@
1
1
  module.exports = require('./lib/agent');
2
+
3
+ // NOTE: exposing jest-snapshot as a part of express-test to avoid
4
+ // version missmatching on the client side
5
+ module.exports.snapshot = {
6
+ mochaHooks: require('@tryghost/jest-snapshot').mochaHooks,
7
+ any: require('@tryghost/jest-snapshot').any,
8
+ stringMatching: require('@tryghost/jest-snapshot').stringMatching
9
+ };
package/lib/agent.js CHANGED
@@ -3,6 +3,14 @@ const ExpectRequest = require('./expect-request');
3
3
  const {RequestOptions} = require('./request');
4
4
 
5
5
  class Agent {
6
+ /**
7
+ *
8
+ * @param {Object} app instance of Express app
9
+ * @param {Object} [defaults]
10
+ * @param {string} [defaults.baseUrl]
11
+ * @param {Object} [defaults.headers]
12
+ * @param {Object} [defaults.queryParams] - custom query params to append to each request
13
+ */
6
14
  constructor(app, defaults = {}) {
7
15
  this.app = app;
8
16
  this.defaults = defaults;
@@ -15,6 +23,20 @@ class Agent {
15
23
  url = `/${this.defaults.baseUrl}/${url}`.replace(/(^|[^:])\/\/+/g, '$1/');
16
24
  }
17
25
 
26
+ if (this.defaults.queryParams) {
27
+ const searchParams = new URLSearchParams();
28
+
29
+ for (const key in this.defaults.queryParams) {
30
+ searchParams.append(key, this.defaults.queryParams[key]);
31
+ }
32
+
33
+ if (url.includes('?')) {
34
+ url = `${url}&${searchParams.toString()}`;
35
+ } else {
36
+ url = `${url}?${searchParams.toString()}`;
37
+ }
38
+ }
39
+
18
40
  return url;
19
41
  }
20
42
 
@@ -3,9 +3,27 @@ const {Request, RequestOptions} = require('./request');
3
3
  const {snapshotManager} = require('@tryghost/jest-snapshot');
4
4
  const {makeMessageFromMatchMessage} = require('./utils');
5
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 {'body' | 'header' | 'status'} type - The type of assertion, with following priority: body > header > status
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
+ */
18
+
6
19
  class ExpectRequest extends Request {
7
20
  constructor(...args) {
8
21
  super(...args);
22
+
23
+ /**
24
+ * @property {ExpressTestAssertion[]} assertions
25
+ */
26
+ this.assertions = [];
9
27
  }
10
28
 
11
29
  expect(callback) {
@@ -25,7 +43,8 @@ class ExpectRequest extends Request {
25
43
  };
26
44
 
27
45
  const assertion = {
28
- fn: wrapperFn
46
+ fn: wrapperFn,
47
+ type: 'body'
29
48
  };
30
49
 
31
50
  this._addAssertion(assertion);
@@ -36,7 +55,8 @@ class ExpectRequest extends Request {
36
55
  expectStatus(expected) {
37
56
  const assertion = {
38
57
  fn: this._assertStatus,
39
- expected
58
+ expected,
59
+ type: 'status'
40
60
  };
41
61
 
42
62
  this._addAssertion(assertion);
@@ -48,7 +68,8 @@ class ExpectRequest extends Request {
48
68
  const assertion = {
49
69
  fn: this._assertHeader,
50
70
  expectedField: expectedField.toLowerCase(),
51
- expectedValue
71
+ expectedValue,
72
+ type: 'header'
52
73
  };
53
74
 
54
75
  this._addAssertion(assertion);
@@ -60,7 +81,8 @@ class ExpectRequest extends Request {
60
81
  let assertion = {
61
82
  fn: this._assertSnapshot,
62
83
  properties: properties,
63
- field: 'body'
84
+ field: 'body',
85
+ type: 'body'
64
86
  };
65
87
 
66
88
  this._addAssertion(assertion);
@@ -72,7 +94,8 @@ class ExpectRequest extends Request {
72
94
  let assertion = {
73
95
  fn: this._assertSnapshot,
74
96
  properties: properties,
75
- field: 'headers'
97
+ field: 'headers',
98
+ type: 'header'
76
99
  };
77
100
 
78
101
  this._addAssertion(assertion);
@@ -87,7 +110,7 @@ class ExpectRequest extends Request {
87
110
  }
88
111
 
89
112
  try {
90
- if (this.assertions) {
113
+ if (this.assertions && this.assertions.length) {
91
114
  this._assertAll(response);
92
115
  }
93
116
  return callback(null, response);
@@ -97,12 +120,51 @@ class ExpectRequest extends Request {
97
120
  });
98
121
  }
99
122
 
123
+ /**
124
+ * @description Runs through the assertions queue and executes them
125
+ * in following order:
126
+ * 1. Assertions with no type, e.g: body snapshot match, generic assertions
127
+ * 2. Assertions of "header" type
128
+ * 3. Assertions of "status" type
129
+ *
130
+ * The order is here to assert the most informative assertions first and the
131
+ * least informative ones last. For example status code assertions, usually
132
+ * are setup first but give the least amount of information needed to
133
+ * investigate the reason of failure. Same problem with header assertions.
134
+ * By changing the default order of assertions it's expected to improve
135
+ * the developer experience creating/debugging tests.
136
+ *
137
+ * @param {Object} response - HTTP response object
138
+ */
100
139
  _assertAll(response) {
140
+ const statusCodeAssertions = [];
141
+ const headerAssertions = [];
142
+
101
143
  for (const assertion of this.assertions) {
144
+ if (assertion.type === 'body') {
145
+ assertion.fn(response, assertion);
146
+ } else if (assertion.type === 'status') {
147
+ statusCodeAssertions.push(assertion);
148
+ } else if (assertion.type === 'header') {
149
+ headerAssertions.push(assertion);
150
+ } else {
151
+ headerAssertions.push(assertion);
152
+ }
153
+ }
154
+
155
+ for (const assertion of headerAssertions) {
156
+ assertion.fn(response, assertion);
157
+ }
158
+
159
+ for (const assertion of statusCodeAssertions) {
102
160
  assertion.fn(response, assertion);
103
161
  }
104
162
  }
105
163
 
164
+ /**
165
+ * Adds assertion to the queue of assertions to be performed
166
+ * @param {ExpressTestAssertion} assertion
167
+ */
106
168
  _addAssertion(assertion) {
107
169
  // We create the error here so that we get a useful stack trace
108
170
  let error = new assert.AssertionError({
@@ -114,7 +176,6 @@ class ExpectRequest extends Request {
114
176
  error.contextString = this.reqOptions.toString();
115
177
  assertion.error = error;
116
178
 
117
- this.assertions = this.assertions || [];
118
179
  this.assertions.push(assertion);
119
180
  }
120
181
 
package/lib/request.js CHANGED
@@ -1,14 +1,13 @@
1
1
  const {default: reqresnext} = require('reqresnext');
2
2
  const {CookieAccessInfo} = require('cookiejar');
3
3
  const {parse} = require('url');
4
- const {isJSON, convertKeysToLowerCase} = require('./utils');
4
+ const {isJSON} = require('./utils');
5
5
 
6
6
  class RequestOptions {
7
7
  constructor({method, url, headers, body} = {}) {
8
8
  this.method = method || 'GET';
9
9
  this.url = url || '/';
10
- // TODO: remove convertKeysToLowerCase utility once https://github.com/antongolub/reqresnext/issues/33 is fixed
11
- this.headers = convertKeysToLowerCase(headers) || {};
10
+ this.headers = headers || {};
12
11
  this.body = body;
13
12
  }
14
13
 
@@ -30,8 +29,7 @@ class Request {
30
29
  }
31
30
 
32
31
  header(name, value) {
33
- // TODO: remove toLowerCase once https://github.com/antongolub/reqresnext/issues/33 is fixed
34
- this.reqOptions.headers[name.toLowerCase()] = value;
32
+ this.reqOptions.headers[name] = value;
35
33
  return this;
36
34
  }
37
35
 
package/lib/utils.js CHANGED
@@ -9,13 +9,3 @@ module.exports.makeMessageFromMatchMessage = function makeMessageFromMatchMessag
9
9
  messageLines.splice(0, 1, errorMessage);
10
10
  return messageLines.join('\n');
11
11
  };
12
-
13
- module.exports.convertKeysToLowerCase = function (map) {
14
- const newMap = {};
15
-
16
- for (const key in map) {
17
- newMap[key.toLowerCase()] = map[key];
18
- }
19
-
20
- return newMap;
21
- };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@tryghost/express-test",
3
- "version": "0.4.2",
3
+ "version": "0.6.1",
4
4
  "repository": "https://github.com/TryGhost/framework/tree/main/packages/express-test",
5
5
  "author": "Ghost Foundation",
6
6
  "license": "MIT",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
9
  "dev": "echo \"Implement me!\"",
10
- "test": "NODE_ENV=testing c8 mocha --require ./test/utils/overrides.js './test/**/*.test.js'",
10
+ "test": "NODE_ENV=testing c8 --all --reporter text --reporter cobertura mocha --require ./test/utils/overrides.js './test/**/*.test.js'",
11
11
  "lint": "eslint . --ext .js --cache",
12
12
  "posttest": "yarn lint"
13
13
  },
@@ -20,15 +20,15 @@
20
20
  },
21
21
  "devDependencies": {
22
22
  "c8": "7.11.0",
23
- "express": "^4.17.2",
24
- "express-session": "^1.17.2",
25
- "mocha": "9.2.0",
23
+ "express": "4.17.3",
24
+ "express-session": "1.17.2",
25
+ "mocha": "9.2.1",
26
26
  "sinon": "13.0.1"
27
27
  },
28
28
  "dependencies": {
29
- "@tryghost/jest-snapshot": "^0.3.1",
29
+ "@tryghost/jest-snapshot": "^0.3.4",
30
30
  "cookiejar": "^2.1.3",
31
- "reqresnext": "^1.6.6"
31
+ "reqresnext": "^1.7.0"
32
32
  },
33
- "gitHead": "fdec1082aa5efa81196205cbcd8a31c61b50c8fa"
33
+ "gitHead": "3768b2b29fe5c2ae4e535264246bc78933eba372"
34
34
  }