{"id":16903084,"url":"https://github.com/glenjamin/skin-deep","last_synced_at":"2025-04-05T02:08:18.791Z","repository":{"id":33350385,"uuid":"36995190","full_name":"glenjamin/skin-deep","owner":"glenjamin","description":"Test assertion helpers for use with React's shallowRender test utils","archived":false,"fork":false,"pushed_at":"2017-09-27T11:10:19.000Z","size":175,"stargazers_count":200,"open_issues_count":6,"forks_count":40,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-14T18:09:06.708Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/glenjamin.png","metadata":{"files":{"readme":"README.md","changelog":null,"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-06-06T21:08:36.000Z","updated_at":"2022-07-31T18:23:21.000Z","dependencies_parsed_at":"2022-08-07T20:31:01.364Z","dependency_job_id":null,"html_url":"https://github.com/glenjamin/skin-deep","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/glenjamin%2Fskin-deep","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glenjamin%2Fskin-deep/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glenjamin%2Fskin-deep/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glenjamin%2Fskin-deep/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/glenjamin","download_url":"https://codeload.github.com/glenjamin/skin-deep/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247276164,"owners_count":20912288,"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-13T18:09:20.981Z","updated_at":"2025-04-05T02:08:18.771Z","avatar_url":"https://github.com/glenjamin.png","language":"JavaScript","readme":"# skin-deep\n\nTesting helpers for use with React's shallowRender test utils.\n\n[![npm version](https://img.shields.io/npm/v/skin-deep.svg)](https://www.npmjs.com/package/skin-deep) [![Build Status](https://img.shields.io/travis/glenjamin/skin-deep/master.svg)](https://travis-ci.org/glenjamin/skin-deep) [![Coverage Status](https://coveralls.io/repos/glenjamin/skin-deep/badge.svg?branch=master)](https://coveralls.io/r/glenjamin/skin-deep?branch=master) ![MIT Licensed](https://img.shields.io/npm/l/skin-deep.svg)\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Install](#install)\n- [Quick Start](#quick-start)\n- [Breaking Changes in 1.0](#breaking-changes-in-10)\n- [Usage](#usage)\n  - [Rendering](#rendering)\n  - [Extracting portions of the result](#extracting-portions-of-the-result)\n  - [Triggering events](#triggering-events)\n  - [Going deeper](#going-deeper)\n  - [Use with test frameworks](#use-with-test-frameworks)\n- [API Docs](#api-docs)\n  - [.shallowRender(element [, context])](#shallowrenderelement--context)\n  - [tree](#tree)\n    - [tree.type](#treetype)\n    - [tree.props](#treeprops)\n    - [tree.reRender(props [, context])](#treererenderprops--context)\n    - [tree.text()](#treetext)\n    - [tree.toString()](#treetostring)\n    - [tree.getRenderOutput()](#treegetrenderoutput)\n    - [tree.getMountedInstance()](#treegetmountedinstance)\n    - [tree.subTree(selector [, matcher])](#treesubtreeselector--matcher)\n    - [tree.everySubTree(selector [, matcher])](#treeeverysubtreeselector--matcher)\n    - [tree.dive(path)](#treedivepath)\n  - [Using Selectors](#using-selectors)\n  - [Using Matchers](#using-matchers)\n  - [.exact(props)](#exactprops)\n  - [.any](#any)\n  - [.hasClass(node, className)](#hasclassnode-classname)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Install\n\n```sh\nnpm install skin-deep\n```\n\nThis lib should work on any version of React since 0.14. To allow for greater flexibility by users, no `peerDependencies` are included in the `package.json`. Your dependencies need to be as follows:\n\nFor React \u003c 15.5:\n * `react`\n * `react-addons-test-utils`\n\n\nFor React \u003e= 15.5:\n * `react`\n * `react-dom`\n * `react-test-renderer`\n\n## Quick Start\n\n```jsx\nvar React = require('react');\n\nvar MyComponent = React.createClass({\n  displayName: 'MyComponent',\n  render: function() {\n    return (\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"/\"\u003eHome\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"/abc\"\u003eDraw\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"/def\"\u003eAway\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    )\n  }\n});\n\nvar assert = require('assert');\nvar sd = require('skin-deep');\n\nvar tree = sd.shallowRender(\u003cMyComponent /\u003e);\n\nvar homeLink = tree.subTree('a', { href: '/' });\n\nassert.equal(homeLink.type, 'a');\nassert.equal(homeLink.props.href, '/');\nassert.equal(homeLink.text(), 'Home');\n```\n\n## Breaking Changes in 1.0\n\n * React 0.13 is no longer supported\n * Rendering a function (which would be called) is no longer supported, this only existed for React 0.13's owner-based context\n * `subTreeLike` has been renamed to `subTree`\n * `everySubTreeLike` has been renamed to `everySubTree`\n * The original `subTree` has been removed, `exact` can be used to get this behaviour back, but I don't recommend you do.\n * The original `subTreeLike` has been removed, `exact` can be used to get this behaviour back, but I don't recommend you do.\n * removed `findNode`, use `subTree` instead\n * removed `textIn`, use `subTree().text()` instead\n * removed `fillField`, use `subTree().props.onChange` instead\n * removed `findComponent`, use `subTree()` instead\n * removed `findComponentLike`, use `subTree()` instead\n * `toString()` uses a completely different approach now. Previously it would use React's string rendering and produce the HTML including expanded children. Now it produces a pretty-printed representation of the result of the render.\n * `reRender()` now takes props instead of a ReactElement.\n\n## Usage\n\nThe goal of skin-deep is to provide higher level functionality built on top of the Shallow Rendering test utilities provided by React.\n\nBy default, shallow rendering gives you a way to see what a component would render without continuing along into rendering its children. This is a very powerful baseline, but in my opinion it isn't enough to create good UI tests. You either have to assert on the whole rendered component, or manually traverse the tree like this:\n\n```js\nassert(rendered.props.children[1].props.children[2].children, 'Click Here');\n```\n\nBy their nature user interfaces change a lot - sometimes these changes are to behaviour, but sometimes they're simply changes to wording or minor display changes. Ideally, we'd want non-brittle UI tests which can survive these superficial changes, but still check that the application behaves as expected.\n\n### Rendering\n\nUse the `shallowRender` function to get a `tree` you can interact with.\n\n```jsx\nvar sd = require('skin-deep');\n\nvar tree = sd.shallowRender(\u003cMyComponent /\u003e);\n```\n\nYou can now inspect the the tree to see its contents\n\n```js\ntree.getRenderOutput();\n// -\u003e ReactElement, same as normal shallow rendering\n\ntree.type;\n// -\u003e The component type of the root element\n\ntree.props;\n// -\u003e The props of the root element\n```\n\nThe real benefits of skin deep come from the ability to extract small portions of the tree with a jQuery-esque API. You can then assert only on these sub-trees.\n\n### Extracting portions of the result\n\nExtraction methods all take a CSS-esque selector as the first argument. This is commonly a component or tag name, but can also be a class or ID selector. The special value '*' can be used to match anything.\n\nThe second (optional) argument is the matcher, this can be an object to match against props, or a predicate function which will be passed each node and can decide whether to include it.\n\n```jsx\ntree.subTree('Button');\n// -\u003e the first Button component\n\ntree.everySubTree('Button');\n// -\u003e all the button components\n\ntree.subTree('.button-primary');\n// -\u003e the first component with class of button-primary\n\ntree.subTree('#submit-button');\n// -\u003e the first component with id of submit-button\n\ntree.subTree('Button', { type: 'submit' });\n// -\u003e the first Button component with type=submit\n\ntree.subTree('*', { type: 'button' });\n// -\u003e All components / elements with type=button\n\ntree.subTree('*', function(node) { return node.props.size \u003e 20; });\n// -\u003e All components / elements with size prop above 20\n```\n\n### Triggering events\n\nThere's no DOM involved, so events could be a bit tricky - but as we're just using data, we can call functions directly!\n\n```js\nvar MyButton = React.createClass({\n  clicked: function(e) {\n    console.log(e.target.innerHTML);\n  },\n  render: function() {\n    return \u003cbutton onClick={this.clicked}\u003eClick {this.props.n}\u003c/button\u003e;\n  }\n});\nvar tree = sd.shallowRender(\u003cMyButton /\u003e);\ntree.subTree('button').props.onClick({\n  target: {\n    innerHTML: 'Whatever you want!'\n  }\n});\n```\n\n### Going deeper\n\nSometimes shallow rendering isn't enough - often you'll want to have some integration tests which can render a few layers of your application. I prefer not to have to use a full browser or jsdom for this sort of thing - so we introduced the `dive` method. This allows you to move down the tree, recursively shallow rendering as needed.\n\n```js\nvar MyList = React.createClass({\n  render: function() {\n    return \u003cul\u003e{[1,2,3].map(function(n) { return \u003cMyItem n={n} /\u003e; })}\u003c/ul\u003e;\n  }\n});\nvar MyItem = React.createClass({\n  render: function() {\n    return \u003cli\u003e\u003cMyButton\u003e{this.props.n}\u003c/MyButton\u003e\u003c/li\u003e;\n  }\n});\nvar MyButton = React.createClass({\n  render: function() {\n    return \u003cbutton\u003eClick {this.props.n}\u003c/button\u003e;\n  }\n});\n\nvar tree = sd.shallowRender(\u003cMyList /\u003e);\nvar buttonElement = tree.dive(['MyItem', 'MyButton']);\nassert(buttonElement.text(), 'Click 1');\n```\n\n### Use with test frameworks\n\n\u003e TODO: flesh this out a bit more\n\nSkin deep doesn't care which test framework you use, it just gives you the data you need to make assertions.\n\nIf you want to take this further, should be pretty simple to extend your favorite assertion library to be skin-deep aware.\n\nAs we use tend to use `chai`, there's a `chai` plugin bundled inside this package. You can use it via `chai.use(require('skin-deep/chai'))`.\n\n## API Docs\n\n### .shallowRender(element [, context])\n\nGet a tree instance by shallow-rendering a renderable [ReactElement](http://facebook.github.io/react/docs/glossary.html#react-elements).\n\n* `element {ReactElement}` - element to render\n* `context {object}` - _optional_ [context](http://facebook.github.io/react/docs/context.html)\n\nReturns [`tree`](#tree)\n\n### tree\n\n#### tree.type\n\nAccess the type of the rendered root element.\n\nReturns [`ReactComponent class`](http://facebook.github.io/react/docs/glossary.html#react-components) or `string`.\n\n#### tree.props\n\nAccess the props of the rendered root element.\n\nReturns `object`\n\n#### tree.reRender(props [, context])\n\nRe-render the element with new props into the same tree as the previous render. Useful for testing how a component changes over time in response to new props from its parent.\n\n* `props {object}` - the new props, which will replace the previous ones\n* `context {object}` - _optional_ [context](http://facebook.github.io/react/docs/context.html)\n\nReturns `null`\n\n#### tree.text()\n\nAccess the textual content of the rendered root element including any text of its children. This method doesn't understand CSS, or really anything about HTML rendering, so might include text which wouldn't be displayed to the user.\n\nIf any components are found in the tree, their textual representation will be a stub like `\u003cWidget /\u003e`. This is because they could do anything with their props, and thus are not really suitable for text assertions. If you have any suggestions for how to make it easier to do text assertions on custom components, please let me know via issues.\n\nReturns `string`\n\n#### tree.toString()\n\nProduce a friendly JSX-esque representation of the rendered tree.\n\nThis is not really suitable for asserting against as it will lead to very brittle tests. Its main purpose is supposed to be for printing out nice debugging information, eg `\"no \u003cselector\u003e found in \u003ctree.toString()\u003e\"`.\n\nReturns `string`\n\n#### tree.getRenderOutput()\n\nAccess the rendered component tree. This is the same result you would get using shallow rendering without skin-deep.\n\nYou usually shouldn't need to use this.\n\nReturns [`ReactElement`](http://facebook.github.io/react/docs/glossary.html#react-elements)\n\n#### tree.getMountedInstance()\n\nAccess the mounted instance of the component.\n\nReturns `object`\n\n#### tree.subTree(selector [, matcher])\n\nExtract a portion of the rendered component tree. If multiple nodes match the selector, will return the first.\n\n* `selector {`[`Selector`](#using-selectors)`}` - how to find trees\n* `matcher {`[`Matcher`](#using-matchers)`}` - _optional_ additional conditions\n\nReturns [`tree`](#tree) or `false`\n\n#### tree.everySubTree(selector [, matcher])\n\nExtract multiple portions of the rendered component tree.\n\n* `selector {`[`Selector`](#using-selectors)`}` - how to find trees\n* `matcher {`[`Matcher`](#using-matchers)`}` - _optional_ additional conditions\n\nReturns `array` of [`tree`](#tree)s\n\n#### tree.dive(path)\n\n\"Dive\" into the rendered component tree, rendering the next level down as it goes. See [Going Deeper](#going-deeper) for an example.\n\n* `path {array of `[`Selector`](#using-selectors)`s}`\n\nReturns `tree`\nThrows if the path cannot be found.\n\n### Using Selectors\n\n\u003e TODO\n\n### Using Matchers\n\n\u003e TODO\n\n### .exact(props)\n\nCreate a matcher which only accepts nodes that have exactly those `props` passed in - no extra props.\n\n* `props {object}` - to match against\n\nReturns `function`\n\n### .any\n\nA magic value which can be used in a prop matcher that will allow any value to be matched. It will still fail if the key doesn't exist\n\neg.\n```jsx\n{ abc: sd.any }\n\n// Will match each of the following\n\u003cComponent abc=\"1\" /\u003e\n\u003cComponent abc={100} /\u003e\n\u003cComponent abc={function(){}} /\u003e\n\n// but not\n\u003cComponent /\u003e\n\u003cComponent def=\"1\" /\u003e\n```\n\n### .hasClass(node, className)\n\nHelper function to check if a node has the HTML class specified. Exported in case you want to use this in a custom matcher.\n","funding_links":[],"categories":["Unit Testing"],"sub_categories":["Assertion"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglenjamin%2Fskin-deep","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglenjamin%2Fskin-deep","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglenjamin%2Fskin-deep/lists"}