@tryghost/express-test 2.0.3 → 2.1.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/README.md CHANGED
@@ -8,8 +8,7 @@ Rapid express testing without HTTP
8
8
 
9
9
  or
10
10
 
11
- `yarn add @tryghost/express-test`
12
-
11
+ `pnpm add @tryghost/express-test`
13
12
 
14
13
  ## Purpose
15
14
 
@@ -26,7 +25,6 @@ For the most up-to-date and clear usage info, there's a live working example of
26
25
 
27
26
  An instantiated agent can make HTTP-like calls, with a supertest-like chained API to set headers & body and to check status, headers and anything else.
28
27
 
29
-
30
28
  ```
31
29
  const agent = new TestAgent(app)
32
30
 
@@ -56,9 +54,10 @@ await agent.logout().get('/protected-route');
56
54
 
57
55
  This is an initial version for review. More docs coming if it works :)
58
56
 
59
-
60
57
  ### Assertion execution order
61
- The order of *chained* assertion execution is NOT the order they were declared in. The framework follows the priority order of the assertion types:
58
+
59
+ The order of _chained_ assertion execution is NOT the order they were declared in. The framework follows the priority order of the assertion types:
60
+
62
61
  1. `expect`
63
62
  2. `expectHeader`
64
63
  3. `expectStatus`
@@ -69,25 +68,21 @@ The custom order is here to prioritize assertions with the most context first. T
69
68
 
70
69
  ## Develop
71
70
 
72
- This is a mono repository, managed with [lerna](https://lernajs.io/).
71
+ This is a mono repository, managed with [Nx](https://nx.dev).
73
72
 
74
73
  Follow the instructions for the top-level repo.
75
- 1. `git clone` this repo & `cd` into it as usual
76
- 2. Run `yarn` to install top-level dependencies.
77
74
 
75
+ 1. `git clone` this repo & `cd` into it as usual
76
+ 2. Run `pnpm install` to install top-level dependencies.
78
77
 
79
78
  ## Run
80
79
 
81
- - `yarn dev`
82
-
80
+ - `pnpm dev`
83
81
 
84
82
  ## Test
85
83
 
86
- - `yarn lint` run just eslint
87
- - `yarn test` run lint and tests
88
-
89
-
90
-
84
+ - `pnpm lint` runs oxlint
85
+ - `pnpm test` runs lint and tests
91
86
 
92
87
  # Copyright & License
93
88
 
package/index.js CHANGED
@@ -7,5 +7,5 @@ module.exports.snapshot = {
7
7
  snapshotManager: require('@tryghost/jest-snapshot').snapshotManager,
8
8
  matchSnapshotAssertion: require('@tryghost/jest-snapshot').matchSnapshotAssertion,
9
9
  any: require('@tryghost/jest-snapshot').any,
10
- stringMatching: require('@tryghost/jest-snapshot').stringMatching
10
+ stringMatching: require('@tryghost/jest-snapshot').stringMatching,
11
11
  };
package/lib/Agent.js CHANGED
@@ -1,8 +1,7 @@
1
- /* eslint-disable ghost/ghost-custom/no-native-error */
2
- const {CookieJar} = require('cookiejar');
1
+ const { CookieJar } = require('cookiejar');
3
2
  const ExpectRequest = require('./ExpectRequest');
4
- const {RequestOptions} = require('./Request');
5
- const {normalizeURL} = require('./utils');
3
+ const { RequestOptions } = require('./Request');
4
+ const { normalizeURL } = require('./utils');
6
5
 
7
6
  /**
8
7
  * @typedef AgentOptions
@@ -73,22 +72,22 @@ class Agent {
73
72
  _mergeOptions(method, url, options = {}) {
74
73
  // It doesn't make sense to call this method without these properties
75
74
  if (!method) {
76
- throw new Error('_mergeOptions cannot be called without a method'); /* eslint-disable-line no-restricted-syntax */
75
+ throw new Error('_mergeOptions cannot be called without a method');
77
76
  }
78
77
 
79
78
  if (!url) {
80
- throw new Error('_mergeOptions cannot be called without a url'); /* eslint-disable-line no-restricted-syntax */
79
+ throw new Error('_mergeOptions cannot be called without a url');
81
80
  }
82
81
 
83
82
  // urlOptions
84
- const {baseUrl, queryParams} = options;
83
+ const { baseUrl, queryParams } = options;
85
84
 
86
85
  return new RequestOptions({
87
86
  method,
88
- url: this._makeUrl(url, Object.assign({}, {baseUrl, queryParams})),
87
+ url: this._makeUrl(url, Object.assign({}, { baseUrl, queryParams })),
89
88
  headers: Object.assign({}, this.defaults.headers, options.headers),
90
89
  // Set this to an empty object for ease, as express.json will do this anyway
91
- body: Object.assign({}, this.defaults.body, options.body)
90
+ body: Object.assign({}, this.defaults.body, options.body),
92
91
  });
93
92
  }
94
93
 
@@ -105,9 +104,14 @@ class Agent {
105
104
  ['get', 'post', 'put', 'patch', 'delete', 'options', 'head'].forEach((method) => {
106
105
  Agent.prototype[method] = function (url, options) {
107
106
  if (!url) {
108
- throw new Error('Cannot make a request without supplying a url'); /* eslint-disable-line no-restricted-syntax */
107
+ throw new Error('Cannot make a request without supplying a url');
109
108
  }
110
- return new ExpectRequest(this.app, this.jar, this._mergeOptions(method.toUpperCase(), url, options), this.snapshotManager);
109
+ return new ExpectRequest(
110
+ this.app,
111
+ this.jar,
112
+ this._mergeOptions(method.toUpperCase(), url, options),
113
+ this.snapshotManager,
114
+ );
111
115
  };
112
116
  });
113
117
 
@@ -1,6 +1,6 @@
1
1
  const assert = require('assert');
2
- const {Request, RequestOptions} = require('./Request');
3
- const {snapshotManager} = require('@tryghost/jest-snapshot');
2
+ const { Request, RequestOptions } = require('./Request');
3
+ const { snapshotManager } = require('@tryghost/jest-snapshot');
4
4
 
5
5
  /**
6
6
  * @typedef {object} ExpressTestAssertion
@@ -30,9 +30,8 @@ class ExpectRequest extends Request {
30
30
 
31
31
  expect(callback) {
32
32
  if (typeof callback !== 'function') {
33
- // eslint-disable-next-line ghost/ghost-custom/no-native-error
34
33
  throw new Error(
35
- 'express-test expect() requires a callback function, did you mean expectStatus or expectHeader?'
34
+ 'express-test expect() requires a callback function, did you mean expectStatus or expectHeader?',
36
35
  );
37
36
  }
38
37
 
@@ -47,7 +46,7 @@ class ExpectRequest extends Request {
47
46
 
48
47
  const assertion = {
49
48
  fn: wrapperFn,
50
- type: 'body'
49
+ type: 'body',
51
50
  };
52
51
 
53
52
  this._addAssertion(assertion);
@@ -59,7 +58,7 @@ class ExpectRequest extends Request {
59
58
  const assertion = {
60
59
  fn: this._assertStatus,
61
60
  expected,
62
- type: 'status'
61
+ type: 'status',
63
62
  };
64
63
 
65
64
  this._addAssertion(assertion);
@@ -72,7 +71,7 @@ class ExpectRequest extends Request {
72
71
  fn: this._assertHeader,
73
72
  expectedField: expectedField.toLowerCase(),
74
73
  expectedValue,
75
- type: 'header'
74
+ type: 'header',
76
75
  };
77
76
 
78
77
  this._addAssertion(assertion);
@@ -84,7 +83,7 @@ class ExpectRequest extends Request {
84
83
  const assertion = {
85
84
  fn: this._assertEmptyBody,
86
85
  expected: {},
87
- type: 'body'
86
+ type: 'body',
88
87
  };
89
88
 
90
89
  this._addAssertion(assertion);
@@ -97,7 +96,7 @@ class ExpectRequest extends Request {
97
96
  fn: this._assertSnapshot,
98
97
  properties: properties,
99
98
  field: 'body',
100
- type: 'body'
99
+ type: 'body',
101
100
  };
102
101
 
103
102
  this._addAssertion(assertion);
@@ -110,7 +109,7 @@ class ExpectRequest extends Request {
110
109
  fn: this._assertSnapshot,
111
110
  properties: properties,
112
111
  field: 'headers',
113
- type: 'header'
112
+ type: 'header',
114
113
  };
115
114
 
116
115
  this._addAssertion(assertion);
@@ -185,7 +184,7 @@ class ExpectRequest extends Request {
185
184
  let error = new assert.AssertionError({
186
185
  message: 'Unexpected assertion error',
187
186
  expected: assertion.expected,
188
- stackStartFn: this._addAssertion
187
+ stackStartFn: this._addAssertion,
189
188
  });
190
189
 
191
190
  error.contextString = this.reqOptions.toString();
@@ -195,11 +194,16 @@ class ExpectRequest extends Request {
195
194
  }
196
195
 
197
196
  _assertStatus(response, assertion) {
198
- const {error} = assertion;
197
+ const { error } = assertion;
199
198
 
200
199
  error.message = `Expected statusCode ${assertion.expected}, got statusCode ${response.statusCode} ${error.contextString}`;
201
200
 
202
- if (response.body && response.body.errors && response.body.errors[0] && response.body.errors[0].message) {
201
+ if (
202
+ response.body &&
203
+ response.body.errors &&
204
+ response.body.errors[0] &&
205
+ response.body.errors[0].message
206
+ ) {
203
207
  error.message += `\n${response.body.errors[0].message}`;
204
208
 
205
209
  if (response.body.errors[0].context) {
@@ -213,7 +217,7 @@ class ExpectRequest extends Request {
213
217
  }
214
218
 
215
219
  _assertHeader(response, assertion) {
216
- const {expectedField, expectedValue, error} = assertion;
220
+ const { expectedField, expectedValue, error } = assertion;
217
221
  const actual = response.headers[expectedField];
218
222
 
219
223
  const expectedHeaderString = `${expectedField}: ${expectedValue}`;
@@ -237,7 +241,7 @@ class ExpectRequest extends Request {
237
241
  }
238
242
 
239
243
  _assertEmptyBody(response, assertion) {
240
- const {error, expected} = assertion;
244
+ const { error, expected } = assertion;
241
245
  const actual = response.body;
242
246
 
243
247
  error.actual = actual;
package/lib/Request.js CHANGED
@@ -1,9 +1,9 @@
1
1
  const http = require('http');
2
- const {Writable, Readable} = require('stream');
2
+ const { Writable, Readable } = require('stream');
3
3
  const express = require('express');
4
- const {CookieAccessInfo} = require('cookiejar');
5
- const {parse} = require('url');
6
- const {isJSON, attachFile} = require('./utils');
4
+ const { CookieAccessInfo } = require('cookiejar');
5
+ const { parse } = require('url');
6
+ const { isJSON, attachFile } = require('./utils');
7
7
 
8
8
  class MockSocket extends Writable {
9
9
  constructor() {
@@ -16,7 +16,7 @@ class MockSocket extends Writable {
16
16
  }
17
17
 
18
18
  class RequestOptions {
19
- constructor({method, url, headers, body} = {}) {
19
+ constructor({ method, url, headers, body } = {}) {
20
20
  this.method = method || 'GET';
21
21
  this.url = url || '/';
22
22
  this.headers = headers || {};
@@ -31,7 +31,8 @@ class RequestOptions {
31
31
  class Request {
32
32
  constructor(app, cookieJar, reqOptions) {
33
33
  this.app = app;
34
- this.reqOptions = reqOptions instanceof RequestOptions ? reqOptions : new RequestOptions(reqOptions);
34
+ this.reqOptions =
35
+ reqOptions instanceof RequestOptions ? reqOptions : new RequestOptions(reqOptions);
35
36
  this.cookieJar = cookieJar;
36
37
  this._formData = null; // Track FormData instance for multiple attachments
37
38
  }
@@ -53,7 +54,7 @@ class Request {
53
54
  // body is FormData (we cannot reliably check instanceof, not working in all situations)
54
55
  const requestBuffer = body.getBuffer();
55
56
  const headers = body.getHeaders({
56
- ...this.reqOptions.headers
57
+ ...this.reqOptions.headers,
57
58
  });
58
59
  this.reqOptions.headers = headers;
59
60
  return this.body(requestBuffer);
@@ -110,7 +111,7 @@ class Request {
110
111
  },
111
112
  read: () => {
112
113
  return readableStream.read(...arguments);
113
- }
114
+ },
114
115
  };
115
116
  return this;
116
117
  }
@@ -149,7 +150,7 @@ class Request {
149
150
  }
150
151
 
151
152
  _getReqRes() {
152
- const {app, reqOptions} = this;
153
+ const { app, reqOptions } = this;
153
154
 
154
155
  // Create proper Node.js req/res objects using built-in http module.
155
156
  // MockSocket provides a writable stream so that res.end() properly emits 'finish'.
@@ -159,7 +160,7 @@ class Request {
159
160
  // When streaming body data, the socket must appear readable so that
160
161
  // body-parser's on-finished check doesn't skip the request as "already finished".
161
162
  if (hasStreamOverrides) {
162
- Object.defineProperty(socket, 'readable', {value: true});
163
+ Object.defineProperty(socket, 'readable', { value: true });
163
164
  }
164
165
 
165
166
  const req = new http.IncomingMessage(socket);
@@ -211,12 +212,15 @@ class Request {
211
212
  if (hasStreamOverrides) {
212
213
  const props = Object.keys(this.reqOptions.methodOverrides);
213
214
  for (const prop of props) {
214
- const descriptor = Object.getOwnPropertyDescriptor(this.reqOptions.methodOverrides, prop);
215
+ const descriptor = Object.getOwnPropertyDescriptor(
216
+ this.reqOptions.methodOverrides,
217
+ prop,
218
+ );
215
219
  Object.defineProperty(req, prop, descriptor);
216
220
  }
217
221
  }
218
222
 
219
- return {req, res};
223
+ return { req, res };
220
224
  }
221
225
 
222
226
  _buildResponse(res) {
@@ -229,12 +233,12 @@ class Request {
229
233
  body = text && JSON.parse(text);
230
234
  }
231
235
 
232
- return {statusCode, headers, text, body, response: res};
236
+ return { statusCode, headers, text, body, response: res };
233
237
  }
234
238
 
235
239
  _doRequest(callback) {
236
240
  try {
237
- const {req, res} = this._getReqRes();
241
+ const { req, res } = this._getReqRes();
238
242
 
239
243
  this._restoreCookies(req);
240
244
 
@@ -255,11 +259,7 @@ class Request {
255
259
  _getCookies(req) {
256
260
  const url = parse(req.url);
257
261
 
258
- const access = new CookieAccessInfo(
259
- url.hostname,
260
- url.pathname,
261
- url.protocol === 'https:'
262
- );
262
+ const access = new CookieAccessInfo(url.hostname, url.pathname, url.protocol === 'https:');
263
263
 
264
264
  return this.cookieJar.getCookies(access).toValueString();
265
265
  }
package/lib/utils.js CHANGED
@@ -29,7 +29,7 @@ module.exports.attachFile = function attachFile(name, filePath, existingFormData
29
29
 
30
30
  formData.append(name, fileContent, {
31
31
  filename,
32
- contentType
32
+ contentType,
33
33
  });
34
34
 
35
35
  return formData;
package/package.json CHANGED
@@ -1,40 +1,40 @@
1
1
  {
2
2
  "name": "@tryghost/express-test",
3
- "version": "2.0.3",
3
+ "version": "2.1.0",
4
+ "license": "MIT",
5
+ "author": "Ghost Foundation",
4
6
  "repository": {
5
7
  "type": "git",
6
8
  "url": "git+https://github.com/TryGhost/framework.git",
7
9
  "directory": "packages/express-test"
8
10
  },
9
- "author": "Ghost Foundation",
10
- "license": "MIT",
11
- "main": "index.js",
12
- "scripts": {
13
- "dev": "echo \"Implement me!\"",
14
- "test": "NODE_ENV=testing vitest run --coverage",
15
- "lint": "eslint . --ext .js --cache",
16
- "posttest": "yarn lint"
17
- },
18
11
  "files": [
19
12
  "index.js",
20
13
  "lib"
21
14
  ],
15
+ "main": "index.js",
22
16
  "publishConfig": {
23
17
  "access": "public"
24
18
  },
19
+ "dependencies": {
20
+ "cookiejar": "2.1.4",
21
+ "form-data": "4.0.5",
22
+ "mime-types": "3.0.2",
23
+ "@tryghost/jest-snapshot": "2.1.0"
24
+ },
25
25
  "devDependencies": {
26
26
  "express": "5.2.1",
27
27
  "express-session": "1.19.0",
28
28
  "multer": "2.1.1",
29
- "sinon": "21.0.2"
29
+ "sinon": "21.1.2"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "express": "^4.0.0 || ^5.0.0"
33
33
  },
34
- "dependencies": {
35
- "@tryghost/jest-snapshot": "^2.0.3",
36
- "cookiejar": "2.1.4",
37
- "form-data": "4.0.5",
38
- "mime-types": "3.0.2"
34
+ "scripts": {
35
+ "dev": "echo \"Implement me!\"",
36
+ "test": "NODE_ENV=testing vitest run --coverage",
37
+ "lint": "oxlint -c ../../.oxlintrc.json .",
38
+ "posttest": "pnpm run lint"
39
39
  }
40
- }
40
+ }