{"id":18317935,"url":"https://github.com/idiocc/http","last_synced_at":"2025-07-21T15:09:23.346Z","repository":{"id":57103814,"uuid":"185293348","full_name":"idiocc/http","owner":"idiocc","description":"The Http(s) Testing Context For Super-Test Style Assertions. Includes Standard Assertions (get, set, assert), And Allows To Be Extended With JSDocumented Custom Assertions.","archived":false,"fork":false,"pushed_at":"2019-12-24T09:24:23.000Z","size":1465,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-07-10T10:57:41.471Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://idio.cc","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/idiocc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-07T00:52:14.000Z","updated_at":"2019-12-24T09:24:26.000Z","dependencies_parsed_at":"2022-08-20T17:10:19.873Z","dependency_job_id":null,"html_url":"https://github.com/idiocc/http","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/idiocc/http","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Fhttp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Fhttp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Fhttp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Fhttp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/idiocc","download_url":"https://codeload.github.com/idiocc/http/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Fhttp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266324521,"owners_count":23911231,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-21T11:47:31.412Z","response_time":64,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-05T18:07:56.276Z","updated_at":"2025-07-21T15:09:23.317Z","avatar_url":"https://github.com/idiocc.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @contexts/http\n\n[![npm version](https://badge.fury.io/js/%40contexts%2Fhttp.svg)](https://www.npmjs.com/package/@contexts/http)\n\n`@contexts/http` is The Http(s) Testing Context For Super-Test Style Assertions.\n\n- Includes [Standard Assertions](https://github.com/idiocc/http#class-tester) (get, set, assert) And [Cookies Assertions](https://github.com/idiocc/http#cookiescontext) (count, presence, value, attribute).\n- Allows To Be [Extended](https://github.com/idiocc/http#extending) With JSDocumented Custom Assertions.\n- Supports [sessions](https://github.com/idiocc/http#session-tester) to carry on cookies between requests automatically.\n\n```sh\nyarn add @contexts/http\n```\n\n## Table Of Contents\n\n- [Table Of Contents](#table-of-contents)\n- [API](#api)\n- [class HttpContext](#class-httpcontext)\n  * [`start(fn: (req: IncomingMessage, res: ServerResponse), secure: boolean=): Tester`](#startfn-req-incomingmessage-res-serverresponsesecure-boolean-tester)\n  * [`startPlain(fn: (req: IncomingMessage, res: ServerResponse), secure: boolean=): Tester`](#startplainfn-req-incomingmessage-res-serverresponsesecure-boolean-tester)\n  * [`listen(server: http.Server|https.Server): Tester`](#listenserver-httpserverhttpsserver-tester)\n  * [`debug(on: boolean=)`](#debugon-boolean-void)\n- [`Tester`](#type-tester)\n  * [`assert(code: number, body: (string|RegExp|Object)=): Tester`](#assertcode-numberbody-stringregexpobject-tester)\n  * [`assert(header: string, value: ?(string|RegExp)): Tester`](#assertheader-stringvalue-stringregexp-tester)\n  * [`assert(assertion: function(Aqt.Return)): Tester`](#assertassertion-functionaqtreturn-tester)\n    * [`_rqt.AqtReturn`](#type-_rqtaqtreturn)\n  * [`set(header: string, value: string): Tester`](#setheader-stringvalue-string-tester)\n  * [`post(path: string?, data: string|Object?, options: AqtOptions?): Tester`](#postpath-stringdata-stringobjectoptions-aqtoptions-tester)\n  * [`postForm(path: string?, cb: async function(Form), options: AqtOptions?): Tester`](#postformpath-stringcb-async-functionformoptions-aqtoptions-tester)\n  * [`session(): Tester`](#session-tester)\n- [Extending](#extending)\n- [CookiesContext](#cookiescontext)\n- [Copyright](#copyright)\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/0.svg?sanitize=true\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n## API\n\nThe package is available by importing its default and named classes. When [extending](#extending) the context, the `Tester` class is required. The [_CookiesContext_](#CookiesContext) is an extension of the _HttpContext_ that provides assertions for the returned `set-cookie` header.\n\n```js\nimport HttpContext, { Tester } from '@contexts/http'\nimport CookiesContext, { CookiesTester } from '@contexts/http/cookie'\n```\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/1.svg?sanitize=true\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n## class HttpContext\n\nThis testing context is to be used with [_Zoroaster Context Testing Framework_](https://contexttesting.com). Once it is defined as part of a test suite, it will be available to all inner tests via the arguments. It allows to specify the middleware function to start the server with, and provides an API to send requests, while setting headers, and then assert on the result that came back. It was inspired by `supertest`, but is asynchronous in nature so that no `done` has to be called \u0026mdash; just the promise needs to be awaited on.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth\u003eUsing \u003cem\u003eHttpContext\u003c/em\u003e Example\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nconst users = {\n  'secret-token': 'ExampleUser',\n}\n\n/**\n * User Authentication Route.\n * @param {http.IncomingMessage} req\n * @param {http.ServerResponse} res\n */\nconst middleware = (req, res) =\u003e {\n  const token = req.headers['x-auth']\n  if (!token) throw new Error('The authentication is required.')\n  const user = users[token]\n  if (!user) throw new Error('The user is not found.')\n  res.setHeader('set-cookie', `user=${user}`)\n  res.end(`Hello, ${user}`)\n}\n\nexport default middleware\n\n/**\n * @typedef {import('http').IncomingMessage} http.IncomingMessage\n * @typedef {import('http').ServerResponse} http.ServerResponse\n */\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\nFor example, we might want to test some synchronous middleware. It will check for the authentication token in the headers, reject the request if it is not present, or if the corresponding user is not found, and write the response if everything is OK.\n\u003c/tr\u003e\u003c/td\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n\u003ca href=\"example/test/spec/default.js\"\u003e\n  \u003cimg src=\"aty/2.gif\" alt=\"Writing Tests With HttpContext\"\u003e\n\u003c/a\u003e\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eThe tests are written for \u003cem\u003eZoroaster\u003c/em\u003e in such a way that test suite objects are exported. When the \u003ccode\u003econtext\u003c/code\u003e property is found on the test suite, it will be instantiated for all inner tests. The \u003ccode\u003estart\u003c/code\u003e method will wrap the request listener in try-catch block to send statuses \u003cem\u003e200\u003c/em\u003e and \u003cem\u003e500\u003c/em\u003e accordingly (see below).\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```\nexample/test/spec/default.js\n  ✓  prevents unauthorised\n  ✓  does not find the user\n  ✓  authenticates known user\n\n🦅  Executed 3 tests.\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eThe tests can be run with \u003cem\u003eZoroaster\u003c/em\u003e test runner: \u003ccode\u003ezoroaster example/test/spec -a\u003c/code\u003e.\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/2.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n\n\n### \u003ccode\u003e\u003cins\u003estart\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`fn: (req: IncomingMessage, res: ServerResponse),`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`secure: boolean=,`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nStarts the server with the given request listener function. It will setup an upper layer over the listener to try it and catch any errors in it. If there were errors, the status code will be set to `500` and the response will be ended with the error message. If there was no error, the status code will be set by _Node.JS_ to `200` automatically, if the request listener didn't set it. This is done so that assertion methods can be called inside of the supplied function. If the server needs to be started without the wrapper handler, the [`startPlain`](#startplainfn-req-incomingmessage-res-serverresponsesecure-boolean-tester) method can be used instead.\n\nWhen the `secure` option is passed, the HTTPS server with self-signed keys will be started and `process.env.NODE_TLS_REJECT_UNAUTHORIZED` will be set to `0` so make sure this context is only used for testing, and not on the production env.\n\n```js\n// the handler installed by the `start` method.\nconst handler = async (req, res) =\u003e {\n  try {\n    await fn(req, res)\n    res.statusCode = 200\n  } catch (err) {\n    res.statusCode = 500\n    res.write(err.message)\n    if (this._debug) console.error(error.stack)\n  } finally {\n    res.end()\n  }\n}\nserver.start(handler)\n```\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth\u003eMiddleware Constructor Testing Strategy\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\n/**\n * Creates a middleware for the given list of users.\n * @param {Object\u003cstring, string\u003e} users\n */\nconst makeMiddleware = (users) =\u003e {\n  /**\n   * Updates the request to have the user information if the token is found.\n   * @param {http.IncomingMessage} req\n   */\n  const middleware = (req) =\u003e {\n    const token = req.headers['x-auth']\n    const user = users[token]\n    if (user) req['user'] = user\n  }\n  return middleware\n}\n\nexport default makeMiddleware\n\n/**\n * @typedef {import('http').IncomingMessage} http.IncomingMessage\n * @typedef {import('http').ServerResponse} http.ServerResponse\n */\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eWe update the source code to export a constructor of middleware, based on the given options. In this case, the middleware will be created with the \u003ccode\u003eusers\u003c/code\u003e object that is scoped within the function, rather that file, so that we can pass it at the point of creating the middleware.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nimport { ok, equal } from 'assert'\nimport HttpContext from '@contexts/http'\nimport createMiddleware from '../../src/constructor'\n\nclass Context {\n  /**\n   * Creates a request listener for testing.\n   * @param {function(http.IncomingMessage, http.ServerResponse)} next\n   * Assertion method.\n   * @param {Object\u003cstring, string\u003e} [users] The list of tokens-users.\n   */\n  c(next, users = {}) {\n    return (req, res) =\u003e {\n      const mw = createMiddleware(users)\n      mw(req, res) // set the user on request\n      next(req, res)\n    }\n  }\n}\n\n/** @type {Object\u003cstring, (c: Context, h: HttpContext)} */\nconst TS = {\n  context: [Context, HttpContext],\n  async 'does not set the user without token'({ c }, { start }) {\n    await start(c((req) =\u003e {\n      ok(!req.user)\n    }))\n      .get('/')\n      .assert(200)\n  },\n  async 'does not set the user with missing token'({ c }, { start }) {\n    await start(c((req) =\u003e {\n      ok(!req.user)\n    }), { 'secret-token': 'User' })\n      .set('x-auth', 'missing-token')\n      .get('/')\n      .assert(200)\n  },\n  async 'sets the user with https'({ c }, { start }) {\n    await start(c((req) =\u003e {\n      ok(req.user)\n      ok(req.connection.encrypted)\n    }, { 'secret-token': 'User' }), true)\n      .set('x-auth', 'secret-token')\n      .get('/')\n      .assert(200)\n  },\n  async 'sets the correct name'({ c }, { start }) {\n    await start(c((req) =\u003e {\n      equal(req.user, 'Expected-User')\n    }, { 'secret-token': 'Actual-User' }))\n      .set('x-auth', 'secret-token')\n      .get('/')\n      .assert(200) // expecting fail\n  },\n}\n\nexport default TS\n\n/**\n * @typedef {import('http').IncomingMessage} http.IncomingMessage\n * @typedef {import('http').ServerResponse} http.ServerResponse\n */\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eThe new tests require implementing a method that will call the middleware constructor prior to continuing with the request. This method is creates as part of a different context, called simply \u003cem\u003eContext\u003c/em\u003e. It will help to create the request listener to pass to the \u003ccode\u003estart\u003c/code\u003e method, where the assertions will be written in another middleware executed after the source code one.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```\nexample/test/spec/constructor.js\n  ✓  does not set the user without token\n  ✓  does not set the user with missing token\n  ✓  sets the user with https\n  ✗  sets the correct name\n  | Error: 500 == 200 'Actual-User' == 'Expected-User'\n  |     at sets the correct name (example/test/spec/constructor.js:54:8)\n\nexample/test/spec/constructor.js \u003e sets the correct name\n  Error: 500 == 200 'Actual-User' == 'Expected-User'\n      at sets the correct name (example/test/spec/constructor.js:54:8)\n\n🦅  Executed 4 tests: 1 error.\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eWe expected the last test to fail because in the assertion method we specified that the user name should be different from the one that was passed in the options to the middleware. Other tests pass because there were no errors in the assertion middleware. It is always required to call \u003ccode\u003eassert\u003c/code\u003e on the context instance, because simply requesting data with \u003ccode\u003eget\u003c/code\u003e will not throw anything even if the status code was not \u003cem\u003e200\u003c/em\u003e.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/3.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003estartPlain\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`fn: (req: IncomingMessage, res: ServerResponse),`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`secure: boolean=,`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nStarts the server without wrapping the listener in the handler that would set status `200` on success and status `500` on error, and automatically finish the request. This means that the listener must manually do these things. Any uncaught error will result in run-time errors which will be caught by _Zoroaster_'s error handling mechanism outside of the test scope, but ideally they should be dealt with by the developer. If the middleware did not end the request, the test will timeout and the connection will be destroyed by the context to close the request.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth\u003ePlain Listener Testing\u003c/th\u003e\u003cth\u003eWrapper Listener Testing\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nimport Http from '@contexts/http'\n\n/** @type {Object\u003cstring, (h: Http)} */\nconst TS = {\n  context: Http,\n  async 'sets the status code and body'(\n    { startPlain }) {\n    await startPlain((req, res) =\u003e {\n      res.statusCode = 200\n      res.end('Hello World')\n    })\n      .get('/')\n      .assert(200, 'Hello World')\n  },\n  // expect to fail with global error\n  async 'throws an error'({ startPlain }) {\n    await startPlain(() =\u003e {\n      throw new Error('Unhandled error.')\n    })\n      .get('/')\n  },\n  // expect to timeout\n  async 'does not finish the request'(\n    { startPlain }) {\n    await startPlain((req, res) =\u003e {\n      res.write('hello')\n    })\n      .get('/')\n  },\n}\n\nexport default TS\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```js\nclass C {\n  c(listener) {\n    return (req, res) =\u003e {\n      try {\n        listener(req, res)\n      } catch (err) {\n        res.statusCode = 500\n      } finally {\n        res.end()\n      }\n    }\n  }\n}\n\n/** @type {Object\u003cstring, (c:C, h: Http)} */\nexport const handled = {\n  context: [C, Http],\n  async 'throws an error'({ c },\n    { startPlain }) {\n    await startPlain(c(() =\u003e {\n      throw new Error('Unhandled error.')\n    }))\n      .get('/')\n      .assert(500)\n  },\n  async 'times out'({ c }, { startPlain }) {\n    await startPlain(c((req, res) =\u003e {\n      res.write('hello')\n    }))\n      .get('/')\n      .assert(200, 'hello')\n  },\n}\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003eWith plain listener testing, the developer can test the function as if it was used on the server without any other middleware, such as error handling or automatic finishing of requests. The listener can also be wrapped in a custom service middleware that will do these admin things to support testing.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n```\nexample/test/spec/plain\n   handled\n    ✓  throws an error\n    ✓  times out\n   plain\n    ✓  sets the status code and body\n    ✗  throws an error\n    | Error: Unhandled error.\n    |     at startPlain (example/test/spec/plain/plain.js:18:13)\n    |     at Server.handler (src/index.js:88:15)\n    ✗  does not finish the request\n    | Error: Test has timed out after 200ms\n\nexample/test/spec/plain \u003e plain \u003e throws an error\n  Error: Unhandled error.\n      at startPlain (example/test/spec/plain/plain.js:18:13)\n      at Server.handler (src/index.js:88:15)\n\nexample/test/spec/plain \u003e plain \u003e does not finish the request\n  Error: Test has timed out after 200ms\n\n🦅  Executed 5 tests: 2 errors.\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003eThe output shows how tests with listeners that did not handle errors fail, so did the tests with listeners that did not end the request. The \u003ccode\u003ehandled\u003c/code\u003e test suite (on the right above), wraps the plain listener in a middleware that closed the connection and caught errors, setting the status code to \u003ccode\u003e500\u003c/code\u003e, therefore all tests passed there. The strategy is similar to the \u003ccode\u003estart\u003c/code\u003e method, but allows to implement a custom handler.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/4.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003elisten\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`server: http.Server|https.Server,`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nStarts the given server by calling the `listen` method. This method is used to test apps such as `Koa`, `Express`, `Connect` _etc_, or many middleware chained together, therefore it's a higher level of testing aka integration testing that does not allow to access the `response` object because no middleware is inserted into the server itself. It only allows to open URLs and assert on the results received by the request library, such as status codes, body and the headers. The server will be closed by the end of each test by the context.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eServer (App) Testing\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nimport { createServer } from 'http'\nimport connect from 'connect'\n\nconst app = connect()\napp.use((req, res, next) =\u003e {\n  if (req.url == '/error')\n    throw new Error('Uncaught error')\n  res.write('hello, ')\n  next()\n})\napp.use((req, res) =\u003e {\n  res.statusCode = 200\n  res.end('world!')\n})\n\nexport default createServer(app)\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```js\nimport H from '@contexts/http'\nimport server from '../../src/server'\n\n/** @type {Object\u003cstring, (h: H)} */\nconst TS = {\n  context: H,\n  async 'access the server'({ listen }) {\n    await listen(server)\n      .get('/')\n      .assert(200, 'hello, world!')\n  },\n  async 'connect catches errors'({ listen }) {\n    await listen(server)\n      .get('/error')\n      .assert(500)\n  },\n}\n\nexport default TS\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003eWhen a server needs to be tested as a whole of its middleware, the \u003ccode\u003elisten\u003c/code\u003e method of the \u003cem\u003eHttpContext\u003c/em\u003e is used. It allows to start the server on a random port, navigate to pages served by it, and assert on the results.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n```\nexample/test/spec/listen.js\n  ✓  access the server\n  ✓  connect catches errors\n\n🦅  Executed 2 tests.\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003eThe tests will be run as usual, but if there were any errors, they will be either handled by the server library, or caught by \u003cem\u003eZoroaster\u003c/em\u003e as global errors. Any unended requests will result in the test timing out.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/5.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003edebug\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`on: boolean=,`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003evoid\u003c/i\u003e\u003c/code\u003e\n\nSwitches on the debugging for the `start` method, because it catches the error and sets the response to 500, without giving any info about the error. This will log the error that happened during assertions in the request listener. Useful to see at what point the request failed.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth\u003eDebugging Errors In Start\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nasync 'sets the code to 200'({ start, debug }) {\n  debug()\n  await start(middleware)\n    .get()\n    .assert(200)\n},\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eThe debug is called once before the test. When called with \u003ccode\u003efalse\u003c/code\u003e, it will be switched off, but that use case is probably not going to be ever used, since it's just to debug tests.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```\nexample/test/spec/debug.js\n  ✗  sets the code to 200\n  | Error: 500 == 200 The authentication is required.\n  |     at sets the code to 200 (example/test/spec/debug.js:12:8)\n\nexample/test/spec/debug.js \u003e sets the code to 200\n  Error: 500 == 200 The authentication is required.\n      at sets the code to 200 (example/test/spec/debug.js:12:8)\n\n🦅  Executed 1 test: 1 error.\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eThe output normally fails with the error on the status code assertions, since the handler which wraps the request listener in the \u003ccode\u003estart\u003c/code\u003e methods, catches any errors and sets the response to be of status \u003ccode\u003e500\u003c/code\u003e and the body to the error message.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```\nError: The authentication is required.\n    at middleware (/Users/zavr/idiocc/http/example/src/index.js:12:21)\n    at Server.handler (/Users/zavr/idiocc/http/src/index.js:67:15)\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eThe \u003ccode\u003estderr\u003c/code\u003e output, on the other hand, will now print the full error stack that lead to the error.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/6.svg?sanitize=true\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n__\u003ca name=\"type-tester\"\u003e`Tester`\u003c/a\u003e__: The instance of a _Tester_ class is returned by the `start`, `startPlain` and `listen` methods. It is used to chain the actions together and extends the promise that should be awaited for during the test. It provides a testing API similar to the _SuperTest_ package, but does not require calling `done` method, because the _Tester_ class is asynchronous.\n\u003ctable\u003e\n \u003cthead\u003e\u003ctr\u003e\n  \u003cth\u003eName\u003c/th\u003e\n  \u003cth\u003eType \u0026amp; Description\u003c/th\u003e\n \u003c/tr\u003e\u003c/thead\u003e\n \u003ctr\u003e\n  \u003ctd rowSpan=\"3\" align=\"center\"\u003e\u003cins\u003econstructor\u003c/ins\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cem\u003enew () =\u003e \u003ca href=\"#type-tester\" title=\"The instance of a _Tester_ class is returned by the `start`, `startPlain` and `listen` methods. It is used to chain the actions together and extends the promise that should be awaited for during the test. It provides a testing API similar to the _SuperTest_ package, but does not require calling `done` method, because the _Tester_ class is asynchronous.\"\u003eTester\u003c/a\u003e\u003c/em\u003e\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd\u003e\n   Constructor method.\n  \u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd rowSpan=\"3\" align=\"center\"\u003e\u003cins\u003eget\u003c/ins\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cem\u003e(path?: string) =\u003e \u003ca href=\"#type-tester\" title=\"The instance of a _Tester_ class is returned by the `start`, `startPlain` and `listen` methods. It is used to chain the actions together and extends the promise that should be awaited for during the test. It provides a testing API similar to the _SuperTest_ package, but does not require calling `done` method, because the _Tester_ class is asynchronous.\"\u003eTester\u003c/a\u003e\u003c/em\u003e\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd\u003e\n\nSend a GET request. View examples at [Wiki](https://github.com/idiocc/http/wiki/Get)\n```js\nasync 'redirects to /'({ start }) {\n  await start(middleware)\n    .get()\n    .assert(302)\n    .assert('location', 'index.html')\n},\nasync 'opens sitemap'({ start }) {\n  await start(middleware)\n    .get('/sitemap')\n    .assert(200)\n},\n```\n  \u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd rowSpan=\"3\" align=\"center\"\u003e\u003cins\u003eoptions\u003c/ins\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cem\u003e(path?: string) =\u003e \u003ca href=\"#type-tester\" title=\"The instance of a _Tester_ class is returned by the `start`, `startPlain` and `listen` methods. It is used to chain the actions together and extends the promise that should be awaited for during the test. It provides a testing API similar to the _SuperTest_ package, but does not require calling `done` method, because the _Tester_ class is asynchronous.\"\u003eTester\u003c/a\u003e\u003c/em\u003e\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd\u003e\n\nSend a request for the `Allow` and CORS pre-flight headers.\n```js\nasync 'sends options request'({ start }) {\n  let method\n  await start((req, res) =\u003e {\n    method = req.method\n    res.setHeader('allow', 'HEAD, GET')\n    res.statusCode = 204\n  })\n    .options('/')\n    .assert(204)\n    .assert('allow', 'HEAD, GET')\n  equal(method, 'OPTIONS')\n},\n```\n  \u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd rowSpan=\"3\" align=\"center\"\u003e\u003cins\u003ehead\u003c/ins\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cem\u003e(path?: string, data?: (string | !Object), options?: !_rqt.AqtOptions) =\u003e \u003ca href=\"#type-tester\" title=\"The instance of a _Tester_ class is returned by the `start`, `startPlain` and `listen` methods. It is used to chain the actions together and extends the promise that should be awaited for during the test. It provides a testing API similar to the _SuperTest_ package, but does not require calling `done` method, because the _Tester_ class is asynchronous.\"\u003eTester\u003c/a\u003e\u003c/em\u003e\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd\u003e\n\nSend a HEAD request. View examples at [Wiki](https://github.com/idiocc/http/wiki/Head)\n```js\nasync 'sends redirect for index'({ start }) {\n  await start(middleware)\n    .head()\n    .assert(302)\n    .assert('location', 'index.html')\n},\nasync 'sends 200 for sitemap'({ start }) {\n  await start(middleware)\n    .head('/sitemap')\n    .assert(200)\n},\n```\n  \u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd rowSpan=\"3\" align=\"center\"\u003e\u003cins\u003eput\u003c/ins\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cem\u003e(path?: string, data?: (string | !Object), options?: !_rqt.AqtOptions) =\u003e \u003ca href=\"#type-tester\" title=\"The instance of a _Tester_ class is returned by the `start`, `startPlain` and `listen` methods. It is used to chain the actions together and extends the promise that should be awaited for during the test. It provides a testing API similar to the _SuperTest_ package, but does not require calling `done` method, because the _Tester_ class is asynchronous.\"\u003eTester\u003c/a\u003e\u003c/em\u003e\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\u003c/tr\u003e\n \u003ctr\u003e\n  \u003ctd\u003e\n   Send a PUT request.\n  \u003c/td\u003e\n \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/7.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n\n\n### \u003ccode\u003e\u003cins\u003eassert\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`code: number,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`body: (string|RegExp|Object)=,`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nAssert on the status code and body. The error message will contain the body if it was present. If the response was in JSON, it will be automatically parses by the request library, and the deep assertion will be performed.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eassert(code, body=)\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nasync 'status code'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.statusCode = 205\n    res.end()\n  })\n    .get()\n    .assert(205)\n},\nasync 'status code with message'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.statusCode = 205\n    res.end('example')\n  })\n    .get('/sitemap')\n    .assert(205, 'example')\n},\nasync 'status code with regexp'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.statusCode = 205\n    res.end('Example')\n  })\n    .get('/sitemap')\n    .assert(205, /example/i)\n},\nasync 'status code with json'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.statusCode = 205\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n    .get('/sitemap')\n    .assert(205, { hello: 'world' })\n},\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```\nexample/test/spec/assert/code.js\n  ✓  status code\n  ✓  status code with message\n  ✓  status code with regexp\n  ✓  status code with json\n\n🦅  Executed 4 tests.\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/8.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003eassert\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`header: string,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`value: ?(string|RegExp),`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nAssert on the response header. The value must be either a string, regular expression to match the value of the header, or null to assert that the header was not set.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eassert(header, ?value)\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\n// pass\nasync 'header'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.statusCode = 205\n    res.setHeader('content-type',\n      'application/json')\n    res.end('[]')\n  })\n    .get('/sitemap')\n    .assert(205)\n    .assert('content-type',\n      'application/json')\n},\nasync 'header with regexp'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.setHeader('content-type',\n      'application/json; charset=utf-8')\n    res.end('[]')\n  })\n    .get('/')\n    .assert('content-type',\n      /application\\/json/)\n},\nasync 'absence of a header'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n\n\n    res.end()\n  })\n    .get('/sitemap')\n    .assert('content-type', null)\n},\n```\n\u003c/td\u003e\u003ctd\u003e\n\n```js\n// fail\nasync 'header'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.statusCode = 205\n    res.setHeader('content-type',\n      'application/xml')\n    res.end('\u003cpages /\u003e')\n  })\n    .get('/sitemap')\n    .assert(205)\n    .assert('content-type',\n      'application/json')\n},\nasync 'header with regexp'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.setHeader('content-type',\n      'application/json; charset=utf-8')\n    res.end('[]')\n  })\n    .get('/')\n    .assert('content-type',\n      /application\\/xml/)\n},\nasync 'absence of a header'({ startPlain }) {\n  await startPlain((_, res) =\u003e {\n    res.setHeader('content-type',\n      'text/plain')\n    res.end()\n  })\n    .get('/sitemap')\n    .assert('content-type', null)\n},\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\nShow \u003cem\u003eZoroaster\u003c/em\u003e output\n\u003c/summary\u003e\n\n```diff\nexample/test/spec/assert/header.js\n  ✓  header\n  ✓  header with regexp\n  ✓  absence of a header\n example/test/spec/assert/header-fail.js\n  ✗  header\n  | Error: Header content-type did not match value:\n  | - application/json\n  | + application/xml\n  |     at header (example/test/spec/assert/header-fail.js:17:8)\n  ✗  header with regexp\n  | Error: Header content-type did not match RexExp:\n  |   - /application//xml/\n  |   + application/json; charset=utf-8\n  |     at header with regexp (example/test/spec/assert/header-fail.js:27:8)\n  ✗  absence of a header\n  | Error: Header content-type was not expected:\n  |   + text/plain\n  |     at absence of a header (example/test/spec/assert/header-fail.js:37:8)\n\nexample/test/spec/assert/header-fail.js \u003e header\n  Error: Header content-type did not match value:\n  - application/json\n  + application/xml\n      at header (example/test/spec/assert/header-fail.js:17:8)\n\nexample/test/spec/assert/header-fail.js \u003e header with regexp\n  Error: Header content-type did not match RexExp:\n    - /application//xml/\n    + application/json; charset=utf-8\n      at header with regexp (example/test/spec/assert/header-fail.js:27:8)\n\nexample/test/spec/assert/header-fail.js \u003e absence of a header\n  Error: Header content-type was not expected:\n    + text/plain\n      at absence of a header (example/test/spec/assert/header-fail.js:37:8)\n\n🦅  Executed 6 tests: 3 errors.\n```\n\u003c/details\u003e\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/9.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003eassert\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`assertion: function(Aqt.Return),`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nPerform an assertion using the function that will receive the response object which is the result of the request operation with `aqt`. If the tester was started with `start` or `startPlain` methods, it is possible to get the  response object from the request listener by calling the `getResponse` method on the context.\n\n[`import('http').IncomingHttpHeaders`](https://nodejs.org/api/http.html) __\u003ca name=\"type-httpincominghttpheaders\"\u003e`http.IncomingHttpHeaders`\u003c/a\u003e__: The hash map of headers that are set by the server (e.g., when accessed via IncomingMessage.headers)\n\n\u003cstrong\u003e\u003ca name=\"type-_rqtaqtreturn\"\u003e`_rqt.AqtReturn`\u003c/a\u003e\u003c/strong\u003e: The return type of the function.\n\n|        Name        |                                                                                            Type                                                                                             |                                                                                                                    Description                                                                                                                     |\n| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| __body*__          | \u003cem\u003e!(string \\| Object \\| Buffer)\u003c/em\u003e                                                                                                                                                      | The return from the server. In case the `json` content-type was set by the server, the response will be parsed into an object. If `binary` option was used for the request, a `Buffer` will be returned. Otherwise, a string response is returned. |\n| __headers*__       | \u003cem\u003e\u003ca href=\"#type-httpincominghttpheaders\" title=\"The hash map of headers that are set by the server (e.g., when accessed via IncomingMessage.headers)\"\u003e!http.IncomingHttpHeaders\u003c/a\u003e\u003c/em\u003e | Incoming headers returned by the server.                                                                                                                                                                                                           |\n| __statusCode*__    | \u003cem\u003enumber\u003c/em\u003e                                                                                                                                                                             | The status code returned by the server.                                                                                                                                                                                                            |\n| __statusMessage*__ | \u003cem\u003estring\u003c/em\u003e                                                                                                                                                                             | The status message set by the server.                                                                                                                                                                                                              |\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eassert(assertion)\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nasync 'using a function'({ start }) {\n  await start((_, res) =\u003e {\n    res.statusCode = 205\n    res.setHeader('content-type', 'application/xml')\n    res.end()\n  })\n    .get('/sitemap')\n    .assert((res) =\u003e {\n      equal(res.headers['content-type'],\n        'application/xml')\n    })\n},\nasync 'with response object'({ start, getResponse }) {\n  await start((_, res) =\u003e {\n    res.setHeader('content-type', 'application/xml')\n    res.end()\n  })\n    .get('/sitemap')\n    .assert(() =\u003e {\n      const res = getResponse()\n      equal(res.getHeader('content-type'),\n        'application/xml')\n    })\n},\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```\nexample/test/spec/assert/function.js\n  ✓  using a function\n  ✓  with response object\n\n🦅  Executed 2 tests.\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/10.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003eset\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`header: string,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`value: string,`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nSets the outgoing headers. Must be called before the `get` method. It is possible to remember the result of the first request using the `assert` method by storing it in a variable, and then use it for headers in the second request (see example).\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eset(header, value)\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nasync 'sets the header'({ startPlain }) {\n  await startPlain((req, res) =\u003e {\n    if (req.headers['x-auth'] == 'token') {\n      res.statusCode = 205\n      res.end('hello')\n    } else {\n      res.statusCode = 403\n      res.end('forbidden')\n    }\n  })\n    .set('x-auth', 'token')\n    .get()\n    .assert(205)\n},\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```js\nasync 'sets a header with a function'({ start }) {\n  let cookie\n  await start((req, res) =\u003e {\n    res.setHeader('x-test', 'world')\n    res.end(req.headers['test'])\n  })\n    .set('test', 'hello')\n    .get('/')\n    .assert(200, 'hello')\n    .assert(({ headers: h }) =\u003e {\n      cookie = h['x-test']\n    })\n    .set('test', () =\u003e cookie)\n    .get('/')\n    .assert(200, 'world')\n},\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\nShow \u003cem\u003eZoroaster\u003c/em\u003e output\n\u003c/summary\u003e\n\n```\nexample/test/spec/assert/set.js\n  ✓  sets the header\n example/test/spec/assert/set-fn.js\n  ✓  sets a header with a function\n\n🦅  Executed 2 tests.\n```\n\u003c/details\u003e\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/11.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003epost\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`path: string?,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`data: string|Object?,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`options: AqtOptions?,`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nPosts data to the server. By default, a string will be sent with the `text/plain` _Content-Type_, whereas an object will be encoded as the `application/json` type, or it can be sent as `application/x-www-form-urlencoded` data by specifying `type: form` in options. To send `multipart/form-data` requests, use the `postForm` method.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003e\u003ca href=\"example/test/spec/assert/post.js\"\u003epost(path, data?, options?)\u003c/a\u003e\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n```js\nasync 'posts string data'({ startPlain }, { middleware }) {\n  await startPlain(middleware)\n    .post('/submit', 'hello')\n    .assert(200, `Received data: hello `\n      + `with Content-Type text/plain`)\n},\nasync 'posts object data'({ startPlain }, { middleware }) {\n  await startPlain(middleware)\n    .post('/submit', { test: 'ok' })\n    .assert(200, `Received data: {\"test\":\"ok\"} `\n      + `with Content-Type application/json`)\n},\nasync 'posts urlencoded data'({ startPlain }, { middleware }) {\n  await startPlain(middleware)\n    .post('/submit', { test: 'ok' }, {\n      type: 'form',\n      headers: {\n        'User-Agent': 'testing',\n      },\n    })\n    .assert(200, `Received data: test=ok `\n      + `with Content-Type application/x-www-form-urlencoded `\n      + `and User-Agent testing`)\n},\n```\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\nShow \u003cem\u003eZoroaster\u003c/em\u003e output\n\u003c/summary\u003e\n\n```\nexample/test/spec/assert/post.js\n  ✓  posts string data\n  ✓  posts object data\n  ✓  posts urlencoded data\n\n🦅  Executed 3 tests.\n```\n\u003c/details\u003e\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/12.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003epostForm\u003c/ins\u003e(\u003c/code\u003e\u003csub\u003e\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`path: string?,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`cb: async function(Form),`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`options: AqtOptions?,`\u003cbr/\u003e\u003c/sub\u003e\u003ccode\u003e): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nCreates a form instance, to which data and files can be appended via the supplied callback, and sends the request as `multipart/form-data` to the server. See the [Form interface](https://github.com/idiocc/form#class-form) full documentation.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003e\u003ca href=\"example/test/spec/assert/post-form.js\"\u003epostForm(path, cb, options?)\u003c/a\u003e\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n```js\nasync 'posts multipart/form-data'({ startPlain }, { middleware }) {\n  await startPlain(middleware)\n    .postForm('/submit', async (form) =\u003e {\n      form.addSection('field', 'hello-world')\n      await form.addFile('test/fixture/test.txt', 'file')\n      await form.addFile('test/fixture/test.txt', 'file', {\n        filename: 'testfile.txt',\n      })\n    })\n    .assert(200, [\n      [ 'field', 'hello-world', '7bit', 'text/plain' ],\n      [ 'file',  'test.txt', '7bit', 'application/octet-stream' ],\n      [ 'file',  'testfile.txt', '7bit', 'application/octet-stream' ],\n    ])\n},\n```\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\nShow \u003cem\u003eZoroaster\u003c/em\u003e output\n\u003c/summary\u003e\n\n```\nexample/test/spec/assert/post-form.js\n  ✓  posts multipart/form-data\n\n🦅  Executed 1 test.\n```\n\u003c/details\u003e\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/13.svg?sanitize=true\" width=\"25\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n### \u003ccode\u003e\u003cins\u003esession\u003c/ins\u003e(): \u003ci\u003eTester\u003c/i\u003e\u003c/code\u003e\n\nTurns the session mode on. In the session mode, the cookies received from the server will be stored in the internal variable, and sent along with each following request. If the server removed the cookies by setting them to an empty string, or by setting the expiry date to be in the past, they will be removed from the tester and not sent to the server.\n\nThis feature can also be switched on by setting `session=true` on the context itself, so that `.session()` calls are not required.\n\nAdditional cookies can be set using the `.set('Cookie', {value})` method, and they will be concatenated to the cookies maintained by the session.\n\nAt the moment, only `expire` property is handled, without the `path`, or `httpOnly` directives. This will be added in future versions.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003esession()\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nimport HttpContext from '../../src'\n\n/** @type {TestSuite} */\nexport const viaSessionMethod = {\n  context: HttpContext,\n  async 'maintains the session'({ start, debug }) {\n    debug()\n    await start((req, res) =\u003e {\n      if (req.url == '/') {\n        res.setHeader('set-cookie', 'koa:sess=eyJtZ; path=/; httponly')\n        res.end('hello world')\n      } else if (req.url == '/exit') {\n        res.setHeader('set-cookie', 'koa:sess=; path=/; httponly')\n        res.end()\n      } else if (req.url == '/test') {\n        res.end(req.headers['cookie'])\n      }\n    })\n      .session()\n      .get('/')\n      .assert(200, 'hello world')\n      .set('Cookie', 'testing=true')\n      .get('/test')\n      .assert(200, 'koa:sess=eyJtZ;testing=true')\n      .get('/exit')\n      .get('/test')\n      .assert(200, 'testing=true')\n  },\n}\n\n/** @type {TestSuite} */\nexport const viaExtendingContext = {\n  context: class extends HttpContext {\n    constructor() {\n      super()\n      this.session = true\n    }\n  },\n  async 'maintains the session'({ start, debug }) {\n    debug()\n    await start((req, res) =\u003e {\n      if (req.url == '/') {\n        res.setHeader('set-cookie', 'koa:sess=eyJtZ; path=/; httponly')\n        res.end('hello world')\n      } else if (req.url == '/exit') {\n        res.setHeader('set-cookie', 'koa:sess=; path=/; httponly')\n        res.end()\n      } else if (req.url == '/test') {\n        res.end(req.headers['cookie'])\n      }\n    })\n      .get('/')\n      .assert(200, 'hello world')\n      .get('/test')\n      .assert(200, 'koa:sess=eyJtZ')\n      .get('/exit')\n      .get('/test')\n      .assert(200, '')\n  },\n}\n\n/** @typedef {import('../context').TestSuite} TestSuite */\n```\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003ctd colspan=\"2\"\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\nShow \u003cem\u003eZoroaster\u003c/em\u003e output\n\u003c/summary\u003e\n\n```\ntest/spec/session.js\n   viaSessionMethod\n    ✓  maintains the session\n   viaExtendingContext\n    ✓  maintains the session\n\n🦅  Executed 2 tests.\n```\n```\n/ Setting cookie koa:sess to eyJtZ \n/exit Server deleted cookie koa:sess\n/ Setting cookie koa:sess to eyJtZ \n/exit Server deleted cookie koa:sess\n```\n\u003c/details\u003e\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/14.svg?sanitize=true\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n## Extending\n\nThe package was designed to be extended with custom assertions which are easily documented for use in tests. The only thing required is to import the _Tester_ class, and extend it, following a few simple rules.\n\nThere are 2 parts of the _@contexts/Http_ software: the context and the tester. The context is used to start the server, remember the response object as well as to destroy the server. The tester is what is returned by the `start/startPlain/listen` methods, and is used to query the server. To implement the custom assertions with support for JSDoc, the _HttpContext_ needs to be extended to include any private methods that could be used by the tester's assertions, but might not have to be part of the _Tester_ API, and then implement those assertions in the tester by calling the private `_addLink` method which will add the action to the promise chain, so that the `await` syntax is available.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth\u003eImplementing Custom Assertions For Cookies\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nimport Http from '@context/http'\nimport CookiesTester from './tester'\nimport mistmatch from 'mismatch'\n\n/**\n * Extends _HTTPContext_ to assert on the cookies.\n */\nexport default class Cookies extends Http {\n  constructor() {\n    super()\n    this.TesterConstructor = CookiesTester\n    /**\n     * Parsed cookies.\n     * @private\n     */\n    this._cookies = null\n  }\n  /**\n   * Creates a server and wraps the supplied listener in the handler that will\n   * set status code `500` if the listener threw and the body to the error text.\n   * @param {function(http.IncomingMessage, http.ServerResponse)} fn\n   * @param {boolean} secure\n   */\n  start(fn, secure) {\n    const tester = /** @type {CookiesTester} */ (super.start(fn, secure))\n    return tester\n  }\n  /**\n   * Creates a server with the supplied listener.\n   * @param {function(http.IncomingMessage, http.ServerResponse)} fn\n   * @param {boolean} secure\n   */\n  startPlain(fn, secure) {\n    const tester = /** @type {CookiesTester} */ (super.startPlain(fn, secure))\n    return tester\n  }\n  getCookies() {\n    if (this._cookies) return this._cookies\n    const setCookies = /** @type {Array\u003cstring\u003e} */\n      (this.tester.res.headers['set-cookie']) || []\n    const res = setCookies.map(Cookies.parseSetCookie)\n    this._cookies = res\n    return res\n  }\n  /**\n   * Parses the `set-cookie` header.\n   * @param {string} header\n   */\n  static parseSetCookie(header) {\n    const pattern = /\\s*([^=;]+)(?:=([^;]*);?|;|$)/g\n\n    const pairs = mistmatch(pattern, header, ['name', 'value'])\n\n    /** @type {{ name: string, value: string }} */\n    const cookie = pairs.shift()\n\n    for (let i = 0; i \u003c pairs.length; i++) {\n      const match = pairs[i]\n      cookie[match.name.toLowerCase()] = (match.value || true)\n    }\n\n    return cookie\n  }\n  /**\n   * Returns the cookie record for the given name.\n   * @param {string} name\n   */\n  getCookieForName(name) {\n    const cookies = this.getCookies()\n\n    return cookies.find(({ name: n }) =\u003e {\n      return name == n\n    })\n  }\n  _reset() {\n    super._reset()\n    this._cookies = null\n  }\n}\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eThe \u003cem\u003eCookies\u003c/em\u003e context should extend the \u003cem\u003eHttp\u003c/em\u003e context, and set \u003ccode\u003ethis.TesterConstructor = CookieTester\u003c/code\u003e in its constructor, so that the \u003ccode\u003estart/startPlain/listen\u003c/code\u003e methods of the superclass will construct the appropriate tester. The additional step involved is overriding the \u003ccode\u003estart\u003c/code\u003e method to update the JSDoc type of the tester returned to \u003ccode\u003eCookieTester\u003c/code\u003e so that the autocompletion hints are available in tests. Now, additional methods that are required for assertions, can be added to the context. They will be accessible via the \u003ccode\u003ethis.context\u003c/code\u003e in the tester as shown below. The tester itself is accessible via the \u003ccode\u003ethis.tester\u003c/code\u003e, and the AQT response object can be accessed via the \u003ccode\u003ethis.tester.res\u003c/code\u003e property. Finally, the context should implement the \u003ccode\u003ereset\u003c/code\u003e method which will be called by the super class prior to any additional requests being made, to reset its state. For example, the \u003cem\u003eCookies\u003c/em\u003e context hashes the parsed cookies in the \u003ccode\u003ecookies\u003c/code\u003e property, which needs to be set to null before further requests, to make sure it's not the hashed values that are tested.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```js\nimport { Tester } from '@context/http'\nimport erotic from 'erotic'\n\n/**\n * The tester for assertion on cookies.\n */\nexport class CookiesTester extends Tester {\n  constructor() {\n    super()\n    /** @type {import('./').default} */\n    this.context = null\n  }\n  /**\n   * Assert on the number of times the cookie was set.\n   * @param {number} num The expected count.\n   */\n  count(num) {\n    const e = erotic(true)\n    this._addLink(() =\u003e {\n      const count = this.context.getCookies().length\n      equal(count, num, 'Should set cookie ' + num + ' times, not ' + count + '.')\n    }, e)\n    return this\n  }\n\n  /**\n   * Asserts on the value of the cookie.\n   * @param {string} name The name of the cookie.\n   * @param {string} val The value of the cookie.\n   */\n  value(name, val) {\n    const e = erotic(true)\n    this._addLink(() =\u003e {\n      const cookie = this.context.getCookieForName(name)\n      ok(cookie, wasExpectedError('Cookie', name, val))\n      equal(cookie.value, val,\n        didNotMatchValue('Cookie', name, val, cookie.value))\n    }, e)\n    return this\n  }\n  /**\n   * Asserts on the presence of an attribute in the cookie.\n   * @param {string} name The name of the cookie.\n   * @param {string} attrib The name of the attribute.\n   */\n  attribute(name, attrib) {\n    const e = erotic(true)\n    this._addLink(() =\u003e {\n      const cookie = this.context.getCookieForName(name)\n      assertAttribute(name, cookie, attrib)\n    }, e)\n    return this\n  }\n}\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eThe \u003cem\u003eCookiesTester\u003c/em\u003e class allows to add the assertions to the tester. To help write assertions, the \u003ccode\u003ethis.context\u003c/code\u003e type need to be updated to the \u003ccode\u003e/** @type {import('.').default} */ this.context = null\u003c/code\u003e in the constructor. Each assertion is documented with standard JSDoc. The assertion method might want to create an \u003ccode\u003eerotic\u003c/code\u003e object at first, to remember the point of entry to the function, so that the assertion will fail with an error whose stack consists of a single line where the assertion is called. This \u003ccode\u003ee\u003c/code\u003e object will have to be passed as the second argument to the \u003ccode\u003ethis._addLink\u003c/code\u003e method. The assertion logic, either sync or async must be implemented withing the callback passed to the \u003ccode\u003ethis_addLink\u003c/code\u003e method that will update the chain and execute the assertion in its turn. If the assertion explicitly returns \u003ccode\u003efalse\u003c/code\u003e, no other assertions in the chain will be called.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n\u003ca href=\"example/test/spec/cookie/default.js\"\u003e\n  \u003cimg src=\"aty/jsdoc.gif\" alt=\"Writing JSDoc Enabled Assertions\"\u003e\n\u003c/a\u003e\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eNow the \u003cem\u003eCookiesTester\u003c/em\u003e methods which are used in tests, will come up with JSDoc documentation. The context must be imported as usual from the \u003ccode\u003econtext\u003c/code\u003e directory, and set up on test suites in the \u003ccode\u003econtext\u003c/code\u003e property. If there are multiple test suites in a file, the \u003ccode\u003eexport const context = CookieContext\u003c/code\u003e would also work without having to specify the context on each individual test suite. The JSDoc enabling line, \u003ccode\u003e/** type {Object\u003cstring, (h: CookieContext)} */\u003c/code\u003e still needs to be present.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```\nexample/test/spec/cookie/\n  ✓  sets the HttpOnly cookie\n  ✓  deletes the cookie\n  ✗  sets cookie for a path\n  | Error: Attribute path of cookie example was expected.\n  |     at sets cookie for a path (example/test/spec/cookie/default.js:32:8)\n\nexample/test/spec/cookie/ \u003e sets cookie for a path\n  Error: Attribute path of cookie example was expected.\n      at sets cookie for a path (example/test/spec/cookie/default.js:32:8)\n\n🦅  Executed 3 tests: 1 error.\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eBecause we used \u003ccode\u003eerotic\u003c/code\u003e, the test will fail at the line of where the assertion method was called. It is useful to remove too much information in errors stacks, and especially for async assertions, which otherwise would have the stack beginning at \u003ccode\u003e\u0026lt;anonymous\u0026gt;\u003c/code\u003e, and only pointing to the internal lines in the \u003cem\u003eCookiesTester\u003c/em\u003e, but not the test suite.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/15.svg?sanitize=true\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n## CookiesContext\n\nThe _CookiesContext_ provides assertion methods on the `set-cookie` header returned by the server. It allows to check how many times cookies were set as well as what attributes and values they had.\n\n- `count(number)`: Assert on the number of times the cookie was set.\n- `name(string)`: Assert on the presence of a cookie with the given name. Same as `.assert('set-cookie', /name/)`.\n- `value(name, value)`: Asserts on the value of the cookie.\n- `attribute(name, attrib)`: Asserts on the presence of an attribute in the cookie.\n- `attributeAndValue(name, attrib, value)`: Asserts on the value of the cookie's attribute.\n- `noAttribute(name, attrib)`: Asserts on the absence of an attribute in the cookie.\n\nThe context was adapted from the work in https://github.com/pillarjs/cookies. See how [the tests are implemented](https://github.com/idiocc/cookies/blob/master/test/spec/set.js) for more info.\n\n**Examples:**\n\n1. [Testing Session Middleware](https://github.com/idiocc/session/blob/master/test/spec/cookie.js#L131).\n    ```js\n    async 'sets the cookie again after a change'({ app, startApp }) {\n      app.use((ctx) =\u003e {\n        if (ctx.path == '/set') {\n          ctx.session.message = 'hello'\n          ctx.status = 204\n        } else {\n          ctx.body = ctx.session.message\n          ctx.session.money = '$$$'\n        }\n      })\n      await startApp()\n        .get('/set').assert(204)\n        .count(2)\n        .get('/').assert(200, 'hello')\n        .name('koa:sess')\n        .count(2)\n    },\n    ```\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/16.svg?sanitize=true\"\u003e\n\u003c/a\u003e\u003c/p\u003e\n\n## Copyright\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003e\n      \u003ca href=\"https://artd.eco\"\u003e\n        \u003cimg width=\"100\" src=\"https://raw.githubusercontent.com/wrote/wrote/master/images/artdeco.png\"\n          alt=\"Art Deco\"\u003e\n      \u003c/a\u003e\n    \u003c/th\u003e\n    \u003cth\u003e© \u003ca href=\"https://artd.eco\"\u003eArt Deco\u003c/a\u003e for \u003ca href=\"https://idio.cc\"\u003eIdio\u003c/a\u003e 2019\u003c/th\u003e\n    \u003cth\u003e\n      \u003ca href=\"https://idio.cc\"\u003e\n        \u003cimg src=\"https://avatars3.githubusercontent.com/u/40834161?s=100\" width=\"100\" alt=\"Idio\"\u003e\n      \u003c/a\u003e\n    \u003c/th\u003e\n    \u003cth\u003e\n      \u003ca href=\"https://www.technation.sucks\" title=\"Tech Nation Visa\"\u003e\n        \u003cimg width=\"100\" src=\"https://raw.githubusercontent.com/idiocc/cookies/master/wiki/arch4.jpg\"\n          alt=\"Tech Nation Visa\"\u003e\n      \u003c/a\u003e\n    \u003c/th\u003e\n    \u003cth\u003e\u003ca href=\"https://www.technation.sucks\"\u003eTech Nation Visa Sucks\u003c/a\u003e\u003c/th\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\n  \u003cimg src=\"/.documentary/section-breaks/-1.svg?sanitize=true\"\u003e\n\u003c/a\u003e\u003c/p\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fidiocc%2Fhttp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fidiocc%2Fhttp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fidiocc%2Fhttp/lists"}