{"_id":"@bupkis/supertest","_rev":"2-af5c9a30d6c3d277821138d73a1c57e2","name":"@bupkis/supertest","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@bupkis/supertest","version":"0.1.0","keywords":["bupkis","supertest","http","response","assert","assertion","test","fetch","axios"],"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","_id":"@bupkis/supertest@0.1.0","maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"homepage":"https://github.com/boneskull/bupkis#readme","bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"dist":{"shasum":"73f77b1b8caaafd5a6f7d0842957c98db5612109","tarball":"https://registry.npmjs.org/@bupkis/supertest/-/supertest-0.1.0.tgz","fileCount":40,"integrity":"sha512-nNN63RusJycjY+pnZJgQFleSmkl4ZLuOK1GZqWSXuBq1pRD2jhKz5yEZiHgiBNj1ybdgiRoiqUQex/K7dzlPaQ==","signatures":[{"sig":"MEUCIBj2O0JK2bpIP6S4X3RQNeDGBbQ/ZLuy0YWFyveu+BpDAiEArHmK7WJtgPK5e8NRdT1nGmsV4w48AoK0jTbG18P9iz0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":156169},"main":"./dist/index.cjs","type":"module","zshy":{"exports":{".":"./src/index.ts","./package.json":"./package.json"}},"types":"./dist/index.d.cts","module":"./dist/index.js","engines":{"node":"^20.19.0 || ^22.12.0 || >=23"},"exports":{".":{"types":"./dist/index.d.cts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"f09ff1cfb21ceb03dee850971611a3d1e98086b4","scripts":{"test":"npm run test:base -- \"test/*.test.ts\"","build":"zshy","test:dev":"npm run test:base -- --watch \"test/*.test.ts\"","test:base":"node --import tsx --test --test-reporter=spec","test:manual":"tsx examples/manual-test.ts","test:node20":"npm run test:base -- test/*.test.ts","prepublishOnly":"npm run build"},"_npmUser":{"name":"boneskull","email":"boneskull@boneskull.com"},"prettier":{"tsdoc":true,"plugins":["prettier-plugin-jsdoc","prettier-plugin-pkg"],"singleQuote":true,"jsdocPreferCodeFences":true,"jsdocCommentLineStrategy":"keep"},"repository":{"url":"git+https://github.com/boneskull/bupkis.git","type":"git","directory":"packages/supertest"},"_npmVersion":"11.6.2","description":"HTTP response assertions for Bupkis - works with supertest, fetch, and axios","directories":{},"_nodeVersion":"24.12.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"fast-check":"4.5.2"},"peerDependencies":{"bupkis":">=0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/supertest_0.1.0_1768604283926_0.7157012677617103","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."}},"time":{"created":"2026-01-16T22:58:03.820Z","modified":"2026-01-20T22:22:36.925Z","0.1.0":"2026-01-16T22:58:04.149Z"},"bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","homepage":"https://github.com/boneskull/bupkis#readme","keywords":["bupkis","supertest","http","response","assert","assertion","test","fetch","axios"],"repository":{"url":"git+https://github.com/boneskull/bupkis.git","type":"git","directory":"packages/supertest"},"description":"HTTP response assertions for Bupkis - works with supertest, fetch, and axios","maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"readme":"# @bupkis/supertest\n\nHTTP response assertions for [Bupkis](https://bupkis.zip).\n\nWorks with [supertest](https://github.com/ladjs/supertest), [superagent](https://github.com/ladjs/superagent), fetch responses, axios responses, or any object with a numeric `status` property.\n\n## Installation\n\n```bash\nnpm install @bupkis/supertest bupkis\n```\n\n## Usage\n\n```typescript\nimport { use } from 'bupkis';\nimport supertestAssertions from '@bupkis/supertest';\nimport request from 'supertest';\n\nconst { expect } = use(supertestAssertions);\n\n// Status assertions\nconst response = await request(app).get('/api/users');\nexpect(response, 'to have status', 200);\nexpect(response, 'to have status', 'ok');\n\n// Header assertions\nexpect(response, 'to have header', 'content-type');\nexpect(response, 'to have header', 'content-type', 'application/json');\nexpect(response, 'to have header', 'content-type', /json/);\n\n// Body assertions\nexpect(response, 'to have body');\nexpect(response, 'to have JSON body');\nexpect(response, 'to have JSON body satisfying', { users: [] });\n\n// Redirect assertions\nconst redirect = await request(app).get('/old-page');\nexpect(redirect, 'to redirect');\nexpect(redirect, 'to redirect to', '/new-page');\n```\n\n## Assertions\n\n### {Response} to have status {number}\n\n> Aliases:\n>\n>     {Response} to have status {number}\n>     {Response} to respond with status {number}\n\nAsserts that a response has a specific HTTP status code.\n\n**Success**:\n\n```js\nexpect({ status: 200 }, 'to have status', 200);\nexpect({ status: 404 }, 'to have status', 404);\nexpect({ status: 500 }, 'to respond with status', 500);\n```\n\n**Failure**:\n\n```js\nexpect({ status: 404 }, 'to have status', 200);\n// AssertionError: Expected response to have status 200\n```\n\n**Negation**:\n\n```js\nexpect({ status: 404 }, 'not to have status', 200);\n```\n\n### {Response} to have status {category}\n\n> Aliases:\n>\n>     {Response} to have status {category}\n>     {Response} to respond with status {category}\n\nAsserts that a response has a status code within a category. Valid categories are:\n\n- `'ok'` - 2xx status codes (200-299)\n- `'redirect'` - 3xx status codes (300-399)\n- `'client error'` - 4xx status codes (400-499)\n- `'server error'` - 5xx status codes (500-599)\n\n**Success**:\n\n```js\nexpect({ status: 200 }, 'to have status', 'ok');\nexpect({ status: 201 }, 'to have status', 'ok');\nexpect({ status: 301 }, 'to have status', 'redirect');\nexpect({ status: 404 }, 'to have status', 'client error');\nexpect({ status: 500 }, 'to have status', 'server error');\n```\n\n**Failure**:\n\n```js\nexpect({ status: 404 }, 'to have status', 'ok');\n// AssertionError: Expected response to have ok status\n```\n\n**Negation**:\n\n```js\nexpect({ status: 404 }, 'not to have status', 'ok');\n```\n\n### {Response} to have header {string}\n\n> Aliases:\n>\n>     {Response} to have header {string}\n>     {Response} to include header {string}\n\nAsserts that a response has a specific header (existence check only). Header names are matched case-insensitively.\n\n**Success**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'Content-Type': 'application/json' },\n};\nexpect(response, 'to have header', 'content-type');\nexpect(response, 'to have header', 'Content-Type');\nexpect(response, 'to include header', 'CONTENT-TYPE');\n```\n\n**Failure**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'content-type': 'application/json' },\n};\nexpect(response, 'to have header', 'x-custom-header');\n// AssertionError: Expected response to have header \"x-custom-header\"\n```\n\n**Negation**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'content-type': 'application/json' },\n};\nexpect(response, 'not to have header', 'x-custom-header');\n```\n\n### {Response} to have header {string} {string}\n\n> Aliases:\n>\n>     {Response} to have header {string} {string}\n>     {Response} to include header {string} {string}\n\nAsserts that a response has a header with an exact value.\n\n**Success**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'content-type': 'application/json' },\n};\nexpect(response, 'to have header', 'content-type', 'application/json');\n```\n\n**Failure**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'content-type': 'text/html' },\n};\nexpect(response, 'to have header', 'content-type', 'application/json');\n// AssertionError: Expected header \"content-type\" to equal \"application/json\"\n```\n\n**With array header values** (e.g., multiple `Set-Cookie` headers):\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'set-cookie': ['a=1', 'b=2'] },\n};\nexpect(response, 'to have header', 'set-cookie', 'a=1, b=2');\n```\n\n**Negation**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'content-type': 'text/html' },\n};\nexpect(response, 'not to have header', 'content-type', 'application/json');\n```\n\n### {Response} to have header {string} {RegExp}\n\n> Aliases:\n>\n>     {Response} to have header {string} {RegExp}\n>     {Response} to include header {string} {RegExp}\n\nAsserts that a response has a header matching a regex pattern.\n\n**Success**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'content-type': 'application/json; charset=utf-8' },\n};\nexpect(response, 'to have header', 'content-type', /json/);\nexpect(response, 'to have header', 'content-type', /^application\\//);\n\nconst cacheResponse = {\n  status: 200,\n  headers: { 'cache-control': 'max-age=3600, public' },\n};\nexpect(cacheResponse, 'to have header', 'cache-control', /max-age=\\d+/);\n```\n\n**Failure**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'content-type': 'text/html' },\n};\nexpect(response, 'to have header', 'content-type', /json/);\n// AssertionError: Expected header \"content-type\" to match /json/\n```\n\n**Negation**:\n\n```js\nconst response = {\n  status: 200,\n  headers: { 'content-type': 'text/html' },\n};\nexpect(response, 'not to have header', 'content-type', /json/);\n```\n\n### {Response} to have body\n\nAsserts that a response has a non-empty body. Empty objects `{}` and empty arrays `[]` are considered to have a body (they're valid JSON responses).\n\n**Success**:\n\n```js\nexpect({ status: 200, text: 'Hello' }, 'to have body');\nexpect({ status: 200, body: { users: [] } }, 'to have body');\nexpect({ status: 200, body: {} }, 'to have body'); // empty object counts\nexpect({ status: 200, body: [] }, 'to have body'); // empty array counts\n```\n\n**Failure**:\n\n```js\nexpect({ status: 200 }, 'to have body');\n// AssertionError: Expected response to have a body\n\nexpect({ status: 200, text: '', body: '' }, 'to have body');\n// AssertionError: Expected response to have a body\n```\n\n**Negation**:\n\n```js\nexpect({ status: 204 }, 'not to have body');\n```\n\n### {Response} to have body {string}\n\nAsserts that a response has an exact string body.\n\n**Success**:\n\n```js\nexpect({ status: 200, text: 'Hello, World!' }, 'to have body', 'Hello, World!');\n\n// Object bodies are stringified for comparison\nexpect({ status: 200, body: { id: 1 } }, 'to have body', '{\"id\":1}');\n```\n\n**Failure**:\n\n```js\nexpect({ status: 200, text: 'Hello' }, 'to have body', 'Goodbye');\n// AssertionError: Expected response body to equal string\n```\n\n**Negation**:\n\n```js\nexpect({ status: 200, text: 'Hello' }, 'not to have body', 'Goodbye');\n```\n\n### {Response} to have JSON body\n\nAsserts that a response has a JSON content-type and a body. Checks for `application/json` in either the `type` property or `content-type` header.\n\n**Success**:\n\n```js\nconst response = {\n  status: 200,\n  type: 'application/json',\n  body: { users: [] },\n};\nexpect(response, 'to have JSON body');\n\n// Also works with content-type header\nconst response2 = {\n  status: 200,\n  headers: { 'content-type': 'application/json; charset=utf-8' },\n  body: { data: 'test' },\n};\nexpect(response2, 'to have JSON body');\n```\n\n**Failure**:\n\n```js\nconst htmlResponse = {\n  status: 200,\n  type: 'text/html',\n  body: '<html></html>',\n};\nexpect(htmlResponse, 'to have JSON body');\n// AssertionError: Expected response to have JSON content-type\n\nconst emptyResponse = {\n  status: 204,\n  type: 'application/json',\n};\nexpect(emptyResponse, 'to have JSON body');\n// AssertionError: Expected response to have a JSON body\n```\n\n**Negation**:\n\n```js\nconst htmlResponse = {\n  status: 200,\n  type: 'text/html',\n  body: '<html></html>',\n};\nexpect(htmlResponse, 'not to have JSON body');\n```\n\n### {Response} to have JSON body satisfying {object}\n\nAsserts that a response has a JSON body containing all specified properties with matching values. Uses partial/subset matching - the response may contain additional properties beyond those specified.\n\n**Success**:\n\n```js\nconst response = {\n  status: 200,\n  body: { id: 1, name: 'John', email: 'john@example.com' },\n};\n\n// Partial match - only checks specified properties\nexpect(response, 'to have JSON body satisfying', { id: 1 });\nexpect(response, 'to have JSON body satisfying', { name: 'John' });\nexpect(response, 'to have JSON body satisfying', { id: 1, name: 'John' });\n\n// Nested objects\nconst nestedResponse = {\n  status: 200,\n  body: {\n    user: { id: 1, profile: { name: 'John', age: 30 } },\n    meta: { version: '1.0' },\n  },\n};\nexpect(nestedResponse, 'to have JSON body satisfying', {\n  user: { profile: { name: 'John' } },\n});\n\n// Arrays\nconst arrayResponse = {\n  status: 200,\n  body: { users: [{ id: 1 }, { id: 2 }] },\n};\nexpect(arrayResponse, 'to have JSON body satisfying', {\n  users: [{ id: 1 }, { id: 2 }],\n});\n```\n\n**Failure**:\n\n```js\nconst response = {\n  status: 200,\n  body: { id: 1 },\n};\nexpect(response, 'to have JSON body satisfying', { name: 'John' });\n// AssertionError: Expected response body to satisfy specification\n\nconst response2 = {\n  status: 200,\n  body: { id: 1, name: 'Jane' },\n};\nexpect(response2, 'to have JSON body satisfying', { name: 'John' });\n// AssertionError: Expected response body to satisfy specification\n```\n\n**Negation**:\n\n```js\nconst response = {\n  status: 200,\n  body: { id: 1, name: 'Jane' },\n};\nexpect(response, 'not to have JSON body satisfying', { name: 'John' });\n```\n\n### {Response} to have body satisfying {RegExp}\n\nAsserts that a response body (as text) matches a regex pattern.\n\n**Success**:\n\n```js\nexpect(\n  { status: 200, text: 'Hello, World!' },\n  'to have body satisfying',\n  /World/,\n);\nexpect(\n  { status: 200, text: '{\"id\":123}' },\n  'to have body satisfying',\n  /\"id\":\\d+/,\n);\n```\n\n**Failure**:\n\n```js\nexpect({ status: 200, text: 'Hello' }, 'to have body satisfying', /Goodbye/);\n// AssertionError: Expected response body to match /Goodbye/\n\nexpect({ status: 200 }, 'to have body satisfying', /anything/);\n// AssertionError: Expected response to have a body\n```\n\n**Negation**:\n\n```js\nexpect(\n  { status: 200, text: 'Hello' },\n  'not to have body satisfying',\n  /Goodbye/,\n);\n```\n\n### {Response} to have body satisfying {object}\n\nAsserts that a response body satisfies a partial object match. Similar to `to have JSON body satisfying` but doesn't require JSON content-type.\n\n**Success**:\n\n```js\nconst response = {\n  status: 200,\n  body: { id: 1, name: 'John', extra: 'ignored' },\n};\nexpect(response, 'to have body satisfying', { id: 1 });\n```\n\n**Failure**:\n\n```js\nconst response = {\n  status: 200,\n  body: { id: 1 },\n};\nexpect(response, 'to have body satisfying', { name: 'John' });\n// AssertionError: Expected response body to satisfy specification\n```\n\n**Negation**:\n\n```js\nconst response = {\n  status: 200,\n  body: { id: 1 },\n};\nexpect(response, 'not to have body satisfying', { name: 'John' });\n```\n\n### {Response} to redirect\n\nAsserts that a response is a redirect (has a 3xx status code).\n\n**Success**:\n\n```js\nexpect({ status: 301 }, 'to redirect');\nexpect({ status: 302 }, 'to redirect');\nexpect({ status: 307 }, 'to redirect');\nexpect({ status: 308 }, 'to redirect');\n```\n\n**Failure**:\n\n```js\nexpect({ status: 200 }, 'to redirect');\n// AssertionError: Expected response to be a redirect, but got status 200\n\nexpect({ status: 404 }, 'to redirect');\n// AssertionError: Expected response to be a redirect, but got status 404\n```\n\n**Negation**:\n\n```js\nexpect({ status: 200 }, 'not to redirect');\n```\n\n### {Response} to redirect to {string}\n\nAsserts that a response redirects to a specific URL. The response must be a redirect (3xx) and have a `Location` header matching the expected URL exactly.\n\n**Success**:\n\n```js\nconst response = {\n  status: 302,\n  headers: { location: '/login' },\n};\nexpect(response, 'to redirect to', '/login');\n\nconst fullUrl = {\n  status: 301,\n  headers: { location: 'https://example.com/new-page' },\n};\nexpect(fullUrl, 'to redirect to', 'https://example.com/new-page');\n```\n\n**Failure**:\n\n```js\n// Not a redirect\nconst okResponse = {\n  status: 200,\n  headers: { location: '/somewhere' },\n};\nexpect(okResponse, 'to redirect to', '/somewhere');\n// AssertionError: Expected response to be a redirect, but got status 200\n\n// Missing Location header\nexpect({ status: 302 }, 'to redirect to', '/login');\n// AssertionError: Expected redirect response to have a Location header\n\n// Location doesn't match\nconst response = {\n  status: 302,\n  headers: { location: '/dashboard' },\n};\nexpect(response, 'to redirect to', '/login');\n// AssertionError: Expected redirect to \"/login\"\n```\n\n**Negation**:\n\n```js\nconst response = {\n  status: 302,\n  headers: { location: '/dashboard' },\n};\nexpect(response, 'not to redirect to', '/login');\n```\n\n### {Response} to redirect to {RegExp}\n\nAsserts that a response redirects to a URL matching a pattern. The response must be a redirect (3xx) and have a `Location` header matching the regex.\n\n**Success**:\n\n```js\nconst response = {\n  status: 302,\n  headers: { location: '/auth/login?redirect=/dashboard' },\n};\nexpect(response, 'to redirect to', /\\/auth/);\nexpect(response, 'to redirect to', /redirect=/);\nexpect(response, 'to redirect to', /^\\/auth\\/login/);\n```\n\n**Failure**:\n\n```js\n// Not a redirect\nconst okResponse = {\n  status: 200,\n  headers: { location: '/somewhere' },\n};\nexpect(okResponse, 'to redirect to', /somewhere/);\n// AssertionError: Expected response to be a redirect, but got status 200\n\n// Location doesn't match pattern\nconst response = {\n  status: 302,\n  headers: { location: '/dashboard' },\n};\nexpect(response, 'to redirect to', /login/);\n// AssertionError: Expected redirect Location to match /login/\n```\n\n**Negation**:\n\n```js\nconst response = {\n  status: 302,\n  headers: { location: '/dashboard' },\n};\nexpect(response, 'not to redirect to', /login/);\n```\n\n## Compatible Response Objects\n\nThis library works with any object that has a numeric `status` property. It's designed to be compatible with:\n\n- **supertest** responses\n- **superagent** responses (uses `header` instead of `headers`)\n- **fetch** responses (after calling `.json()` or similar)\n- **axios** responses\n- Plain objects for testing\n\n```js\n// Minimal response\nexpect({ status: 200 }, 'to have status', 200);\n\n// supertest/superagent style\nexpect(\n  {\n    status: 200,\n    headers: { 'content-type': 'application/json' },\n    body: { users: [] },\n    text: '{\"users\":[]}',\n    type: 'application/json',\n  },\n  'to have JSON body',\n);\n\n// superagent uses 'header' (singular)\nexpect(\n  {\n    status: 200,\n    header: { 'content-type': 'text/html' },\n  },\n  'to have header',\n  'content-type',\n);\n```\n\n## License\n\nCopyright © 2026 [Christopher \"boneskull\" Hiller][boneskull]. Licensed under [BlueOak-1.0.0](https://blueoakcouncil.org/license/1.0.0).\n\n[boneskull]: https://github.com/boneskull\n","readmeFilename":"README.md"}