{"id":14384847,"url":"https://github.com/bahmutov/snap-shot","last_synced_at":"2025-04-07T17:12:37.486Z","repository":{"id":57364544,"uuid":"80491340","full_name":"bahmutov/snap-shot","owner":"bahmutov","description":"Jest-like snapshot feature for the rest of us, works magically by finding the right caller function","archived":false,"fork":false,"pushed_at":"2021-01-31T17:43:31.000Z","size":205,"stargazers_count":170,"open_issues_count":18,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-31T16:17:59.346Z","etag":null,"topics":["jest","jsx","mocha","react","snapshot","test","unit-testing","vue"],"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/bahmutov.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":"2017-01-31T04:34:29.000Z","updated_at":"2025-03-23T03:25:23.000Z","dependencies_parsed_at":"2022-09-13T21:11:21.734Z","dependency_job_id":null,"html_url":"https://github.com/bahmutov/snap-shot","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bahmutov%2Fsnap-shot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bahmutov%2Fsnap-shot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bahmutov%2Fsnap-shot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bahmutov%2Fsnap-shot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bahmutov","download_url":"https://codeload.github.com/bahmutov/snap-shot/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247694877,"owners_count":20980733,"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":["jest","jsx","mocha","react","snapshot","test","unit-testing","vue"],"created_at":"2024-08-28T18:01:43.109Z","updated_at":"2025-04-07T17:12:37.464Z","avatar_url":"https://github.com/bahmutov.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# snap-shot\n\n\u003e Jest-like snapshot feature for the rest of us + data-driven testing!\n\n**deprecated** please use [snap-shot-it][snap-shot-it] - it grabs test instance\nat runtime, avoiding looking at the source code and getting lost in the transpiled code.\n\n[snap-shot-it]: https://github.com/bahmutov/snap-shot-it\n\n[![NPM][npm-icon] ][npm-url]\n\n[![Build status][ci-image] ][ci-url]\n[![semantic-release][semantic-image] ][semantic-url]\n[![js-standard-style][standard-image]][standard-url]\n\n[![status][link1]][url1] [snap-shot-jest-test](https://github.com/bahmutov/snap-shot-jest-test) - for testing `snap-shot` against Jest runner (React + JSX)\n\n[![status][link2]][url2] [snap-shot-modules-test](https://github.com/bahmutov/snap-shot-modules-test) - for testing against transpiled ES6 modules\n\n[![status][link3]][url3] [snap-shot-vue-test](https://github.com/bahmutov/snap-shot-vue-test) - for testing `snap-shot` inside Jest + Vue.js project\n\n[![status][link4]][url4] [snap-shot-jsdom-test](https://github.com/bahmutov/snap-shot-jsdom-test) - for testing `snap-shot` using mock DOM adapter and element serialization\n\n[![status][link5]][url5] [snap-shot-ava-test](https://github.com/bahmutov/snap-shot-ava-test) - for testing how `snap-shot` works with [Ava][ava] test framework\n\n[link1]: https://travis-ci.org/bahmutov/snap-shot-jest-test.svg?branch=master\n[url1]: https://travis-ci.org/bahmutov/snap-shot-jest-test\n[link2]: https://travis-ci.org/bahmutov/snap-shot-modules-test.svg?branch=master\n[url2]: https://travis-ci.org/bahmutov/snap-shot-modules-test\n[link3]: https://travis-ci.org/bahmutov/snap-shot-vue-test.svg?branch=master\n[url3]: https://travis-ci.org/bahmutov/snap-shot-vue-test\n[link4]: https://travis-ci.org/bahmutov/snap-shot-jsdom-test.svg?branch=master\n[url4]: https://travis-ci.org/bahmutov/snap-shot-jsdom-test\n[link5]: https://travis-ci.org/bahmutov/snap-shot-ava-test.svg?branch=master\n[url5]: https://travis-ci.org/bahmutov/snap-shot-ava-test\n[ava]: https://github.com/avajs/ava\n\n## Why\n\nRead [Snapshot testing the hard way](https://glebbahmutov.com/blog/snapshot-testing/)\n\nI like [Jest snapshot](https://facebook.github.io/jest/blog/2016/07/27/jest-14.html)\nidea and want it without the rest of Jest testing framework. This module is\nJUST a single assertion method to be used in BDD frameworks (Mocha, Jasmine)\n\nAlso, I really really really wanted to keep API as simple and as \"smart\"\nas possible. Thus `snap-shot` tries to find the surrounding unit test name\nby inspecting its call site\n(using [stack-sites](https://github.com/bahmutov/stack-sites) or\n[callsites](https://github.com/sindresorhus/callsites#readme))\nand parsing AST of the spec file\n(using [falafel](https://github.com/substack/node-falafel#readme)).\n\nSnapshot values are compared using\n[variable-diff](https://github.com/taylorhakes/variable-diff) (objects)\nand [disparity](https://github.com/millermedeiros/disparity)\n(multi line strings). See [images/README.md](images/README.md) for screenshots.\n\nThis function also includes [data-driven][data-driven] testing mode,\nsimilar to [sazerac][sazerac], see [Data-driven testing](#data-driven-testing)\nsection below.\n\n[data-driven]: https://hackernoon.com/sazerac-data-driven-testing-for-javascript-e3408ac29d8c#.9s4ikt67d\n[sazerac]: https://github.com/mikec/sazerac\n\n## Example\n\nInstall: `npm install --save-dev snap-shot`\n\n```js\nconst snapshot = require('snap-shot')\n// in ES6 code use\nimport snapshot from 'snap-shot'\nit('is 42', () =\u003e {\n  snapshot(42)\n})\n```\n\nRun it first time with `mocha spec.js`.\nThis will create snapshots JSON values file inside\n`__snapshots__/spec.js.snap-shot`.\nIn general this file should close to Jest format, but the file extension is\ndifferent to avoid clashing with Jest persistence logic.\n\n```sh\n$ mocha spec.js\n$ cat __snapshots__/spec.js.snap-shot\nmodule.exports[`is 42 1`] = 42\n```\n\nNow modify the `spec.js` file\n\n```js\nconst snapshot = require('snap-shot')\nit('is 42', () =\u003e {\n  snapshot(80)\n})\n```\n\n```sh\n$ mocha spec.js\n1) is 42:\n    Error: expected 42 got 80\n```\n\n**Note** `snap-shot` does not store or handle `undefined` values, since they\nare likely an edge case. If you disagree, open\n[an issue](https://github.com/bahmutov/snap-shot/issues) please.\n\n**Note** All values are saved as JSON, thus non-serializable values, like\nfunctions are stripped before comparison.\n\n## Promises\n\nFor asynchronous code, please have a function inside the spec before\ncalling `snap-shot` or let `snap-shot` wrap the promise.\n\n```js\nit('promise to function', () =\u003e {\n  return Promise.resolve(20)\n    .then(data =\u003e snapshot(data))\n})\n\nit('snap-shot can wrap promise', () =\u003e {\n  return snapshot(Promise.resolve('value'))\n})\n\n// does NOT work\nit('promise to snapshot', () =\u003e {\n  return Promise.resolve(20)\n    .then(snapshot)\n})\n```\n\nIn the last test, the stack trace from `snap-shot` cannot get any parent\ninformation, thus it cannot find the unit test.\n\nSee [src/async-spec.js](src/async-spec.js)\n\n## Jest\n\nYou can use this assertion inside Jest tests too.\n\n```js\nconst snapshot = require('snap-shot')\ntest('my test', () =\u003e {\n  snapshot(myValue)\n})\n```\n\n## Ava\n\nShould work (including `async / await` syntax), see\n[snap-shot-ava-test](https://github.com/bahmutov/snap-shot-ava-test)\nfor the current status.\n\n```js\nimport test from 'ava'\nimport snapshot from 'snap-shot'\ntest('concat strings', t =\u003e {\n  snapshot('f' + 'oo')\n})\n```\n\n## DOM testing (via jsdom and [jsdom-global][jsdom-global])\n\nYou can easily mock DOM and use snapshots (either in text or JSON format),\nsee [snap-shot-jsdom-test](https://github.com/bahmutov/snap-shot-jsdom-test)\nproject.\n\n[jsdom-global]: https://github.com/rstacruz/jsdom-global\n\n## React + JSX\n\nIf `snap-shot` finds `.babelrc` inside the current working folder, it will\ntry transpiling loaded files using\n[babel-core](https://babeljs.io/docs/usage/api/) API. This makes it useful\nfor testing React code. For full example see\n[Link.test.js](https://github.com/bahmutov/snap-shot-jest-test/blob/master/Link.test.js)\n\n## Showing snapshots when saving\n\nIf you just want to see what a new schema would be, without saving it,\nrun the tests with `DRY=1 npm test` option.\n\nIf you want to see the schema and save it, run the tests with `SHOW=1 npm test`\n\n```\n$ SHOW=1 npm test\nsaving snapshot \"spec name\" for file ./src/valid-message-spec.js\n{ firstLine: 'break(log): new log format',\n  type: 'major',\n  scope: 'log',\n  subject: 'new log format'\n}\n```\n\n## Update snapshots\n\nTo update all saved values, run with `UPDATE=1` environment variable.\n\n```sh\n$ UPDATE=1 mocha spec.js\n```\n\nTo update snapshot inside a single test function, use second argument\n\n```js\nconst snapshot = require('snap-shot')\nit('is 42', () =\u003e {\n  snapshot(80, true)\n})\n// snapshot file now has {\"is 42\": 80)\n```\n\nYou can also update a single or several tests when running Mocha by filtering\nthe tests using [grep feature](http://mochajs.org/#g---grep-pattern).\n\n```sh\n$ UPDATE=1 mocha -g \"test name pattern\" *-spec.js\n```\n\n## CI\n\n**Note** most CIs (like Travis, Circle, GitLabCI) define environment variable `CI`, which\nwe take into consideration as well. It makes no sense to allow saving snapshot on CI, thus if\nthe `process.env.CI` is set, the snapshot MUST exist on disk.\n\n## Format\n\nThere is no magic in formatting snapshots. Just use any function or compose\nwith `snapshot` before comparing. Both choices work\n\n```js\nconst snapshot = require('snap-shot')\nit('compares just keys', () =\u003e {\n  const o = {\n    foo: Math.random(),\n    bar: Math.random()\n  }\n  snapshot(Object.keys(o))\n})\n// snapshot will be something like\n/*\nexports['compares just keys 1'] = [\n  \"foo\",\n  \"bar\"\n]\n*/\n\nconst compose = (f, g) =\u003e x =\u003e f(g(x))\nconst upperCase = x =\u003e x.toUpperCase()\nconst upValue = compose(snapshot, upperCase)\n\nit('compares upper case string', () =\u003e {\n  upValue('foo')\n})\n/*\nexports['compares upper case string 1'] = \"FOO\"\n*/\n```\n\nSee [src/format-spec.js](src/format-spec.js)\n\n### General advice\n\nSimple is better. Format the data and then call snapshot.\n\n```js\nit('works', () =\u003e {\n  const domNode = ...\n  const structure = formatHtml(domNode)\n  snapshot(structure)\n})\n```\n\n## Tests with dynamic names\n\nSometimes tests are generated dynamically without hardcoded names. In this\ncase SHA256 of the test callback function is used to find its value.\n\n```js\n// this still works\nconst testName = 'variable test name (value 30)'\nconst value = 30\nit(testName, () =\u003e {\n  // this is a test without hard coded name\n  snapshot(value)\n})\n// snapshot file will have something like\n// exports['465fb... 1'] = 30\n```\n\nThe best strategy in this case is to use meaningful name for the callback\nfunction\n\n```js\nconst testName = 'variable test name (value 30)'\nconst value = 30\nit(testName, function is30() {\n  snapshot(value)\n})\n// snapshot file will have something like\n// exports['is30 1'] = 30\n```\n\nSee [src/unknown-name-spec.js](src/unknown-name-spec.js)\n\n## Multiple snapshots\n\nA single test can have multiple snapshots.\n\n```js\nit('handles multiple snapshots', () =\u003e {\n  snapshot(1)\n  snapshot(2)\n})\nit('uses counter of snapshot calls', () =\u003e {\n  for (let k = 0; k \u003c 10; k += 1) {\n    snapshot(`snap ${k}`)\n  }\n})\n```\n\nSee [src/multiple-spec.js](src/multiple-spec.js)\n\n## Data-driven testing\n\nWriting multiple input / output pairs for a function under test quickly\nbecomes tedious. Luckily, you can test a function by providing multiple\ninputs and a single snapshot of function's *behavior* will be saved.\n\n```js\n// checks if n is prime\nconst isPrime = n =\u003e ...\nit('tests prime', () =\u003e {\n  snapshot(isPrime, 1, 2, 3, 4, 5, 6, 7, 8, 9)\n})\n```\n\nThe saved snapshot file will have clear mapping between given input and\nproduced result\n\n```js\n// snapshot file\nexports['tests prime 1'] = {\n  \"name\": \"isPrime\",\n  \"behavior\": [\n    {\n      \"given\": 1,\n      \"expect\": false\n    },\n    {\n      \"given\": 2,\n      \"expect\": true\n    },\n    {\n      \"given\": 3,\n      \"expect\": true\n    },\n    {\n      \"given\": 4,\n      \"expect\": false\n    },\n    {\n      \"given\": 5,\n      \"expect\": true\n    },\n    ...\n  ]\n}\n```\n\nYou can also test functions that expect multiple arguments by providing\narrays of inputs.\n\n```js\nconst add = (a, b) =\u003e a + b\nit('checks behavior of binary function add', () =\u003e {\n  snapshot(add, [1, 2], [2, 2], [-5, 5], [10, 11])\n})\n```\n\nAgain, the snapshot file gives clear picture of the `add` behavior\n\n```js\n// snapshot file\nexports['checks behavior of binary function add 1'] = {\n  \"name\": \"add\",\n  \"behavior\": [\n    {\n      \"given\": [\n        1,\n        2\n      ],\n      \"expect\": 3\n    },\n    {\n      \"given\": [\n        2,\n        2\n      ],\n      \"expect\": 4\n    },\n    {\n      \"given\": [\n        -5,\n        5\n      ],\n      \"expect\": 0\n    },\n    {\n      \"given\": [\n        10,\n        11\n      ],\n      \"expect\": 21\n    }\n  ]\n}\n```\n\nSee [src/data-driven-spec.js](src/data-driven-spec.js) for more examples.\n\n## Debugging\n\nRun with `DEBUG=snap-shot` environment variable\n\n```sh\n$ DEBUG=snap-shot mocha spec.js\n```\n\nIf you want to see messages only when new values are stored use\n\n```\n$ DEBUG=save mocha spec.js\nsave Saved for \"is 42 1\" snapshot 42\n```\n\nThere are special projects that are setup to test this code in isolation\nas dependent projects, see above.\n\n## Related\n\n* [chai-jest-snapshot](https://github.com/suchipi/chai-jest-snapshot) if\n  you are using [Chai](http://chaijs.com/)\n* [schema-shot](https://github.com/bahmutov/schema-shot) - it is like snapshot\n  testing but for dynamic data\n  ([explanation](https://glebbahmutov.com/blog/schema-shot/))\n\n### Small print\n\nAuthor: Gleb Bahmutov \u0026lt;gleb.bahmutov@gmail.com\u0026gt; \u0026copy; 2017\n\n* [@bahmutov](https://twitter.com/bahmutov)\n* [glebbahmutov.com](http://glebbahmutov.com)\n* [blog](http://glebbahmutov.com/blog)\n\nLicense: MIT - do anything with the code, but don't blame me if it does not work.\n\nSupport: if you find any problems with this module, email / tweet /\n[open issue](https://github.com/bahmutov/snap-shot/issues) on Github\n\n## MIT License\n\nCopyright (c) 2017 Gleb Bahmutov \u0026lt;gleb.bahmutov@gmail.com\u0026gt;\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\n[npm-icon]: https://nodei.co/npm/snap-shot.svg?downloads=true\n[npm-url]: https://npmjs.org/package/snap-shot\n[ci-image]: https://travis-ci.org/bahmutov/snap-shot.svg?branch=master\n[ci-url]: https://travis-ci.org/bahmutov/snap-shot\n[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg\n[semantic-url]: https://github.com/semantic-release/semantic-release\n[standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg\n[standard-url]: http://standardjs.com/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbahmutov%2Fsnap-shot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbahmutov%2Fsnap-shot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbahmutov%2Fsnap-shot/lists"}