{"id":13422592,"url":"https://github.com/bruderstein/unexpected-react","last_synced_at":"2025-04-06T01:11:00.779Z","repository":{"id":53578497,"uuid":"46226079","full_name":"bruderstein/unexpected-react","owner":"bruderstein","description":"Plugin for http://unexpected.js.org to enable testing the full React virtual DOM, and also the shallow renderer","archived":false,"fork":false,"pushed_at":"2021-03-23T11:36:00.000Z","size":1228,"stargazers_count":186,"open_issues_count":23,"forks_count":19,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-03-30T00:09:01.971Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://bruderstein.github.io/unexpected-react","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/bruderstein.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":"2015-11-15T16:38:43.000Z","updated_at":"2024-09-29T10:57:29.000Z","dependencies_parsed_at":"2022-09-05T14:40:33.356Z","dependency_job_id":null,"html_url":"https://github.com/bruderstein/unexpected-react","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bruderstein%2Funexpected-react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bruderstein%2Funexpected-react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bruderstein%2Funexpected-react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bruderstein%2Funexpected-react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bruderstein","download_url":"https://codeload.github.com/bruderstein/unexpected-react/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247419861,"owners_count":20936012,"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-07-30T23:00:48.413Z","updated_at":"2025-04-06T01:11:00.753Z","avatar_url":"https://github.com/bruderstein.png","language":"JavaScript","readme":"[![Build Status](https://travis-ci.org/bruderstein/unexpected-react.svg?branch=master)](https://travis-ci.org/bruderstein/unexpected-react)\n[![Coverage Status](https://coveralls.io/repos/github/bruderstein/unexpected-react/badge.svg?branch=master)](https://coveralls.io/github/bruderstein/unexpected-react?branch=master)\n[![npm version](https://badge.fury.io/js/unexpected-react.svg)](https://badge.fury.io/js/unexpected-react) \n\n# unexpected-react\n\nPlugin for [unexpected](https://unexpected.js.org) to allow for testing the full virtual DOM, and \nagainst the shallow renderer (replaces [unexpected-react-shallow](https://github.com/bruderstein/unexpected-react-shallow))\n\n!![output_demo](https://cloud.githubusercontent.com/assets/91716/11253997/c5d2a9b4-8e3e-11e5-8da5-2df95598865c.png)\n\nSee the blog post for an introduction: https://medium.com/@bruderstein/the-missing-piece-to-the-react-testing-puzzle-c51cd30df7a0\n\n# Documentation\n\nThe full documentation with all the assertions: http://bruderstein.github.io/unexpected-react\n\n# Important note about React v16 support\n\nNote that in a mocha jsdom environment you also need a [polyfill for `requestAnimationFrame`](https://gist.github.com/paulirish/1579671)\n\n# Features\n\n* Assert React component's output using the [shallow renderer](http://facebook.github.io/react/docs/test-utils.html#shallow-rendering)\n* Assert React component's output using the full renderer and JSX \"expected\" values (e.g. `TestUtils.renderIntoDocument()`) \n* Assert React component's output using the test renderer ([react-test-renderer](https://www.npmjs.com/package/react-test-renderer) (require `unexpected-react/test-renderer`)\n* Trigger events on components in shallow, full and test renderers\n* Locate components using JSX queries in shallow, full and test renderers\n* All assertions work identically with the shallow, full and test renderers, allowing you to mix and match in your tests, based on what you need.\n\n# Examples\n\n* Checking a simple render\n\n```js\nvar todoList = TestUtils.renderIntoDocument(\n);\n\nexpect(\n  todoList,\n  'to have rendered', \n  \u003cdiv className='items'\u003e\n    \u003cTodoItem id={1}\u003e\n      \u003cspan className=\"label\"\u003eBuy flowers\u003c/span\u003e\n    \u003c/TodoItem\u003e\n    \u003cTodoItem id={2}\u003e\n      \u003cspan className=\"label\"\u003eMow the lawn\u003c/span\u003e\n    \u003c/TodoItem\u003e\n    \u003cTodoItem id={3}\u003e\n      \u003cspan className=\"label\"\u003eBuy groceries\u003c/span\u003e\n    \u003c/TodoItem\u003e\n  \u003c/div\u003e\n);\n```\n\n* Direct rendering for shallow and deep rendering:\n```js\nexpect(\n  \u003cTodoList items={todoItems} /\u003e,\n  'when rendered',\n  'to have rendered', \n  \u003cdiv className='items'\u003e\n    \u003cTodoItem id={1} label=\"Buy flowers\" /\u003e\n    \u003cTodoItem id={2} label=\"Mow the lawn\" /\u003e\n    \u003cTodoItem id={3} label=\"Buy groceries\" /\u003e\n  \u003c/div\u003e\n);\n```\n\n\n* Triggering an event on a button inside a subcomponent (using the `eventTarget` prop to identify where the event should be triggered)\n\n```js\nexpect(\n  todoList,\n  'with event click',\n  'on', \u003cTodoItem id={2}\u003e\u003cspan className=\"label\" eventTarget /\u003e\u003c/TodoItem\u003e,\n  'to contain',\n  \u003cTodoItem id={2}\u003e\n    \u003cdiv className='completed'\u003e\n      \u003cspan\u003eCompleted!\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/TodoItem\u003e\n);\n```\n\n\n* Locating a component with `queried for` then validating the render\n\n```js\nexpect(\n  todoList,\n  'queried for', \u003cTodoItem id={2} /\u003e,\n  'to have rendered',\n  \u003cTodoItem id={2}\u003e\n    \u003cdiv className='completed'/\u003e\n  \u003c/TodoItem\u003e\n);\n```\n\n\n* Locating a component and then checking the state of the component with the full renderer\n\n```js#async:true\nexpect(todoList,\n  'with event click',\n  'on', \u003cTodoItem id={1}\u003e\u003cspan className=\"label\" eventTarget /\u003e\u003c/TodoItem\u003e,\n  'queried for', \u003cTodoItem id={1} /\u003e\n).then(todoItem =\u003e {\n  // Here we're checking the state, but we could perform\n  // any operation on the instance of the component.\n  expect(todoItem.state, 'to satisfy', { completed: true });\n});\n```\n\n* Calling an event and validating the output using the test renderer\n\n```js#evaluate:false\nconst unexpected = require('unexpected');\nconst React = require('react');\nconst TestRenderer = require('react-test-renderer');\nconst expect = unexpected.clone().use(require('unexpected-react/test-renderer'));\n\ndescribe('ClickCounterButton', function () {\n  \n  it('shows the increased click count after a click event', function () {\n    const renderer = TestRenderer.create(\u003cClickCounterButton /\u003e);\n    expect(renderer, \n        'with event', 'click',\n        'to have rendered',\n        \u003cbutton\u003eClicked {1} time\u003c/button\u003e\n    );\n  });\n});\n```\n\n# Usage\n\n```\nnpm install --save-dev unexpected unexpected-react\n```\n\n## Initialising\n\n### With the shallow renderer\n\n```js#evaluate:false\n\nvar unexpected = require('unexpected');\nvar unexpectedReact = require('unexpected-react');\n\nvar React = require('react');\nvar ReactTestUtils = require('react-dom/test-utils');\n\n// Require the component we want to test\nvar MyComponent = require('../MyComponent');\n\n// Declare our `expect` instance to use unexpected-react\nvar expect = unexpected.clone()\n    .use(unexpectedReact);\n    \ndescribe('MyComponent', function () {\n    it('renders a button', function () {\n        var renderer = ReactTestUtils.createRenderer();\n        renderer.render(\u003cMyComponent /\u003e);\n        expect(renderer, 'to have rendered', \u003cbutton\u003eClick me\u003c/button\u003e);\n    });\n});\n\n```\n\n### With the [test renderer](https://www.npmjs.com/package/react-test-renderer)\n\nIf you want to use the [react-test-renderer](https://www.npmjs.com/package/react-test-renderer), then **`require('unexpected-react/test-renderer')`**\n\n```js#evaluate:false\n\nvar unexpected = require('unexpected');\n\n// Note that for the test-renderer, we need a different `require`\nvar unexpectedReact = require('unexpected-react/test-renderer');\n\nvar React = require('react');\nvar TestRenderer = require('react-test-renderer');\n\nvar MyComponent = require('../MyComponent');\n\n// define our instance of the `expect` function to use unexpected-react\nconst expect = unexpected.clone()\n    .use(unexpectedReact);\n    \n\ndescribe('MyComponent', function () {\n    it('renders a button', function () {\n        var renderer = TestRenderer.create(\u003cMyComponent /\u003e);\n        expect(renderer, 'to have rendered', \u003cbutton\u003eClick me\u003c/button\u003e);\n    });\n});\n```\n\n### With the full virtual DOM (all custom components AND the DOM elements)\n\nIf you want to assert over the whole virtual DOM, then you need to emulate the DOM \n(note this library is not designed for use in the browser - it may be possible, but at the \nvery least, you'll need to disable the react-devtools)\n\nIf you don't need the virtual DOM, and you're just using the [shallow renderer](http://facebook.github.io/react/docs/test-utils.html#shallow-rendering),\nthen the order of the requires is not important, and you obviously don't need the `emulateDom.js` require.\n\nThe **order of `require`'s is important**. `unexpected-react` must be required **before** `react` is required. That means `unexpected-react` must be required\nbefore any other file is required that requires React (e.g. your components!)\n\n(You can also use the shallow renderer interchangeably with this setup)\n\n```js#evaluate:false\n// First require your DOM emulation file (see below)\nrequire( '../testHelpers/emulateDom');\n\nvar unexpected = require('unexpected');\n\n// then require unexpected-react\nvar unexpectedReact = require('unexpected-react');\n\n// then react\nvar React = require('react');\n\n// ...and optionally the addons\nvar TestUtils = require('react-dom/test-utils');\n\n// then our component(s)\nvar MyComponent = require('../MyComponent);\n\n// define our instance of the `expect` function to use unexpected-react\nconst expect = unexpected.clone()\n    .use(unexpectedReact);\n    \ndescribe('MyComponent', function () {\n    it('renders a button', function () {\n        var component = TestUtils.renderIntoDocument(\u003cMyComponent /\u003e);\n\n        // All custom components and DOM elements are included in the tree,\n        // so you can assert to whatever level you wish\n        expect(component, 'to have rendered', \n          \u003cMyComponent\u003e\n            \u003cbutton\u003eClick me\u003c/button\u003e\n          \u003c/MyComponent\u003e);\n    });\n});\n```\n\n## Using with Jest\n\n`unexpected-react` works just the same with [jest](https://facebook.github.io/jest/), complete with snapshot support (and you don't need your own DOM emulation, as jest has that built in).  To use jest with the shallow and full renderers and include snapshot support, simply require `unexpected-react/jest`. Snapshotting the shallow renderer and the full DOM rendering works out of the box, no need to add any extra packages.\n\ne.g.\n\n```js\nconst unexpectedReact = require('unexpected-react/jest');\n\nconst expect = require('unexpected')\n  .clone()\n  .use(unexpectedReact);\n```\n\nThis `expect` will then be used instead of the default one provided by jest.\n\nIf you want to use the test renderer (the same as jest snapshots use), require `unexpected-react/test-renderer-jest`.\n\ne.g.\n\n```js\nconst unexpectedReact = require('unexpected-react/test-renderer-jest');\n\nconst expect = require('unexpected')\n  .clone()\n  .use(unexpectedReact);\n```\n\n## Emulating the DOM\n\nIf you're using [Jest](https://facebook.github.io/jest/), you can skip this part, as it comes with built in jsdom support.\n\nFor React v16, we recommend using [jsdom-global](https://npm.im/jsdom-global) and the [requireAnimationFrame polyfill](https://gist.github.com/paulirish/1579671) from Erik Möller, Paul Irish and Tino Zijdel. For previous versions, you can use the boilerplate presented here.\n\nThe `emulateDom` file depends on whether you want to use [`domino`](https://npmjs.com/package/domino), or [`jsdom`](https://npmjs.com/package/jsdom).  If you're using Jest, jsdom is built in, so you can ignore this section.\n\nFor `jsdom`:\n\n```js#evaluate:false\n// emulateDom.js - jsdom variant\n\nif (typeof document === 'undefined') {\n\n    const jsdom = require('jsdom').jsdom;\n    global.document = jsdom('');\n    global.window = global.document.defaultView;\n\n    for (let key in global.window) {\n        if (!global[key]) {\n            global[key] = global.window[key];\n        }\n    }\n}\n```\n\nFor `domino`:\n\n```js#evaluate:false\n// emulateDom.js - domino variant\n\nif (typeof document === 'undefined') {\n\n    const domino = require('domino');\n    global.window = domino.createWindow('');\n    global.document = global.window.document;\n    global.navigator = { userAgent: 'domino' };\n\n    for (let key in global.window) {\n        if (!global[key]) {\n            global[key] = global.window[key];\n        }\n    }\n}\n```\n\n# React Compatibility\nv5.x.x is compatible with React v16 and up\nv4.x.x is compatible with React v15.5 and up\nv3.x.x is compatible with React v0.14.x and v15. Warning with v15.5, but supported\nv2.x.x is compatible with React v0.13.x and v0.14.x\n\nIt is not planned to make further releases of the v2 and v3 branch, but if you still need 0.13 / 0.14 support,\nand are missing things from v4/5, please raise an issue.\n\n# Tests\n\nFor the shallow renderer, you can assert on the renderer itself (you can also write the same assertion for the result of `getRenderOutput()`)\n\n```js\nvar renderer = TestUtils.createRenderer();\n\nrenderer.render(\u003cMyButton /\u003e);\n\nexpect(renderer, 'to have rendered',\n  \u003cbutton\u003e\n      Button was clicked 1 times\n  \u003c/button\u003e\n);\n```\n\nIf this fails for some reason, you get a nicely formatted error, with the differences highlighted:\n\n```output\nexpected\n\u003cbutton onClick={function bound onClick() { /* native code */ }}\u003e\n  Button was clicked 0 times\n\u003c/button\u003e\nto have rendered \u003cbutton\u003eButton was clicked 1 times\u003c/button\u003e\n\n\u003cbutton onClick={function bound onClick() { /* native code */ }}\u003e\n  Button was clicked 0 times // -Button was clicked 0 times\n                             // +Button was clicked 1 times\n\u003c/button\u003e\n```\n\nYou can also use `when rendered` to directly render a component to a shallow renderer:\n\n```js\n\nexpect(\u003cMyButton /\u003e, \n  'when rendered',\n  'to have rendered',\n  \u003cbutton\u003e\n      Button was clicked 1 times\n  \u003c/button\u003e\n);\n```\n\nIf you've emulated the DOM, you can write a similar test, but using `ReactDOM.render()` (or `TestUtils.renderIntoDocument()`)\n\n```js\nvar component = TestUtils.renderIntoDocument(\u003cMyButton/\u003e)\nexpect(component, 'to have rendered',\n  \u003cbutton\u003e\n      Button was clicked 1 times\n  \u003c/button\u003e\n);\n```\n\n```output\nexpected\n\u003cMyButton\u003e\n  \u003cbutton onClick={function bound onClick() { /* native code */ }}\u003e\n    Button was clicked 0 times\n  \u003c/button\u003e\n\u003c/MyButton\u003e\nto have rendered \u003cbutton\u003eButton was clicked 1 times\u003c/button\u003e\n\n\u003cMyButton\u003e\n  \u003cbutton onClick={function bound onClick() { /* native code */ }}\u003e\n    Button was clicked 0 times // -Button was clicked 0 times\n                               // +Button was clicked 1 times\n  \u003c/button\u003e\n\u003c/MyButton\u003e\n```\n\nNote the major difference between the shallow renderer and the \"normal\" renderer, is that child components are also\nrendered.  That is easier to see with these example components:\n\n```js\n\nvar Text = React.createClass({\n   render() {\n       return \u003cspan\u003e{this.props.content}\u003c/span\u003e;\n   }\n});\n\nvar App = React.createClass({\n   render() {\n        return (\n            \u003cdiv className=\"testing-is-fun\"\u003e\n              \u003cText content=\"hello\" /\u003e\n              \u003cText content=\"world\" /\u003e\n            \u003c/div\u003e\n        );\n   }\n});\n\n```\n\nRendering the `App` component with the shallow renderer will not render the `span`s, only the \n`Text` component with the props.  If you wanted to test for the content of the span elements, you'd \nneed to use `TestUtils.renderIntoDocument(...)`, or `ReactDOM.render(...)`\n\nBecause unexpected-react` by default ignores wrapper elements, and also \"extra\" children (child\nnodes that appear in the actual render, but are not in the expected result), it is possible to \ntest both scenarios with the full renderer. To demonstrate, all the following tests will pass:\n\n```js\nvar component = TestUtils.renderIntoDocument(\u003cApp /\u003e);\n\n// renders the Text components with the spans with the full renderer\nexpect(component, 'to have rendered', \n  \u003cApp\u003e\n    \u003cdiv className=\"testing-is-fun\"\u003e\n      \u003cText content=\"hello\"\u003e\n        \u003cspan\u003ehello\u003c/span\u003e\n      \u003c/Text\u003e\n      \u003cText content=\"world\"\u003e\n        \u003cspan\u003eworld\u003c/span\u003e\n      \u003c/Text\u003e\n    \u003c/div\u003e\n  \u003c/App\u003e\n);\n```\n\n```js\n// renders the Text nodes with the full renderer'\n   \nexpect(component, 'to have rendered', \n  \u003cdiv className=\"testing-is-fun\"\u003e\n      \u003cText content=\"hello\" /\u003e\n      \u003cText content=\"world\" /\u003e\n  \u003c/div\u003e\n);\n```\n\n```js\n// renders the spans with the full renderer\n\nexpect(component, 'to have rendered', \n  \u003cdiv className=\"testing-is-fun\"\u003e\n      \u003cspan\u003ehello\u003c/span\u003e\n      \u003cspan\u003eworld\u003c/span\u003e\n  \u003c/div\u003e\n);\n\n```\n\nThe first test shows the full virtual DOM that gets rendered. The second test skips the `\u003cApp\u003e` \"wrapper\" \ncomponent, and leaves out the `\u003cspan\u003e` children of the `\u003cText\u003e` components. The third tests skips both \nthe `\u003cApp\u003e` wrapper component, and the `\u003cText\u003e` wrapper component.\n\n\n## Stateless components\n\nBecause [stateless components](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions) can't be instantiated, `renderIntoDocument` won't return an instance back. \nUsing the shallow renderer works as shown in the first example. \nFor full rendering, use the `when deeply rendered` to render the component\n\n```js\nexpect(\u003cStatelessComponent name=\"Daniel\" /\u003e, \n  'when deeply rendered', \n  'to have rendered',\n  \u003cdiv\u003eHello, Daniel!\u003c/div\u003e);\n```\n\n## Cleaning up\n\nWhen using the normal renderer, unexpected-react makes use of [`react-render-hook`](https://npmjs.org/package/react-render-hook),\nwhich utilises the code from the [React devtools](https://github.com/facebook/react-devtools). As there is no way for `react-render-hook` \nto know when a test is completed, it has to keep a reference to every rendered component. Whilst this shouldn't normally be an issue,\nif you use a test runner that keeps the process alive (such as [wallaby.js](http://wallabyjs.com)), it is a good idea to call\n`unexpectedReact.clearAll()` in a global `beforeEach()` or `afterEach()` block. This clears the cache of rendered nodes.\n\n## Roadmap / Plans\n\n* [DONE] ~~There are some performance optimisations to do. The performance suffers a bit due to the possible asynchronous nature of the inline assertions. Most of the time these will be synchronous, and hence we don't need to pay the price.~~\n* [DONE] ~~`queried for` implementation~~\n* [DONE] ~~Directly calling events on both the shallow renderer, and the full virtual DOM renderer~~\n* [DONE] ~~Support Snapshot testing in Jest~~\n* Cleanup output - where there are no differences to highlight, we could skip the branch\n\n# Contributing\n\nWe welcome pull requests, bug reports, and extra test cases. If you find something that doesn't work\nas you believe it should, or where the output isn't as good as it could be, raise an issue!\n\n## Thanks\n\nHuge thanks to [@Munter](https://github.com/munter) for [unexpected-dom](https://github.com/munter/unexpected-dom),\nand along with [@dmatteo](https://github.com/dmatteo) from Podio for handing over the unexpected-react name. \n\n[Unexpected](http://unexpected.js.org) is a great library to work with, and I offer my sincere thanks to [@sunesimonsen](https://github.com/sunesimonsen)\nand [@papandreou](https://github.com/papandreou), who have created an assertion library that makes testing JavaScript a joy.\n\n## License\nMIT\n\n\n","funding_links":[],"categories":["Dev Tools","Uncategorized"],"sub_categories":["Test","Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbruderstein%2Funexpected-react","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbruderstein%2Funexpected-react","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbruderstein%2Funexpected-react/lists"}