Repository: pactumjs/pactum
Pull request #46 (2021-03-08 03:59:29)

3.0.7
- stores inside interaction
- added expectJsonStrictMatch in bdd


Patch:
diff --git a/package-lock.json b/package-lock.json
index 1117360..cbe8cc3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "pactum",
-  "version": "3.0.6",
+  "version": "3.0.7",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index d17046c..297c308 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pactum",
-  "version": "3.0.6",
+  "version": "3.0.7",
   "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/exports/expect.d.ts b/src/exports/expect.d.ts
index 995d633..ef1f10b 100644
--- a/src/exports/expect.d.ts
+++ b/src/exports/expect.d.ts
@@ -12,6 +12,8 @@ export interface Have {
   jsonSchemaAt(path: string, schema: object): void;
   jsonMatch(value: object): void;
   jsonMatchAt(path: string, value: object): void;
+  jsonMatchStrict(value: object): void;
+  jsonMatchStrictAt(path: string, value: object): void;
   responseTimeLessThan(ms: number): void;
   _(handler: string, data: any): Promise<void>;
 }
diff --git a/src/exports/expect.js b/src/exports/expect.js
index 8059287..0ade240 100644
--- a/src/exports/expect.js
+++ b/src/exports/expect.js
@@ -74,6 +74,16 @@ class Have {
     this._validate();
   }
 
+  jsonMatchStrict(value) {
+    this.expect.jsonMatchStrict.push(value);
+    this._validate();
+  }
+
+  jsonMatchStrictAt(path, value) {
+    this.expect.jsonMatchStrictQuery.push({ path, value });
+    this._validate();
+  }
+
   responseTimeLessThan(ms) {
     this.expect.responseTime = ms;
     this._validate();
diff --git a/src/helpers/toss.helper.js b/src/helpers/toss.helper.js
index 4fa8d75..bae81ee 100644
--- a/src/helpers/toss.helper.js
+++ b/src/helpers/toss.helper.js
@@ -12,20 +12,26 @@ function getCaptureHandlerName(name) {
   }
 }
 
-function getPathValueFromSpec(path, spec) {
+function getPathValueFromRequestResponse(path, request, response) {
   let data;
-  if (path.startsWith('req.headers')) {
+  if (path.startsWith('req.pathParams')) {
+    path = path.replace('req.pathParams', '');
+    data = request.pathParams;
+  } else if (path.startsWith('req.queryParams')) {
+    path = path.replace('req.queryParams', '');
+    data = request.query;
+  } else if (path.startsWith('req.headers')) {
     path = path.replace('req.headers', '');
-    data = spec._request.headers;
+    data = request.headers;
   } else if (path.startsWith('req.body')) {
     path = path.replace('req.body', '');
-    data = spec._request.body;
+    data = request.body;
   } else if (path.startsWith('res.headers')) {
     path = path.replace('res.headers', '');
-    data = spec._response.headers;
+    data = response.headers;
   } else {
     path = path.replace('res.body', '');
-    data = spec._response.json;
+    data = response.json;
   }
   return jqy(path, { data }).value;
 }
@@ -39,7 +45,7 @@ function storeSpecData(spec, stores) {
     if (captureHandler) {
       specData[store.name] = hr.capture(captureHandler, ctx);
     } else {
-      specData[store.name] = getPathValueFromSpec(store.path, spec);
+      specData[store.name] = getPathValueFromRequestResponse(store.path, spec._request, spec._response);
     }
     stash.addDataStore(specData);
   }
@@ -53,7 +59,7 @@ function recordSpecData(spec, recorders) {
     if (captureHandler) {
       spec.recorded[name] = hr.capture(captureHandler, ctx);
     } else {
-      spec.recorded[name] = getPathValueFromSpec(path, spec);
+      spec.recorded[name] = getPathValueFromRequestResponse(path, spec._request, spec._response);
     }
   });
 }
@@ -71,7 +77,7 @@ function getOutput(spec, returns) {
       if (captureHandler) {
         outputs.push(hr.capture(captureHandler, ctx));
       } else {
-        outputs.push(getPathValueFromSpec(_return, spec));
+        outputs.push(getPathValueFromRequestResponse(_return, spec._request, spec._response));
       }
     }
   }
@@ -84,8 +90,27 @@ function getOutput(spec, returns) {
   }
 }
 
+function storeInteractionData(request, interaction) {
+  const interactionData = {};
+  const { response, stores } = interaction;
+  const ctx = { req: request, res: response, store: stash.getDataStore() };
+  const names = Object.keys(stores);
+  for (let i = 0; i < names.length; i++) {
+    const name = names[i];
+    const path = stores[name];
+    const captureHandler = getCaptureHandlerName(path);
+    if (captureHandler) {
+      interactionData[name] = hr.capture(captureHandler, ctx);
+    } else {
+      interactionData[name] = getPathValueFromRequestResponse(path, request, response);
+    }
+  }
+  stash.addDataStore(interactionData);
+}
+
 module.exports = {
   storeSpecData,
   recordSpecData,
-  getOutput
+  getOutput,
+  storeInteractionData
 };
\ No newline at end of file
diff --git a/src/helpers/utils.js b/src/helpers/utils.js
index 015d912..40dff65 100644
--- a/src/helpers/utils.js
+++ b/src/helpers/utils.js
@@ -79,6 +79,7 @@ function validatePath(req, interaction) {
         }
       }
     }
+    req.pathParams = actual;
     return compare(actual, expected, matchingRules, '$.path').equal;
   } else {
     return compare(actualPath, expectedPath, matchingRules, '$.path').equal;
diff --git a/src/models/Interaction.model.js b/src/models/Interaction.model.js
index d4ba626..22232e2 100644
--- a/src/models/Interaction.model.js
+++ b/src/models/Interaction.model.js
@@ -173,13 +173,21 @@ class InteractionExpectations {
 class Interaction {
 
   constructor(raw) {
+    let unprocessedResponse;
+    if (raw && raw.response) {
+      unprocessedResponse = raw.response;
+      delete raw.response;
+    }
     raw = process(raw);
+    if (raw && unprocessedResponse) {
+      raw.response = unprocessedResponse;
+    }
     setRawDefaults(raw);
     validate(raw);
     this.callCount = 0;
     this.exercised = false;
     this.calls = [];
-    const { id, provider, flow, strict, request, response, expects } = raw;
+    const { id, provider, flow, strict, request, response, expects, stores } = raw;
     this.id = id || helper.getRandomId();
     if (flow) this.flow = flow;
     if (provider) this.provider = provider;
@@ -187,6 +195,9 @@ class Interaction {
     this.request = new InteractionRequest(request);
     this.response = setResponse(response);
     this.expects = new InteractionExpectations(expects);
+    if (stores && typeof stores === 'object') {
+      this.stores = stores;
+    }
   }
 
 }
diff --git a/src/models/server.js b/src/models/server.js
index e6a21ea..bfde56d 100644
--- a/src/models/server.js
+++ b/src/models/server.js
@@ -6,6 +6,8 @@ const utils = require('../helpers/utils');
 const log = require('../helpers/logger');
 const hr = require('../helpers/handler.runner');
 const rlc = require('../helpers/reporter.lifeCycle');
+const th = require('../helpers/toss.helper');
+const processor = require('../helpers/dataProcessor');
 const reporter = require('../exports/reporter');
 const config = require('../config');
 
@@ -103,15 +105,19 @@ function registerAllRoutes(server, app) {
  */
 function sendInteractionFoundResponse(req, res, interaction) {
   interaction.exercised = true;
-  const { response } = interaction;
-  if (typeof response === 'function') {
-    response(req, res);
+  if (interaction.stores) {
+    th.storeInteractionData(req, interaction);
+  }
+  interaction.response = processor.processData(interaction.response);
+  const interactionResponse = interaction.response;
+  if (typeof interactionResponse === 'function') {
+    interactionResponse(req, res);
   } else {
     let _response = {};
-    if (response[interaction.callCount]) {
-      _response = response[interaction.callCount];
+    if (interactionResponse[interaction.callCount]) {
+      _response = interactionResponse[interaction.callCount];
     } else {
-      _response = response;
+      _response = interactionResponse;
     }
     res.set(_response.headers);
     res.status(_response.status);


Test patch:
diff --git a/test/component/bdd.spec.js b/test/component/bdd.spec.js
index 7789483..abd0edf 100644
--- a/test/component/bdd.spec.js
+++ b/test/component/bdd.spec.js
@@ -80,6 +80,14 @@ describe('BDD', () => {
     expect(response).to.have.jsonMatchAt('.', like({ name: 'snow' }));
   });
 
+  it('should return a strict match', async () => {
+    expect(response).to.have.jsonMatchStrict(like({ name: 'snow' }));
+  });
+
+  it('should return a strict match at', async () => {
+    expect(response).to.have.jsonMatchStrictAt('.', like({ name: 'snow' }));
+  });
+
   it('should return a response within 500 ms', async () => {
     expect(response).to.have.responseTimeLessThan(500);
     let err;
diff --git a/test/component/interactions.stores.spec.js b/test/component/interactions.stores.spec.js
new file mode 100644
index 0000000..14a1f0a
--- /dev/null
+++ b/test/component/interactions.stores.spec.js
@@ -0,0 +1,155 @@
+const pactum = require('../../src/index');
+
+describe('Interaction', () => {
+
+  it('Stores - Path Params', async () => {
+    await pactum.spec()
+      .useInteraction({
+        request: {
+          method: 'GET',
+          path: '/api/projects/{id}',
+          pathParams: {
+            id: '101'
+          }
+        },
+        response: {
+          status: 200,
+          body: {
+            id: '$S{ProjectId}'
+          }
+        },
+        stores: {
+          ProjectId: 'req.pathParams.id'
+        }
+      })
+      .get('http://localhost:9393/api/projects/101')
+      .expectStatus(200)
+      .expectJson({
+        id: '101'
+      });
+  });
+
+  it('Stores - Query Params', async () => {
+    await pactum.spec()
+      .useInteraction({
+        request: {
+          method: 'GET',
+          path: '/api/projects',
+          queryParams: {
+            id: '102'
+          }
+        },
+        response: {
+          status: 200,
+          body: {
+            id: '$S{ProjectId}'
+          }
+        },
+        stores: {
+          ProjectId: 'req.queryParams.id'
+        }
+      })
+      .get('http://localhost:9393/api/projects')
+      .withQueryParams('id', '102')
+      .expectStatus(200)
+      .expectJson({
+        id: '102'
+      });
+  });
+
+  it('Stores - Headers', async () => {
+    await pactum.spec()
+      .useInteraction({
+        request: {
+          method: 'GET',
+          path: '/api/projects'
+        },
+        response: {
+          status: 200,
+          body: {
+            token: '$S{Token}'
+          }
+        },
+        stores: {
+          Token: 'req.headers.token'
+        }
+      })
+      .get('http://localhost:9393/api/projects')
+      .withHeaders('token', 'xyz')
+      .expectStatus(200)
+      .expectJson({
+        token: 'xyz'
+      });
+  });
+
+  it('Stores - Body', async () => {
+    await pactum.spec()
+      .useInteraction({
+        request: {
+          method: 'POST',
+          path: '/api/projects',
+          body: {
+            name: 'abc'
+          }
+        },
+        response: {
+          status: 200,
+          body: {
+            name: '$S{Name}'
+          }
+        },
+        stores: {
+          Name: 'req.body.name'
+        }
+      })
+      .post('http://localhost:9393/api/projects')
+      .withJson({
+        'name': 'abc'
+      })
+      .expectStatus(200)
+      .expectJson({
+        name: 'abc'
+      });
+  });
+
+  it('Stores - Multiple', async () => {
+    pactum.handler.addCaptureHandler('GetID', ({ req }) => req.body.id);
+    await pactum.spec()
+      .useInteraction({
+        request: {
+          method: 'POST',
+          path: '/api/projects',
+          body: {
+            id: 'id2',
+            name: 'abc2'
+          }
+        },
+        response: {
+          status: 200,
+          body: {
+            id: '$S{Id}',
+            name: '$S{Name}',
+            token: '$S{Token}'
+          }
+        },
+        stores: {
+          Id: '#GetID',
+          Name: 'req.body.name',
+          Token: 'req.headers.token'
+        }
+      })
+      .post('http://localhost:9393/api/projects')
+      .withHeaders('token', 'xyz2')
+      .withJson({
+        'id': 'id2',
+        'name': 'abc2'
+      })
+      .expectStatus(200)
+      .expectJson({
+        id: 'id2',
+        name: 'abc2',
+        token: 'xyz2'
+      });
+  });
+
+});
\ No newline at end of file