{"id":13525877,"url":"https://github.com/mzgoddard/preact-render-spy","last_synced_at":"2025-04-05T05:09:00.276Z","repository":{"id":46938802,"uuid":"94256499","full_name":"mzgoddard/preact-render-spy","owner":"mzgoddard","description":"Render preact components with access to the produced virtual dom for testing.","archived":false,"fork":false,"pushed_at":"2023-01-12T09:51:40.000Z","size":655,"stargazers_count":178,"open_issues_count":32,"forks_count":24,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-30T05:57:28.379Z","etag":null,"topics":["jsx","preact","test","testing","vdom"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/preact-render-spy","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mzgoddard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-13T20:48:45.000Z","updated_at":"2024-05-31T05:33:12.000Z","dependencies_parsed_at":"2023-02-09T11:15:47.452Z","dependency_job_id":null,"html_url":"https://github.com/mzgoddard/preact-render-spy","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzgoddard%2Fpreact-render-spy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzgoddard%2Fpreact-render-spy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzgoddard%2Fpreact-render-spy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzgoddard%2Fpreact-render-spy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mzgoddard","download_url":"https://codeload.github.com/mzgoddard/preact-render-spy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289429,"owners_count":20914464,"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":["jsx","preact","test","testing","vdom"],"created_at":"2024-08-01T06:01:23.090Z","updated_at":"2025-04-05T05:09:00.251Z","avatar_url":"https://github.com/mzgoddard.png","language":"JavaScript","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"readme":"# preact-render-spy\n\npreact-render-spy is a package designed to cover many of the use cases for testing\npreact components.  The API is roughly modeled after enzyme, but we do not support\nas many options currently.\n\n[![Build Status](https://travis-ci.org/mzgoddard/preact-render-spy.svg?branch=master)](https://travis-ci.org/mzgoddard/preact-render-spy)\n\n## Support:\n\nWe do our best to support Node.JS v6.11.0 and up, and speficially testing with jest, though other test runners should have no problems.\n\n## Expressive Testing Example:\n\n```jsx\nimport {h} from 'preact';\nimport {shallow} from 'preact-render-spy';\nimport Testable from './testable';\n\nit('lets you do cool things with preact components', () =\u003e {\n  const context = shallow(\u003cTestable /\u003e);\n  expect(context.find('div').contains(\u003ca\u003elink\u003c/a\u003e)).toBeTruthy();\n  context.find('[onClick]').simulate('click');\n  expect(context.find('a').text()).toBe('clicked');\n});\n```\n\n## How it works\n\nThe main render method takes some arbitrary JSX and replaces any component nodes with\nspies which wrap the component.  These spies then look at the rendered output of the\nsecond level of JSX and stores a big map of all the JSX virtual DOM nodes that have\nbeen created by your component.\n\nThere is also concept of limiting the `depth` of the rendering such that it will only resolve\na certain number of levels deep in the component tree.  The `depth` of the default renderer\nis set to `Infinity`, and we provide another renderer called `shallow` to render with\n`{ depth: 1 }`.\n\n## Jest Snapshot support\nWe provide a plugin for rendering your jsx snapshots to a formatted string that you can enable\nusing the jest configuration:\n\n```json\n{\n  \"snapshotSerializers\": [ \"preact-render-spy/snapshot\" ]\n}\n```\n\n## Configuration\nWe export a `config` object which you can use to change some of the internal options.\nThe values shown in this section are the defaults:\n\n```js\nimport {config} from 'preact-render-spy';\n\n// This property is used by the spy renderer to pass information to the spies about where\n// they are in the vdom tree, it is generally removed from the properties passed down to your\n// component, but it might show up in some cases, and we want you to be able to pick it.\nconfig.SPY_PRIVATE_KEY = 'SPY_PRIVATE_KEY';\n\n// These options are passed to preact-render-to-string/jsx whenever you snapshot a VNode or\n// FindWrapper (and on the FindWrapper's toString method)\nconfig.toStringOptions = { shallow: true, skipFalseAttributes: false };\n\n// This option allows you to use a custom DOM implementation instead of relying on a global\n// document.createDocumentFragment()\nconfig.createFragment = () =\u003e document.createDocumentFragment();\n```\n\n## Exported Methods\n\n### `deep(jsx, {depth = Infinity} = {})`\nCreates a new `RenderContext` and renders using `opts.depth` to specify how many components deep\nit should allow the renderer to render.  Also exported as `render` and `default`.\n\nExample:\n```jsx\nconst Deeper = () =\u003e \u003cdiv\u003eUntil Infinity!\u003c/div\u003e;\nconst Second = () =\u003e \u003cDeeper /\u003e;\nconst First = () =\u003e \u003cSecond /\u003e;\n\nlet context;\ncontext = deep(\u003cFirst /\u003e);\nexpect(context.find('div').text()).toBe('Until Infinity!');\n\ncontext = deep(\u003cFirst /\u003e, { depth: 2 });\n// We rendered First and Second, but not Deeper, so we never render a \u003cdiv\u003e\nexpect(context.find('div').length).toBe(0);\n```\n\n### `shallow(jsx)`\nCreates a new `RenderContext` with `{ depth: 1 }`.\n\n### `RenderContext#find(selector)`\nGiven a rendered context, `find` accepts a \"css like\" language of selectors to search through the\nrendered vdom for given nodes.  **NOTE:** We only support this very limited set of \"selectors\", and no nesting.\nWe may expand this selector language in future versions, but it acheives our goals so far!\n\n* `find('.selector')` - searches for any nodes with `class` or `className` attribute that matches `selector`\n* `find('#selector')` - searches for any nodes with an `id` attribute that matches `selector`\n* `find('[selector]')` - searches for any nodes which have an attribute named `selector`\n* `find('Selector')` - searches for any nodes which have a nodeName that matches `Selector`,\n  this will search for function/classes whos `name` is `Selector`, or `displayName` is `Selector`.\n  If the `Selector` starts with a lower case letter, it will also check for tags like `div`.\n* `find(\u003cSelector simple=\"attributes\" class=\"working\" /\u003e)` - searches for any nodes whos nodeName equals `Selector`\n  and attributes match the ones given in the JSX.  **NOTE:** This does not support children, just simple attributes.\n  Can be useful to find components from minified output that don't include display names.\n  `.find(\u003cImportedComponent /\u003e)` will look for JSX nodes using the same `ImportedComponent` function.\n\nThis will return you a [`FindWrapper`](#findwrapper) which has other useful methods for testing.\n\n### `RenderContext` extends `FindWrapper`\n\nLike [`#find(selector)`](#rendercontextfindselector) `RenderContext` has the rest of `FindWrapper`'s methods.\n\n### `RenderContext#render(jsx)`\nRenders the root level jsx node using the same depth initially requested.  This can be useful for testing\n`componentWillReceiveProps` hooks.\n\nExample:\n\n```jsx\nconst Node = ({name}) =\u003e \u003cdiv\u003e{name}\u003c/div\u003e\nconst context = shallow(\u003cNode name=\"example\" /\u003e);\nexpect(context.find('div').text()).toBe('example');\n\ncontext.render(\u003cNode name=\"second\" /\u003e);\nexpect(context.find('div').text()).toBe('second');\n```\n\n### `RenderContext.rerender()`\nCalls `preact.rerender()` which performs any state changes in the render queue.\n\n### `FindWrapper`\nContains a selection of nodes from `RenderContext#find(selector)`.\nHas numeric indexed properties and length like an array.\n\n### `FindWrapper#at(index)`\nReturns another `FindWrapper` at the specific index in the selection.  Similar to `wrapper[0]` but will\nallow using other `FindWrapper` methods on the result.\n\n### `FindWrapper#attr(name)`\nRequires a single node selection to work.\nReturns the value of the `name` attribute on the jsx node.\n\n### `FindWrapper#attrs()`\nRequires a single node selection to work.\nReturns a copy of the attributes passed to the jsx node.\n\n### `FindWrapper#component()`\nRequires a single node, which is a class based component.\nReturns the **Spied** component. preact-render-spy creates a subclass of your components that enable us to spy things, you'll get a `class Spy extends YourComponent` instance.\n\nExample:\n```jsx\nconst context = shallow(\u003cMyComponent /\u003e);\nexpect(context.component()).toBeInstanceOf(MyComponent);\n```\n\n### `FindWrapper#contains(vdom)`\nSearches for any children matching the vdom or text passed.\n\n### `FindWrapper#children()`\nReturns `FindWrapper` with children of current wrapper.\n\n### `FindWrapper#childAt(index)`\nReturns `FindWrapper` with child at given index.\nThis has the same effect as calling `wrapper.children().at(index)`.\n\n```jsx\nconst context = shallow(\u003cList items={items} /\u003e);\nexpect(context.childAt(1).text()).toBe('Second list element');\n```\n\n### `FindWrapper#exists()`\nReturns whether or not given node exists.\n\n### `FindWrapper#filter(selector)`\nReturns a new `FindWrapper` with a subset of the previously selected elements given the selector argument.\n\nUses the same possible selectors as [`RenderContext#find(selector)`](#rendercontextfindselector).\n\n### `FindWrapper#map(fn)`\nMaps array of nodes from this `FindWrapper` to another array.\nEach node is passed in as a `FindWrapper` to the map function along with index number of element.\n\n```jsx\nconst context = shallow((\n  \u003cul\u003e\n    \u003cli class=\"item\"\u003efirst\u003c/li\u003e\n    \u003cli class=\"item\"\u003esecond\u003c/li\u003e\n    \u003cli class=\"item\"\u003ethird\u003c/li\u003e\n  \u003c/ul\u003e\n));\n\nconst items = context.find('.item').map(node =\u003e node.text());\nexpect(items).toEqual(['first', 'second', 'third']);\n```\n\n### `FindWrapper#find(selector)`\nSelects descendents of the elements previously selected. Returns a new `FindWrapper` with the newly selected elements.\n\nUses the same possible selectors as [`RenderContext#find(selector)`](#rendercontextfindselector).\n\n### `FindWrapper#first()`\nReturns another `FindWrapper` at the first index in the selection.\n\n### `FindWrapper#last()`\nReturns another `FindWrapper` at the last index in the selection.\n\n### `FindWrapper#setState(newState)`\nRequires a single node, which is a class based component.\nAllows you to set the state of a rendered component.  Automatically `rerender()`s the view.\n\nExample:\n```jsx\nconst context = shallow(\u003cClickCounter /\u003e);\ncontext.setState({ count: 2 });\nexpect(context.text()).toEqual('2');\n```\n\n### `FindWrapper#simulate(event, ...args)`\nLooks for an attribute properly named `onEvent` or `onEventCapture` and calls it, passing the arguments.\n\n### `FindWrapper#state(key)`\nRequires a single node, which is a class based component.\nReads the current state from the component. When passed `key`, this is essentially shorthand `state(key) === state()[key]`.\n\nExample:\n```jsx\nconst context = shallow(\u003cClickCounter /\u003e);\nexpect(context.state()).toEqual({ count: 0 });\ncontext.find('[onClick]').simulate('click');\nexpect(context.state('count')).toEqual(1);\n```\n\n### `FindWrapper#text()`\nReturns the flattened string of any text children of any child component.\n\n### `FindWrapper#output()`\nRequires a single Component or functional node. Returns the vdom output of the given component.\nAny Component or functional nodes will be \"recursive\" up to the depth you specified.  I.E.:\n\nExample:\n```jsx\n\nconst Second = ({ children }) =\u003e \u003cdiv\u003esecond {children}\u003c/div\u003e;\nconst First = () =\u003e \u003cSecond\u003efirst\u003c/Second\u003e;\n\n// rendered deep, we get the div output\nexpect(deep(\u003cFirst /\u003e).output()).toEqual(\u003cdiv\u003esecond first\u003c/div\u003e);\n// rendered shallow, we get the \u003cSecond\u003e jsx node back\nexpect(shallow(\u003cFirst /\u003e).output()).toEqual(\u003cSecond\u003efirst\u003c/Second\u003e);\n```\n\n## Examples\n\nThere are many examples in the source files.  Some [tests specific to shallow](https://github.com/mzgoddard/preact-render-spy/blob/master/src/shallow-render.test.js), [tests specific to deep](https://github.com/mzgoddard/preact-render-spy/blob/master/src/deep-render.test.js), and many more [tests against both](https://github.com/mzgoddard/preact-render-spy/blob/master/src/shared-render.test.js).\n\n### Simulate Clicks:\n```jsx\nclass ClickCount extends Component {\n  constructor(...args) {\n    super(...args);\n\n    this.state = {count: 0};\n    this.onClick = this.onClick.bind(this);\n  }\n\n  onClick() {\n    this.setState({count: this.state.count + 1});\n  }\n\n  render({}, {count}) {\n    return \u003cdiv onClick={this.onClick}\u003e{count}\u003c/div\u003e;\n  }\n}\nconst context = shallow(\u003cClickCount/\u003e);\nexpect(context.find('div').contains('0')).toBeTruthy();\ncontext.find('[onClick]').simulate('click');\nexpect(context.find('div').contains('1')).toBeTruthy();\n```\n\n### Testing componentWillUnmount\n\n```jsx\nimport { h, Component } from 'preact';\nimport { shallow } from 'preact-render-spy';\n\nclass Unmount extends Component {\n\n  componentWillUnmount() {\n    this.props.onUnmount(this);\n  }\n\n  render() {\n    return \u003cdiv\u003eUnmount me\u003c/div\u003e;\n  }\n}\n\nit('triggers unmount', () =\u003e {\n  const trigger = jest.fn();\n  const context = shallow(\u003cUnmount onUnmount={trigger} /\u003e);\n  expect(trigger).not.toHaveBeenCalled();\n\n  // This will trigger the componentWillUnmount\n  context.render(null);\n  expect(trigger).toHaveBeenCalled();\n});\n```\n\n### Testing componentWillReceiveProps\n\n```jsx\nimport { h, Component } from 'preact';\nimport { shallow } from 'preact-render-spy';\n\nclass ReceivesProps extends Component {\n  constructor(props) {\n    this.state = { value: props.value };\n  }\n\n  componentWillReceiveProps({ value }) {\n    if (value !== this.props.value) {\n      this.setState({ value: `_${value}_` })\n    }\n  }\n\n  render() {\n    return \u003cdiv\u003e{this.state.value}\u003c/div\u003e\n  }\n}\n\nit('receives props', () =\u003e {\n  const context = shallow(\u003cReceivesProps value=\"test\" /\u003e);\n  expect(context.text()).toBe('test');\n\n  context.render(\u003cReceivesProps value=\"second\" /\u003e);\n  expect(context.text()).toBe('_second_');\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmzgoddard%2Fpreact-render-spy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmzgoddard%2Fpreact-render-spy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmzgoddard%2Fpreact-render-spy/lists"}