{"id":16528712,"url":"https://github.com/zetlen/jort","last_synced_at":"2025-03-03T07:42:52.970Z","repository":{"id":57281609,"uuid":"42686593","full_name":"zetlen/jort","owner":"zetlen","description":"javascript oversimplified request testing","archived":false,"fork":false,"pushed_at":"2016-01-11T23:36:43.000Z","size":143,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2024-04-29T14:22:36.882Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zetlen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-09-17T23:04:01.000Z","updated_at":"2018-03-12T16:28:28.000Z","dependencies_parsed_at":"2022-09-09T00:22:49.438Z","dependency_job_id":null,"html_url":"https://github.com/zetlen/jort","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zetlen%2Fjort","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zetlen%2Fjort/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zetlen%2Fjort/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zetlen%2Fjort/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zetlen","download_url":"https://codeload.github.com/zetlen/jort/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241629739,"owners_count":19993707,"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","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-10-11T17:41:18.749Z","updated_at":"2025-03-03T07:42:52.923Z","avatar_url":"https://github.com/zetlen.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## javascript oversimplified request testing\n\n\u003ccenter\u003e![jorts in space](http://i.imgur.com/D2gwbHN.png)\u003c/center\u003e\n\n*just the right amount of coverage*\n\n### jort is a tool for unit testing things that make HTTP requests\nTools like [nock](https://github.com/pgte/nock) and [sinon](http://sinonjs.org) make it easy to write rich, complete simulations of a REST API or an HTTP web service, but not easy enough for the likes of us. These tools are more suited to integration testing or end-to-end testing. When you're writing small unit tests, you shouldn't have to spin up a server and declare a route, just to make something that replies to a GET with an [HTTP 418](https://tools.ietf.org/html/rfc2324#section-2.3.2). Even a famously concise tool like Express seems too imperative and wordy for these common cases. Why not slip into something cool, casual and comfortable?\n\n```js\nvar assert  = require('assert');\nvar request = require('request');\nvar jort    = require('jort');\n\njort({\n  totalCount: 2,\n  items: [\"daisy\",\"duke\"]\n}).then(function(twoItems) {\n\n  request(twoItems, function(error, response, json) {\n    assert.equal(json.totalCount, 2);\n    assert.equal(json.items[0], \"daisy\");\n  });\n\n});\n\n```\n\n### jort is aggressively oversimplified\n\nJort has features, but they're hidden from view, in deep, embroidered pockets; the simple version you just saw above assumes a few things.\n\n#### the `jort` function\n\nThe `jort` function, the main interface to jort, takes one argument: an object which will be the response. It must be a plain object, serializable as JSON, because that's what will happen to it.\n```js\nvar coupleNicknames = {\n  nickiAndMeek: [\"Meeki\", \"Neck\"],\n  drakeAndLorde: [\"Drorde\", \"Lake\"]\n};\n\nvar nicknamesResponder = jort(coupleNicknames);\n```\n\nThe function will return a Promise for a string. It'll be something like `http://localhost:8099/`, but you don't have to care. Call that URL once. It will respond  HTTP 200 OK, with appropriate headers for a JSON response, return that response, and then disappear.\n```js\nnicknamesResponder.then(function(nicknamesResponse) {\n  request(nicknamesResponse, function(error, response, json) {\n\n    json.drakeAndLorde.map(console.log);\n    /*\n     *  Drorde\n     *  Lake\n     *\n     */\n\n  });\n});\n```\n\n#### if you want anything different than that\n\nYou can pass two arguments to `jort()`, \u003cs\u003eone for each leg\u003c/s\u003e the first as the payload, similar to above. The second argument is a collection of options. I wonder when we'll find out what they can be!\n\n```js\nvar emptyPocket = jort(\n  /* payload */\n  { \n    errorCode: \"NOT_FOUND\", \n    message: \"The requested document was not found, hoss!\"\n  },\n  /* options */\n  { \n    status: 404,\n    delay: 20000\n  }\n);\n```\n\n#### available options\n\n - `status`: an integer for the HTTP status code to return. Default `200`\n - `headers`: an object of http headers to add or override, e.g. `{ 'Content-Type': 'text/html' }`\n - `delay`: an integer of ms to delay the first byte of the response. For testing timeouts. Default `0`\n - `use`: an array of [connect middleware](https://github.com/senchalabs/connect) to use (or a single function to be used as one middleware)\n - `leaveOpen`: by default, every jort will only serve one request. Set this to `true` to leave the jort running until you turn it off. Of course, you can't turn it off unless you've called `jort.serve` or `jort.serveSteps` to get an actual server handle, so this option is non-functional unless you called one of those methods. Your Jort takes care of you.\n - `ipv6`: by default, jort will use ipv6 where available, so if your system supports ipv6, jort will produce urls that look like `http://[::]:8000/`. If you don't like this (or another library doesn't recognize this as a valid URL), then you can pass an explicit `ipv6:false` to force ipv4-style urls.\n - `port`: by default, jort will find an open, unused port using [portfinder](https://www.npmjs.com/package/portfinder), unless you specify a port with this option; then it will use that port.\n\nIf the first argument is a JS object, Jort will assume it should be serving JSON. If the first argument is a string, Jort will pass the string through unchanged (and you ought to provide a content type header in that case).\n\n#### oh, you want the actual server instance, not just its url?\n\nNot very jorty of you, but you can get this by running `jort.serve` instead of just `jort`. `jort.serve` has the exact same API as `jort`.\n\nThe returned Promise will fulfill an object which has two properties: `url` will be the url to call, just as if you'd called `jort`, and `server` will be the Node [http.Server](https://nodejs.org/api/http.html#http_class_http_server). The server has already been bound to a port, so you don't have to do that manually. It'll also close by itself unless you passed `leaveOpen: true` as an option.\n\n```js\nvar bananaHammock = jort.serve(\"Bananas!\", { headers: { 'Content-Type': 'text/bananas' } });\nbananaHammock.then(function(bananas) { console.log(bananas.url, bananas.server.address()) });\n// http://127.0.0.1:8001\n// { protocol: 'http:', address: '127.0.0.1', port: 8001 }\n```\n\n#### i'm testing something that places several calls in a row. i need to jort them all.\n\nIf it get more complicated than this, you'll want to switch to just making your own express server, but ok, here goes. jort has a method called `steps` that will return a promise for a URL, just like jort, but this URL will respond a little differently. `jort.steps` takes an array of arguments that you might normally send to `jort`. \n\nIt yields a URL that will respond with the payloads of each of these arguments, in order, and different behavior based on the arguments. this must be an *array of arrays*, each sub-array representing the arguments that you might normally send directly to `jort()`. if you don't need a second `options` argument, then it can be an array of payloads; each item in the array will be interpreted as a single payload with no options if it is not an array, and a payload/options tuple if it is an array. Therefore, if you want your response to be a JSON array, it just needs to be an array wrapped in an array. Hey, you asked for a complicated feature in an oversimplified tool!\n\n```js\njort.steps([\n  /* first request, for authentication */\n  [\n    /* payload */\n    {\n      authToken: 'fake-auth-token',\n      issued: (new Date()).toISOString()\n    },\n    /* options for first request */\n    {\n      delay: 1000,\n      headers: {\n        \"X-Custom-Greeting\": \"Cheers!\"\n      }\n    }\n  ],\n  /* second request, for metadata */\n  [\n    {\n      totalCount: 20000,\n      publishState: 'draft'\n    }\n    /* no options */\n  ],\n  /* third request, for data */\n  [\n    /* payload is array */\n    [\n      {\n        firstName: 'Sam',\n        lastName: 'Malone',\n        occupation: 'Barman'\n      },\n      {\n        firstName: 'Diane',\n        lastName: 'Chambers',\n        occupation: 'Student/Barmaid'\n      }\n    ],\n    {\n      delay : 5000\n    }\n  ]\n],\n/* global options that will apply to the server or to all requests*/\n{\n  // `leaveOpen: true` could go here\n}).then(function(url) {\n  // the url will respond three times, each time with the next payload, \n  // and then disappear into the eldritch mist.\n\n  fetch(uri).then(function(res) {\n      assert(res.authToken);\n      return fetch(uri);\n  }).then(function(res) {\n      assert(res.totalCount === 10000);\n      return fetch(uri);\n  }).then(function(res) {\n      assert(res[o].firstName === \"Sam\");\n  });\n\n});\n```\n\nThe `leaveOpen` option will, of course, not work for individual request options, but all other options will. The base options object you supply after the steps array will be applied to every step, but will be overridden by any options present for that step.\n\nYou can call `jort.serveSteps` to get a `steps` implementation that will return a server, instead of just a url. It has the same API as `jort.steps`, of course.\n\n#### but what about about routing? what about responding to GET, PUT, POST, and DELETE?\nThat's probably not what you're testing. Your app knows how to use the right HTTP verbs, I'm sure. Don't sell yourself jort.\n\n#### but my app isn't built in a way where i can change the URLs it calls!\nI agree that this is a problem, but it doesn't seem like a problem I can solve for you.\n\n\u003ccenter\u003e![jorts in repose](http://i.imgur.com/LONbcQT.jpg)\u003c/center\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzetlen%2Fjort","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzetlen%2Fjort","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzetlen%2Fjort/lists"}