Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

pactum

Package Overview
Dependencies
Maintainers
1
Versions
112
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pactum - npm Package Compare versions

Comparing version 2.0.5 to 2.0.6

4

package.json
{
"name": "pactum",
"version": "2.0.5",
"version": "2.0.6",
"description": "REST API Testing Tool to write e2e, integration, contract & component or service level tests",

@@ -30,3 +30,3 @@ "main": "./src/index.js",

"REST",
"automate",
"automation",
"testing",

@@ -33,0 +33,0 @@ "component",

@@ -17,3 +17,4 @@ # pactum

* [API Testing](https://github.com/ASaiAnudeep/pactum/wiki)
* [API Testing](https://github.com/ASaiAnudeep/pactum/wiki/API-Testing)
* [Mock Server](https://github.com/ASaiAnudeep/pactum/wiki/Mock-Server)
* [Component Testing](https://github.com/ASaiAnudeep/pactum/wiki/Component-Testing)

@@ -23,4 +24,2 @@ * [Contract Testing](https://github.com/ASaiAnudeep/pactum/wiki/Contract-Testing)

* [Provider Verification](https://github.com/ASaiAnudeep/pactum/wiki/Provider-Verification)
* [Interactions](https://github.com/ASaiAnudeep/pactum/wiki/Interactions)
* [Mock Server](https://github.com/ASaiAnudeep/pactum/wiki/Mock-Server)

@@ -48,2 +47,4 @@ ## Installation

#### Using Mocha
Running simple api test expectations.

@@ -55,3 +56,3 @@

it('should be a teapot', async () => {
await pactum
await pactum.spec()
.get('http://httpbin.org/status/418')

@@ -62,5 +63,5 @@ .expectStatus(418);

it('should save a new user', async () => {
await pactum
await pactum.spec()
.post('https://jsonplaceholder.typicode.com/users')
.withHeader('Authorization', 'Basic xxxx')
.withHeaders('Authorization', 'Basic xxxx')
.withJson({

@@ -79,2 +80,33 @@ name: 'bolt',

#### Using Cucumber
```javascript
// steps.js
const pactum = require('pactum');
const { Given, When, Then, Before } = require('cucumber');
let spec = pactum.spec();
Before(() => { spec = pactum.spec(); });
Given('I make a GET request to {string}', function (url) {
spec.get(url);
});
When('I receive a response', async function () {
await spec.toss();
});
Then('response should have a status {int}', async function (code) {
spec.response().should.have.status(code);
});
```
```gherkin
Scenario: Check TeaPot
Given I make a GET request to "http://httpbin.org/status/418"
When I receive a response
Then response should have a status 200
```
### Complex HTTP Assertions

@@ -88,3 +120,3 @@

it('should have a user with id', () => {
return pactum
return pactum.spec()
.get('https://jsonplaceholder.typicode.com/users/1')

@@ -154,3 +186,3 @@ .expectStatus(201)

```javascript
await pactum
await pactum.spec()
.post('https://jsonplaceholder.typicode.com/posts')

@@ -175,7 +207,7 @@ .withJson({

it('should return all posts and first post should have comments', async () => {
const postID = await pactum
const postID = await pactum.spec()
.get('http://jsonplaceholder.typicode.com/posts')
.expectStatus(200)
.returns('[0].id');
const response = await pactum
const response = await pactum.spec()
.get(`http://jsonplaceholder.typicode.com/posts/${postID}/comments`)

@@ -193,3 +225,3 @@ .expectStatus(200);

```javascript
await pactum
await pactum.spec()
.get('https://jsonplaceholder.typicode.com/posts/12')

@@ -211,3 +243,3 @@ .retry({

stash.loadDataTemplates({
stash.addDataTemplate({
'User.New': {

@@ -219,3 +251,3 @@ FirstName: 'Jon',

await pactum
await pactum.spec()
.post('/api/users')

@@ -250,3 +282,3 @@ // json will be replaced with above template & overrides last name

it('should get jon snow details', () => {
return pactum
return pactum.spec()
// adds interaction to mock server & removes it after the spec

@@ -310,3 +342,3 @@ .useInteraction({

it('GET - one interaction', async () => {
await pactum
await pactum.spec()
.usePactInteraction({

@@ -366,3 +398,3 @@ provider: 'projects-service',

**pactum** can act as a standalone *mock server* or as a *service virtualization* tool. It comes with a **powerful request & response matching**.
**pactum** can act as a standalone *mock server* or as a *service virtualization* tool. It comes with a **powerful request & response matching** and out of the box **Data Management**.

@@ -369,0 +401,0 @@ Running **pactum** as a standalone *mock server*.

@@ -18,5 +18,10 @@ const config = {

data: {
map: {
enabled: false,
processed: false
ref: {
map: {
enabled: false,
processed: false
},
fun: {
enabled: false
}
},

@@ -23,0 +28,0 @@ template: {

@@ -9,3 +9,3 @@ const config = require('../config');

if (typeof dir !== 'string') {
throw new PactumConfigurationError(`Invalid directory provided for saving pact files - ${dir}`);
throw new PactumConfigurationError('`dir` is required');
}

@@ -17,3 +17,3 @@ config.pact.dir = dir;

if (typeof name !== 'string' || !name) {
throw new PactumConfigurationError(`Invalid consumer name - ${name}`);
throw new PactumConfigurationError('`name` is required');
}

@@ -20,0 +20,0 @@ config.pact.consumer = name;

@@ -13,2 +13,3 @@ export interface Have {

responseTimeLessThan(ms: number): void;
_(handler: string, data: any): void
}

@@ -22,2 +23,3 @@

to: To;
should: To;
}

@@ -24,0 +26,0 @@

@@ -5,5 +5,6 @@ const ExpectModel = require('../models/expect');

constructor(response) {
constructor(response, request) {
this.expect = new ExpectModel();
this.response = response;
this.request = request;
}

@@ -66,2 +67,7 @@

_(handler, data) {
this.expect.customExpectHandlers.push({ handler, data });
this._validate();
}
_validate() {

@@ -75,4 +81,4 @@ this.expect.validate({}, this.response);

constructor(response) {
this.have = new Have(response);
constructor(response, request) {
this.have = new Have(response, request);
}

@@ -84,4 +90,5 @@

constructor(response) {
this.to = new To(response);
constructor(response, request) {
this.to = new To(response, request);
this.should = new To(response, request);
}

@@ -91,6 +98,6 @@

function expect(response) {
return new Expect(response);
function expect(response, request) {
return new Expect(response, request);
}
module.exports = expect;
import * as Spec from '../models/Spec';
import { BasicInteraction, MockInteraction, PactInteraction } from './mock';
import { IncomingMessage } from 'http';

@@ -29,2 +30,6 @@

interface DataHandlerContext {
data?: any;
}
export type ExpectHandlerFunction = (ctx: ExpectHandlerContext) => void;

@@ -34,2 +39,6 @@ export type RetryHandlerFunction = (ctx: RequestResponseContext) => boolean;

export type StateHandlerFunction = (ctx: StateHandlerContext) => any;
export type DataHandlerFunction = (ctx: DataHandlerContext) => any;
export type BasicInteractionHandlerFunction = (ctx: DataHandlerContext) => BasicInteraction;
export type MockInteractionHandlerFunction = (ctx: DataHandlerContext) => MockInteraction;
export type PactInteractionHandlerFunction = (ctx: DataHandlerContext) => PactInteraction;

@@ -55,1 +64,21 @@ /**

export function addStateHandler(name: string, func: StateHandlerFunction): void;
/**
* adds a custom data handler
*/
export function addDataHandler(name: string, func: DataHandlerFunction): void;
/**
* adds a custom basic interaction handler
*/
export function addInteractionHandler(name: string, func: BasicInteractionHandlerFunction): void;
/**
* adds a custom mock interaction handler
*/
export function addMockInteractionHandler(name: string, func: MockInteractionHandlerFunction): void;
/**
* adds a custom pact interaction handler
*/
export function addPactInteractionHandler(name: string, func: PactInteractionHandlerFunction): void;
const { PactumHandlerError } = require('../helpers/errors');
const config = require('../config');

@@ -7,2 +8,6 @@ const expectHandlers = {};

const stateHandlers = {};
const dataHandlers = {};
const interactionHandlers = {};
const mockInteractionHandlers = {};
const pactInteractionHandlers = {};

@@ -12,3 +17,3 @@ const handler = {

addExpectHandler(name, func) {
isValidHandler(name, func, 'expect');
isValidHandler(name, func);
expectHandlers[name] = func;

@@ -23,3 +28,3 @@ },

addRetryHandler(name, func) {
isValidHandler(name, func, 'retry');
isValidHandler(name, func);
retryHandlers[name] = func;

@@ -34,3 +39,3 @@ },

addReturnHandler(name, func) {
isValidHandler(name, func, 'return');
isValidHandler(name, func);
returnHandlers[name] = func;

@@ -44,3 +49,3 @@ },

addStateHandler(name, func) {
isValidHandler(name, func, 'state');
isValidHandler(name, func);
stateHandlers[name] = func;

@@ -52,2 +57,43 @@ },

throw new PactumHandlerError(`Custom State Handler Not Found - ${name}`);
},
addDataHandler(name, func) {
isValidHandler(name, func);
dataHandlers[name] = func;
config.data.ref.fun.enabled = true;
},
getDataHandler(name) {
if (dataHandlers[name]) return dataHandlers[name];
throw new PactumHandlerError(`Custom Data Handler Not Found - ${name}`);
},
addInteractionHandler(name, func) {
isValidHandler(name, func);
interactionHandlers[name] = func;
},
getInteractionHandler(name) {
if (interactionHandlers[name]) return interactionHandlers[name];
throw new PactumHandlerError(`Custom Interaction Handler Not Found - ${name}`);
},
addMockInteractionHandler(name, func) {
isValidHandler(name, func);
mockInteractionHandlers[name] = func;
},
getMockInteractionHandler(name) {
if (mockInteractionHandlers[name]) return mockInteractionHandlers[name];
throw new PactumHandlerError(`Custom Mock Interaction Handler Not Found - ${name}`);
},
addPactInteractionHandler(name, func) {
isValidHandler(name, func);
pactInteractionHandlers[name] = func;
},
getPactInteractionHandler(name) {
if (pactInteractionHandlers[name]) return pactInteractionHandlers[name];
throw new PactumHandlerError(`Custom Pact Interaction Handler Not Found - ${name}`);
}

@@ -57,8 +103,8 @@

function isValidHandler(name, func, type) {
function isValidHandler(name, func) {
if (typeof name !== 'string' || name === '') {
throw new PactumHandlerError(`Invalid custom ${type} handler name`);
throw new PactumHandlerError('`name` is required');
}
if (typeof func !== 'function') {
throw new PactumHandlerError(`Custom ${type} handler should be a function`);
throw new PactumHandlerError('`func` is required');
}

@@ -65,0 +111,0 @@ }

@@ -10,3 +10,3 @@ export interface BasicInteraction {

/** body to return */
return?: string | number | object;
return?: any;
}

@@ -21,3 +21,3 @@

export interface PactInteractionRequest {
export interface InteractionRequest {
method: RequestMethod;

@@ -31,10 +31,3 @@ path: string;

export interface MockInteractionRequest extends PactInteractionRequest {
/** ignores query while matching this interaction */
ignoreQuery: boolean;
/** ignores body while matching this interaction */
ignoreBody: boolean;
}
export interface PactInteractionResponse {
export interface InteractionResponse {
status: number;

@@ -50,3 +43,3 @@ headers?: object;

export interface MockInteractionResponseWithDelay extends PactInteractionResponse {
export interface InteractionResponseWithDelay extends InteractionResponse {
fixedDelay?: number;

@@ -57,13 +50,14 @@ randomDelay?: RandomDelay;

export interface OnCall {
[key: number]: MockInteractionResponseWithDelay
[key: number]: InteractionResponseWithDelay
}
export interface MockInteractionResponse extends MockInteractionResponseWithDelay {
export interface MockInteractionResponse extends InteractionResponseWithDelay {
onCall?: OnCall
}
// TODO - accept function - (req, res)
export interface MockInteraction {
id?: string;
provider?: string;
withRequest: MockInteractionRequest;
withRequest: InteractionRequest;
willRespondWith: MockInteractionResponse;

@@ -80,4 +74,4 @@ }

uponReceiving: string;
withRequest: PactInteractionRequest;
willRespondWith: PactInteractionResponse;
withRequest: InteractionRequest;
willRespondWith: InteractionResponse;
}

@@ -112,10 +106,5 @@

export function addInteraction(interaction: BasicInteraction): string;
export function addInteraction(interactions: BasicInteraction[]): string[];
/**
* adds basic mock interactions
* @returns interaction ids
*/
export function addInteractions(interactions: BasicInteraction[]): string[];
/**
* adds a mock interaction

@@ -136,12 +125,5 @@ * @returns interaction id

export function addMockInteraction(interaction: MockInteraction): string;
export function addMockInteraction(interaction: MockInteraction[]): string[];
// TODO - accept function - (req, res)
/**
* adds mock interactions
* @returns interaction ids
*/
export function addMockInteractions(interaction: MockInteraction[]): string[];
/**
* adds pact interaction used for contract testing

@@ -165,10 +147,5 @@ * @returns interaction id

export function addPactInteraction(interaction: PactInteraction): string;
export function addPactInteraction(interactions: PactInteraction[]): string[];
/**
* adds pact interactions used for contract testing
* @returns interaction ids
*/
export function addPactInteractions(interactions: PactInteraction[]): string[];
/**
* removes specified interaction from the mock server

@@ -175,0 +152,0 @@ * @param id interaction id

@@ -32,70 +32,63 @@ const Interaction = require('../models/interaction');

addInteraction(basicInteraction) {
const rawInteraction = {
withRequest: helper.getRequestFromBasicInteraction(basicInteraction),
willRespondWith: {
status: basicInteraction.status || 200,
body: basicInteraction.return || ''
addInteraction(interaction) {
if (Array.isArray(interaction)) {
const rawInteractions = [];
for (let i = 0; i < interaction.length; i++) {
const basicInteraction = interaction[i];
const rawInteraction = {
withRequest: helper.getRequestFromBasicInteraction(basicInteraction),
willRespondWith: {
status: basicInteraction.status || 200,
body: basicInteraction.return || ''
}
};
rawInteractions.push(rawInteraction);
}
};
return this.addMockInteraction(rawInteraction);
},
addInteractions(basicInteractions) {
const rawInteractions = [];
for (let i = 0; i < basicInteractions.length; i++) {
const basicInteraction = basicInteractions[i];
return this.addMockInteraction(rawInteractions);
} else {
const rawInteraction = {
withRequest: helper.getRequestFromBasicInteraction(basicInteraction),
withRequest: helper.getRequestFromBasicInteraction(interaction),
willRespondWith: {
status: basicInteraction.status || 200,
body: basicInteraction.return || ''
status: interaction.status || 200,
body: interaction.return || ''
}
};
rawInteractions.push(rawInteraction);
return this.addMockInteraction(rawInteraction);
}
return this.addMockInteractions(rawInteractions);
},
addMockInteraction(interaction) {
const interactionObj = new Interaction(interaction, true);
this._server.addMockInteraction(interactionObj.id, interactionObj);
log.debug('Added default mock interaction with id', interactionObj.id);
return interactionObj.id;
},
addMockInteractions(interactions) {
if (!Array.isArray(interactions)) {
throw new PactumInteractionError(`Invalid mock interactions array passed - ${interactions}`);
}
const ids = [];
for (let i = 0; i < interactions.length; i++) {
const interactionObj = new Interaction(interactions[i], true);
if (Array.isArray(interaction)) {
const ids = [];
for (let i = 0; i < interaction.length; i++) {
const interactionObj = new Interaction(interaction[i], true);
this._server.addMockInteraction(interactionObj.id, interactionObj);
ids.push(interactionObj.id);
}
log.debug('Added default mock interactions with ids', ids);
return ids;
} else {
const interactionObj = new Interaction(interaction, true);
this._server.addMockInteraction(interactionObj.id, interactionObj);
ids.push(interactionObj.id);
// this._interactionIds.add(interactionObj.id);
log.debug('Added default mock interaction with id', interactionObj.id);
return interactionObj.id;
}
log.debug('Added default mock interactions with ids', ids);
return ids;
},
addPactInteraction(interaction) {
const interactionObj = new Interaction(interaction);
this._server.addPactInteraction(interactionObj.id, interactionObj);
log.debug('Added default pact interactions with id', interactionObj.id);
return interactionObj.id;
},
addPactInteractions(interactions) {
if (!Array.isArray(interactions)) {
throw new PactumInteractionError(`Invalid pact interactions array passed - ${interactions}`);
}
const ids = [];
for (let i = 0; i < interactions.length; i++) {
const interactionObj = new Interaction(interactions[i], false);
if (Array.isArray(interaction)) {
const ids = [];
for (let i = 0; i < interaction.length; i++) {
const interactionObj = new Interaction(interaction[i], false);
this._server.addPactInteraction(interactionObj.id, interactionObj);
ids.push(interactionObj.id);
}
log.debug('Added default pact interactions with ids', ids);
return ids;
} else {
const interactionObj = new Interaction(interaction);
this._server.addPactInteraction(interactionObj.id, interactionObj);
ids.push(interactionObj.id);
log.debug('Added default pact interactions with id', interactionObj.id);
return interactionObj.id;
}
log.debug('Added default pact interactions with ids', ids);
return ids;
},

@@ -102,0 +95,0 @@

/**
* loads data maps
* @example
* stash.loadDataMaps({
* stash.addDataMap({
* 'User': {

@@ -11,3 +11,3 @@ * 'Name': 'Snow',

*/
export function loadDataMaps(maps: object): void;
export function addDataMap(maps: object): void;

@@ -17,3 +17,3 @@ /**

* @example
* stash.loadDataTemplates({
* stash.addDataTemplate({
* 'User.NewUser': {

@@ -26,2 +26,2 @@ * 'Name': 'Snow',

*/
export function loadDataTemplates(template: object): void;
export function addDataTemplate(template: object): void;

@@ -8,5 +8,5 @@ const config = require('../config');

loadDataMaps(maps) {
addDataMap(maps) {
Object.assign(dataMap, maps);
config.data.map.processed = false;
config.data.ref.map.processed = false;
},

@@ -20,7 +20,7 @@

dataMap = {};
config.data.map.processed = false;
config.data.map.enabled = false;
config.data.ref.map.processed = false;
config.data.ref.map.enabled = false;
},
loadDataTemplates(templates) {
addDataTemplate(templates) {
Object.assign(dataTemplate, templates);

@@ -27,0 +27,0 @@ config.data.template.processed = false;

@@ -5,8 +5,9 @@ const override = require('deep-override');

const stash = require('../exports/stash');
const handler = require('../exports/handler');
const logger = require('./logger');
const config = require('../config');
const DATA_MAP_PATTERN = /(@DATA:\w+::[^@]+@)/g;
const DATA_MAP_TYPE_PATTERN = /(@DATA:\w+::)/g;
const DATA_MAP_VALUE_PATTERN = /(::.*[^@])/g;
const DATA_REF_PATTERN = /(@DATA:\w+::[^@]+@)/g;
const DATA_REF_TYPE_PATTERN = /(@DATA:\w+::)/g;
const DATA_REF_VALUE_PATTERN = /(::.*[^@])/g;

@@ -19,9 +20,9 @@ const dataProcessor = {

processMaps() {
if (!config.data.map.processed) {
if (!config.data.ref.map.processed) {
const orgMap = stash.getDataMap();
this.map = JSON.parse(JSON.stringify(orgMap));
this.map = this.processDataMaps(this.map);
config.data.map.processed = true;
this.map = this.processDataRefs(this.map);
config.data.ref.map.processed = true;
if (Object.keys(this.map).length > 0) {
config.data.map.enabled = true;
config.data.ref.map.enabled = true;
}

@@ -47,4 +48,5 @@ }

}
if (config.data.map.enabled) {
data = this.processDataMaps(data);
const ref = config.data.ref;
if (ref.map.enabled || ref.fun.enabled) {
data = this.processDataRefs(data);
}

@@ -79,9 +81,9 @@ return data;

processDataMaps(data) {
processDataRefs(data) {
if (typeof data === 'string') {
return this.getDataMapValue(data);
return this.getDataRefValue(data);
}
if (typeof data === 'object') {
for (prop in data) {
data[prop] = this.processDataMaps(data[prop]);
data[prop] = this.processDataRefs(data[prop]);
}

@@ -92,12 +94,16 @@ }

getDataMapValue(raw) {
const dataMapMatches = raw.match(DATA_MAP_PATTERN);
if (dataMapMatches) {
for (let i = 0; i < dataMapMatches.length; i++) {
const dataMapMatch = dataMapMatches[i];
const mapType = dataMapMatch.match(DATA_MAP_TYPE_PATTERN)[0];
const mapValue = dataMapMatch.match(DATA_MAP_VALUE_PATTERN)[0];
if (mapType === '@DATA:MAP::') {
return jq(mapValue.replace('::', ''), { data: this.map }).value;
getDataRefValue(raw) {
const dataRefMatches = raw.match(DATA_REF_PATTERN);
if (dataRefMatches) {
for (let i = 0; i < dataRefMatches.length; i++) {
const dataRefMatch = dataRefMatches[i];
const refType = dataRefMatch.match(DATA_REF_TYPE_PATTERN)[0].replace('::', '');
const refValue = dataRefMatch.match(DATA_REF_VALUE_PATTERN)[0].replace('::', '');
if (refType === '@DATA:MAP') {
return jq(refValue, { data: this.map }).value;
}
if (refType === '@DATA:FUN') {
const handlerFun = handler.getDataHandler(refValue);
return handlerFun();
}
}

@@ -104,0 +110,0 @@ }

@@ -200,24 +200,18 @@ const fs = require('fs');

const request = {
method: 'GET',
ignoreQuery: true,
ignoreBody: true
method: 'GET'
};
if (typeof interaction === 'string') {
request.path = interaction;
} else {
if (interaction.get) {
request.path = interaction.get;
} else if (interaction.post) {
request.method = 'POST';
request.path = interaction.post;
} else if (interaction.put) {
request.method = 'PUT';
request.path = interaction.put;
} else if (interaction.patch) {
request.method = 'PATCH';
request.path = interaction.patch;
} else if (interaction.delete) {
request.method = 'DELETE';
request.path = interaction.delete;
}
if (interaction.get) {
request.path = interaction.get;
} else if (interaction.post) {
request.method = 'POST';
request.path = interaction.post;
} else if (interaction.put) {
request.method = 'PUT';
request.path = interaction.put;
} else if (interaction.patch) {
request.method = 'PATCH';
request.path = interaction.patch;
} else if (interaction.delete) {
request.method = 'DELETE';
request.path = interaction.delete;
}

@@ -224,0 +218,0 @@ return request;

@@ -29,8 +29,3 @@ const graphQL = require('./graphQL');

}
let isValidQuery = true;
if (!interaction.withRequest.ignoreQuery) {
if (Object.keys(req.query).length > 0 || interaction.withRequest.query) {
isValidQuery = validateQuery(req.query, interaction.withRequest.query, interaction.withRequest.matchingRules);
}
}
let isValidQuery = xValidateQuery(req, interaction);
if (!isValidQuery) {

@@ -40,6 +35,3 @@ log.debug(`Interaction with id ${interactionId} failed to match - HTTP Query Params`);

}
let isValidHeaders = true;
if (interaction.withRequest.headers) {
isValidHeaders = validateHeaders(req.headers, interaction.withRequest.headers, interaction.withRequest.matchingRules);
}
let isValidHeaders = xValidateHeaders(req, interaction);
if (!isValidHeaders) {

@@ -49,16 +41,3 @@ log.debug(`Interaction with id ${interactionId} failed to match - HTTP Headers`);

}
let isValidBody = true;
if (!interaction.withRequest.ignoreBody) {
if (interaction.withRequest.graphQL) {
isValidBody = graphQL.compare(req.body, interaction.withRequest.body);
} else {
if (typeof req.body === 'object') {
if (Object.keys(req.body).length > 0) {
isValidBody = validateBody(req.body, interaction.withRequest.body, interaction.withRequest.matchingRules);
}
} else if (req.body) {
isValidBody = validateBody(req.body, interaction.withRequest.body, interaction.withRequest.matchingRules);
}
}
}
let isValidBody = xValidateBody(req, interaction);
if (isValidMethod && isValidPath && isValidQuery && isValidHeaders && isValidBody) {

@@ -80,9 +59,18 @@ return interaction;

function validateQuery(actual, expected, matchingRules) {
if (typeof actual !== 'object' || typeof expected !== 'object') {
return false;
function xValidateQuery(req, interaction) {
const { mock, withRequest } = interaction;
if (mock) {
if (withRequest.query) {
return validateQuery(req.query, withRequest.query, withRequest.matchingRules, mock);
}
} else if (Object.keys(req.query).length > 0 || withRequest.query) {
return validateQuery(req.query, withRequest.query, withRequest.matchingRules, mock);
}
return true;
}
function validateQuery(actual, expected, matchingRules, mock) {
const compare = new Compare();
const response = compare.jsonMatch(actual, expected, matchingRules, '$.query');
if (response.equal) {
if (response.equal && !mock) {
for (const prop in actual) {

@@ -100,2 +88,10 @@ if (!Object.prototype.hasOwnProperty.call(expected, prop)) {

function xValidateHeaders(req, interaction) {
const { withRequest } = interaction;
if (withRequest.headers) {
return validateHeaders(req.headers, withRequest.headers, withRequest.matchingRules);
}
return true;
}
function validateHeaders(actual, expected, matchingRules) {

@@ -116,6 +112,26 @@ // covert props of header to lower case : Content-Type -> content-type

function validateBody(actual, expected, matchingRules) {
function xValidateBody(req, interaction) {
const { mock, withRequest } = interaction;
if (mock) {
if (withRequest.graphQL) {
return graphQL.compare(req.body, withRequest.body);
}
if (withRequest.body) {
return validateBody(req.body, withRequest.body, withRequest.matchingRules, mock);
}
} else {
if (withRequest.graphQL) {
return graphQL.compare(req.body, withRequest.body);
}
if (req.body || withRequest.body) {
return validateBody(req.body, withRequest.body, withRequest.matchingRules, mock);
}
}
return true;
}
function validateBody(actual, expected, matchingRules, mock) {
const compare = new Compare();
const response = compare.jsonMatch(actual, expected, matchingRules, '$.body');
if (response.equal) {
if (response.equal && !mock) {
return compare.jsonMatch(expected, actual, matchingRules, '$.body').equal;

@@ -122,0 +138,0 @@ } else {

@@ -1,2 +0,1 @@

import { BasicInteraction, MockInteraction, PactInteraction } from './exports/mock';
import * as Spec from './models/Spec';

@@ -16,147 +15,10 @@

/**
* returns instance of spec
*/
export function spec(): Spec;
/**
* runs the specified state handler
* returns an instance of a spec
* @example
* await pactum
* .setState('there are users in the system')
* await pactum.spec()
* .get('/api/users')
* .expectStatus(200);
*/
export function setState(name: string, data?: any): Spec;
export function spec(): Spec;
/**
* adds a basic mock interaction to the server & auto removed after execution
* @example
* await pactum
* .useInteraction({
* get: '/api/address/4'
* return: {
* city: 'WinterFell'
* }
* })
* .get('/api/users/4')
* .expectStatus(200);
*/
export function useInteraction(interaction: BasicInteraction): Spec;
/**
* adds a mock interaction to the server & auto removed after execution
* @example
* await pactum
* .useMockInteraction({
* withRequest: {
* method: 'GET',
* path: '/api/projects/1'
* },
* willRespondWith: {
* status: 200,
* body: {
* id: 1,
* name: 'fake'
* }
* }
* })
* .get('http://localhost:9393/projects/1')
* .expectStatus(200);
*/
export function useMockInteraction(interaction: MockInteraction): Spec;
/**
* adds a pact interaction to the server & auto removed after execution
* @example
* await pactum
* .usePactInteraction({
* provider: 'project-provider',
* state: 'when there is a project with id 1',
* uponReceiving: 'a request for project 1',
* withRequest: {
* method: 'GET',
* path: '/api/projects/1'
* },
* willRespondWith: {
* status: 200,
* body: {
* id: 1,
* name: 'fake'
* }
* }
* })
* .get('http://localhost:9393/projects/1')
* .expectStatus(200);
*/
export function usePactInteraction(interaction: PactInteraction): Spec;
/**
* The GET method requests a representation of the specified resource.
* @example
* await pactum
* .get('https://jsonplaceholder.typicode.com/posts')
* .withQueryParam('postId', 1)
* .expectStatus(200)
* .expectJsonLike({
* userId: 1,
* id: 1
* });
*/
export function get(url: string): Spec;
/**
* The HEAD method asks for a response identical to that of a GET request, but without the response body.
*/
export function head(url: string): Spec;
/**
* The PATCH method is used to apply partial modifications to a resource.
* @example
* await pactum
* .patch('https://jsonplaceholder.typicode.com/posts/1')
* .withJson({
* title: 'foo'
* })
* .expectStatus(200);
*/
export function patch(url: string): Spec;
/**
* The POST method is used to submit an entity to the specified resource, often causing a change in state or side effects on the server.
* @example
* await pactum
* .post('https://jsonplaceholder.typicode.com/posts')
* .withJson({
* title: 'foo',
* body: 'bar',
* userId: 1
* })
* .expectStatus(201);
*/
export function post(url: string): Spec;
/**
* The PUT method replaces all current representations of the target resource with the request payload.
* @example
* await pactum
* .put('https://jsonplaceholder.typicode.com/posts/1')
* .withJson({
* id: 1,
* title: 'foo',
* body: 'bar',
* userId: 1
* })
* .expectStatus(200);
*/
export function put(url: string): Spec;
/**
* The DELETE method deletes the specified resource.
* @example
* await pactum
* .del('https://jsonplaceholder.typicode.com/posts/1')
* .expectStatus(200);
*/
export function del(url: string): Spec;
export namespace pactum { }

@@ -29,42 +29,2 @@ const Spec = require('./models/Spec');

return new Spec();
},
setState(name, data) {
return new Spec().setState(name, data);
},
useInteraction(interaction) {
return new Spec().useInteraction(interaction);
},
useMockInteraction(interaction) {
return new Spec().useMockInteraction(interaction);
},
usePactInteraction(interaction) {
return new Spec().usePactInteraction(interaction);
},
get(url) {
return new Spec().get(url);
},
head(url) {
return new Spec().head(url);
},
patch(url) {
return new Spec().patch(url);
},
post(url) {
return new Spec().post(url);
},
put(url) {
return new Spec().put(url);
},
del(url) {
return new Spec().del(url);
}

@@ -71,0 +31,0 @@

const helper = require('../helpers/helper');
const processor = require('../helpers/dataProcessor');
const config = require('../config');

@@ -22,3 +23,3 @@ const { PactumInteractionError } = require('../helpers/errors');

}
this.headers = helper.setValueFromMatcher(request.headers);
this.headers = processor.processData(helper.setValueFromMatcher(request.headers));
if (request.query && typeof request.query === 'object') {

@@ -28,3 +29,3 @@ this.rawQuery = JSON.parse(JSON.stringify(request.query));

}
this.query = helper.setValueFromMatcher(request.query);
this.query = processor.processData(helper.setValueFromMatcher(request.query));
for (const prop in this.query) {

@@ -37,11 +38,3 @@ this.query[prop] = this.query[prop].toString();

}
this.body = helper.setValueFromMatcher(request.body);
this.ignoreBody = false;
this.ignoreQuery = false;
if (typeof request.ignoreBody === 'boolean') {
this.ignoreBody = request.ignoreBody;
}
if (typeof request.ignoreQuery === 'boolean') {
this.ignoreQuery = request.ignoreQuery;
}
this.body = processor.processData(helper.setValueFromMatcher(request.body));
if (request.graphQL) {

@@ -71,3 +64,3 @@ this.graphQL = new InteractionRequestGraphQL(request.graphQL);

this.status = response.status;
this.headers = response.headers;
this.headers = processor.processData(response.headers);
if (response.body && typeof response.body === 'object') {

@@ -78,3 +71,3 @@ this.rawBody = JSON.parse(JSON.stringify(response.body));

}
this.body = helper.setValueFromMatcher(response.body);
this.body = processor.processData(helper.setValueFromMatcher(response.body));
if (response.fixedDelay) {

@@ -87,3 +80,2 @@ this.delay = new InteractionResponseDelay('FIXED', response.fixedDelay);

}
}

@@ -113,2 +105,4 @@

constructor(rawInteraction, mock = false) {
processor.processMaps();
processor.processTemplates();
this.setDefaults(rawInteraction, mock);

@@ -193,11 +187,10 @@ this.validateInteraction(rawInteraction, mock);

}
if (typeof withRequest.query !== 'undefined') {
if (!withRequest.query || typeof withRequest.query !== 'object' || Array.isArray(withRequest.query)) {
throw new PactumInteractionError('`withRequest.query` should be object');
}
}
}
validateInvalidFieldsPact(withRequest, willRespondWith) {
if (withRequest.ignoreQuery) {
throw new PactumInteractionError(`Pact interaction won't support ignore query`);
}
if (withRequest.ignoreBody) {
throw new PactumInteractionError(`Pact interaction won't support ignore body`);
}
if (typeof willRespondWith === 'function') {

@@ -204,0 +197,0 @@ throw new PactumInteractionError(`Pact interaction won't support function response`);

@@ -222,2 +222,7 @@ const polka = require('polka');

switch (req.path) {
case '/api/pactum/health':
res.writeHead(200, { "Content-Type": "text/plain" });
res.write("OK");
res.end();
break;
case '/api/pactum/mockInteraction':

@@ -224,0 +229,0 @@ handleRemoteInteractions(req, res, server, 'MOCK');

@@ -44,2 +44,3 @@ import FormData from 'form-data';

useInteraction(interaction: BasicInteraction): Spec;
useInteraction(handler: string, data?: any): Spec;

@@ -67,2 +68,3 @@ /**

useMockInteraction(interaction: MockInteraction): Spec;
useMockInteraction(handler: string, data?: any): Spec;

@@ -93,3 +95,4 @@ /**

usePactInteraction(interaction: PactInteraction): Spec;
usePactInteraction(handler: string, data?: any): Spec;
/**

@@ -165,20 +168,11 @@ * The GET method requests a representation of the specified resource.

/**
* appends query param to the request url - /comments?postId=1&user=snow
* adds query params to the request url - /comments?id=1&user=snow&sort=asc
* @example
* await pactum
* .get('https://jsonplaceholder.typicode.com/comments')
* .withQueryParam('postId', '1')
* .withQueryParam('user', 'snow')
* .get('/api/users')
* .withQueryParams('sort', 'asc')
* .withQueryParams({ 'id': '1', 'user': 'snow' })
* .expectStatus(200);
*/
withQueryParam(key: string, value: string): Spec;
/**
* adds query params to the request url - /comments?postId=1&user=snow
* @example
* await pactum
* .get('https://jsonplaceholder.typicode.com/comments')
* .withQueryParams({ 'postId': '1', 'user': 'snow' })
* .expectStatus(200);
*/
withQueryParams(key: string, value: any): Spec;
withQueryParams(params: object): Spec;

@@ -228,19 +222,7 @@

/**
* appends header to the request
* @example
* await pactum
* .post('https://google.com/login')
* .withHeader('Authorization', 'Basic xxx')
* .withHeader('Accept', 'json')
* .expectStatus(200)
*/
withHeader(key: string, value: string): Spec;
withHeader(key: string, value: number): Spec;
withHeader(key: string, value: boolean): Spec;
/**
* attaches headers to the request
* @example
* await pactum
* .get('https://jsonplaceholder.typicode.com/posts')
* .get('/api/posts')
* .withHeaders('Authorization', 'Basic xxx')
* .withHeaders({

@@ -251,2 +233,3 @@ * 'content-type': 'application/json'

*/
withHeaders(key: string, value: any): Spec;
withHeaders(headers: object): Spec;

@@ -253,0 +236,0 @@

@@ -10,3 +10,4 @@ const FormData = require('form-data');

const mock = require('../exports/mock');
const chaiExpect = require('../exports/expect');
const responseExpect = require('../exports/expect');
const handler = require('../exports/handler');

@@ -33,3 +34,6 @@ class Spec {

useInteraction(basicInteraction) {
useInteraction(basicInteraction, data) {
if (typeof basicInteraction === 'string') {
basicInteraction = handler.getInteractionHandler(basicInteraction)({ data });
}
const rawInteraction = {

@@ -45,3 +49,6 @@ withRequest: helper.getRequestFromBasicInteraction(basicInteraction),

useMockInteraction(rawInteraction) {
useMockInteraction(rawInteraction, data) {
if (typeof rawInteraction === 'string') {
rawInteraction = handler.getMockInteractionHandler(rawInteraction)({ data });
}
const interaction = new Interaction(rawInteraction, true);

@@ -53,3 +60,6 @@ log.debug('Mock Interaction added to Mock Server -', interaction.id);

usePactInteraction(rawInteraction) {
usePactInteraction(rawInteraction, data) {
if (typeof rawInteraction === 'string') {
rawInteraction = handler.getPactInteractionHandler(rawInteraction)({ data });
}
const interaction = new Interaction(rawInteraction, false);

@@ -103,24 +113,20 @@ log.debug('Pact Interaction added to Mock Server -', interaction.id);

withQueryParam(key, value) {
if (!helper.isValidString(key)) {
throw new PactumRequestError(`Invalid key in query parameter for request - ${key}`);
}
if (value === undefined || value === null) {
throw new PactumRequestError(`Invalid value in query parameter for request - ${value}`);
}
if (this._request.qs === undefined) {
withQueryParams(key, value) {
if (!this._request.qs) {
this._request.qs = {};
}
this._request.qs[key] = value;
return this;
}
withQueryParams(params) {
if (!helper.isValidObject(params) || Object.keys(params).length === 0) {
throw new PactumRequestError(`Invalid query parameters object - ${params ? JSON.stringify(params) : params}`);
if (typeof key === 'string') {
if (!helper.isValidString(key)) {
throw new PactumRequestError('`key` is required');
}
if (value === undefined || value === null) {
throw new PactumRequestError('`value` is required');
}
this._request.qs[key] = value;
} else {
if (!helper.isValidObject(key) || Object.keys(key).length === 0) {
throw new PactumRequestError('`params` are required');
}
Object.assign(this._request.qs, key);
}
if (!this._request.qs) {
this._request.qs = {};
}
Object.assign(this._request.qs, params);
return this;

@@ -159,18 +165,14 @@ }

withHeader(key, value) {
withHeaders(key, value) {
if (!this._request.headers) {
this._request.headers = {};
}
this._request.headers[key] = value;
return this;
}
withHeaders(headers) {
if (!helper.isValidObject(headers)) {
throw new PactumRequestError(`Invalid headers in request - ${headers}`);
if (typeof key === 'string') {
this._request.headers[key] = value;
} else {
if (!helper.isValidObject(key)) {
throw new PactumRequestError('`headers` are required');
}
Object.assign(this._request.headers, key);
}
if (!this._request.headers) {
this._request.headers = {};
}
Object.assign(this._request.headers, headers);
return this;

@@ -327,3 +329,3 @@ }

}
return chaiExpect(this._response);
return responseExpect(this._response, this._request);
}

@@ -330,0 +332,0 @@

@@ -38,3 +38,5 @@ const phin = require('phin');

updateRequest() {
const query = helper.getPlainQuery(this.request.qs);
processor.processMaps();
processor.processTemplates();
const query = helper.getPlainQuery(processor.processData(this.request.qs));
if (query) {

@@ -46,5 +48,4 @@ this.request.url = this.request.url + '?' + query;

setHeaders(this.request);
this.request.headers = processor.processData(this.request.headers);
setMultiPartFormData(this.request);
processor.processMaps();
processor.processTemplates();
this.request.data = processor.processData(this.request.data);

@@ -51,0 +52,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc