Repository: pactumjs/pactum
Pull request #78 (2021-08-08 12:16:58)

3.0.18
- added functions to set custom adapters for plugins
- added support for expect schema options


Patch:
diff --git a/package-lock.json b/package-lock.json
index f7a9884..afdda90 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "pactum",
-  "version": "3.0.17",
+  "version": "3.0.18",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 32a2920..1df976c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pactum",
-  "version": "3.0.17",
+  "version": "3.0.18",
   "description": "REST API Testing Tool for all levels in a Test Pyramid",
   "main": "./src/index.js",
   "types": "./src/index.d.ts",
diff --git a/src/adapters/json.schema.js b/src/adapters/json.schema.js
index b9091e9..602a36b 100644
--- a/src/adapters/json.schema.js
+++ b/src/adapters/json.schema.js
@@ -1,7 +1,8 @@
 const { validator } = require('@exodus/schemasafe');
 
-function validate(schema, target) {
-  const validate = validator(schema, { includeErrors: true });
+function validate(schema, target, options = {}) {
+  options.includeErrors = true;
+  const validate = validator(schema, options);
   validate(target);
   if (validate.errors) {
     return JSON.stringify(validate.errors, null, 2);
diff --git a/src/exports/expect.d.ts b/src/exports/expect.d.ts
index 4008df3..83b22e6 100644
--- a/src/exports/expect.d.ts
+++ b/src/exports/expect.d.ts
@@ -11,7 +11,9 @@ export interface Have {
   jsonLike(value: any): void;
   jsonLike(path: string, value: any): void;
   jsonSchema(schema: object): void;
+  jsonSchema(schema: object, options: object): void;
   jsonSchema(path: string, schema: object): void;
+  jsonSchema(path: string, schema: object, options: object): void;
   jsonMatch(value: object): void;
   jsonMatch(path: string, value: object): void;
   jsonMatchStrict(value: object): void;
diff --git a/src/exports/expect.js b/src/exports/expect.js
index b3c9468..b7ad79a 100644
--- a/src/exports/expect.js
+++ b/src/exports/expect.js
@@ -54,8 +54,20 @@ class Have {
     this._validate();
   }
 
-  jsonSchema(path, value) {
-    typeof value === 'undefined' ? this.expect.jsonSchema.push(path) : this.expect.jsonSchemaQuery.push({ path, value });
+  jsonSchema(path, value, options) {
+    if (typeof options === 'object') {
+      this.expect.jsonSchemaQuery.push({ path, value, options });
+    } else {
+      if (typeof value === 'undefined') {
+        this.expect.jsonSchema.push({ value: path });
+      } else {
+        if (typeof path === 'object' && typeof value === 'object') {
+          this.expect.jsonSchema.push({ value: path, options: value });
+        } else {
+          this.expect.jsonSchemaQuery.push({ path, value });
+        }
+      }
+    }
     this._validate();
   }
 
diff --git a/src/exports/settings.d.ts b/src/exports/settings.d.ts
index 0569651..719863f 100644
--- a/src/exports/settings.d.ts
+++ b/src/exports/settings.d.ts
@@ -16,6 +16,29 @@ export interface Logger {
 }
 export function setLogger(logger: Logger): void;
 
+export type JsonLikeValidateFunction = (actual: any, expected: any, options?: any) => any;
+export interface JsonLikeAdapter {
+  validate: JsonLikeValidateFunction;
+}
+export function setJsonLikeAdapter(adapter: JsonLikeAdapter): void;
+
+export type GetMatchingRulesFunction = (data: any, path: any, rules: any) => any;
+export type GetRawValueFunction = (data: any) => any;
+export type JsonMatchValidateFunction = (actual: any, expected: any, rules: any, path: any, strict: any) => any;
+export interface JsonMatchAdapter {
+  getMatchingRules: GetMatchingRulesFunction;
+  getRawValue: GetRawValueFunction;
+  validate: JsonMatchValidateFunction;
+}
+export function setJsonMatchAdapter(adapter: JsonMatchAdapter): void;
+
+export type JsonSchemaValidateFunction = (schema: any, target: any, options?: any) => any;
+export interface JsonSchemaAdapter {
+  validate: JsonSchemaValidateFunction;
+}
+export function setJsonSchemaAdapter(adapter: JsonSchemaAdapter): void;
+
+
 export interface Strategy {
   starts?: string;
   ends?: string;
diff --git a/src/exports/settings.js b/src/exports/settings.js
index afccc05..93a3e57 100644
--- a/src/exports/settings.js
+++ b/src/exports/settings.js
@@ -1,5 +1,8 @@
 const config = require('../config');
 const logger = require('../plugins/logger');
+const jsonLike = require('../plugins/json.like');
+const jsonMatch = require('../plugins/json.match');
+const jsonSchema = require('../plugins/json.schema');
 
 const settings = {
 
@@ -11,6 +14,18 @@ const settings = {
     logger.setAdapter(lgr);
   },
 
+  setJsonLikeAdapter(adapter) {
+    jsonLike.setAdapter(adapter);
+  },
+
+  setJsonMatchAdapter(adapter) {
+    jsonMatch.setAdapter(adapter);
+  },
+
+  setJsonSchemaAdapter(adapter) {
+    jsonSchema.setAdapter(adapter);
+  },
+
   setAssertHandlerStrategy(strategy) {
     config.strategy.assert.handler = strategy;
   },
diff --git a/src/models/Spec.d.ts b/src/models/Spec.d.ts
index d9604a2..64aa5da 100644
--- a/src/models/Spec.d.ts
+++ b/src/models/Spec.d.ts
@@ -298,7 +298,9 @@ declare class Spec {
    * @see https://pactumjs.github.io/#/response-validation?id=expectjsonschema
    */
   expectJsonSchema(schema: object): Spec;
+  expectJsonSchema(schema: object, options: object): Spec;
   expectJsonSchema(path: string, schema: object): Spec;
+  expectJsonSchema(path: string, schema: object, options: object): Spec;
 
   /**
    * expects the json to match with value
diff --git a/src/models/Spec.js b/src/models/Spec.js
index 8b60055..4082900 100644
--- a/src/models/Spec.js
+++ b/src/models/Spec.js
@@ -369,8 +369,20 @@ class Spec {
   }
   expectJsonLikeAt(...args) { return this.expectJsonLike(...args); }
 
-  expectJsonSchema(path, value) {
-    typeof value === 'undefined' ? this._expect.jsonSchema.push(path) : this._expect.jsonSchemaQuery.push({ path, value });
+  expectJsonSchema(path, value, options) {
+    if (typeof options === 'object') {
+      this._expect.jsonSchemaQuery.push({ path, value, options });
+    } else {
+      if (typeof value === 'undefined') {
+        this._expect.jsonSchema.push({ value: path });
+      } else {
+        if (typeof path === 'object' && typeof value === 'object') {
+          this._expect.jsonSchema.push({ value: path, options: value });
+        } else {
+          this._expect.jsonSchemaQuery.push({ path, value });
+        }
+      }
+    }
     return this;
   }
   expectJsonSchemaAt(...args) { return this.expectJsonSchema(...args); }
diff --git a/src/models/expect.js b/src/models/expect.js
index f31dcd2..e5a64a1 100644
--- a/src/models/expect.js
+++ b/src/models/expect.js
@@ -246,7 +246,7 @@ class Expect {
   _validateJsonSchema(response) {
     this.jsonSchema = processor.processData(this.jsonSchema);
     for (let i = 0; i < this.jsonSchema.length; i++) {
-      const errors = jsv.validate(this.jsonSchema[i], response.json);
+      const errors = jsv.validate(this.jsonSchema[i].value, response.json, this.jsonSchema[i].options);
       if (errors) {
         this.fail(`Response doesn't match with JSON schema - ${errors}`);
       }
@@ -258,7 +258,7 @@ class Expect {
     for (let i = 0; i < this.jsonSchemaQuery.length; i++) {
       const jQ = this.jsonSchemaQuery[i];
       const value = jqy(jQ.path, { data: response.json }).value;
-      const errors = jsv.validate(jQ.value, value);
+      const errors = jsv.validate(jQ.value, value, jQ.options);
       if (errors) {
         this.fail(`Response doesn't match with JSON schema at ${jQ.path}: \n ${JSON.stringify(errors, null, 2)}`);
       }
diff --git a/src/plugins/json.schema.js b/src/plugins/json.schema.js
index 8a19a94..15d9e3a 100644
--- a/src/plugins/json.schema.js
+++ b/src/plugins/json.schema.js
@@ -1,8 +1,8 @@
 const BasePlugin = require('./plugin.base');
 
 class JsonSchemaValidator extends BasePlugin {
-  validate(schema, target) {
-    return this.adapter.validate(schema, target);
+  validate(schema, target, options) {
+    return this.adapter.validate(schema, target, options);
   }
 }
 


Test patch:
diff --git a/test/component/bdd.spec.js b/test/component/bdd.spec.js
index 68049cf..734c06c 100644
--- a/test/component/bdd.spec.js
+++ b/test/component/bdd.spec.js
@@ -76,10 +76,39 @@ describe('BDD', () => {
     expect(response).to.have.jsonSchema({ properties: { name: { type: 'string' } } });
   });
 
+  it('should return a valid schema with options', async () => {
+    expect(response).to.have.jsonSchema(
+      {
+        properties: {
+          name: {
+            type: 'string',
+            format: 'only-snow'
+          }
+        }
+      },
+      {
+        formats: {
+          'only-snow': /^snow$/
+        }
+      }
+    );
+  });
+
   it('should return a valid schema at', async () => {
     expect(response).to.have.jsonSchema('.', { properties: { name: { type: 'string' } } });
   });
 
+  it('should return a valid schema at with options', async () => {
+    expect(response).to.have.jsonSchema('.',
+      { properties: { name: { type: 'string', format: 'only-snow' } } },
+      {
+        formats: {
+          'only-snow': /^snow$/
+        }
+      }
+    );
+  });
+
   it('should return a match', async () => {
     expect(response).to.have.jsonMatch(like({ name: 'snow' }));
   });
diff --git a/test/component/expects.spec.js b/test/component/expects.spec.js
index 8eb1ad0..80d185e 100644
--- a/test/component/expects.spec.js
+++ b/test/component/expects.spec.js
@@ -1028,4 +1028,37 @@ describe('Expects', () => {
     expect(err).not.undefined;
   });
 
+  it('json schema with options', async () => {
+    await pactum.spec()
+      .useInteraction('default get')
+      .get('http://localhost:9393/default/get')
+      .expectJsonSchema({
+        type: 'object',
+        properties: {
+          method: {
+            type: 'string',
+            format: 'only-get'
+          }
+        }
+      }, {
+        formats: {
+          'only-get': /^GET$/
+        }
+      });
+  });
+
+  it('json schema at with options', async () => {
+    await pactum.spec()
+      .useInteraction('default get')
+      .get('http://localhost:9393/default/get')
+      .expectJsonSchema('method', {
+        type: 'string',
+        format: 'only-get'
+      }, {
+        formats: {
+          'only-get': /^GET$/
+        }
+      });
+  });
+
 });
\ No newline at end of file
diff --git a/test/unit/settings.spec.js b/test/unit/settings.spec.js
index 778fb37..620628d 100644
--- a/test/unit/settings.spec.js
+++ b/test/unit/settings.spec.js
@@ -15,6 +15,18 @@ describe('Settings', () => {
     settings.setLogger(logger);
   });
 
+  it('setJsonLikeAdapter', () => {
+    settings.setJsonLikeAdapter(require('../../src/adapters/json.like'));
+  });
+
+  it('setJsonMatchAdapter', () => {
+    settings.setJsonMatchAdapter(require('../../src/adapters/json.match'));
+  });
+
+  it('setJsonSchemaAdapter', () => {
+    settings.setJsonSchemaAdapter(require('../../src/adapters/json.schema'));
+  });
+
   after(() => {
     settings.setSnapshotDirectoryPath('.pactum/snapshots');
   });