jest-matcher-http 1.1.2 → 1.3.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/.eslintrc.js CHANGED
@@ -80,5 +80,11 @@ module.exports = {
80
80
  'jest/valid-title': 'error',
81
81
  quotes: [2, 'single', { avoidEscape: true }],
82
82
  'comma-dangle': [2, 'always-multiline'],
83
+ 'import/no-extraneous-dependencies': [
84
+ 'error',
85
+ {
86
+ devDependencies: ['integration_tests/**'],
87
+ },
88
+ ],
83
89
  },
84
90
  };
@@ -5,11 +5,11 @@ updates:
5
5
  schedule:
6
6
  interval: "weekly"
7
7
  commit-message:
8
- prefix: "fix"
8
+ prefix: "chore"
9
9
 
10
10
  - package-ecosystem: "npm"
11
11
  directory: "/"
12
12
  schedule:
13
13
  interval: "daily"
14
14
  commit-message:
15
- prefix: "fix"
15
+ prefix: "chore"
@@ -15,7 +15,7 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
  strategy:
17
17
  matrix:
18
- node-version: [14.x, 16.x, 18.x]
18
+ node-version: [16.x, 18.x]
19
19
  steps:
20
20
  - uses: actions/checkout@v3
21
21
  - name: Use Node.js ${{ matrix.node-version }}
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <img alt="npm latest version" src="https://img.shields.io/npm/v/jest-matcher-http/latest.svg">
6
6
  </a>
7
7
  <a href="https://github.com/rimesime/jest-matcher-http/actions?query=workflow%3ATest+branch%3Amain">
8
- <img alt="Build states" src="https://github.com/semantic-release/semantic-release/workflows/Test/badge.svg">
8
+ <img alt="Build states" src="https://github.com/rimesime/jest-matcher-http/workflows/Test/badge.svg">
9
9
  </a>
10
10
  <a href="#badge">
11
11
  <img alt="semantic-release: angular" src="https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release">
@@ -20,7 +20,11 @@ Logs received body and headers if response is not as expected.
20
20
  Supported http libraries:
21
21
  - [superagent](https://www.npmjs.com/package/superagent)/[supertest](https://www.npmjs.com/package/supertest)
22
22
  - [axios](https://www.npmjs.com/package/axios)
23
- - (more to come)
23
+ - [needle](https://www.npmjs.com/package/needle)
24
+
25
+ Supported content types:
26
+ - application/json
27
+ - text/plain
24
28
 
25
29
  # Installation & Configuration
26
30
 
@@ -1,66 +1,68 @@
1
1
  'use strict';
2
2
 
3
- const express = require('express');
4
- const http = require('http');
5
3
  const axios = require('axios');
6
4
 
7
- describe('axios', () => {
8
- let server;
9
- let url;
10
-
11
- const body = { name: 'john' };
12
- const headerName = 'Some';
13
- const headerValue = 'Header';
5
+ const { runAgainstServer, staticVariables } = require('./localServer.int.helper');
14
6
 
15
- let response;
16
-
17
- // eslint-disable-next-line jest/no-done-callback
18
- beforeAll((done) => {
19
- // eslint-disable-next-line new-cap
20
- const app = new express();
21
- app.get('/', (req, res) => {
22
- res
23
- .status(200)
24
- .set(headerName, headerValue)
25
- .json(body);
26
- });
7
+ describe('axios', () => {
8
+ let responseJson;
9
+ let responseText;
10
+ const {
11
+ resultJson,
12
+ resultText,
13
+ headerName,
14
+ headerValue,
15
+ } = staticVariables;
27
16
 
28
- server = http.createServer(app);
29
- const listener = server.listen(async () => {
30
- url = `http://localhost:${listener.address().port}`;
17
+ beforeAll(async () => {
18
+ await runAgainstServer(async (url) => {
31
19
  const request = axios.create({ baseURL: url });
32
- response = await request.get(`${url}/`);
33
- server.close(done);
20
+ responseJson = await request.get('/json');
21
+ responseText = await request.get('/text');
34
22
  });
35
23
  });
36
24
 
37
25
  describe('toReturnHttpCode', () => {
38
26
  it('should succeed if http code is as expected', async () => {
39
- expect(response).toReturnHttpCode(200);
27
+ expect(responseJson).toReturnHttpCode(200);
28
+ });
29
+
30
+ it('should fail if http code is not as expected for application/json responses', async () => {
31
+ let caughtError;
32
+ try {
33
+ expect(responseJson).toReturnHttpCode(500);
34
+ } catch (error) {
35
+ caughtError = error;
36
+ }
37
+
38
+ expect(caughtError.message).toContain('expected http status code 200 to equal 500\n');
39
+ expect(caughtError.message).toContain(`${JSON.stringify(resultJson, null, 2)}\n`);
40
+ expect(caughtError.message).toContain(`"${headerName.toLowerCase()}": "${headerValue}"`);
40
41
  });
41
42
 
42
- it('should fail if http code is not as expected', async () => {
43
+ it('should fail if http code is not as expected for plain/text responses', async () => {
43
44
  let caughtError;
44
45
  try {
45
- expect(response).toReturnHttpCode(500);
46
+ expect(responseText).toReturnHttpCode(500);
46
47
  } catch (error) {
47
48
  caughtError = error;
48
49
  }
49
50
 
50
51
  expect(caughtError.message).toContain('expected http status code 200 to equal 500\n');
52
+ expect(caughtError.message).toContain(`${JSON.stringify(resultText, null, 2)}\n`);
51
53
  expect(caughtError.message).toContain(`"${headerName.toLowerCase()}": "${headerValue}"`);
52
54
  });
53
55
  });
54
56
 
55
57
  describe('toReturnHttpHeader', () => {
56
58
  it('should succeed if http header is as expected', async () => {
57
- expect(response).toReturnHttpHeader(headerName, headerValue);
59
+ expect(responseJson).toReturnHttpHeader(headerName, headerValue);
58
60
  });
59
61
 
60
62
  it('should fail if http header is not as expected', async () => {
61
63
  let caughtError;
62
64
  try {
63
- expect(response).toReturnHttpHeader(headerName, '');
65
+ expect(responseJson).toReturnHttpHeader(headerName, '');
64
66
  } catch (error) {
65
67
  caughtError = error;
66
68
  }
@@ -69,7 +71,7 @@ describe('axios', () => {
69
71
  `expected http header "${headerName.toLowerCase()}" with value ""\n`
70
72
  + '\n'
71
73
  + 'server responded with body:\n'
72
- + `${JSON.stringify(body, null, 2)}\n`
74
+ + `${JSON.stringify(resultJson, null, 2)}\n`
73
75
  + '\n'
74
76
  + 'server responded with headers:\n',
75
77
  );
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const express = require('express');
4
+ const http = require('http');
5
+ const { createHttpTerminator } = require('http-terminator');
6
+ const Signal = require('signal-promise');
7
+
8
+ const resultJson = { name: 'john' };
9
+ const resultText = 'Text';
10
+ const headerName = 'Some';
11
+ const headerValue = 'Header';
12
+
13
+ /**
14
+ * Start a simple http server and invoke the given function as
15
+ * soon as the server is ready.
16
+ *
17
+ * @param {Function} func - The (async) function
18
+ * that is called when http server is ready.
19
+ */
20
+ async function runAgainstServer(func) {
21
+ // eslint-disable-next-line new-cap
22
+ const app = new express();
23
+ app.get('/json', (req, res) => {
24
+ res
25
+ .status(200)
26
+ .set('Content-Type', 'application/json')
27
+ .set(headerName, headerValue)
28
+ .json(resultJson);
29
+ });
30
+ app.get('/text', (req, res) => {
31
+ res
32
+ .status(200)
33
+ .set('Content-Type', 'text/plain')
34
+ .set(headerName, headerValue)
35
+ .send(resultText);
36
+ });
37
+ const server = http.createServer(app);
38
+ const httpTerminator = createHttpTerminator({ server });
39
+ const barrier = new Signal();
40
+ const listener = server.listen(async () => {
41
+ await func(`http://localhost:${listener.address().port}`);
42
+ await httpTerminator.terminate();
43
+ barrier.notify();
44
+ });
45
+
46
+ await barrier.wait();
47
+ }
48
+
49
+ module.exports = {
50
+ runAgainstServer,
51
+ staticVariables: {
52
+ resultJson,
53
+ resultText,
54
+ headerName,
55
+ headerValue,
56
+ },
57
+ };
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+
3
+ const needle = require('needle');
4
+
5
+ const { runAgainstServer, staticVariables } = require('./localServer.int.helper');
6
+
7
+ describe('needle', () => {
8
+ let responseJson;
9
+ let responseText;
10
+ const {
11
+ resultJson,
12
+ resultText,
13
+ headerName,
14
+ headerValue,
15
+ } = staticVariables;
16
+
17
+ beforeAll(async () => {
18
+ await runAgainstServer(async (url) => {
19
+ responseJson = await needle('get', `${url}/json`);
20
+ responseText = await needle('get', `${url}/text`);
21
+ });
22
+ });
23
+
24
+ describe('toReturnHttpCode', () => {
25
+ it('should succeed if http code is as expected', async () => {
26
+ expect(responseJson).toReturnHttpCode(200);
27
+ });
28
+
29
+ it('should fail if http code is not as expected for application/json responses', async () => {
30
+ let caughtError;
31
+ try {
32
+ expect(responseJson).toReturnHttpCode(500);
33
+ } catch (error) {
34
+ caughtError = error;
35
+ }
36
+
37
+ expect(caughtError.message).toContain('expected http status code 200 to equal 500\n');
38
+ expect(caughtError.message).toContain(`${JSON.stringify(resultJson, null, 2)}\n`);
39
+ expect(caughtError.message).toContain(`"${headerName.toLowerCase()}": "${headerValue}"`);
40
+ });
41
+
42
+ it('should fail if http code is not as expected for plain/text responses', async () => {
43
+ let caughtError;
44
+ try {
45
+ expect(responseText).toReturnHttpCode(500);
46
+ } catch (error) {
47
+ caughtError = error;
48
+ }
49
+
50
+ expect(caughtError.message).toContain('expected http status code 200 to equal 500\n');
51
+ expect(caughtError.message).toContain(`${JSON.stringify(resultText, null, 2)}\n`);
52
+ expect(caughtError.message).toContain(`"${headerName.toLowerCase()}": "${headerValue}"`);
53
+ });
54
+ });
55
+
56
+ describe('toReturnHttpHeader', () => {
57
+ it('should succeed if http header is as expected', async () => {
58
+ expect(responseJson).toReturnHttpHeader(headerName, headerValue);
59
+ });
60
+
61
+ it('should fail if http header is not as expected', async () => {
62
+ let caughtError;
63
+ try {
64
+ expect(responseJson).toReturnHttpHeader(headerName, '');
65
+ } catch (error) {
66
+ caughtError = error;
67
+ }
68
+
69
+ expect(caughtError.message).toContain(
70
+ `expected http header "${headerName.toLowerCase()}" with value ""\n`
71
+ + '\n'
72
+ + 'server responded with body:\n'
73
+ + `${JSON.stringify(resultJson, null, 2)}\n`
74
+ + '\n'
75
+ + 'server responded with headers:\n',
76
+ );
77
+ expect(caughtError.message).toContain(`"${headerName.toLowerCase()}": "${headerValue}"`);
78
+ });
79
+ });
80
+ });
@@ -1,65 +1,67 @@
1
1
  'use strict';
2
2
 
3
- const express = require('express');
4
- const http = require('http');
5
3
  const superagent = require('superagent');
6
4
 
7
- describe('superagent', () => {
8
- let server;
9
- let url;
10
-
11
- const body = { name: 'john' };
12
- const headerName = 'Some';
13
- const headerValue = 'Header';
5
+ const { runAgainstServer, staticVariables } = require('./localServer.int.helper');
14
6
 
15
- let response;
16
-
17
- // eslint-disable-next-line jest/no-done-callback
18
- beforeAll((done) => {
19
- // eslint-disable-next-line new-cap
20
- const app = new express();
21
- app.get('/', (req, res) => {
22
- res
23
- .status(200)
24
- .set(headerName, headerValue)
25
- .json(body);
26
- });
7
+ describe('superagent', () => {
8
+ let responseJson;
9
+ let responseText;
10
+ const {
11
+ resultJson,
12
+ resultText,
13
+ headerName,
14
+ headerValue,
15
+ } = staticVariables;
27
16
 
28
- server = http.createServer(app);
29
- const listener = server.listen(async () => {
30
- url = `http://localhost:${listener.address().port}`;
31
- response = await superagent.get(`${url}/`);
32
- server.close(done);
17
+ beforeAll(async () => {
18
+ await runAgainstServer(async (url) => {
19
+ responseJson = await superagent.get(`${url}/json`);
20
+ responseText = await superagent.get(`${url}/text`);
33
21
  });
34
22
  });
35
23
 
36
24
  describe('toReturnHttpCode', () => {
37
25
  it('should succeed if http code is as expected', async () => {
38
- expect(response).toReturnHttpCode(200);
26
+ expect(responseJson).toReturnHttpCode(200);
27
+ });
28
+
29
+ it('should fail if http code is not as expected for application/json responses', async () => {
30
+ let caughtError;
31
+ try {
32
+ expect(responseJson).toReturnHttpCode(500);
33
+ } catch (error) {
34
+ caughtError = error;
35
+ }
36
+
37
+ expect(caughtError.message).toContain('expected http status code 200 to equal 500\n');
38
+ expect(caughtError.message).toContain(`${JSON.stringify(resultJson, null, 2)}\n`);
39
+ expect(caughtError.message).toContain(`"${headerName.toLowerCase()}": "${headerValue}"`);
39
40
  });
40
41
 
41
- it('should fail if http code is not as expected', async () => {
42
+ it('should fail if http code is not as expected for plain/text responses', async () => {
42
43
  let caughtError;
43
44
  try {
44
- expect(response).toReturnHttpCode(500);
45
+ expect(responseText).toReturnHttpCode(500);
45
46
  } catch (error) {
46
47
  caughtError = error;
47
48
  }
48
49
 
49
50
  expect(caughtError.message).toContain('expected http status code 200 to equal 500\n');
51
+ expect(caughtError.message).toContain(`${JSON.stringify(resultText, null, 2)}\n`);
50
52
  expect(caughtError.message).toContain(`"${headerName.toLowerCase()}": "${headerValue}"`);
51
53
  });
52
54
  });
53
55
 
54
56
  describe('toReturnHttpHeader', () => {
55
57
  it('should succeed if http header is as expected', async () => {
56
- expect(response).toReturnHttpHeader(headerName, headerValue);
58
+ expect(responseJson).toReturnHttpHeader(headerName, headerValue);
57
59
  });
58
60
 
59
61
  it('should fail if http header is not as expected', async () => {
60
62
  let caughtError;
61
63
  try {
62
- expect(response).toReturnHttpHeader(headerName, '');
64
+ expect(responseJson).toReturnHttpHeader(headerName, '');
63
65
  } catch (error) {
64
66
  caughtError = error;
65
67
  }
@@ -68,7 +70,7 @@ describe('superagent', () => {
68
70
  `expected http header "${headerName.toLowerCase()}" with value ""\n`
69
71
  + '\n'
70
72
  + 'server responded with body:\n'
71
- + `${JSON.stringify(body, null, 2)}\n`
73
+ + `${JSON.stringify(resultJson, null, 2)}\n`
72
74
  + '\n'
73
75
  + 'server responded with headers:\n',
74
76
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jest-matcher-http",
3
- "version": "1.1.2",
3
+ "version": "1.3.0",
4
4
  "description": "Additional Jest matchers for HTTP responses.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -41,8 +41,11 @@
41
41
  "eslint-plugin-jest": "^27.1.6",
42
42
  "eslint-plugin-jsdoc": "^39.6.4",
43
43
  "express": "^4.18.2",
44
+ "http-terminator": "^3.2.0",
44
45
  "jest": "^29.3.1",
46
+ "needle": "^3.2.0",
45
47
  "semantic-release": "^19.0.5",
48
+ "signal-promise": "^1.0.3",
46
49
  "superagent": "^8.0.5"
47
50
  },
48
51
  "dependencies": {
package/src/matchers.js CHANGED
@@ -1,17 +1,41 @@
1
1
  'use strict';
2
2
 
3
+ /**
4
+ * Extract the result from the response.
5
+ *
6
+ * @param {object} response - The response of the request.
7
+ * @returns {object} The result.
8
+ * @throws {Error} If no result could be extracted.
9
+ */
10
+ function extractResult(response) {
11
+ if (response.data) {
12
+ return response.data;
13
+ }
14
+
15
+ if (response.text && response.text !== JSON.stringify(response.body)) {
16
+ return response.text;
17
+ }
18
+
19
+ if (response.body) {
20
+ return response.body;
21
+ }
22
+
23
+ throw new Error('jest-matcher-http does not support this library');
24
+ }
25
+
3
26
  /**
4
27
  * Expect a http request to return given http status code.
5
28
  * Log response body and headers otherwise.
6
29
  *
7
- * @param {{status: number, body: any, headers: any}} response - The response of the request.
30
+ * @param {object} response - The response of the request.
8
31
  * @param {number} expectedHttpStatusCode - The expected http status code.
9
32
  * @returns {{message: Function, pass: boolean}} The expect result according to jest.
10
33
  * @see {@link https://jestjs.io/docs/expect#expectextendmatchers}
11
34
  */
12
35
  function toReturnHttpCode(response, expectedHttpStatusCode) {
13
- const body = response.body ?? response.data;
14
- const { status, headers } = response;
36
+ const result = extractResult(response);
37
+ const status = response.status ?? response.statusCode;
38
+ const { headers } = response;
15
39
 
16
40
  const pass = status === expectedHttpStatusCode;
17
41
 
@@ -22,7 +46,7 @@ function toReturnHttpCode(response, expectedHttpStatusCode) {
22
46
  return {
23
47
  pass,
24
48
  message: () => `expected http status code ${status} to equal ${expectedHttpStatusCode}\n\n`
25
- + `server responded with body:\n${JSON.stringify(body, null, 2)}\n\n`
49
+ + `server responded with body:\n${JSON.stringify(result, null, 2)}\n\n`
26
50
  + `server responded with headers:\n${JSON.stringify(headers, null, 2)}`,
27
51
  };
28
52
  }
@@ -31,14 +55,14 @@ function toReturnHttpCode(response, expectedHttpStatusCode) {
31
55
  * Expect a http request to return the given http header.
32
56
  * Log response body and headers otherwise.
33
57
  *
34
- * @param {{body: any, headers: any}} response - The response of the request.
58
+ * @param {object} response - The response of the request.
35
59
  * @param {string} headerField - The expected http header field.
36
60
  * @param {string} headerValue - The expected http header value.
37
61
  * @returns {{message: Function, pass: boolean}} The expect result according to jest.
38
62
  * @see {@link https://jestjs.io/docs/expect#expectextendmatchers}
39
63
  */
40
64
  function toReturnHttpHeader(response, headerField, headerValue) {
41
- const body = response.body ?? response.data;
65
+ const result = extractResult(response);
42
66
  const { headers } = response;
43
67
  const headerFieldLowerCase = headerField.toLowerCase();
44
68
 
@@ -51,7 +75,7 @@ function toReturnHttpHeader(response, headerField, headerValue) {
51
75
  return {
52
76
  pass,
53
77
  message: () => `expected http header "${headerFieldLowerCase}" with value "${headerValue}"\n\n`
54
- + `server responded with body:\n${JSON.stringify(body, null, 2)}\n\n`
78
+ + `server responded with body:\n${JSON.stringify(result, null, 2)}\n\n`
55
79
  + `server responded with headers:\n${JSON.stringify(headers, null, 2)}`,
56
80
  };
57
81
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  describe('matchers', () => {
4
4
  describe('toReturnHttpCode', () => {
5
- it('should succeed if http code is as expected', async () => {
5
+ it('should succeed if http code is as expected - status', async () => {
6
6
  const response = {
7
7
  status: 200,
8
8
  body: {},
@@ -12,6 +12,36 @@ describe('matchers', () => {
12
12
  expect(response).toReturnHttpCode(response.status);
13
13
  });
14
14
 
15
+ it('should succeed if http code is as expected - statusCode', async () => {
16
+ const response = {
17
+ statusCode: 200,
18
+ body: {},
19
+ headers: {},
20
+ };
21
+
22
+ expect(response).toReturnHttpCode(response.statusCode);
23
+ });
24
+
25
+ it('should succeed if data instead of body provided', async () => {
26
+ const response = {
27
+ status: 200,
28
+ data: {},
29
+ headers: {},
30
+ };
31
+
32
+ expect(response).toReturnHttpCode(response.status);
33
+ });
34
+
35
+ it('should succeed if result is in text', async () => {
36
+ const response = {
37
+ status: 200,
38
+ text: 'result',
39
+ headers: {},
40
+ };
41
+
42
+ expect(response).toReturnHttpCode(response.status);
43
+ });
44
+
15
45
  it('should fail if http code is not as expected', async () => {
16
46
  const response = {
17
47
  status: 200,
@@ -36,6 +66,22 @@ describe('matchers', () => {
36
66
  + '{}',
37
67
  );
38
68
  });
69
+
70
+ it('should throw if result is not supported', async () => {
71
+ const response = {
72
+ status: 200,
73
+ headers: {},
74
+ };
75
+
76
+ let caughtError;
77
+ try {
78
+ expect(response).toReturnHttpCode(1);
79
+ } catch (error) {
80
+ caughtError = error;
81
+ }
82
+
83
+ expect(caughtError.message).toBe('jest-matcher-http does not support this library');
84
+ });
39
85
  });
40
86
 
41
87
  describe('toReturnHttpHeader', () => {
@@ -51,6 +97,18 @@ describe('matchers', () => {
51
97
  expect(response).toReturnHttpHeader('request-id', 'some-uuid');
52
98
  });
53
99
 
100
+ it('should succeed if data instead of body provided', async () => {
101
+ const response = {
102
+ status: 200,
103
+ data: {},
104
+ headers: {
105
+ 'request-id': 'some-uuid',
106
+ },
107
+ };
108
+
109
+ expect(response).toReturnHttpHeader('request-id', 'some-uuid');
110
+ });
111
+
54
112
  it('should fail if http header is not as expected', async () => {
55
113
  const response = {
56
114
  status: 200,