specmatic 0.67.6 → 0.68.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.
@@ -1,4 +1,4 @@
1
- name: NPM Publish
1
+ name: publish
2
2
 
3
3
  on:
4
4
  release:
@@ -8,8 +8,8 @@ jobs:
8
8
  publish:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
- - uses: actions/checkout@v2
12
- - uses: actions/setup-node@v2
11
+ - uses: actions/checkout@v3
12
+ - uses: actions/setup-node@v3
13
13
  with:
14
14
  node-version: 14
15
15
  - run: npm install
@@ -17,7 +17,6 @@ jobs:
17
17
  - uses: reedyuk/npm-version@1.1.1
18
18
  with:
19
19
  version: ${{ github.event.release.tag_name }}
20
- - uses: JS-DevTools/npm-publish@v1
20
+ - uses: JS-DevTools/npm-publish@v2
21
21
  with:
22
- check-version: true
23
22
  token: ${{ secrets.NPM_TOKEN }}
@@ -1,4 +1,4 @@
1
- name: Run Tests
1
+ name: test
2
2
 
3
3
  on:
4
4
  push:
@@ -7,11 +7,14 @@ on:
7
7
 
8
8
  jobs:
9
9
  test:
10
+ strategy:
11
+ matrix:
12
+ node-version: [14.x, 16.x, 18.x, 20.x]
10
13
  runs-on: ubuntu-latest
11
14
  steps:
12
- - uses: actions/checkout@v2
13
- - uses: actions/setup-node@v2
15
+ - uses: actions/checkout@v3
16
+ - uses: actions/setup-node@v3
14
17
  with:
15
- node-version: 14
18
+ node-version: ${{ matrix.node-version }}
16
19
  - run: npm install
17
20
  - run: npm test
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Specmatic Framework Node Module
2
2
 
3
- ![tests](https://github.com/znsio/specmatic-node/actions/workflows/test.yml/badge.svg)
4
- [![publish](https://github.com/znsio/specmatic-node/actions/workflows/npm-publish.yml/badge.svg)](https://www.npmjs.com/package/specmatic)
5
- [![GitHub release badge](https://badgen.net/github/release/znsio/specmatic-node/master)](https://github.com/znsio/specmatic-node/releases/latest)
3
+ ![test](https://github.com/znsio/specmatic-node/actions/workflows/test.yml/badge.svg)
4
+ [![publish](https://github.com/znsio/specmatic-node/actions/workflows/publish.yml/badge.svg)](https://www.npmjs.com/package/specmatic)
5
+ [![release](https://badgen.net/github/release/znsio/specmatic-node/master)](https://github.com/znsio/specmatic-node/releases/latest)
6
6
 
7
7
  This node module is a thin wrapper over the [standalone executable jar](https://specmatic.in/getting_started.html#setup). All core capabilities are in the main [Specmatic project](https://github.com/znsio/specmatic). The purpose of this wrapper module is to act as a convenience to help with below aspects.
8
8
 
@@ -35,7 +35,7 @@ https://github.com/znsio/specmatic-order-bff-nodejs
35
35
 
36
36
  Specmatic JS library exposes some of the commands as methods that can be run programmatically from any javascript testing framework, during setup or test phases.
37
37
 
38
- ```
38
+ ```javascript
39
39
  import {
40
40
  startStub,
41
41
  stopStub,
@@ -75,3 +75,9 @@ By default only warning and error messages are displayed. You can configure the
75
75
  ```
76
76
 
77
77
  logLevel accepts all values supported by winston logger (https://github.com/winstonjs/winston#logging-levels)
78
+
79
+ ## Known Issues
80
+
81
+ ### Node 17/18 - Connection Refused error when connecting to stub
82
+
83
+ Node 18 apparently shifted to IPv6 as first choice for resolving hostname when both IPv4 and IPv6 addresses are available. This means `localhost` most likely resolves to `::1` rather than `127.0.0.1` or `0.0.0.0`. Now specmatic node wrapper does not start the stub server but the java program under the hood does it and java still resolves to IPv4 address by default. Thus localhost on node v18 and java might resolve to a different address and any connection from node to the running stub will fail. To resolve this, until we have a permanent solution, we request to disable any IPv6 address mapping to a named host in your DNS resolver or `/etc/hosts`.
package/dist/bin/core.js CHANGED
@@ -6,11 +6,15 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _execSh = _interopRequireDefault(require("exec-sh"));
8
8
  var _path = _interopRequireDefault(require("path"));
9
+ var _child_process = require("child_process");
10
+ var _fs = _interopRequireDefault(require("fs"));
9
11
  var _config = require("../config");
10
- var _logger = _interopRequireDefault(require("../logger"));
12
+ var _logger = _interopRequireDefault(require("../common/logger"));
11
13
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
14
  var callSpecmaticCli = args => {
13
- var specmaticJarPath = _path.default.resolve(_config.specmaticJarPathLocal);
15
+ var rootPath = _path.default.resolve(__dirname, '..', '..');
16
+ var specmaticJarPath = _path.default.resolve(rootPath, _config.specmaticJarName);
17
+ _logger.default.debug("CLI: Specmatic jar path: ".concat(specmaticJarPath));
14
18
  var cliArgs = (args || process.argv).slice(2).join(' ');
15
19
  _logger.default.info("CLI: Running with args \"".concat(cliArgs, "\""));
16
20
  (0, _execSh.default)("java -jar ".concat(specmaticJarPath, " ").concat(cliArgs), {}, err => {
@@ -23,5 +27,32 @@ var callSpecmaticCli = args => {
23
27
  }
24
28
  });
25
29
  };
30
+ function getSpecmaticJarPath() {
31
+ return new Promise((resolve, _reject) => {
32
+ var specmaticJarPath = _path.default.resolve(_config.specmaticJarPathLocal);
33
+ resolve(specmaticJarPath);
34
+ }).then(specmaticJarPath => {
35
+ //Get current directory
36
+ console.log("Script basename: ".concat(__dirname));
37
+ var rootPath = _path.default.resolve(__dirname, '..', '..');
38
+ var specmaticJarPath2 = _path.default.resolve(rootPath, _config.specmaticJarName);
39
+ console.log("Script jar path: ".concat(specmaticJarPath2));
40
+ console.log("Script jar path exists? ".concat(_fs.default.existsSync(specmaticJarPath2)));
41
+ if (!_fs.default.existsSync(specmaticJarPath)) {
42
+ return getGlobalSpecmaticJarPath();
43
+ } else {
44
+ return specmaticJarPath;
45
+ }
46
+ });
47
+ }
48
+ function getGlobalSpecmaticJarPath() {
49
+ return new Promise((resolve, _reject) => {
50
+ (0, _child_process.exec)('npm root -g', (_err, stdout) => {
51
+ var npmGlobalModuleInstallPath = stdout.replace(/\n/g, '');
52
+ var jarPath = _path.default.join(npmGlobalModuleInstallPath, 'specmatic', _config.specmaticJarName);
53
+ resolve(jarPath);
54
+ });
55
+ });
56
+ }
26
57
  var _default = callSpecmaticCli;
27
58
  exports.default = _default;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ exports.initLogger = initLogger;
8
+ var _winston = require("winston");
9
+ var _fs = _interopRequireDefault(require("fs"));
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ var logFormat = _winston.format.printf(_ref => {
12
+ var {
13
+ level,
14
+ message,
15
+ label,
16
+ timestamp
17
+ } = _ref;
18
+ return "[".concat(label, "] ").concat(timestamp, " ").concat(level, ": ").concat(message);
19
+ });
20
+ function getSpecmaticConfig() {
21
+ var packageJsonPath = './package.json';
22
+ var specmaticConfig;
23
+ if (_fs.default.existsSync(packageJsonPath)) {
24
+ var packageJsonContent;
25
+ try {
26
+ packageJsonContent = _fs.default.readFileSync(packageJsonPath);
27
+ } catch (error) {
28
+ packageJsonContent = "{}";
29
+ }
30
+ var packageConfig = JSON.parse(packageJsonContent);
31
+ specmaticConfig = packageConfig.specmatic;
32
+ }
33
+ return specmaticConfig || {};
34
+ }
35
+ function initLogger() {
36
+ var specmaticConfig = getSpecmaticConfig();
37
+ var logger = (0, _winston.createLogger)({
38
+ level: specmaticConfig.logLevel || 'warn',
39
+ format: _winston.format.combine(_winston.format.label({
40
+ label: 'specmatic'
41
+ }), _winston.format.timestamp({
42
+ format: 'DD-MM-YYYY HH:mm:ss'
43
+ }), logFormat),
44
+ transports: [new _winston.transports.Console()]
45
+ });
46
+ return logger;
47
+ }
48
+ var _default = initLogger();
49
+ exports.default = _default;
package/dist/config.js CHANGED
@@ -3,8 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.specmaticJarPathRemote = exports.specmaticJarPathLocal = exports.specmatic = void 0;
7
- var specmaticJarPathLocal = './node_modules/specmatic/specmatic.jar';
6
+ exports.specmaticJarPathRemote = exports.specmaticJarPathLocal = exports.specmaticJarName = exports.specmatic = void 0;
7
+ var specmaticJarName = 'specmatic.jar';
8
+ exports.specmaticJarName = specmaticJarName;
9
+ var specmaticJarPathLocal = "./node_modules/specmatic/".concat(specmaticJarName);
8
10
  exports.specmaticJarPathLocal = specmaticJarPathLocal;
9
11
  var specmaticJarPathRemote = 'https://github.com/znsio/specmatic/releases/download/0.24.2/specmatic.jar';
10
12
  exports.specmaticJarPathRemote = specmaticJarPathRemote;
package/dist/lib/index.js CHANGED
@@ -10,7 +10,7 @@ var _execSh = _interopRequireDefault(require("exec-sh"));
10
10
  var _config = require("../config");
11
11
  var _fastXmlParser = require("fast-xml-parser");
12
12
  var _fs = _interopRequireDefault(require("fs"));
13
- var _logger = _interopRequireDefault(require("../logger"));
13
+ var _logger = _interopRequireDefault(require("../common/logger"));
14
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
15
  var specmaticJarPath = _path.default.resolve(_config.specmaticJarPathLocal);
16
16
  var startStub = (host, port, stubDir) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specmatic",
3
- "version": "0.67.6",
3
+ "version": "0.68.1",
4
4
  "description": "Node wrapper for Specmatic",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -31,9 +31,6 @@
31
31
  "jest-extended/all"
32
32
  ]
33
33
  },
34
- "specmatic": {
35
- "logLevel": "debug"
36
- },
37
34
  "dependencies": {
38
35
  "@babel/cli": "^7.21.5",
39
36
  "@babel/core": "^7.21.8",
package/specmatic.jar CHANGED
Binary file
@@ -1,13 +1,35 @@
1
1
  import execSh from 'exec-sh';
2
2
  import path from 'path';
3
3
  import callSpecmaticCli from '../core';
4
- import { specmaticJarPathLocal } from '../../config';
4
+ import { specmaticJarName, specmaticJarPathLocal } from '../../config';
5
+ import fs from 'fs';
6
+ import { exec } from 'child_process';
5
7
 
6
8
  jest.mock('exec-sh');
9
+ jest.mock('child_process');
10
+ const execMock = exec as unknown as jest.Mock;
7
11
 
8
- test('arguments to be passed correctly to Specmatic lib', () => {
12
+ beforeEach(() => {
13
+ jest.resetAllMocks();
14
+ });
15
+
16
+ test('pass all wrapper arguments to the jar', async () => {
17
+ jest.spyOn(fs, 'existsSync').mockReturnValue(true);
9
18
  const testArgs = ['node', 'index.js', 'stub', '*.specmatic', '--data', 'src/mocks', '--host', 'localhost', '--port', '8000'];
10
19
  callSpecmaticCli(testArgs);
11
- expect(execSh.mock.calls[0][0]).toBe(`java -jar ${path.resolve(specmaticJarPathLocal)} ${testArgs.slice(2).join(' ')}`);
20
+ const specmaticJarPath = path.resolve(__dirname, '..', '..', '..', specmaticJarName);
21
+ expect(execSh.mock.calls[0][0]).toBe(`java -jar ${path.resolve(specmaticJarPath)} ${testArgs.slice(2).join(' ')}`);
12
22
  expect(execSh).toHaveBeenCalledTimes(1);
13
23
  });
24
+
25
+ // test('look for jar in specmatic globally installled directory when run from global install', async () => {
26
+ // jest.spyOn(fs, 'existsSync').mockReturnValue(false);
27
+ // const testArgs = ['node', 'index.js', 'stub', '*.specmatic', '--data', 'src/mocks', '--host', 'localhost', '--port', '8000'];
28
+ // const NPM_GLOBAL_PATH = '/npm/global/path';
29
+ // setTimeout(() => {
30
+ // execMock.mock.calls[0][1](0, NPM_GLOBAL_PATH);
31
+ // }, 0);
32
+ // await callSpecmaticCli(testArgs);
33
+ // expect(execSh.mock.calls[0][0]).toBe(`java -jar ${path.resolve(NPM_GLOBAL_PATH, 'specmatic', specmaticJarName)} ${testArgs.slice(2).join(' ')}`);
34
+ // expect(execSh).toHaveBeenCalledTimes(1);
35
+ // });
package/src/bin/core.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  import execSh from 'exec-sh';
2
2
  import path from 'path';
3
- import { specmaticJarPathLocal } from '../config';
4
- import logger from '../logger';
3
+ import { exec } from 'child_process';
4
+ import fs from 'fs';
5
+ import { specmaticJarPathLocal, specmaticJarName } from '../config';
6
+ import logger from '../common/logger';
5
7
 
6
8
  const callSpecmaticCli = (args?: string[]) => {
7
- const specmaticJarPath = path.resolve(specmaticJarPathLocal);
9
+ const rootPath = path.resolve(__dirname, '..', '..');
10
+ const specmaticJarPath = path.resolve(rootPath, specmaticJarName);
11
+ logger.debug(`CLI: Specmatic jar path: ${specmaticJarPath}`);
8
12
  const cliArgs = (args || process.argv).slice(2).join(' ');
9
13
  logger.info(`CLI: Running with args "${cliArgs}"`);
10
14
  execSh(`java -jar ${specmaticJarPath} ${cliArgs}`, {}, (err: any) => {
@@ -18,4 +22,33 @@ const callSpecmaticCli = (args?: string[]) => {
18
22
  });
19
23
  };
20
24
 
25
+ function getSpecmaticJarPath() {
26
+ return new Promise<string>((resolve, _reject) => {
27
+ let specmaticJarPath = path.resolve(specmaticJarPathLocal);
28
+ resolve(specmaticJarPath);
29
+ }).then(specmaticJarPath => {
30
+ //Get current directory
31
+ console.log(`Script basename: ${__dirname}`);
32
+ const rootPath = path.resolve(__dirname, '..', '..');
33
+ const specmaticJarPath2 = path.resolve(rootPath, specmaticJarName);
34
+ console.log(`Script jar path: ${specmaticJarPath2}`);
35
+ console.log(`Script jar path exists? ${fs.existsSync(specmaticJarPath2)}`);
36
+ if (!fs.existsSync(specmaticJarPath)) {
37
+ return getGlobalSpecmaticJarPath();
38
+ } else {
39
+ return specmaticJarPath;
40
+ }
41
+ });
42
+ }
43
+
44
+ function getGlobalSpecmaticJarPath() {
45
+ return new Promise((resolve, _reject) => {
46
+ exec('npm root -g', (_err, stdout) => {
47
+ const npmGlobalModuleInstallPath = stdout.replace(/\n/g, '');
48
+ const jarPath = path.join(npmGlobalModuleInstallPath, 'specmatic', specmaticJarName);
49
+ resolve(jarPath);
50
+ });
51
+ });
52
+ }
53
+
21
54
  export default callSpecmaticCli;
@@ -0,0 +1,64 @@
1
+ import { initLogger } from '../logger';
2
+
3
+ import fs from 'fs';
4
+
5
+ beforeEach(() => {
6
+ jest.resetAllMocks();
7
+ });
8
+
9
+ test('Logging level is debug as configured in package.json', () => {
10
+ const packageJsonFileReadSpy = jest.spyOn(fs, 'readFileSync').mockReturnValue(
11
+ '{\
12
+ "specmatic": {\
13
+ "logLevel": "debug"\
14
+ }\
15
+ }'
16
+ );
17
+ const logger = initLogger();
18
+ expect(packageJsonFileReadSpy).toHaveBeenCalledTimes(1);
19
+ expect(logger.level).toBe('debug');
20
+ });
21
+
22
+ test('Logging level is info as configured in package.json', () => {
23
+ const packageJsonFileReadSpy = jest.spyOn(fs, 'readFileSync').mockReturnValue(
24
+ '{\
25
+ "specmatic": {\
26
+ "logLevel": "info"\
27
+ }\
28
+ }'
29
+ );
30
+ const logger = initLogger();
31
+ expect(packageJsonFileReadSpy).toHaveBeenCalledTimes(1);
32
+ expect(logger.level).toBe('info');
33
+ });
34
+
35
+ test('Defaults logging level to warn when logLevel setting does not exist with specmatic configuration', () => {
36
+ const packageJsonFileReadSpy = jest.spyOn(fs, 'readFileSync').mockReturnValue(
37
+ '{\
38
+ "specmatic": {\
39
+ }\
40
+ }'
41
+ );
42
+ const logger = initLogger();
43
+ expect(packageJsonFileReadSpy).toHaveBeenCalledTimes(1);
44
+ expect(logger.level).toBe('warn');
45
+ })
46
+
47
+ test('Defaults logging level to warn when specmatic configuration does not exist', () => {
48
+ const packageJsonFileReadSpy = jest.spyOn(fs, 'readFileSync').mockReturnValue(
49
+ '{\
50
+ }'
51
+ );
52
+ const logger = initLogger();
53
+ expect(packageJsonFileReadSpy).toHaveBeenCalledTimes(1);
54
+ expect(logger.level).toBe('warn');
55
+ })
56
+
57
+ test('Defaults logging level to warn when package.json does not exist', () => {
58
+ const packageJsonFileReadSpy = jest.spyOn(fs, 'readFileSync').mockImplementation(() => {
59
+ throw new Error();
60
+ });
61
+ const logger = initLogger();
62
+ expect(packageJsonFileReadSpy).toHaveBeenCalledTimes(1);
63
+ expect(logger.level).toBe('warn');
64
+ });
@@ -0,0 +1,34 @@
1
+ import { createLogger, format, transports } from 'winston';
2
+ import fs from 'fs';
3
+
4
+ const logFormat = format.printf(({ level, message, label, timestamp }) => {
5
+ return `[${label}] ${timestamp} ${level}: ${message}`;
6
+ });
7
+
8
+ function getSpecmaticConfig() {
9
+ const packageJsonPath = './package.json';
10
+ var specmaticConfig;
11
+ if (fs.existsSync(packageJsonPath)) {
12
+ let packageJsonContent;
13
+ try {
14
+ packageJsonContent = fs.readFileSync(packageJsonPath);
15
+ } catch (error) {
16
+ packageJsonContent = "{}";
17
+ }
18
+ const packageConfig = JSON.parse(packageJsonContent as unknown as string);
19
+ specmaticConfig = packageConfig.specmatic;
20
+ }
21
+ return specmaticConfig || {};
22
+ }
23
+
24
+ export function initLogger() {
25
+ const specmaticConfig = getSpecmaticConfig();
26
+ const logger = createLogger({
27
+ level: specmaticConfig.logLevel || 'warn',
28
+ format: format.combine(format.label({ label: 'specmatic' }), format.timestamp({ format: 'DD-MM-YYYY HH:mm:ss' }), logFormat),
29
+ transports: [new transports.Console()],
30
+ });
31
+ return logger;
32
+ }
33
+
34
+ export default initLogger();
package/src/config.ts CHANGED
@@ -1,3 +1,4 @@
1
- export const specmaticJarPathLocal = './node_modules/specmatic/specmatic.jar';
1
+ export const specmaticJarName = 'specmatic.jar';
2
+ export const specmaticJarPathLocal = `./node_modules/specmatic/${specmaticJarName}`;
2
3
  export const specmaticJarPathRemote = 'https://github.com/znsio/specmatic/releases/download/0.24.2/specmatic.jar';
3
- export const specmatic = 'specmatic.json'
4
+ export const specmatic = 'specmatic.json';
package/src/lib/index.ts CHANGED
@@ -5,7 +5,7 @@ import { specmaticJarPathLocal, specmatic } from '../config';
5
5
  import { ChildProcess } from 'child_process';
6
6
  import { XMLParser } from 'fast-xml-parser';
7
7
  import fs from 'fs';
8
- import logger from '../logger';
8
+ import logger from '../common/logger';
9
9
 
10
10
  const specmaticJarPath = path.resolve(specmaticJarPathLocal);
11
11
 
package/dist/logger.js DELETED
@@ -1,31 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _winston = require("winston");
8
- var _fs = _interopRequireDefault(require("fs"));
9
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
- var data = _fs.default.readFileSync('./package.json');
11
- var packageConfig = JSON.parse(data);
12
- var specmaticConfig = packageConfig.specmatic || {};
13
- var logFormat = _winston.format.printf(_ref => {
14
- var {
15
- level,
16
- message,
17
- label,
18
- timestamp
19
- } = _ref;
20
- return "[".concat(label, "] ").concat(timestamp, " ").concat(level, ": ").concat(message);
21
- });
22
- var _default = (0, _winston.createLogger)({
23
- level: specmaticConfig.logLevel || 'warn',
24
- format: _winston.format.combine(_winston.format.label({
25
- label: 'specmatic'
26
- }), _winston.format.timestamp({
27
- format: 'DD-MM-YYYY HH:mm:ss'
28
- }), logFormat),
29
- transports: [new _winston.transports.Console()]
30
- });
31
- exports.default = _default;
package/src/logger.ts DELETED
@@ -1,16 +0,0 @@
1
- import { createLogger, format, transports } from 'winston';
2
- import fs from 'fs';
3
-
4
- const data = fs.readFileSync('./package.json');
5
- const packageConfig = JSON.parse(data as unknown as string);
6
- const specmaticConfig = packageConfig.specmatic || {};
7
-
8
- const logFormat = format.printf(({ level, message, label, timestamp }) => {
9
- return `[${label}] ${timestamp} ${level}: ${message}`;
10
- });
11
-
12
- export default createLogger({
13
- level: specmaticConfig.logLevel || 'warn',
14
- format: format.combine(format.label({ label: 'specmatic' }), format.timestamp({ format: 'DD-MM-YYYY HH:mm:ss' }), logFormat),
15
- transports: [new transports.Console()],
16
- });