Skip to content

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

Draft
sanish-bruno wants to merge 2 commits intousebruno:mainfrom
sanish-bruno:fix/pm-jsonbody-translation
Draft

feat: add custom jsonBody Chai assertion + simplify Postman translation#7299
sanish-bruno wants to merge 2 commits intousebruno:mainfrom
sanish-bruno:fix/pm-jsonbody-translation

Conversation

@sanish-bruno
Copy link
Collaborator

@sanish-bruno sanish-bruno commented Feb 25, 2026

Summary

  • Implements jsonBody(...) as a native custom Chai assertion in Bruno, matching Postman's pm.response.to.have.jsonBody(...) behavior
  • Simplifies the Postman-to-Bruno translator from a complex multi-branch AST expansion to a direct 1:1 passthrough
  • Adds support for pm.response.to.not.have.jsonBody(...) negation (previously unhandled)

Motivation

Postman's pm.response.to.have.jsonBody(...) was previously expanded during translation into verbose Chai chains (to.have.nested.property, to.deep.equal, to.satisfy). This made translated scripts harder to read and diverged from the original Postman syntax. By implementing jsonBody as a first-class assertion, the translator becomes a simple passthrough and Bruno users get direct access to the same ergonomic API.

jsonBody Variants

Call Behavior
jsonBody() Validates body is a JSON object or array
jsonBody({...}) Deep equality comparison
jsonBody("path") Checks nested path exists (dot/bracket notation)
jsonBody("path", value) Checks nested path equals value

All variants support negation via .not (e.g., to.not.have.jsonBody("key")).

Changes

File What changed
bruno-js/.../assert-runtime.js Register jsonBody Chai method for Node VM runtime
bruno-js/.../quickjs/shims/test.js Register jsonBody on Chai prototype for QuickJS sandbox
bruno-converters/.../postman-to-bruno-translator.js Replace multi-branch expansion with passthrough; add .not.have.jsonBody pattern
bruno-converters/tests/.../response.test.js Update 9 existing tests + add 4 negation tests
bruno-converters/tests/.../multiline-syntax.test.js Add assertions for jsonBody in multiline test
bruno-tests/.../res/jsonBody.bru New E2E test covering all variants and edge cases

Test plan

  • npm run test --workspace=packages/bruno-converters — 679 tests pass (52 suites)
  • npm run test:e2e — new jsonBody.bru validates the assertion at runtime
  • Manual: import a Postman collection using pm.response.to.have.jsonBody(...) and verify translated scripts work

Summary by CodeRabbit

Release Notes

  • New Features

    • Added jsonBody() assertion for validating JSON response bodies with support for deep equality checks, nested property verification, and value matching.
  • Tests

    • Added comprehensive test coverage for jsonBody() assertions including negation scenarios and multiline syntax support.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Walkthrough

Unifies Postman pm.response.jsonBody translations to assert on res.getBody(), adds a new Chai jsonBody assertion in runtime and QuickJS shims, expands translator to handle negation and various argument shapes, and updates tests and a Bruno example to exercise these assertions. (47 words)

Changes

Cohort / File(s) Summary
Translator
packages/bruno-converters/src/utils/postman-to-bruno-translator.js
Reworked jsonBody translation: introduced expectGetBody helper, map pm.response.to.have.jsonBody(...)/...not.have.jsonBody(...) to expect(res.getBody()).to[.not].have.jsonBody(...), removed prior path-branching logic and unified output shape.
Tests — translator
packages/bruno-converters/tests/postman/.../response.test.js, packages/bruno-converters/tests/postman/.../multiline-syntax.test.js
Expanded and updated test expectations to reflect unified jsonBody translations, covering no-args, object arg, path, path+value, array/bracket paths, negation variants, and multiline scenarios.
Runtime assertions
packages/bruno-js/src/runtime/assert-runtime.js
Added new Chai assertion jsonBody(...) with support for: no-args (is JSON), object deep equality, path existence, and path+value checks; includes helper getNestedValue.
Sandbox / QuickJS shim
packages/bruno-js/src/sandbox/quickjs/shims/test.js
Added a matching jsonBody assertion implementation for the QuickJS test shim (helper functions, deep equality, error messages) to mirror runtime behavior.
Example tests
packages/bruno-tests/collection/scripting/api/res/jsonBody.bru
New Bruno test script exercising jsonBody matcher across multiple signatures and negation cases against an echo JSON endpoint.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

size/XL

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno
  • bijin-bruno

Poem

In Bruno's world the bodies sing,
json paths and objects spring,
expect and shim now walk in tune,
tests applaud beneath the moon. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: introducing a custom jsonBody Chai assertion and simplifying the Postman translator to use it directly.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/bruno-converters/src/utils/postman-to-bruno-translator.js (1)

558-577: Array.isArray(u) branch in the generated predicate is redundant.

u !== null && typeof u === "object" already returns true for arrays (typeof [] === "object"), so the Array.isArray(u) || short-circuit is never needed for correctness. The generated assertion is semantically equivalent without it, and dropping it produces cleaner output.

♻️ Simplified satisfy predicate
-              j.logicalExpression(
-                '||',
-                j.callExpression(
-                  j.memberExpression(j.identifier('Array'), j.identifier('isArray')),
-                  [j.identifier('u')]
-                ),
-                j.logicalExpression(
-                  '&&',
-                  j.binaryExpression('!==', j.identifier('u'), j.literal(null)),
-                  j.binaryExpression(
-                    '===',
-                    j.unaryExpression('typeof', j.identifier('u'), true),
-                    j.literal('object')
-                  )
-                )
-              )
+              j.logicalExpression(
+                '&&',
+                j.binaryExpression('!==', j.identifier('u'), j.literal(null)),
+                j.binaryExpression(
+                  '===',
+                  j.unaryExpression('typeof', j.identifier('u'), true),
+                  j.literal('object')
+                )
+              )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-converters/src/utils/postman-to-bruno-translator.js` around
lines 558 - 577, The generated predicate includes an unnecessary
Array.isArray(u) branch: inside the j.arrowFunctionExpression that uses
identifier 'u' you can remove the j.callExpression of Array.isArray and the
surrounding '||' logicalExpression and instead keep only the null/type check (u
!== null && typeof u === "object"); update the j.logicalExpression construction
so it no longer builds the '||' with Array.isArray and directly returns the '&&'
binaryExpression chain, removing the Array.isArray-related nodes from the AST.
packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js (1)

658-675: Consider adding an object-arg variant to the existing inside-test-block coverage.

The should handle pm.response.to.have.jsonBody inside test blocks test (line 677) only exercises string-path variants. The new object-argument branch has no corresponding inside-pm.test(…) test, which would confirm the transform behaves correctly when the call is nested inside a callback.

🧪 Suggested additional test case
+  it('should translate pm.response.to.have.jsonBody with object argument inside test block', () => {
+    const code = `
+        pm.test("Body matches schema", function() {
+            pm.response.to.have.jsonBody({ success: true });
+        });
+        `;
+    const translatedCode = translateCode(code);
+    expect(translatedCode).toContain('test("Body matches schema", function() {');
+    expect(translatedCode).toContain('expect(res.getBody()).to.deep.equal');
+    expect(translatedCode).toContain('success: true');
+  });

Based on learnings: "Cover both the 'happy path' and the realistically problematic paths. Validate expected success behaviour, but also validate error handling, edge cases, and degraded-mode behaviour."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js`
around lines 658 - 675, Add a new test variant for the existing "should handle
pm.response.to.have.jsonBody inside test blocks" scenario that uses an object
argument; specifically, inside the same describe/it group call translateCode on
a string like 'pm.test("...", () => { pm.response.to.have.jsonBody({ success:
true, data: { id: 1 } }); });' and assert the result contains the same
expectations as the top-level object-arg tests (e.g.,
expect(res.getBody()).to.deep.equal and the object keys like success: true or
nested data.id). Locate the existing test by the description "should handle
pm.response.to.have.jsonBody inside test blocks" and use translateCode and the
same assertion helpers to mirror the object-argument coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/bruno-converters/src/utils/postman-to-bruno-translator.js`:
- Around line 558-577: The generated predicate includes an unnecessary
Array.isArray(u) branch: inside the j.arrowFunctionExpression that uses
identifier 'u' you can remove the j.callExpression of Array.isArray and the
surrounding '||' logicalExpression and instead keep only the null/type check (u
!== null && typeof u === "object"); update the j.logicalExpression construction
so it no longer builds the '||' with Array.isArray and directly returns the '&&'
binaryExpression chain, removing the Array.isArray-related nodes from the AST.

In
`@packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js`:
- Around line 658-675: Add a new test variant for the existing "should handle
pm.response.to.have.jsonBody inside test blocks" scenario that uses an object
argument; specifically, inside the same describe/it group call translateCode on
a string like 'pm.test("...", () => { pm.response.to.have.jsonBody({ success:
true, data: { id: 1 } }); });' and assert the result contains the same
expectations as the top-level object-arg tests (e.g.,
expect(res.getBody()).to.deep.equal and the object keys like success: true or
nested data.id). Locate the existing test by the description "should handle
pm.response.to.have.jsonBody inside test blocks" and use translateCode and the
same assertion helpers to mirror the object-argument coverage.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81a7544 and 1e33668.

📒 Files selected for processing (2)
  • packages/bruno-converters/src/utils/postman-to-bruno-translator.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js

…nce 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.
@sanish-bruno sanish-bruno changed the title feat: enhance jsonBody translation handling in Postman to Bruno converter feat: add custom jsonBody Chai assertion + simplify Postman translation Feb 26, 2026
@pull-request-size pull-request-size bot added size/L and removed size/M labels Feb 26, 2026
@sanish-bruno sanish-bruno marked this pull request as draft February 26, 2026 08:09
@sanish-bruno
Copy link
Collaborator Author

#4856

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant