{"id":13518980,"url":"https://github.com/pretenderjs/pretender","last_synced_at":"2025-05-13T23:10:33.317Z","repository":{"id":15993511,"uuid":"18736715","full_name":"pretenderjs/pretender","owner":"pretenderjs","description":"A mock server library with a nice routing DSL","archived":false,"fork":false,"pushed_at":"2024-01-29T03:56:02.000Z","size":1261,"stargazers_count":1260,"open_issues_count":61,"forks_count":158,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-05-10T07:28:31.052Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pretenderjs.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-04-13T18:41:21.000Z","updated_at":"2025-05-04T13:13:24.000Z","dependencies_parsed_at":"2024-06-02T06:48:39.422Z","dependency_job_id":"7d338b05-8b61-47a4-b1bb-a5f627ced2c3","html_url":"https://github.com/pretenderjs/pretender","commit_stats":{"total_commits":308,"total_committers":73,"mean_commits":4.219178082191781,"dds":0.8701298701298701,"last_synced_commit":"65fbf0a608f2e4b4fd79657a77a41f52077074a9"},"previous_names":["trek/pretender"],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pretenderjs%2Fpretender","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pretenderjs%2Fpretender/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pretenderjs%2Fpretender/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pretenderjs%2Fpretender/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pretenderjs","download_url":"https://codeload.github.com/pretenderjs/pretender/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254042317,"owners_count":22004901,"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-08-01T05:01:51.584Z","updated_at":"2025-05-13T23:10:28.287Z","avatar_url":"https://github.com/pretenderjs.png","language":"JavaScript","readme":"# Pretender\n\n[![npm version](https://badge.fury.io/js/pretender.svg)](https://badge.fury.io/js/pretender)\n[![Build Status](https://travis-ci.org/pretenderjs/pretender.svg)](https://travis-ci.org/pretenderjs/pretender)\n[![Coverage Status](https://coveralls.io/repos/pretenderjs/pretender/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/pretenderjs/pretender?branch=master)\n[![Dependency Status](https://david-dm.org/pretenderjs/pretender.svg)](https://david-dm.org/pretenderjs/pretender)\n[![devDependency Status](https://david-dm.org/pretenderjs/pretender/dev-status.svg)](https://david-dm.org/pretenderjs/pretender#info=devDependencies)\n[![Code Climate](https://codeclimate.com/github/pretenderjs/pretender/badges/gpa.svg)](https://codeclimate.com/github/pretenderjs/pretender)\n\nPretender is a mock server library for XMLHttpRequest and Fetch, that comes\nwith an express/sinatra style syntax for defining routes and their handlers.\n\nPretender will temporarily replace native XMLHttpRequest and Fetch , intercept\nall requests, and direct them to little pretend service you've defined.\n\n**:warning: Pretender only works in the browser!**\n\n```javascript\nconst PHOTOS = {\n  \"10\": {\n    id: 10,\n    src: 'http://media.giphy.com/media/UdqUo8xvEcvgA/giphy.gif'\n  },\n  \"42\": {\n    id: 42,\n    src: 'http://media0.giphy.com/media/Ko2pyD26RdYRi/giphy.gif'\n  }\n};\n\nconst server = new Pretender(function() {\n  this.get('/photos', request =\u003e {\n    let all =  JSON.stringify(Object.keys(PHOTOS).map(k =\u003e PHOTOS[k]));\n    return [200, {\"Content-Type\": \"application/json\"}, all]\n  });\n\n  this.get('/photos/:id', request =\u003e {\n    return [200, {\"Content-Type\": \"application/json\"}, JSON.stringify(PHOTOS[request.params.id])]\n  });\n});\n\n$.get('/photos/12', {success() =\u003e { ... }})\n```\n\n## Usage\n\n```\nyarn add -D pretender\n# or\nnpm install --save-dev pretender\n```\n\nYou can load Pretender directly in the browser.\n\n```javascript\n\u003cscript src=\"node_modules/pretender/dist/pretender.bundle.js\"\u003e\u003c/script\u003e\n```\n\nOr as a module:\n\n```javascript\nimport Pretender from 'pretender';\nconst server = new Pretender(function() {});\n```\n\n## The Server DSL\nThe server DSL is inspired by express/sinatra. Pass a function to the Pretender constructor\nthat will be invoked with the Pretender instance as its context. Available methods are\n`get`, `put`, `post`, `delete`, `patch`, and `head`. Each of these methods takes a path pattern,\na callback, and an optional [timing parameter](#timing-parameter). The callback will be invoked with a\nsingle argument (the XMLHttpRequest instance that triggered this request) and must return an array\ncontaining the HTTP status code, headers object, and body as a string.\n\n```javascript\nconst server = new Pretender(function() {\n  this.put('/api/songs/99', request =\u003e [404, {}, \"\"]);\n});\n```\n\na Pretender constructor can take multiple maps:\n\n```javascript\nimport adminMaps from \"testing/maps/admin\";\nimport photoMaps from \"testing/maps/photos\";\n\nconst server = new Pretender(photoMaps, adminMaps);\n```\n\n```javascript\n// testing/maps/photos\n\nconst PHOTOS = {\n  \"58\": {\n    id: 58,\n    src: 'https://media.giphy.com/media/65TpAhHZ7A9nuf3GIu/giphy.gif'\n  },\n  \"99\": {\n    id: 99,\n    src: 'https://media.giphy.com/media/4Zd5qAcFv759xnegdo/giphy.gif'\n  }\n};\n\nexport default function() {\n  this.get('/photos/:id', () =\u003e \n   [200, {\"Content-Type\": \"application/json\"}, JSON.stringify(PHOTOS[request.params.id])]\n  );\n}\n```\n\nThe HTTP verb methods can also be called on an instance individually:\n\n```javascript\nconst server = new Pretender();\nserver.put('/api/songs/99', request =\u003e [404, {}, \"\"]);\n```\n\n### Paths\nPaths can either be hard-coded (`this.get('/api/songs/12')`) or contain dynamic segments\n(`this.get('/api/songs/:song_id'`). If there were dynamic segments of the path,\nthese will be attached to the request object as a `params` property with keys matching\nthe dynamic portion and values with the matching value from the path.\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/api/songs/:song_id', request =\u003e request.params.song_id);\n});\n\n$.get('/api/songs/871') // params.song_id will be '871'\n\n```\n\n### Query Parameters\nIf there were query parameters in the request, these will be attached to the request object as a `queryParams`\nproperty.\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/api/songs', request =\u003e request.queryParams.sortOrder);\n});\n\n// typical jQuery-style uses you've probably seen.\n// queryParams.sortOrder will be 'asc' for both styles.\n$.get({url: '/api/songs', data: { sortOrder: 'asc' }});\n$.get('/api/songs?sortOrder=asc');\n\n```\n\n\n### Responding\nYou must return an array from this handler that includes the HTTP status code, an object literal\nof response headers, and a string body.\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/api/songs', request =\u003e {\n    return [\n      200,\n      {'content-type': 'application/javascript'},\n      '[{\"id\": 12}, {\"id\": 14}]'\n    ];\n  });\n});\n```\n\nOr, optionally, return a Promise.\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/api/songs', request =\u003e {\n    return new Promise(resolve =\u003e {\n      let response = [\n        200,\n        {'content-type': 'application/javascript'},\n        '[{\"id\": 12}, {\"id\": 14}]'\n      ];\n\n      resolve(response);\n    });\n  });\n});\n```\n\n### Pass-Through\nYou can specify paths that should be ignored by pretender and made as real XHR requests.\nEnable these by specifying pass-through routes with `pretender.passthrough`:\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/photos/:id', this.passthrough);\n});\n```\n\nIn some cases, you will need to force pretender to passthough, just start your server with the `forcePassthrough` option.\n\n```javascript\nconst server = new Pretender({ forcePassthrough: true })\n```\n\nOther times, you may want to decide whether or not to passthrough when the call is made. In that\ncase you can use the `.passthrough()` function on the fake request itself. (The [`unhandledRequest`\nproperty is discussed below](#unhandled-requests).)\n\n```javascript\nserver.unhandledRequest = function(verb, path, request) {\n  if (myIgnoreRequestChecker(path)) {\n    console.warn(`Ignoring request) ${verb.toUpperCase()} : ${path}`);\n  } else {\n    console.warn(\n      `Unhandled ${verb.toUpperCase()} : ${path} \u003e\u003e Passing along. See eventual response below.`\n    )\n  \n    const xhr = request.passthrough(); // \u003c-- A native, sent xhr is returned\n  \n    xhr.onloadend = (ev) =\u003e {\n        console.warn(`Response for ${path}`, {\n          verb,\n          path,\n          request,\n          responseEvent: ev,\n        })\n      };\n  }\n};\n```\n\nThe `.passthrough()` function will immediately create, send, and return a native `XMLHttpRequest`.\n\n### Timing Parameter\nThe timing parameter is used to control when a request responds. By default, a request responds\nasynchronously on the next frame of the browser's event loop. A request can also be configured to respond\nsynchronously, after a defined amount of time, or never (i.e., it needs to be manually resolved).\n\n**Default**\n```javascript\nconst server = new Pretender(function() {\n  // songHandler will execute the frame after receiving a request (async)\n  this.get('/api/songs', songHandler);\n});\n```\n\n**Synchronous**\n```javascript\nconst server = new Pretender(function() {\n  // songHandler will execute immediately after receiving a request (sync)\n  this.get('/api/songs', songHandler, false);\n});\n```\n\n**Delay**\n```javascript\nconst server = new Pretender(function() {\n  // songHandler will execute two seconds after receiving a request (async)\n  this.get('/api/songs', songHandler, 2000);\n});\n```\n\n**Manual**\n```javascript\nconst server = new Pretender(function() {\n  // songHandler will only execute once you manually resolve the request\n  this.get('/api/songs', songHandler, true);\n});\n\n// resolve a request like this\nserver.resolve(theXMLHttpRequestThatRequestedTheSongsRoute);\n```\n\n#### Using functions for the timing parameter\nYou may want the timing behavior of a response to change from request to request. This can be\ndone by providing a function as the timing parameter.\n\n```javascript\nconst externalState = 'idle';\n\nfunction throttler() {\n  if (externalState === 'OH NO DDOS ATTACK') {\n    return 15000;\n  }\n}\n\nconst server = new Pretender(function() {\n  // songHandler will only execute based on the result of throttler\n  this.get('/api/songs', songHandler, throttler);\n});\n```\n\nNow whenever the songs route is requested, its timing behavior will be determined by the result\nof the call to `throttler`. When `externalState` is idle, `throttler` returns `undefined`, which\nmeans the route will use the default behavior.\n\nWhen the time is right, you can set `externalState` to `\"OH NO DOS ATTACK\"` which will make all\nfuture requests take 15 seconds to respond.\n\n#### Scheduling ProgressEvent\nIf the timing parameter is resolved as async, then a [`ProgressEvent`](https://xhr.spec.whatwg.org/#interface-progressevent)\nwill be scheduled every 50ms until the request has a response or is aborted.\n\nTo listen to the progress, you can define `onprogress` on the `XMLHttpRequest` object or\nits [`upload` attribute](https://xhr.spec.whatwg.org/#the-upload-attribute).\n\n```javascript\nlet xhr = new window.XMLHttpRequest();\nxhr.open('POST', '/uploads');\n// https://fetch.spec.whatwg.org/#concept-request-body\n// https://xhr.spec.whatwg.org/#the-send()-method\nlet postBody = new ArrayBuffer(8);\nxhr.upload.onprogress = function(event) {\n  // event.lengthComputable === true\n  // event.total === 8\n  // event.loaded will be incremented every ~50ms\n};\nxhr.onprogress = function(event) {\n  // xhr onprogress will also be triggered\n};\nxhr.send(postBody);\n```\n\n## Sharing routes\nYou can call `map` multiple times on a Pretender instance. This is a great way to share and reuse\nsets of routes between tests:\n\n```javascript\nexport function authenticationRoutes() {\n  this.post('/authenticate',() =\u003e { ... });\n  this.post('/signout', () =\u003e { ... });\n}\n\nexport function songsRoutes() {\n  this.get('/api/songs',() =\u003e { ... });\n}\n```\n\n\n```javascript\n// a test\n\nimport {authenticationRoutes, songsRoutes} from \"../shared/routes\";\nimport Pretender from \"pretender\";\n\nlet p = new Pretender();\np.map(authenticationRoutes);\np.map(songsRoutes);\n```\n\n## Hooks\n### Handled Requests\nIn addition to responding to the request, your server will call a `handledRequest` method with\nthe HTTP `verb`, `path`, and original `request`. By default this method does nothing. You can\noverride this method to supply your own behavior like logging or test framework integration:\n\n```javascript\nconst server = new Pretender(function() {\n  this.put('/api/songs/:song_id', request =\u003e {\n    return [202, {\"Content-Type\": \"application/json\"}, \"{}\"]\n  });\n});\n\nserver.handledRequest = function(verb, path, request) {\n  console.log(\"a request was responded to\");\n}\n\n$.getJSON(\"/api/songs/12\");\n```\n\n### Unhandled Requests\nYour server will call a `unhandledRequest` method with the HTTP `verb`, `path`, and original `request`,\nobject if your server receives a request for a route that doesn't have a handler. By default, this method\nwill throw an error. You can override this method to supply your own behavior:\n\n```javascript\nconst server = new Pretender(function() {\n  // no routes\n});\n\nserver.unhandledRequest = function(verb, path, request) {\n  console.log(\"what is this I don't even...\");\n}\n\n$.getJSON(\"/these/arent/the/droids\");\n```\n\n### Pass-through Requests\nRequests set to be handled by pass-through will trigger the `passthroughRequest` hook:\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/some/path', this.passthrough);\n});\n\nserver.passthroughRequest = function(verb, path, request) {\n  console.log('request ' + path + ' successfully sent for passthrough');\n}\n```\n\n\n### Error Requests\nYour server will call a `erroredRequest` method with the HTTP `verb`, `path`, original `request`,\nand the original `error` object if your handler code causes an error.\n\nBy default, this will augment the error message with some information about which handler caused\nthe error and then throw the error again. You can override this method to supply your own behavior:\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/api/songs', request =\u003e {\n    undefinedWAT(\"this is no function!\");\n  });\n});\n\nserver.erroredRequest = function(verb, path, request, error) {\n  SomeTestFramework.failTest();\n  console.warn(\"There was an error\", error);\n}\n```\n\n### Mutating the body\nPretender is response format neutral, so you normally need to supply a string body as the\nthird part of a response:\n\n```javascript\nthis.get('/api/songs', request =\u003e {\n  return [200, {}, \"{'id': 12}\"];\n});\n```\n\nThis can become tiresome if you know, for example, that all your responses are\ngoing to be JSON. The body of a response will be passed through a\n`prepareBody` hook before being passed to the fake response object.\n`prepareBody` defaults to an empty function, but can be overridden:\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/api/songs', request =\u003e {\n    return [200, {}, {id: 12}];\n  });\n});\n\nserver.prepareBody = function(body){\n  return body ? JSON.stringify(body) : '{\"error\": \"not found\"}';\n}\n```\n\n### Mutating the headers\nResponse headers can be mutated for the entire service instance by implementing a\n`prepareHeaders` method:\n\n```javascript\nconst server = new Pretender(function() {\n  this.get('/api/songs', request =\u003e {\n    return [200, {}, '{\"id\": 12}'];\n  });\n});\n\nserver.prepareHeaders = function(headers){\n  headers['content-type'] = 'application/javascript';\n  return headers;\n};\n```\n\n## Tracking Requests\nYour pretender instance will track handlers and requests on a few array properties.\nAll handlers are stored on `handlers` property and incoming requests will be tracked in one of\nthree properties: `handledRequests`, `unhandledRequests` and `passthroughRequests`. The handler is also returned from\nany verb function. This is useful if you want to build testing infrastructure on top of\npretender and need to fail tests that have handlers without requests.\nYou can disable tracking requests by passing `trackRequests: false` to pretender options.\n```javascript\nconst server = new Pretender({ trackRequests: false });\n```\n\nEach handler keeps a count of the number of requests is successfully served.\n\n```javascript\nserver.get(/* ... */);\nconst handler = server.handlers[0];\n\n// or\n\nconst handler = server.get(/* ... */);\n\n// then\n\nconst numberOfCalls = handler.numberOfCalls;\n```\n\n## Clean up\nWhen you're done mocking, be sure to call `shutdown()` to restore the native XMLHttpRequest object:\n\n```javascript\nconst server = new Pretender(function() {\n ... routing ...\n});\n\nserver.shutdown(); // all done.\n```\n\n# Development of Pretender\n\n## Running tests\n\n* `yarn build` builds pretender\n* `yarn test` runs tests once\n* `yarn test:server` runs and reruns on changes\n\n## Code of Conduct\n\nIn order to have a more open and welcoming community this project adheres to a [code of conduct](CONDUCT.md) adapted from the [contributor covenant](http://contributor-covenant.org/).\n\nPlease adhere to this code of conduct in any interactions you have with this\nproject's community. If you encounter someone violating these terms, please let\na maintainer (@trek) know and we will address it as soon as possible.\n","funding_links":[],"categories":["JavaScript","Repository"],"sub_categories":["Testing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpretenderjs%2Fpretender","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpretenderjs%2Fpretender","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpretenderjs%2Fpretender/lists"}