Comparing version 2.0.7 to 2.0.8
{ | ||
"name": "pactum", | ||
"version": "2.0.7", | ||
"version": "2.0.8", | ||
"description": "REST API Testing Tool to write e2e, integration, contract & component or service level tests", | ||
@@ -68,4 +68,4 @@ "main": "./src/index.js", | ||
"dependencies": { | ||
"@exodus/schemasafe": "^1.0.0-rc.2", | ||
"deep-override": "^1.0.1", | ||
"djv": "^2.1.3-alpha.0", | ||
"form-data": "^3.0.0", | ||
@@ -72,0 +72,0 @@ "json-query": "^2.2.2", |
181
README.md
@@ -11,4 +11,13 @@ # pactum | ||
This library can be integrated with test runners like **cucumber**, **mocha**, **jest** or any other runners. It is *simple*, *fast*, *easy* and *fun* to use. | ||
#### *why pactum?* | ||
* Lightweight. | ||
* Clear & Comprehensive Testing Style. | ||
* Works with **cucumber**, **mocha**, **jest**. | ||
* Elegant Data Management. | ||
* Customizable Assertions & Retry Mechanisms. | ||
* Powerful Mock Server. | ||
* Ideal for *component*, *contract* & *e2e* testing of APIs. | ||
* It is *simple*, *fast*, *fun* & *easy* to use. | ||
## Documentation | ||
@@ -19,2 +28,4 @@ | ||
* [API Testing](https://github.com/ASaiAnudeep/pactum/wiki/API-Testing) | ||
* [Integration Testing](https://github.com/ASaiAnudeep/pactum/wiki/Integration-Testing) | ||
* [Data Management](https://github.com/ASaiAnudeep/pactum/wiki/Data-Management) | ||
* [Mock Server](https://github.com/ASaiAnudeep/pactum/wiki/Mock-Server) | ||
@@ -43,3 +54,3 @@ * [Component Testing](https://github.com/ASaiAnudeep/pactum/wiki/Component-Testing) | ||
Tests in **pactum** are clear and comprehensive. It uses numerous descriptive methods to build your requests and expectations. Learn more about these methods at [API Testing](https://github.com/ASaiAnudeep/pactum/wiki/API-Testing#request-making). | ||
Tests in **pactum** are clear and comprehensive. It uses numerous descriptive methods to build your requests and expectations. | ||
@@ -113,5 +124,5 @@ ### Simple Test Cases | ||
It allows verification of returned status codes, headers, body, json objects, json schemas & response times. Learn more about available assertions at [API Testing](https://github.com/ASaiAnudeep/pactum/wiki/API-Testing#resonse-validation) | ||
It allows verification of returned status codes, headers, body, json objects, json schemas & response times. | ||
Running complex component test expectations. | ||
Running complex test expectations. | ||
@@ -121,3 +132,3 @@ ```javascript | ||
return pactum.spec() | ||
.get('https://jsonplaceholder.typicode.com/users/1') | ||
.get('/api/users/1') | ||
.expectStatus(201) | ||
@@ -148,3 +159,3 @@ .expectHeaderContains('content-type', 'application/json') | ||
}) | ||
.expectJsonQueryLike('[0].address[*].city', ['Boston', 'NewYork']) | ||
.expectJsonLikeAt('[0].address[*].city', ['Boston', 'NewYork']) | ||
.expectResponseTime(100); | ||
@@ -158,3 +169,2 @@ }); | ||
const pactum = require('pactum'); | ||
const expect = pactum.expect; | ||
@@ -175,3 +185,3 @@ describe('Chai Like Assertions', () => { | ||
it('should return a status 200', () => { | ||
expect(response).to.have.status(200); | ||
spec.response().to.have.status(200); | ||
}); | ||
@@ -185,54 +195,15 @@ | ||
``` | ||
Learn more about building requests & validating responses with **pactum** at [API Testing](https://github.com/ASaiAnudeep/pactum/wiki/API-Testing) | ||
We can also add custom expect handlers that are ideal to make much more complicated assertions by taking advantage of popular assertion libraries like [chai](https://www.npmjs.com/package/chai) | ||
## Integration Testing | ||
```javascript | ||
await pactum.spec() | ||
.post('https://jsonplaceholder.typicode.com/posts') | ||
.withJson({ | ||
title: 'foo', | ||
body: 'bar', | ||
userId: 1 | ||
}) | ||
.expectStatus(201) | ||
.expect(({res}) => { /* Custom Assertion Code */ }); | ||
``` | ||
Integration Testing is defined as a type of testing where software modules or components are logically integrated & tested. | ||
### Data Management | ||
API Integration Testing has many aspects but usually involves passing data between tests or waiting for some action to be reflected in the system. | ||
Data management is made easy with this library by using the concept of *Data Templates* & *Data References*. You can reuse data across tests. Learn more about data management with **pactum** at [Data Management](https://github.com/ASaiAnudeep/pactum/wiki/API-Testing#data-management) | ||
```javascript | ||
const stash = pactum.stash; | ||
stash.addDataTemplate({ | ||
'User.New': { | ||
FirstName: 'Jon', | ||
LastName: 'Snow' | ||
} | ||
}); | ||
await pactum.spec() | ||
.post('/api/users') | ||
// json will be replaced with above template & overrides last name | ||
.withJson({ | ||
'@DATA:TEMPLATE@': 'User.New', | ||
'@OVERRIDES@': { | ||
'LastName': 'Dragon' | ||
} | ||
}); | ||
``` | ||
### Nested Dependent HTTP Calls | ||
API testing is naturally asynchronous, which can make tests complex when these tests need to be chained. With **Pactum**, passing response data to the next tests is very easy. | ||
* `returns` allows us to return custom data from the response using [json-query](https://www.npmjs.com/package/json-query) or custom handler functions. | ||
* `stores` allows us to save response data under *data management* which can be referenced later using [json-query](https://www.npmjs.com/package/json-query). | ||
Learn more about it at [API Testing](https://github.com/ASaiAnudeep/pactum/wiki/API-Testing#nested-dependent-http-calls) | ||
```javascript | ||
const pactum = require('pactum'); | ||
const expect = require('chai').expect; | ||
@@ -244,20 +215,21 @@ it('should return all posts and first post should have comments', async () => { | ||
.returns('[0].id'); | ||
const response = await pactum.spec() | ||
await pactum.spec() | ||
.get(`http://jsonplaceholder.typicode.com/posts/${postID}/comments`) | ||
.expectStatus(200); | ||
const comments = response.json; | ||
expect(comments).deep.equals([]); | ||
}); | ||
``` | ||
it('should return all posts and update first posts title', async () => { | ||
const postID = await pactum.spec() | ||
.get('http://jsonplaceholder.typicode.com/posts') | ||
```javascript | ||
it('create new user', async () => { | ||
await pactum.spec() | ||
.post('/api/users') | ||
.withJson(/* user details */) | ||
.expectStatus(200) | ||
.stores('FirstPostId', '[0].id'); | ||
const response = await pactum.spec() | ||
.patch(`http://jsonplaceholder.typicode.com/posts`) | ||
.withJson({ | ||
id: '@DATA:SPEC::FirstPostId@', | ||
title: 'new title' | ||
}) | ||
.stores('UserID', 'id'); // if response body = { id: 'C001019' } | ||
}); | ||
it('validate new user details', async () => { | ||
await pactum.spec() | ||
.get('/api/users') | ||
.withQueryParams('id', '@DATA:STR::UserId@') | ||
.expectStatus(200); | ||
@@ -269,7 +241,5 @@ }); | ||
Some API operations will take time & for such scenarios **pactum** allows us to add custom retry handlers that will wait for specific conditions to happen before attempting to make assertions on the response. (*Make sure to update test runners default timeout*) | ||
```javascript | ||
await pactum.spec() | ||
.get('https://jsonplaceholder.typicode.com/posts/12') | ||
.get('/some/async/operation') | ||
.retry({ | ||
@@ -283,4 +253,42 @@ count: 2, | ||
Learn more about api testing with **pactum** at [API Testing](https://github.com/ASaiAnudeep/pactum/wiki/API-Testing) | ||
Learn more about these features at [Integration Testing](https://github.com/ASaiAnudeep/pactum/wiki/Integration-Testing) | ||
## Mock Server | ||
Mock Server allows you to mock any server or service via HTTP or HTTPS, such as a REST endpoint. Simply it is a simulator for HTTP-based APIs. | ||
**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**. | ||
Running **pactum** as a standalone *mock server*. | ||
```javascript | ||
const pactum = require('pactum'); | ||
const { regex } = pactum.matchers; | ||
pactum.mock.addMockInteraction({ | ||
withRequest: { | ||
method: 'GET', | ||
path: '/api/projects', | ||
query: { | ||
date: regex(/^\d{4}-\d{2}-\d{2}$/) | ||
} | ||
}, | ||
willRespondWith: { | ||
status: 200, | ||
headers: { | ||
'content-type': 'application/json' | ||
}, | ||
body: { | ||
id: 1, | ||
name: 'fake' | ||
} | ||
} | ||
}); | ||
pactum.mock.start(3000); | ||
``` | ||
Learn more about **pactum** as a *mock server* at [Mock Server](https://github.com/ASaiAnudeep/pactum/wiki/Mock-Server) | ||
## Component Testing | ||
@@ -419,39 +427,4 @@ | ||
## Mock Server | ||
Mock Server allows you to mock any server or service via HTTP or HTTPS, such as a REST endpoint. Simply it is a simulator for HTTP-based APIs. | ||
**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**. | ||
Running **pactum** as a standalone *mock server*. | ||
```javascript | ||
const pactum = require('pactum'); | ||
const { regex } = pactum.matchers; | ||
pactum.mock.addMockInteraction({ | ||
withRequest: { | ||
method: 'GET', | ||
path: '/api/projects', | ||
query: { | ||
date: regex(/^\d{4}-\d{2}-\d{2}$/) | ||
} | ||
}, | ||
willRespondWith: { | ||
status: 200, | ||
headers: { | ||
'content-type': 'application/json' | ||
}, | ||
body: { | ||
id: 1, | ||
name: 'fake' | ||
} | ||
} | ||
}); | ||
pactum.mock.start(3000); | ||
``` | ||
Learn more about **pactum** as a *mock server* at [Mock Server](https://github.com/ASaiAnudeep/pactum/wiki/Mock-Server) | ||
# Notes | ||
@@ -458,0 +431,0 @@ |
@@ -9,5 +9,7 @@ export interface Have { | ||
jsonLike(value: any): void; | ||
jsonQuery(path: string, value: any): void; | ||
jsonQueryLike(path: string, value: any): void; | ||
jsonAt(path: string, value: any): void; | ||
jsonLikeAt(path: string, value: any): void; | ||
jsonSchema(schema: object): void; | ||
jsonSchemaAt(path: string, schema: object): void; | ||
jsonMatch(value: object): void; | ||
responseTimeLessThan(ms: number): void; | ||
@@ -14,0 +16,0 @@ _(handler: string, data: any): void |
@@ -46,3 +46,3 @@ const ExpectModel = require('../models/expect'); | ||
jsonQuery(path, value) { | ||
jsonAt(path, value) { | ||
this.expect.jsonQuery.push({ path, value }); | ||
@@ -52,3 +52,3 @@ this._validate(); | ||
jsonQueryLike(path, value) { | ||
jsonLikeAt(path, value) { | ||
this.expect.jsonQueryLike.push({ path, value }); | ||
@@ -63,2 +63,12 @@ this._validate(); | ||
jsonSchemaAt(path, value) { | ||
this.expect.jsonSchemaQuery.push({ path, value }); | ||
this._validate(); | ||
} | ||
jsonMatch(value) { | ||
this.expect.jsonMatch.push(value); | ||
this._validate(); | ||
} | ||
responseTimeLessThan(ms) { | ||
@@ -65,0 +75,0 @@ this.expect.responseTime = ms; |
@@ -65,3 +65,3 @@ import * as Spec from '../models/Spec'; | ||
*/ | ||
export function addDataHandler(name: string, func: DataHandlerFunction): void; | ||
export function addDataFunHandler(name: string, func: DataHandlerFunction): void; | ||
@@ -68,0 +68,0 @@ /** |
@@ -53,3 +53,3 @@ const { PactumHandlerError } = require('../helpers/errors'); | ||
addDataHandler(name, func) { | ||
addDataFunHandler(name, func) { | ||
isValidHandler(name, func); | ||
@@ -60,3 +60,3 @@ dataHandlers[name] = func; | ||
getDataHandler(name) { | ||
getDataFunHandler(name) { | ||
if (dataHandlers[name]) return dataHandlers[name]; | ||
@@ -63,0 +63,0 @@ throw new PactumHandlerError(`Custom Data Handler Not Found - ${name}`); |
@@ -99,6 +99,6 @@ export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD'; | ||
*/ | ||
export function addMockInteraction(interaction: MockInteraction): string; | ||
export function addMockInteraction(interaction: MockInteraction[]): string[]; | ||
export function addMockInteraction(interaction: MockInteraction): Promise<string>; | ||
export function addMockInteraction(interaction: MockInteraction[]): Promise<string[]>; | ||
export function addMockInteraction(interaction: MockInteraction | string): string; | ||
export function addMockInteraction(interaction: MockInteraction[] | string[]): string[]; | ||
export function addMockInteraction(interaction: MockInteraction | string): Promise<string>; | ||
export function addMockInteraction(interaction: MockInteraction[] | string[]): Promise<string[]>; | ||
@@ -123,7 +123,6 @@ /** | ||
*/ | ||
export function addPactInteraction(interaction: PactInteraction): string; | ||
export function addPactInteraction(interactions: PactInteraction[]): string[]; | ||
export function addPactInteraction(interaction: PactInteraction): Promise<string>; | ||
export function addPactInteraction(interactions: PactInteraction[]): Promise<string[]>; | ||
export function addPactInteraction(interaction: PactInteraction | string): string; | ||
export function addPactInteraction(interactions: PactInteraction[] | string[]): string[]; | ||
export function addPactInteraction(interaction: PactInteraction | string): Promise<string>; | ||
export function addPactInteraction(interactions: PactInteraction[] | string[]): Promise<string[]>; | ||
/** | ||
@@ -130,0 +129,0 @@ * returns interaction details |
@@ -41,3 +41,3 @@ const Interaction = require('../models/interaction'); | ||
if (config.mock.remote) { | ||
return remote.addMockInteraction(interactions, data); | ||
return remote.addMockInteraction(interactions, data, alone); | ||
} | ||
@@ -64,3 +64,3 @@ const ids = []; | ||
if (config.mock.remote) { | ||
return remote.addPactInteraction(interactions, data); | ||
return remote.addPactInteraction(interactions, data, alone); | ||
} | ||
@@ -87,3 +87,3 @@ const ids = []; | ||
if (config.mock.remote) { | ||
return remote.getInteraction(ids); | ||
return remote.getInteraction(ids, alone); | ||
} | ||
@@ -90,0 +90,0 @@ const interactions = []; |
@@ -9,3 +9,3 @@ const fs = require('fs'); | ||
let dataTemplate = {}; | ||
let dataSpec = {}; | ||
let dataStore = {}; | ||
@@ -80,13 +80,13 @@ const stash = { | ||
addDataSpec(spec) { | ||
Object.assign(dataSpec, spec); | ||
addDataStore(store) { | ||
Object.assign(dataStore, store); | ||
config.data.ref.spec.enabled = true; | ||
}, | ||
getDataSpecs() { | ||
return dataSpec; | ||
getDataStore() { | ||
return dataStore; | ||
}, | ||
clearDataSpecs() { | ||
dataSpec = {}; | ||
clearDataStores() { | ||
dataStore = {}; | ||
config.data.ref.spec.enabled = false; | ||
@@ -93,0 +93,0 @@ } |
const log = require('./logger'); | ||
const EXPRESSION_PATTERN = /^@\(.*\)@$/g; | ||
class Compare { | ||
@@ -76,2 +78,16 @@ | ||
expressionCompare(actual, expected, actualPath, expectedPath) { | ||
if (typeof expected === 'string') { | ||
const expressions = expected.match(EXPRESSION_PATTERN); | ||
if (expressions) { | ||
const expression = expressions[0].replace('$', 'actual').slice(2, -2); | ||
const res = eval(expression); | ||
if (res !== true) { | ||
return `Json doesn't fulfil expression '${expression.replace('actual', expectedPath).trim()}'` | ||
} | ||
return true; | ||
} | ||
} | ||
} | ||
valueCompare(actual, expected, actualPath, expectedPath) { | ||
@@ -87,2 +103,6 @@ if (actual === expected) { | ||
} | ||
const exprRes = this.expressionCompare(actual, expected, actualPath, expectedPath); | ||
if (exprRes) { | ||
return exprRes === true ? '' : exprRes; | ||
} | ||
if (typeof expected !== typeof actual) { | ||
@@ -89,0 +109,0 @@ return `Json doesn't have type '${typeof expected}' at '${expectedPath}' but found '${typeof actual}'`; |
@@ -93,2 +93,3 @@ const override = require('deep-override'); | ||
if (dataRefMatches) { | ||
const values = []; | ||
for (let i = 0; i < dataRefMatches.length; i++) { | ||
@@ -100,13 +101,20 @@ const dataRefMatch = dataRefMatches[i]; | ||
const value = jq(refValue, { data: this.map }).value; | ||
return typeof value === 'undefined' ? raw : value; | ||
values.push(typeof value === 'undefined' ? raw : value); | ||
} | ||
if (refType === '@DATA:FUN') { | ||
const handlerFun = handler.getDataHandler(refValue); | ||
return handlerFun(); | ||
const [handlerName, ...args] = refValue.split(':'); | ||
const handlerFun = handler.getDataFunHandler(handlerName); | ||
values.push(handlerFun({ data: args.length > 0 ? args[0].split(',') : args })); | ||
} | ||
if (refType === '@DATA:SPEC') { | ||
const value = jq(refValue, { data: stash.getDataSpecs() }).value; | ||
return typeof value === 'undefined' ? raw : value; | ||
if (refType === '@DATA:STR') { | ||
const value = jq(refValue, { data: stash.getDataStore() }).value; | ||
values.push(typeof value === 'undefined' ? raw : value); | ||
} | ||
} | ||
if (values.length === 1 && raw.length === dataRefMatches[0].length) { | ||
return values[0]; | ||
} | ||
for (let i = 0; i < dataRefMatches.length; i++) { | ||
raw = raw.replace(dataRefMatches[i], values[i]); | ||
} | ||
} | ||
@@ -113,0 +121,0 @@ return raw; |
@@ -1,2 +0,2 @@ | ||
const { options, cyan, magenta, blue, green, yellow, red } = require('./colors'); | ||
const { options, magenta, blue, green, yellow, red } = require('./colors'); | ||
@@ -54,4 +54,4 @@ const LEVEL_TRACE = 3; | ||
if (this.levelValue <= LEVEL_TRACE) { | ||
process.stdout.write(`${cyan('PACTUM')} ${magenta('TRACE')} `); | ||
this.console.debug(...msg); | ||
process.stdout.write(`[${magenta('T')}] `); | ||
msg.forEach(m => this.console.debug(m)); | ||
} | ||
@@ -62,4 +62,4 @@ } | ||
if (this.levelValue <= LEVEL_DEBUG) { | ||
process.stdout.write(`${cyan('PACTUM')} ${blue('DEBUG')} `); | ||
this.console.debug(...msg); | ||
process.stdout.write(`[${blue('D')}] `); | ||
msg.forEach(m => this.console.debug(m)); | ||
} | ||
@@ -70,4 +70,4 @@ } | ||
if (this.levelValue <= LEVEL_INFO) { | ||
process.stdout.write(`${cyan('PACTUM')} ${green('INFO')} `); | ||
this.console.info(...msg); | ||
process.stdout.write(`[${green('I')}] `); | ||
msg.forEach(m => this.console.info(m)); | ||
} | ||
@@ -78,4 +78,4 @@ } | ||
if (this.levelValue <= LEVEL_WARN) { | ||
process.stdout.write(`${cyan('PACTUM')} ${yellow('WARN')} `); | ||
this.console.warn(...msg); | ||
process.stdout.write(`[${yellow('W')}] `); | ||
msg.forEach(m => this.console.warn(m)); | ||
} | ||
@@ -86,4 +86,4 @@ } | ||
if (this.levelValue <= LEVEL_ERROR) { | ||
process.stdout.write(`${cyan('PACTUM')} ${red('ERROR')} `); | ||
this.console.error(...msg); | ||
process.stdout.write(`[${red('E')}] `); | ||
msg.forEach(m => this.console.error(m)); | ||
} | ||
@@ -90,0 +90,0 @@ } |
@@ -7,16 +7,17 @@ const phin = require('phin'); | ||
addMockInteraction(interactions, data) { | ||
return addInteractions(interactions, data, 'api/pactum/mockInteractions', 'MOCK'); | ||
addMockInteraction(interactions, data, alone) { | ||
return addInteractions(interactions, data, 'api/pactum/mockInteractions', 'MOCK', alone); | ||
}, | ||
addPactInteraction(interactions, data) { | ||
return addInteractions(interactions, data, 'api/pactum/pactInteractions', 'PACT'); | ||
addPactInteraction(interactions, data, alone) { | ||
return addInteractions(interactions, data, 'api/pactum/pactInteractions', 'PACT', alone); | ||
}, | ||
async getInteraction(ids) { | ||
const interactions = await Promise.all([ | ||
async getInteraction(ids, alone) { | ||
let interactions = await Promise.all([ | ||
get(`${config.mock.remote}/api/pactum/pactInteractions?ids=${ids.join(',')}`), | ||
get(`${config.mock.remote}/api/pactum/mockInteractions?ids=${ids.join(',')}`) | ||
]); | ||
return interactions[0].concat(interactions[1]); | ||
interactions = interactions[0].concat(interactions[1]); | ||
return alone ? interactions[0] : interactions; | ||
}, | ||
@@ -104,3 +105,3 @@ | ||
async function addInteractions(interactions, data, path, type) { | ||
async function addInteractions(interactions, data, path, type, alone) { | ||
const { raws, handlers } = getRawsAndHandlers(interactions); | ||
@@ -115,5 +116,5 @@ let ids = []; | ||
} | ||
return ids; | ||
return alone ? ids[0] : ids; | ||
} | ||
module.exports = remote; |
const assert = require('assert'); | ||
const jqy = require('json-query'); | ||
const djv = require('djv'); | ||
const Compare = require('../helpers/compare'); | ||
const processor = require('../helpers/dataProcessor'); | ||
const handler = require('../exports/handler'); | ||
const jsv = require('../plugins/json.schema'); | ||
const jmv = require('../plugins/json.match'); | ||
@@ -15,6 +17,8 @@ class Expect { | ||
this.json = []; | ||
this.jsonQuery = []; | ||
this.jsonLike = []; | ||
this.jsonQuery = []; | ||
this.jsonQueryLike = []; | ||
this.jsonSchema = []; | ||
this.jsonSchemaQuery = []; | ||
this.jsonMatch = []; | ||
this.headers = []; | ||
@@ -37,2 +41,4 @@ this.headerContains = []; | ||
this._validateJsonSchema(response); | ||
this._validateJsonSchemaQuery(response); | ||
this._validateJsonMatch(response); | ||
this._validateResponseTime(response); | ||
@@ -52,2 +58,3 @@ this._validateCustomExpectHandlers(request, response); | ||
_validateStatus(response) { | ||
this.statusCode = processor.processData(this.statusCode); | ||
if (this.statusCode !== null) { | ||
@@ -59,2 +66,3 @@ assert.strictEqual(response.statusCode, this.statusCode, `HTTP status ${response.statusCode} !== ${this.statusCode}`); | ||
_validateHeaders(response) { | ||
this.headers = processor.processData(this.headers); | ||
for (let i = 0; i < this.headers.length; i++) { | ||
@@ -83,2 +91,3 @@ const expectedHeaderObject = this.headers[i]; | ||
_validateHeaderContains(response) { | ||
this.headerContains = processor.processData(this.headerContains); | ||
for (let i = 0; i < this.headerContains.length; i++) { | ||
@@ -107,2 +116,3 @@ const expectedHeaderObject = this.headerContains[i]; | ||
_validateBody(response) { | ||
this.body = processor.processData(this.body); | ||
if (this.body !== null) { | ||
@@ -119,2 +129,3 @@ if (response.body instanceof Buffer) { | ||
_validateBodyContains(response) { | ||
this.bodyContains = processor.processData(this.bodyContains); | ||
for (let i = 0; i < this.bodyContains.length; i++) { | ||
@@ -149,2 +160,3 @@ const expectedBodyValue = this.bodyContains[i]; | ||
_validateJson(response) { | ||
this.json = processor.processData(this.json); | ||
for (let i = 0; i < this.json.length; i++) { | ||
@@ -157,2 +169,3 @@ const expectedJSON = this.json[i]; | ||
_validateJsonLike(response) { | ||
this.jsonLike = processor.processData(this.jsonLike); | ||
for (let i = 0; i < this.jsonLike.length; i++) { | ||
@@ -169,2 +182,3 @@ const expectedJSON = this.jsonLike[i]; | ||
_validateJsonQuery(response) { | ||
this.jsonQuery = processor.processData(this.jsonQuery); | ||
for (let i = 0; i < this.jsonQuery.length; i++) { | ||
@@ -182,2 +196,3 @@ const jQ = this.jsonQuery[i]; | ||
_validateJsonQueryLike(response) { | ||
this.jsonQueryLike = processor.processData(this.jsonQueryLike); | ||
for (let i = 0; i < this.jsonQueryLike.length; i++) { | ||
@@ -195,9 +210,7 @@ const jQ = this.jsonQueryLike[i]; | ||
_validateJsonSchema(response) { | ||
this.jsonSchema = processor.processData(this.jsonSchema); | ||
for (let i = 0; i < this.jsonSchema.length; i++) { | ||
const jS = this.jsonSchema[i]; | ||
const jsv = new djv(); | ||
jsv.addSchema('test', { common: jS }); | ||
const validation = jsv.validate('test#/common', response.json); | ||
if (validation) { | ||
this.fail(`Response doesn't match with JSON schema - ${JSON.stringify(validation, null, 2)}`); | ||
const errors = jsv.validate(this.jsonSchema[i], response.json); | ||
if (errors) { | ||
this.fail(`Response doesn't match with JSON schema: \n ${JSON.stringify(errors, null, 2)}`); | ||
} | ||
@@ -207,3 +220,29 @@ } | ||
_validateJsonSchemaQuery(response) { | ||
this.jsonSchemaQuery = processor.processData(this.jsonSchemaQuery); | ||
for (let i = 0; i < this.jsonSchemaQuery.length; i++) { | ||
const jQ = this.jsonSchemaQuery[i]; | ||
const value = jqy(jQ.path, { data: response.json }).value; | ||
const errors = jsv.validate(jQ.value, value); | ||
if (errors) { | ||
this.fail(`Response doesn't match with JSON schema at ${jQ.path}: \n ${JSON.stringify(errors, null, 2)}`); | ||
} | ||
} | ||
} | ||
_validateJsonMatch(response) { | ||
this.jsonMatch = processor.processData(this.jsonMatch); | ||
for (let i = 0; i < this.jsonMatch.length; i++) { | ||
const data = this.jsonMatch[i]; | ||
const rules = jmv.getMatchingRules(data, '$.body'); | ||
const value = jmv.getRawValue(data); | ||
const errors = jmv.validate(response.json, value, rules, '$.body'); | ||
if (errors) { | ||
this.fail(errors.replace('$.body', '$')); | ||
} | ||
} | ||
} | ||
_validateResponseTime(response) { | ||
this.responseTime = processor.processData(this.responseTime); | ||
if (this.responseTime !== null) { | ||
@@ -210,0 +249,0 @@ if (response.responseTime > this.responseTime) { |
@@ -405,6 +405,6 @@ import { RequestOptions } from 'http'; | ||
* .get('some-url') | ||
* .expectJsonQuery('[0].name', 'Matt') | ||
* .expectJsonQuery('[*].name', ['Matt', 'Pet', 'Don']); | ||
* .expectJsonAt('[0].name', 'Matt') | ||
* .expectJsonAt('[*].name', ['Matt', 'Pet', 'Don']); | ||
*/ | ||
expectJsonQuery(path: string, query: any): Spec; | ||
expectJsonAt(path: string, query: any): Spec; | ||
@@ -417,7 +417,38 @@ /** | ||
* .get('some-url') | ||
* .expectJsonQueryLike('[*].name', ['Matt', 'Pet', 'Don']); | ||
* .expectJsonLikeAt('[*].name', ['Matt', 'Pet', 'Don']); | ||
*/ | ||
expectJsonQueryLike(path: string, value: any): Spec; | ||
expectJsonLikeAt(path: string, value: any): Spec; | ||
/** | ||
* expects the response to match with json schema | ||
* @see https://json-schema.org/learn/ | ||
* @example | ||
* await pactum | ||
* .get('/api/users/1') | ||
* .expectJsonSchemaAt('user.address', { | ||
* "type": "object", | ||
* "properties": { | ||
* "city": { | ||
* "type": "string" | ||
* } | ||
* } | ||
* }); | ||
*/ | ||
expectJsonSchemaAt(path: string, schema: object): Spec; | ||
/** | ||
* expects the json at to match with value | ||
* @example | ||
* const { like } = pactum.matchers; | ||
* | ||
* await pactum | ||
* .get('/api/users') | ||
* .expectJsonMatch({ | ||
* id: like(1), | ||
* name: 'jon' | ||
* }); | ||
*/ | ||
expectJsonMatch(value: object): Spec; | ||
/** | ||
* expects request completes within a specified duration (ms) | ||
@@ -424,0 +455,0 @@ */ |
@@ -268,3 +268,8 @@ const FormData = require('form-data'); | ||
expectJsonQuery(path, value) { | ||
expectJsonSchemaAt(path, value) { | ||
this._expect.jsonSchemaQuery.push({ path, value }); | ||
return this; | ||
} | ||
expectJsonAt(path, value) { | ||
this._expect.jsonQuery.push({ path, value }); | ||
@@ -274,3 +279,3 @@ return this; | ||
expectJsonQueryLike(path, value) { | ||
expectJsonLikeAt(path, value) { | ||
this._expect.jsonQueryLike.push({ path, value }); | ||
@@ -280,2 +285,7 @@ return this; | ||
expectJsonMatch(value) { | ||
this._expect.jsonMatch.push(value); | ||
return this; | ||
} | ||
expectResponseTime(value) { | ||
@@ -282,0 +292,0 @@ this._expect.responseTime = value; |
@@ -35,5 +35,3 @@ const phin = require('phin'); | ||
await this.removeInteractionsFromServer(); | ||
this.validateError(); | ||
this.validateInteractions(); | ||
this.validateResponse(); | ||
this.validate() | ||
this.storeSpecData(); | ||
@@ -46,3 +44,4 @@ return this.getOutput(); | ||
processor.processTemplates(); | ||
const query = helper.getPlainQuery(processor.processData(this.request.qs)); | ||
this.request = processor.processData(this.request); | ||
const query = helper.getPlainQuery(this.request.qs); | ||
if (query) { | ||
@@ -54,5 +53,3 @@ this.request.url = this.request.url + '?' + query; | ||
setHeaders(this.request); | ||
this.request.headers = processor.processData(this.request.headers); | ||
setMultiPartFormData(this.request); | ||
this.request.data = processor.processData(this.request.data); | ||
} | ||
@@ -115,3 +112,3 @@ | ||
this.mockInteractions = this.mockInteractions.concat(await mock.getInteraction(this.mockIds)); | ||
await mock.removeInteraction(this.mockIds); | ||
await mock.removeInteraction(this.mockIds); | ||
} | ||
@@ -125,2 +122,19 @@ if (this.pactIds.length > 0) { | ||
validate() { | ||
this.validateError(); | ||
try { | ||
this.validateInteractions(); | ||
this.validateResponse(); | ||
} catch (error) { | ||
const res = { | ||
statusCode: this.response.statusCode, | ||
headers: this.response.headers, | ||
body: this.response.json | ||
} | ||
log.warn('Request', JSON.stringify(this.request, null, 2)); | ||
log.warn('Response', JSON.stringify(res, null, 2)); | ||
throw error; | ||
} | ||
} | ||
validateError() { | ||
@@ -147,3 +161,3 @@ if (this.response instanceof Error) { | ||
specData[store.key] = value; | ||
stash.addDataSpec(specData); | ||
stash.addDataStore(specData); | ||
} | ||
@@ -220,2 +234,3 @@ } | ||
try { | ||
log.debug(`${req.method} ${req.url}`); | ||
res = await phin(req); | ||
@@ -228,20 +243,5 @@ res.json = helper.getJson(res.body); | ||
res.responseTime = Date.now() - requestStartTime; | ||
printRequestResponse(req, res); | ||
return res; | ||
} | ||
function printRequestResponse(req, res) { | ||
if (log.levelValue <= 4) { | ||
const rr = { | ||
request: req, | ||
response: { | ||
statusCode: res.statusCode, | ||
headers: res.headers, | ||
json: res.json | ||
} | ||
} | ||
log.debug('Request & Response =>', JSON.stringify(rr, null, 2)); | ||
} | ||
} | ||
module.exports = Tosser; |
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
162592
46
4451
426
1
+ Added@exodus/schemasafe@1.3.0(transitive)
- Removeddjv@^2.1.3-alpha.0
- Removed@korzio/djv-draft-04@2.0.1(transitive)
- Removeddjv@2.1.4(transitive)