Comparing version 2.0.5 to 2.0.6
{ | ||
"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 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
426
151236
4081