Repository: pactumjs/pactum
Pull request #43 (2021-01-31 12:51:53)

V3
- asynchronous expectations
- renamed data function handler
- get access to data management


Patch:
diff --git a/package.json b/package.json
index d3313b4..b5a1d1a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pactum",
-  "version": "3.0.4",
+  "version": "3.0.5",
   "description": "REST API Testing Tool for all levels in a Test Pyramid",
   "main": "./src/index.js",
   "types": "./src/index.d.ts",
@@ -10,10 +10,7 @@
   "exports": {
     ".": "./src/index.js",
     "./handler": "./src/exports/handler.js",
-    "./matcher": "./src/exports/matcher.js",
     "./mock": "./src/exports/mock.js",
-    "./pact": "./src/exports/pact.js",
-    "./provider": "./src/exports/provider.js",
     "./request": "./src/exports/request.js",
     "./settings": "./src/exports/settings.js",
     "./state": "./src/exports/state.js"
diff --git a/src/exports/expect.d.ts b/src/exports/expect.d.ts
index 2a17b56..995d633 100644
--- a/src/exports/expect.d.ts
+++ b/src/exports/expect.d.ts
@@ -13,7 +13,7 @@ export interface Have {
   jsonMatch(value: object): void;
   jsonMatchAt(path: string, value: object): void;
   responseTimeLessThan(ms: number): void;
-  _(handler: string, data: any): void
+  _(handler: string, data: any): Promise<void>;
 }
 
 export interface To {
diff --git a/src/exports/expect.js b/src/exports/expect.js
index 836daca..26e2d46 100644
--- a/src/exports/expect.js
+++ b/src/exports/expect.js
@@ -80,11 +80,11 @@ class Have {
 
   _(handler, data) {
     this.expect.customExpectHandlers.push({ handler, data });
-    this._validate();
+    return this._validate();
   }
 
   _validate() {
-    this.expect.validate({}, this.response);
+    return this.expect.validate({}, this.response);
   }
 
 }
diff --git a/src/exports/handler.d.ts b/src/exports/handler.d.ts
index a21a267..81cdb38 100644
--- a/src/exports/handler.d.ts
+++ b/src/exports/handler.d.ts
@@ -28,6 +28,10 @@ interface RequestResponseContext {
   res: Response;
 }
 
+interface CaptureContext extends RequestResponseContext {
+  store?: object;
+}
+
 interface ExpectHandlerContext extends RequestResponseContext {
   data?: any;
 }
@@ -53,7 +57,7 @@ interface ParentHandler {
 export type SpecHandlerFunction = (ctx: SpecHandlerContext) => void;
 export type ExpectHandlerFunction = (ctx: ExpectHandlerContext) => void;
 export type RetryHandlerFunction = (ctx: RequestResponseContext) => boolean;
-export type ReturnHandlerFunction = (ctx: RequestResponseContext) => any;
+export type CaptureHandlerFunction = (ctx: CaptureContext) => any;
 export type StateHandlerFunction = (ctx: StateHandlerContext) => any;
 export type DataHandlerFunction = (ctx: DataHandlerContext) => any;
 export type InteractionHandlerFunction = (ctx: InteractionContext) => Interaction | ParentHandler;
@@ -77,7 +81,7 @@ export function addRetryHandler(name: string, func: RetryHandlerFunction): void;
 /**
  * adds a custom return handler
  */
-export function addCaptureHandler(name: string, func: ReturnHandlerFunction): void;
+export function addCaptureHandler(name: string, func: CaptureHandlerFunction): void;
 
 /**
  * adds a custom state handler
@@ -87,7 +91,7 @@ export function addStateHandler(name: string, func: StateHandlerFunction): void;
 /**
  * adds a custom data handler
  */
-export function addDataFunHandler(name: string, func: DataHandlerFunction): void;
+export function addDataFuncHandler(name: string, func: DataHandlerFunction): void;
 
 /**
  * adds a custom interaction handler
diff --git a/src/exports/handler.js b/src/exports/handler.js
index d51cb5b..8d2ff83 100644
--- a/src/exports/handler.js
+++ b/src/exports/handler.js
@@ -62,13 +62,13 @@ const handler = {
     throw new PactumHandlerError(`State Handler Not Found - '${name}'`);
   },
 
-  addDataFunHandler(name, func) {
+  addDataFuncHandler(name, func) {
     isValidHandler(name, func);
     dataHandlers[name] = func;
     config.data.ref.fun.enabled = true;
   },
 
-  getDataFunHandler(name) {
+  getDataFuncHandler(name) {
     if (dataHandlers[name]) return dataHandlers[name];
     throw new PactumHandlerError(`Data Handler Not Found - '${name}'`);
   },
diff --git a/src/exports/stash.d.ts b/src/exports/stash.d.ts
index 9af12e5..bb48348 100644
--- a/src/exports/stash.d.ts
+++ b/src/exports/stash.d.ts
@@ -30,4 +30,8 @@ export function addDataMap(maps: object[]): void;
  * });
  */
 export function addDataTemplate(templates: object): void;
-export function addDataTemplate(templates: object[]): void;
\ No newline at end of file
+export function addDataTemplate(templates: object[]): void;
+
+export function getDataMap(): object;
+export function getDataTemplate(): object;
+export function getDataStore(): object;
\ No newline at end of file
diff --git a/src/helpers/dataProcessor.js b/src/helpers/dataProcessor.js
index 312b880..92cbba8 100644
--- a/src/helpers/dataProcessor.js
+++ b/src/helpers/dataProcessor.js
@@ -99,7 +99,7 @@ const dataProcessor = {
         }
         if (refType === 'F') {
           const [handlerName, ..._args] = refValue.split(':');
-          const handlerFun = handler.getDataFunHandler(handlerName);
+          const handlerFun = handler.getDataFuncHandler(handlerName);
           values.push(handlerFun({ args: _args.length > 0 ? _args[0].split(',') : _args }));
         }
         if (refType === 'S') {
diff --git a/src/helpers/toss.helper.js b/src/helpers/toss.helper.js
index 3530198..4fa8d75 100644
--- a/src/helpers/toss.helper.js
+++ b/src/helpers/toss.helper.js
@@ -31,7 +31,7 @@ function getPathValueFromSpec(path, spec) {
 }
 
 function storeSpecData(spec, stores) {
-  const ctx = { req: spec._request, res: spec._response };
+  const ctx = { req: spec._request, res: spec._response, store: stash.getDataStore() };
   for (let i = 0; i < stores.length; i++) {
     const store = stores[i];
     const specData = {};
diff --git a/src/models/Tosser.js b/src/models/Tosser.js
index 013fd8e..6c0b6e0 100644
--- a/src/models/Tosser.js
+++ b/src/models/Tosser.js
@@ -34,7 +34,7 @@ class Tosser {
     await this.removeInteractionsFromServer();
     this.recordData();
     th.storeSpecData(this.spec, this.spec._stores);
-    this.validate();
+    await this.validate();
     return th.getOutput(this.spec, this.spec._returns);
   }
 
@@ -111,11 +111,11 @@ class Tosser {
     }
   }
 
-  validate() {
+  async validate() {
     this.validateError();
     try {
       this.validateInteractions();
-      this.validateResponse();
+      await this.validateResponse();
       this.spec.status = 'PASSED';
       rlc.afterSpecReport(this.spec);
     } catch (error) {
@@ -140,8 +140,8 @@ class Tosser {
     this.expect.validateInteractions(this.interactions);
   }
 
-  validateResponse() {
-    this.expect.validate(this.request, this.response);
+  async validateResponse() {
+    await this.expect.validate(this.request, this.response);
   }
 
   printReqAndRes() {
diff --git a/src/models/expect.js b/src/models/expect.js
index 22f0036..e0b5177 100644
--- a/src/models/expect.js
+++ b/src/models/expect.js
@@ -48,7 +48,8 @@ class Expect {
     this._validateJsonMatchQuery(response);
     this._validateJsonSnapshot(response);
     this._validateResponseTime(response);
-    this._validateCustomExpectHandlers(request, response);
+    // for asynchronous expectations
+    return this._validateCustomExpectHandlers(request, response);
   }
 
   validateInteractions(interactions) {
@@ -313,15 +314,15 @@ class Expect {
     }
   }
 
-  _validateCustomExpectHandlers(request, response) {
+  async _validateCustomExpectHandlers(request, response) {
     for (let i = 0; i < this.customExpectHandlers.length; i++) {
       const requiredHandler = this.customExpectHandlers[i];
       const ctx = { req: request, res: response, data: requiredHandler.data };
       if (typeof requiredHandler.handler === 'function') {
-        requiredHandler.handler(ctx);
+        await requiredHandler.handler(ctx);
       } else {
         const handlerFun = handler.getExpectHandler(requiredHandler.handler);
-        handlerFun(ctx);
+        await handlerFun(ctx);
       }
     }
   }


Test patch:
diff --git a/test/component/chai.spec.js b/test/component/chai.spec.js
index e08fd42..aa50b74 100644
--- a/test/component/chai.spec.js
+++ b/test/component/chai.spec.js
@@ -14,6 +14,14 @@ describe('Chai Like Assertions', () => {
       const res = ctx.res;
       expect(res).should.have.status(200);
     });
+    handler.addExpectHandler('a status 200', (ctx) => {
+      const res = ctx.res;
+      expect(res).should.have.status(200);
+    });
+    handler.addExpectHandler('a status 400', (ctx) => {
+      const res = ctx.res;
+      expect(res).should.have.status(400);
+    });
   });
 
   it('Given a user with name snow', () => {
@@ -49,8 +57,8 @@ describe('Chai Like Assertions', () => {
   });
 
   it('should return a valid user', async () => {
-    expect(response).to.have.json({ name: 'snow'});
-    expect(response).should.have.jsonLike({ name: 'snow'});
+    expect(response).to.have.json({ name: 'snow' });
+    expect(response).should.have.jsonLike({ name: 'snow' });
     expect(response).to.have.jsonAt('name', 'snow');
     expect(response).to.have.jsonLikeAt('name', 'snow');
     expect(response).to.have.body(`{"name":"snow"}`);
@@ -58,27 +66,42 @@ describe('Chai Like Assertions', () => {
   });
 
   it('should return a valid schema', async () => {
-    expect(response).to.have.jsonSchema({ properties: { name: { type: 'string' } }});
+    expect(response).to.have.jsonSchema({ properties: { name: { type: 'string' } } });
   });
 
   it('should return a valid schema at', async () => {
-    expect(response).to.have.jsonSchemaAt('.', { properties: { name: { type: 'string' } }});
+    expect(response).to.have.jsonSchemaAt('.', { properties: { name: { type: 'string' } } });
   });
 
   it('should return a match', async () => {
-    expect(response).to.have.jsonMatch(like({ name: 'snow'}));
+    expect(response).to.have.jsonMatch(like({ name: 'snow' }));
   });
 
   it('should return a match at', async () => {
-    expect(response).to.have.jsonMatchAt('.', like({ name: 'snow'}));
+    expect(response).to.have.jsonMatchAt('.', like({ name: 'snow' }));
   });
 
   it('should return a response within 500 ms', async () => {
     expect(response).to.have.responseTimeLessThan(500);
+    let err;
+    try {
+      expect(response).to.have.responseTimeLessThan(0);
+    } catch (error) {
+      err = error;
+    }
+    ce(err).not.undefined;
   });
 
   it('should run a custom expect handler', async () => {
-    expect(response).to.have._('a user');
+    let err;
+    await expect(response).to.have._('a user');
+    await expect(response).to.have._('a status 200');
+    try {
+      await expect(response).to.have._('a status 400');
+    } catch (error) {
+      err = error;
+    }
+    ce(err).not.undefined;
   });
 
   it('should return custom data', async () => {
diff --git a/test/component/templates.spec.js b/test/component/templates.spec.js
index 0b7b1ac..331da4c 100644
--- a/test/component/templates.spec.js
+++ b/test/component/templates.spec.js
@@ -29,10 +29,10 @@ describe('Templates & Maps', () => {
     });
     stash.addDataMap([]);
     stash.addDataTemplate([]);
-    pactum.handler.addDataFunHandler('GetZero', () => 0);
-    pactum.handler.addDataFunHandler('GetAuthToken', () => 'Basic xyz');
-    pactum.handler.addDataFunHandler('GetNumber', (ctx) => ctx.args[0]);
-    pactum.handler.addDataFunHandler('GetSum', (ctx) => parseInt(ctx.args[0]) + parseInt(ctx.args[1]));
+    pactum.handler.addDataFuncHandler('GetZero', () => 0);
+    pactum.handler.addDataFuncHandler('GetAuthToken', () => 'Basic xyz');
+    pactum.handler.addDataFuncHandler('GetNumber', (ctx) => ctx.args[0]);
+    pactum.handler.addDataFuncHandler('GetSum', (ctx) => parseInt(ctx.args[0]) + parseInt(ctx.args[1]));
   });
 
   it('new user with pure template', async () => {
diff --git a/test/unit/handler.spec.js b/test/unit/handler.spec.js
index b4a4e17..b50f89c 100644
--- a/test/unit/handler.spec.js
+++ b/test/unit/handler.spec.js
@@ -152,7 +152,7 @@ describe('Data Handler', () => {
   it('invalid handler name', () => {
     let err;
     try {
-      handler.addDataFunHandler();
+      handler.addDataFuncHandler();
     } catch (error) {
       err = error;
     }
@@ -162,7 +162,7 @@ describe('Data Handler', () => {
   it('empty handler name', () => {
     let err;
     try {
-      handler.addDataFunHandler('');
+      handler.addDataFuncHandler('');
     } catch (error) {
       err = error;
     }
@@ -172,7 +172,7 @@ describe('Data Handler', () => {
   it('invalid handler function', () => {
     let err;
     try {
-      handler.addDataFunHandler('hello');
+      handler.addDataFuncHandler('hello');
     } catch (error) {
       err = error;
     }
@@ -182,7 +182,7 @@ describe('Data Handler', () => {
   it('get invalid handler function', () => {
     let err;
     try {
-      handler.getDataFunHandler('hello');
+      handler.getDataFuncHandler('hello');
     } catch (error) {
       err = error;
     }