feat: add custom jsonBody Chai assertion + simplify Postman translation (#7299)

* feat: enhance jsonBody translation handling in Postman to Bruno converter

* feat: implement jsonBody assertion for Postman compatibility and enhance translation handling

- Added custom Chai assertion for jsonBody to validate JSON structures, including deep equality and nested properties.
- Updated Postman to Bruno translation logic to utilize the new jsonBody assertion, improving the handling of response validations.
- Enhanced test coverage for jsonBody translations, including positive and negative cases for nested properties and deep equality checks.

* feat: enhance jsonBody assertion translations for Postman compatibility

- Added translations for `pm.response.not.to.have.jsonBody` and `pm.response.to.have.not.jsonBody` to the Postman to Bruno converter.
- Updated tests to cover new translation cases, ensuring proper handling of negation scenarios for JSON body assertions.
- Enhanced existing jsonBody assertion logic to support new translation patterns, improving overall compatibility with Postman syntax.

* feat: add advanced path parsing for jsonBody assertions

- Introduced a new `parsePath` function to handle various property path formats, including dot notation, numeric brackets, and quoted keys.
- Updated the `getNestedValue` function to utilize the new path parsing logic, enhancing the robustness of jsonBody assertions.
- Expanded test cases to cover a wide range of scenarios, including edge cases for bracket notation and keys with special characters.

* docs: add examples for parsePath function in jsonBody assertions

- Enhanced documentation for the `parsePath` function by including examples of various property path formats.
- Updated comments in both `assert-runtime.js` and `test.js` to clarify the handling of dot notation, numeric brackets, and quoted keys.

* fix: improve path handling in assertions for quoted keys

- Updated condition checks in `assert-runtime.js` and `test.js` to ensure proper handling of quoted keys in path parsing.
- Enhanced robustness of the path parsing logic to prevent potential out-of-bounds errors.
This commit is contained in:
sanish chirayath
2026-04-22 12:59:32 +05:30
committed by GitHub
parent c4dc0bc10d
commit e12b736516
7 changed files with 602 additions and 33 deletions

View File

@@ -40,6 +40,10 @@ const replacements = {
'pm\\.response\\.to\\.not\\.have\\.jsonSchema\\(': 'expect(res.getBody()).to.not.have.jsonSchema(',
'pm\\.response\\.not\\.to\\.have\\.jsonSchema\\(': 'expect(res.getBody()).not.to.have.jsonSchema(',
'pm\\.response\\.to\\.have\\.not\\.jsonSchema\\(': 'expect(res.getBody()).to.have.not.jsonSchema(',
'pm\\.response\\.to\\.have\\.jsonBody\\(': 'expect(res.getBody()).to.have.jsonBody(',
'pm\\.response\\.to\\.not\\.have\\.jsonBody\\(': 'expect(res.getBody()).to.not.have.jsonBody(',
'pm\\.response\\.not\\.to\\.have\\.jsonBody\\(': 'expect(res.getBody()).not.to.have.jsonBody(',
'pm\\.response\\.to\\.have\\.not\\.jsonBody\\(': 'expect(res.getBody()).to.have.not.jsonBody(',
'pm\\.response\\.to\\.have\\.body\\(': 'expect(res.getBody()).to.equal(',
'pm\\.response\\.to\\.have\\.header\\(': 'expect(res.getHeaders()).to.have.property(',
'pm\\.response\\.size\\(\\)': 'res.getSize()',

View File

@@ -467,38 +467,31 @@ const complexTransformations = [
}
},
// pm.response.to.have.jsonBody(path) -> expect(res.getBody()).to.have.nested.property(path)
// pm.response.to.have.jsonBody(...) -> expect(res.getBody()).to.have.jsonBody(...)
{
pattern: 'pm.response.to.have.jsonBody',
transform: (path, j) => {
const callExpr = path.parent.value;
const args = callExpr.arguments;
const expectGetBody = j.callExpression(j.identifier('expect'), [j.callExpression(j.identifier('res.getBody'), [])]);
return j.callExpression(
j.memberExpression(expectGetBody, j.identifier('to.have.jsonBody')),
args
);
}
},
if (args.length === 0) {
// No path provided, just check that body exists
return j.memberExpression(
j.callExpression(j.identifier('expect'), [j.callExpression(j.identifier('res.getBody'), [])]),
j.identifier('to.exist')
);
} else if (args.length === 1) {
// Path provided, check property exists
return j.callExpression(
j.memberExpression(
j.callExpression(j.identifier('expect'), [j.callExpression(j.identifier('res.getBody'), [])]),
j.identifier('to.have.nested.property')
),
args
);
} else {
// Path and value provided, check property equals value
return j.callExpression(
j.memberExpression(
j.callExpression(j.identifier('expect'), [j.callExpression(j.identifier('res.getBody'), [])]),
j.identifier('to.have.nested.property')
),
args
);
}
// pm.response.to.not.have.jsonBody(...) -> expect(res.getBody()).to.not.have.jsonBody(...)
{
pattern: 'pm.response.to.not.have.jsonBody',
transform: (path, j) => {
const callExpr = path.parent.value;
const args = callExpr.arguments;
const expectGetBody = j.callExpression(j.identifier('expect'), [j.callExpression(j.identifier('res.getBody'), [])]);
return j.callExpression(
j.memberExpression(expectGetBody, j.identifier('to.not.have.jsonBody')),
args
);
}
},
@@ -570,6 +563,34 @@ const complexTransformations = [
}
},
// pm.response.not.to.have.jsonBody(...) -> expect(res.getBody()).not.to.have.jsonBody(...)
{
pattern: 'pm.response.not.to.have.jsonBody',
transform: (path, j) => {
const callExpr = path.parent.value;
const args = callExpr.arguments;
const expectGetBody = j.callExpression(j.identifier('expect'), [j.callExpression(j.identifier('res.getBody'), [])]);
return j.callExpression(
j.memberExpression(expectGetBody, j.identifier('not.to.have.jsonBody')),
args
);
}
},
// pm.response.to.have.not.jsonBody(...) -> expect(res.getBody()).to.have.not.jsonBody(...)
{
pattern: 'pm.response.to.have.not.jsonBody',
transform: (path, j) => {
const callExpr = path.parent.value;
const args = callExpr.arguments;
const expectGetBody = j.callExpression(j.identifier('expect'), [j.callExpression(j.identifier('res.getBody'), [])]);
return j.callExpression(
j.memberExpression(expectGetBody, j.identifier('to.have.not.jsonBody')),
args
);
}
},
// Legacy postman.getResponseHeader(name) -> res.getHeader(name)
{
pattern: 'pm.getResponseHeader',

View File

@@ -277,6 +277,10 @@ describe('Multiline Syntax Handling', () => {
expect(translatedCode).toContain('expect(res.getStatus()).to.equal(200)');
expect(translatedCode).toContain('expect(res.getHeaders()).to.have.property("content-type".toLowerCase())');
// Check jsonBody translations (positive and negation)
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody("success", true)');
expect(translatedCode).toContain('expect(res.getBody()).to.not.have.jsonBody("error")');
// Check flow control
expect(translatedCode).toContain('if (res.getStatus() === 401)');
expect(translatedCode).toContain('bru.runner.stopExecution()');

View File

@@ -643,19 +643,38 @@ describe('Response Translation', () => {
it('should translate pm.response.to.have.jsonBody with path', () => {
const code = 'pm.response.to.have.jsonBody("user.id");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.nested.property("user.id")');
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody("user.id")');
});
it('should translate pm.response.to.have.jsonBody with path and value', () => {
const code = 'pm.response.to.have.jsonBody("status", "success");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.nested.property("status", "success")');
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody("status", "success")');
});
it('should translate pm.response.to.have.jsonBody without arguments', () => {
const code = 'pm.response.to.have.jsonBody();';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.exist');
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody()');
});
it('should translate pm.response.to.have.jsonBody with object argument', () => {
const code = 'pm.response.to.have.jsonBody({ success: true, data: { id: 1 } });';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody');
expect(translatedCode).toContain('success: true');
});
it('should translate pm.response.to.have.jsonBody with path and numeric value', () => {
const code = 'pm.response.to.have.jsonBody("data.count", 42);';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody("data.count", 42)');
});
it('should translate pm.response.to.have.jsonBody with array argument as nested property', () => {
const code = 'pm.response.to.have.jsonBody(["a", "b"]);';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody(["a", "b"])');
});
it('should handle pm.response.to.have.jsonBody inside test blocks', () => {
@@ -667,20 +686,45 @@ describe('Response Translation', () => {
`;
const translatedCode = translateCode(code);
expect(translatedCode).toContain('test("Response validation", function() {');
expect(translatedCode).toContain('expect(res.getBody()).to.have.nested.property("data")');
expect(translatedCode).toContain('expect(res.getBody()).to.have.nested.property("data.id", 123)');
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody("data")');
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody("data.id", 123)');
});
it('should translate pm.response.to.have.jsonBody with nested path', () => {
const code = 'pm.response.to.have.jsonBody("response.data.items[0].name");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.nested.property("response.data.items[0].name")');
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody("response.data.items[0].name")');
});
it('should translate pm.response.to.have.jsonBody with variable path', () => {
const code = 'const path = "user.id"; pm.response.to.have.jsonBody(path);';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.nested.property(path)');
expect(translatedCode).toContain('expect(res.getBody()).to.have.jsonBody(path)');
});
it('should translate pm.response.to.not.have.jsonBody without arguments', () => {
const code = 'pm.response.to.not.have.jsonBody();';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.not.have.jsonBody()');
});
it('should translate pm.response.to.not.have.jsonBody with path', () => {
const code = 'pm.response.to.not.have.jsonBody("error");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.not.have.jsonBody("error")');
});
it('should translate pm.response.to.not.have.jsonBody with path and value', () => {
const code = 'pm.response.to.not.have.jsonBody("status", "error");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.not.have.jsonBody("status", "error")');
});
it('should translate pm.response.to.not.have.jsonBody with object argument', () => {
const code = 'pm.response.to.not.have.jsonBody({ error: true });';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.not.have.jsonBody');
expect(translatedCode).toContain('error: true');
});
// --- JSON Schema assertions ---------------------------
@@ -772,4 +816,44 @@ describe('Response Translation', () => {
expect(res.getBody()).to.not.have.jsonSchema(schema);
`);
});
// --- not.to.have.jsonBody ---
it('should translate pm.response.not.to.have.jsonBody without arguments', () => {
const code = 'pm.response.not.to.have.jsonBody();';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).not.to.have.jsonBody()');
});
it('should translate pm.response.not.to.have.jsonBody with path', () => {
const code = 'pm.response.not.to.have.jsonBody("error");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).not.to.have.jsonBody("error")');
});
it('should translate pm.response.not.to.have.jsonBody with path and value', () => {
const code = 'pm.response.not.to.have.jsonBody("status", "error");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).not.to.have.jsonBody("status", "error")');
});
// --- to.have.not.jsonBody ---
it('should translate pm.response.to.have.not.jsonBody without arguments', () => {
const code = 'pm.response.to.have.not.jsonBody();';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.not.jsonBody()');
});
it('should translate pm.response.to.have.not.jsonBody with path', () => {
const code = 'pm.response.to.have.not.jsonBody("error");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.not.jsonBody("error")');
});
it('should translate pm.response.to.have.not.jsonBody with path and value', () => {
const code = 'pm.response.to.have.not.jsonBody("status", "error");';
const translatedCode = translateCode(code);
expect(translatedCode).toContain('expect(res.getBody()).to.have.not.jsonBody("status", "error")');
});
});

View File

@@ -65,6 +65,118 @@ chai.use(function (chai, utils) {
});
});
// Custom assertion for jsonBody (Postman parity)
chai.use(function (chai, utils) {
// Parse a property path into an array of keys.
// Handles: dot notation (a.b), numeric brackets (a[0]), quoted brackets (a["b.c"], a['key']),
// and combinations like data[0]["a.b"].name
//
// Examples:
// "a.b.c" -> ["a", "b", "c"]
// "items[0].name" -> ["items", "0", "name"]
// 'data["a.b"]' -> ["data", "a.b"]
// "matrix[0][1]" -> ["matrix", "0", "1"]
// 'nested["x.y"].z' -> ["nested", "x.y", "z"]
// '["say \\"hi\\""]' -> ["say \"hi\""]
function parsePath(path) {
const keys = [];
let i = 0;
while (i < path.length) {
if (path[i] === '.') {
// Skip dot separator
i++;
} else if (path[i] === '[') {
i++; // skip '['
if (i < path.length && (path[i] === '\'' || path[i] === '"')) {
// Quoted key — collect until matching unescaped quote + ']'
const quote = path[i];
i++; // skip opening quote
let key = '';
while (i < path.length && path[i] !== quote) {
if (path[i] === '\\' && i + 1 < path.length && path[i + 1] === quote) {
key += quote;
i += 2; // skip backslash + escaped quote
} else {
key += path[i];
i++;
}
}
i++; // skip closing quote
i++; // skip ']'
keys.push(key);
} else {
// Unquoted (numeric) key — collect until ']'
let key = '';
while (i < path.length && path[i] !== ']') {
key += path[i];
i++;
}
i++; // skip ']'
keys.push(key);
}
} else {
// Bare key — collect until '.', '[', or end
let key = '';
while (i < path.length && path[i] !== '.' && path[i] !== '[') {
key += path[i];
i++;
}
keys.push(key);
}
}
return keys;
}
function getNestedValue(obj, path) {
const keys = parsePath(path);
let current = obj;
for (const key of keys) {
if (current === null || current === undefined || !Object.prototype.hasOwnProperty.call(Object(current), key)) {
return { found: false };
}
current = current[key];
}
return { found: true, value: current };
}
chai.Assertion.addMethod('jsonBody', function () {
const obj = this._obj;
const args = Array.prototype.slice.call(arguments);
if (args.length === 0) {
// No args: check body is valid JSON (object or array)
this.assert(
typeof obj === 'object' && obj !== null,
`expected ${utils.inspect(obj)} to be a JSON body (object or array)`,
`expected ${utils.inspect(obj)} not to be a JSON body`
);
} else if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null) {
// Object arg: deep equality
this.assert(
utils.eql(obj, args[0]),
`expected body to deeply equal ${utils.inspect(args[0])}`,
`expected body to not deeply equal ${utils.inspect(args[0])}`
);
} else if (args.length === 1) {
// String path: check nested property exists
const result = getNestedValue(obj, String(args[0]));
this.assert(
result.found,
`expected body to have nested property '${args[0]}'`,
`expected body to not have nested property '${args[0]}'`
);
} else {
// Path + value: check nested property equals value
const result = getNestedValue(obj, String(args[0]));
this.assert(
result.found && utils.eql(result.value, args[1]),
`expected body to have nested property '${args[0]}' equal to ${utils.inspect(args[1])}`,
`expected body to not have nested property '${args[0]}' equal to ${utils.inspect(args[1])}`
);
}
});
});
/**
* Assertion operators
*

View File

@@ -108,6 +108,130 @@ const addBruShimToContext = (vm, __brunoTestResults) => {
})();
`
);
// Register custom chai assertion for jsonBody (Postman parity)
vm.evalCode(
`
(function() {
var proto = Object.getPrototypeOf(expect(null));
// Parse a property path into an array of keys.
// Handles: dot notation (a.b), numeric brackets (a[0]), quoted brackets (a["b.c"], a['key']),
// and combinations like data[0]["a.b"].name
//
// Examples:
// "a.b.c" -> ["a", "b", "c"]
// "items[0].name" -> ["items", "0", "name"]
// 'data["a.b"]' -> ["data", "a.b"]
// "matrix[0][1]" -> ["matrix", "0", "1"]
// 'nested["x.y"].z' -> ["nested", "x.y", "z"]
// '["say \\"hi\\""]' -> ["say \\"hi\\""]
function parsePath(path) {
var keys = [];
var i = 0;
while (i < path.length) {
if (path[i] === '.') {
i++;
} else if (path[i] === '[') {
i++;
if (i < path.length && (path[i] === "'" || path[i] === '"')) {
var quote = path[i];
i++;
var key = '';
while (i < path.length && path[i] !== quote) {
if (path[i] === '\\\\' && i + 1 < path.length && path[i + 1] === quote) {
key += quote;
i += 2;
} else {
key += path[i];
i++;
}
}
i++; // skip closing quote
i++; // skip ']'
keys.push(key);
} else {
var key = '';
while (i < path.length && path[i] !== ']') {
key += path[i];
i++;
}
i++; // skip ']'
keys.push(key);
}
} else {
var key = '';
while (i < path.length && path[i] !== '.' && path[i] !== '[') {
key += path[i];
i++;
}
keys.push(key);
}
}
return keys;
}
function getNestedValue(obj, path) {
var keys = parsePath(path);
var current = obj;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (current === null || current === undefined || !Object.prototype.hasOwnProperty.call(Object(current), key)) {
return { found: false };
}
current = current[key];
}
return { found: true, value: current };
}
function deepEqual(a, b) {
if (a === b) return true;
if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;
if (Array.isArray(a) !== Array.isArray(b)) return false;
var keysA = Object.keys(a);
var keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
for (var i = 0; i < keysA.length; i++) {
if (!Object.prototype.hasOwnProperty.call(b, keysA[i]) || !deepEqual(a[keysA[i]], b[keysA[i]])) return false;
}
return true;
}
proto.jsonBody = function() {
var obj = this._obj;
var args = Array.prototype.slice.call(arguments);
if (args.length === 0) {
this.assert(
typeof obj === 'object' && obj !== null,
'expected value to be a JSON body (object or array)',
'expected value not to be a JSON body'
);
} else if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null) {
this.assert(
deepEqual(obj, args[0]),
'expected body to deeply equal given object',
'expected body to not deeply equal given object'
);
} else if (args.length === 1) {
var result = getNestedValue(obj, String(args[0]));
this.assert(
result.found,
"expected body to have nested property '" + args[0] + "'",
"expected body to not have nested property '" + args[0] + "'"
);
} else {
var result = getNestedValue(obj, String(args[0]));
this.assert(
result.found && deepEqual(result.value, args[1]),
"expected body to have nested property '" + args[0] + "' equal to given value",
"expected body to not have nested property '" + args[0] + "' equal to given value"
);
}
return this;
};
})();
`
);
};
module.exports = addBruShimToContext;

View File

@@ -0,0 +1,220 @@
meta {
name: jsonBody
type: http
seq: 9
}
post {
url: {{host}}/api/echo/json
body: json
auth: none
}
body:json {
{
"hello": "bruno",
"data": {
"items": [
{ "name": "first" },
{ "name": "second" }
]
},
"matrix": [[1, 2], [3, 4]],
"tags": ["api", "test"],
"some-key": "hyphenated",
"a.b": "dotted-key",
"nested": {
"x.y": { "z": "deep-dotted" }
},
"it's": "apostrophe-key",
"say \"hi\"": "quoted-key"
}
}
assert {
res.status: eq 200
}
tests {
test("jsonBody() - no args validates JSON body", function() {
const body = res.getBody();
expect(body).to.have.jsonBody();
});
test("jsonBody(object) - deep equality", function() {
const body = res.getBody();
expect(body).to.have.jsonBody({
"hello": "bruno",
"data": {
"items": [
{ "name": "first" },
{ "name": "second" }
]
},
"matrix": [[1, 2], [3, 4]],
"tags": ["api", "test"],
"some-key": "hyphenated",
"a.b": "dotted-key",
"nested": {
"x.y": { "z": "deep-dotted" }
},
"it's": "apostrophe-key",
"say \"hi\"": "quoted-key"
});
});
test("jsonBody(path) - nested property exists", function() {
const body = res.getBody();
expect(body).to.have.jsonBody("hello");
expect(body).to.have.jsonBody("data.items");
});
test("jsonBody(path, value) - nested property equals value", function() {
const body = res.getBody();
expect(body).to.have.jsonBody("hello", "bruno");
});
test("jsonBody with bracket notation", function() {
const body = res.getBody();
expect(body).to.have.jsonBody("data.items[0].name", "first");
});
// --- bracket notation and array access ---
test("bracket notation - access array element returns object", function() {
const body = res.getBody();
expect(body).to.have.jsonBody("data.items[0]", { "name": "first" });
expect(body).to.have.jsonBody("data.items[1]", { "name": "second" });
});
test("bracket notation - access top-level array elements", function() {
const body = res.getBody();
expect(body).to.have.jsonBody("tags[0]", "api");
expect(body).to.have.jsonBody("tags[1]", "test");
});
test("bracket notation - consecutive brackets for nested arrays", function() {
const body = res.getBody();
expect(body).to.have.jsonBody("matrix[0][0]", 1);
expect(body).to.have.jsonBody("matrix[0][1]", 2);
expect(body).to.have.jsonBody("matrix[1][0]", 3);
expect(body).to.have.jsonBody("matrix[1][1]", 4);
});
test("bracket notation - access nested array as whole value", function() {
const body = res.getBody();
expect(body).to.have.jsonBody("matrix[0]", [1, 2]);
expect(body).to.have.jsonBody("matrix[1]", [3, 4]);
});
test("bracket notation - out of bounds index is not found", function() {
const body = res.getBody();
expect(body).to.not.have.jsonBody("tags[5]");
expect(body).to.not.have.jsonBody("data.items[99]");
});
// --- edge cases: string bracket keys and keys with dots ---
test("quoted bracket notation - double quotes for string keys", function() {
const body = res.getBody();
expect(body).to.have.jsonBody('["some-key"]', "hyphenated");
});
test("quoted bracket notation - single quotes for string keys", function() {
const body = res.getBody();
expect(body).to.have.jsonBody("['some-key']", "hyphenated");
});
test("quoted bracket notation - keys containing dots", function() {
const body = res.getBody();
expect(body).to.have.jsonBody('["a.b"]', "dotted-key");
});
test("quoted bracket notation - nested path with dotted keys", function() {
const body = res.getBody();
expect(body).to.have.jsonBody('nested["x.y"].z', "deep-dotted");
});
test("quoted bracket notation - key containing the other quote type", function() {
const body = res.getBody();
// Key is: it's — use double quotes so the single quote is not a delimiter
expect(body).to.have.jsonBody("[\"it's\"]", "apostrophe-key");
});
test("quoted bracket notation - escaped quotes in key", function() {
const body = res.getBody();
// Key is: say "hi" — use escaped double quotes inside double-quoted bracket
expect(body).to.have.jsonBody('["say \\"hi\\""]', "quoted-key");
});
test("to.not.have.jsonBody(path) - negation for missing property", function() {
const body = res.getBody();
expect(body).to.not.have.jsonBody("nonexistent");
});
test("to.not.have.jsonBody(path, value) - negation for wrong value", function() {
const body = res.getBody();
expect(body).to.not.have.jsonBody("hello", "wrong");
});
test("to.not.have.jsonBody(object) - negation for deep inequality", function() {
const body = res.getBody();
expect(body).to.not.have.jsonBody({ "wrong": "data" });
});
// --- not.to.have.jsonBody ---
test("not.to.have.jsonBody(path) - negation for missing property", function() {
const body = res.getBody();
expect(body).not.to.have.jsonBody("nonexistent");
});
test("not.to.have.jsonBody(path, value) - negation for wrong value", function() {
const body = res.getBody();
expect(body).not.to.have.jsonBody("hello", "wrong");
});
test("not.to.have.jsonBody(object) - negation for deep inequality", function() {
const body = res.getBody();
expect(body).not.to.have.jsonBody({ "wrong": "data" });
});
test("not.to.have.jsonBody fails when body matches", function() {
const body = res.getBody();
let failed = false;
try {
expect(body).not.to.have.jsonBody("hello");
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
// --- to.have.not.jsonBody ---
test("to.have.not.jsonBody(path) - negation for missing property", function() {
const body = res.getBody();
expect(body).to.have.not.jsonBody("nonexistent");
});
test("to.have.not.jsonBody(path, value) - negation for wrong value", function() {
const body = res.getBody();
expect(body).to.have.not.jsonBody("hello", "wrong");
});
test("to.have.not.jsonBody(object) - negation for deep inequality", function() {
const body = res.getBody();
expect(body).to.have.not.jsonBody({ "wrong": "data" });
});
test("to.have.not.jsonBody fails when body matches", function() {
const body = res.getBody();
let failed = false;
try {
expect(body).to.have.not.jsonBody("hello");
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
}