diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72bbbb802..3d1f64d48 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,6 +69,68 @@ codebase, however you can always check to see if the source code is compliant by npm run lint ``` +### Publishing + +Enzyme uses [lerna](https://github.com/lerna/lerna) to structure its repo, and has multiple packages +to publish out of this one repo. We use lerna's "independent" mode, which means that the versioning +of each package in the repo is versioned independently. + +We are waiting on [this issue](https://github.com/lerna/lerna/issues/955) to be fixed, so that +`peerDependencies` do not get updated with patch updates. + +Until this issue is fixed, we will publish each package manually instead of with `lerna publish`. In +order to do this, we will: + +For enzyme: + +```bash +# ... update version in enzyme/package.json, make changes to CHANGELOG, etc. +cd packages/enzyme +git commit -m v{version} +git tag -a -m v{version} +git push --follow-tags +npm publish +``` + +For other packages + +```bash +# ... update version in {package}/package.json, make changes to CHANGELOG, etc. +cd packages/{package} +git commit -m "{package}: v{version}" +git tag -a -m "{package}: v{version}" +git push --follow-tags +npm publish +``` + +Once we are able to use `lerna publish`, the process will be as follows: + +Lerna by default will only publish packages that have changed since the last release. It will also +create a tagged commit for each release. + +To publish, run: + +```bash +lerna publish -m "{tag name}" +``` + +The tag name is determined by the `-m` CLI option. If `enzyme` is one of the packages that has +updates, we default to just using that version as the tag name. For instance, when publishing +`enzyme@3.1.1` and `enzyme-adapter-react-16@1.2.3` we would run: + +```bash +lerna publish -m "v3.1.1" +``` + +If `enzyme` is *not* one of the packages being updated, use the other package's name and the version: + +```bash +lerna publish -m "enzyme-adapter-react-16: v1.2.3" +``` + +The `lerna publish` command will present interactive prompts asking which version to use for each +package independently. Just choose whichever + ### Building Docs diff --git a/README.md b/README.md index 219a9c093..b3cf2bd30 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,70 @@ Enzyme [![Discord Channel](https://img.shields.io/badge/discord-testing@reactiflux-738bd7.svg?style=flat-square)](https://discord.gg/0ZcbPKXt5bY8vNTA) - Enzyme is a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components' output. Enzyme's API is meant to be intuitive and flexible by mimicking jQuery's API for DOM manipulation and traversal. +Upgrading from Enzyme 2.x or React < 16 +=========== + +Are you here to check whether or not Enzyme is compatible with React 16? Are you currently using +Enzyme 2.x? Great! Check out our [migration guide](/docs/guides/migration-from-2-to-3.md) for help +moving on to Enzyme v3 where React 16 is supported. + +### [Installation](/docs/installation/README.md) + +To get started with enzyme, you can simply install it via npm. You will need to install enzyme +along with an Adapter corresponding to the version of react (or other UI Component library) you +are using. For instance, if you are using enzyme with React 16, you can run: + +```bash +npm i --save-dev enzyme enzyme-adapter-react-16 +``` + +Each adapter may have additional peer dependencies which you will need to install as well. For instance, +`enzyme-adapter-react-16` has peer dependencies on `react`, `react-dom`, and `react-test-renderer`. + +At the moment, Enzyme has adapters that provide compatibility with `React 16.x`, `React 15.x`, +`React 0.14.x` and `React 0.13.x`. + +The following adapters are officially provided by enzyme, and have the following compatibility with +React: + +| Enzyme Adapter Package | React semver compatibility | +| --- | --- | +| `enzyme-adapter-react-16` | `^16.0.0` | +| `enzyme-adapter-react-15` | `^15.5.0` | +| `enzyme-adapter-react-15.4` | `15.0.0-0 - 15.4.x` | +| `enzyme-adapter-react-14` | `^0.14.0` | +| `enzyme-adapter-react-13` | `^0.13.0` | + +Finally, you need to configure enzyme to use the adapter you want it to use. To do this, you can use +the top level `configure(...)` API. + +```js +import Enzyme from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; + +Enzyme.configure({ adapter: new Adapter() }); +``` + +3rd Party Adapters +============= + +It is possible for the community to create additional (non-official) adapters that will make enzyme +work with other libraries. If you have made one and it's not included in the list below, feel free +to make a PR to this README and add a link to it! The known 3rd party adapters are: + +| Adapter Package | For Library | Status | +| --- | --- | --- | +| [`preact-enzyme-adapter`](https://github.com/aweary/preact-enzyme-adapater) | [`preact`](https://github.com/developit/preact) | (work in progress) | + +Running Enzyme Tests +=========== + Enzyme is unopinionated regarding which test runner or assertion library you use, and should be compatible with all major test runners and assertion libraries out there. The documentation and examples for enzyme use [mocha](https://mochajs.org/) and [chai](http://chaijs.com/), but you @@ -49,32 +106,6 @@ testing your React components, you can consider using: [Using Enzyme with Tape and AVA](/docs/guides/tape-ava.md) -### [Installation](/docs/installation/README.md) - -To get started with enzyme, you can simply install it with npm: - -```bash -npm i --save-dev enzyme -``` - -Enzyme is currently compatible with `React 15.x`, `React 0.14.x` and `React 0.13.x`. In order to -achieve this compatibility, some dependencies cannot be explicitly listed in our `package.json`. - -If you are using `React 0.14` or `React <15.5`, in addition to `enzyme`, you will have to ensure that -you also have the following npm modules installed if they were not already: - -```bash -npm i --save-dev react-addons-test-utils react-dom -``` - -If you are using `React >=15.5`, in addition to `enzyme`, you will have to ensure that you also have -the following npm modules installed if they were not already: - -```bash -npm i --save-dev react-test-renderer react-dom -``` - - Basic Usage =========== diff --git a/SUMMARY.md b/SUMMARY.md index 88d1c780f..6d69e4d14 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -2,6 +2,7 @@ * [Introduction](/README.md) * [Guides](/docs/guides.md) + * [Migration from 2.x to 3.x](/docs/guides/migration-from-2-to-3.md) * [Browserify](/docs/guides/browserify.md) * [WebPack](/docs/guides/webpack.md) * [JSDOM](/docs/guides/jsdom.md) @@ -12,9 +13,10 @@ * [Lab](/docs/guides/lab.md) * [Tape and AVA](/docs/guides/tape-ava.md) * [Installation](/docs/installation/README.md) - * [Working with React 0.13.x](/docs/installation/react-013.md) - * [Working with React 0.14.x](/docs/installation/react-014.md) + * [Working with React 16.x](/docs/installation/react-16.md) * [Working with React 15.x](/docs/installation/react-15.md) + * [Working with React 0.14.x](/docs/installation/react-014.md) + * [Working with React 0.13.x](/docs/installation/react-013.md) * [API Reference](/docs/api/README.md) * [Shallow Rendering](/docs/api/shallow.md) * [at(index)](/docs/api/ShallowWrapper/at.md) @@ -130,5 +132,4 @@ * [Selectors](/docs/api/selector.md) * [Change Log](/CHANGELOG.md) * [Future](/docs/future.md) - * [Adapter & Compatibility Proposal](/docs/future/compatibility.md) * [Contributing Guide](/CONTRIBUTING.md) diff --git a/docs/api/render.md b/docs/api/render.md index cfb666bea..394c0b285 100644 --- a/docs/api/render.md +++ b/docs/api/render.md @@ -1,6 +1,6 @@ # Static Rendering API -Enzyme's `render` function is used to render react components to static HTML and analyze the +enzyme's `render` function is used to render react components to static HTML and analyze the resulting HTML structure. `render` returns a wrapper very similar to the other renderers in enzyme, [`mount`](mount.md) and diff --git a/docs/api/selector.md b/docs/api/selector.md index 4e7779aad..3d072357a 100644 --- a/docs/api/selector.md +++ b/docs/api/selector.md @@ -1,12 +1,12 @@ -# Enzyme Selectors +# enzyme Selectors -Many methods in Enzyme's API accept a *selector* as an argument. Selectors in Enzyme can fall into +Many methods in enzyme's API accept a *selector* as an argument. Selectors in enzyme can fall into one of the following four categories: ### 1. A Valid CSS Selector -Enzyme supports a subset of valid CSS selectors to find nodes inside a render tree. Support is as +enzyme supports a subset of valid CSS selectors to find nodes inside a render tree. Support is as follows: - class syntax (`.foo`, `.foo-bar`, etc.) @@ -40,7 +40,7 @@ input#input-name label[foo=true] ``` -Enzyme also gives support for the following contextual selectors +enzyme also gives support for the following contextual selectors ``` .foo .bar @@ -60,7 +60,7 @@ enzyme that will likely be focused on in the future. ### 2. A React Component Constructor -Enzyme allows you to find components based on their constructor. You can pass in the reference to +enzyme allows you to find components based on their constructor. You can pass in the reference to the component's constructor: ```jsx @@ -76,7 +76,7 @@ const myComponents = wrapper.find(MyComponent); ### 3. A React Component's displayName -Enzyme allows you to find components based on a component's `displayName`. If a component exists +enzyme allows you to find components based on a component's `displayName`. If a component exists in a render tree where its `displayName` is set and has its first character as a capital letter, a string can be used to find it: @@ -99,7 +99,7 @@ selector using the tag syntax. ### 4. Object Property Selector -Enzyme allows you to find components and nodes based on a subset of their properties: +enzyme allows you to find components and nodes based on a subset of their properties: ```jsx diff --git a/docs/common-issues.md b/docs/common-issues.md index 648d61366..7dc295d13 100644 --- a/docs/common-issues.md +++ b/docs/common-issues.md @@ -2,44 +2,6 @@ This list aims to be comprehensive. If you find an issue that has been frequently brought up in Github *issues* that is not here, please open a PR to add it. -### Webpack Build Issues - -- [Related Github issue](https://github.com/airbnb/enzyme/issues/684) - -###### Common Solutions - -_Mismatched versions of React and React* libraries._ - -It is important to ensure all React and React* libraries your project depend on are matching versions. -If you are using React 15.4.0, you should ensure your React* libraries (like react-test-utils) are equivalently on 15.4.0. - -_Bad configuration._ - -Please see the guide for [webpack](/docs/guides/webpack) to ensure your configuration is correct for weback. - -### Error: Cannot find module 'react-dom/lib/ReactTestUtils' - -- [Related Github issue](https://github.com/airbnb/enzyme/issues/684) -- [Related code](https://github.com/airbnb/enzyme/blob/3aeb02461eabf2fd402613991915d8d6f4b88536/src/react-compat.js#L97-L105) - -###### Reason - -In order to properly support multiple versions of React, we have conditional requirements that npm does not support with tools like -`peerDependencies`. Instead we manually require and throw errors if the dependency is not met. - -###### Solution - -Install a matching version of React for `react-test-utils`. Example package.json - -```json -{ - "devDependencies": { - "react": "15.4.0", - "react-test-utils": "15.4.0" - } -} -``` - ### Query Selector fails ###### Reason @@ -50,13 +12,13 @@ selectors we currently support: https://github.com/airbnb/enzyme/blob/master/doc ### Testing third party libraries -Some third party libraries are difficult or impossible to test. Enzyme's scope is severly limited to what -React exposes and provides for us. Things like "portals" are not currently testable with Enzyme directly for that reason. +Some third party libraries are difficult or impossible to test. enzyme's scope is severly limited to what +React exposes and provides for us. Things like "portals" are not currently testable with enzyme directly for that reason. An example: If you are testing a library that creates a Modal, and it manually appends it to a different part of the DOM, React has lost -track of this component, and therefore Enzyme has also lost track of it. +track of this component, and therefore enzyme has also lost track of it. Even more so, if this library appends dom elements into react components, react still does not know about it. A library like d3 which appends DOM elements would be an example here. @@ -66,7 +28,7 @@ appends DOM elements would be an example here. You can use the `render` API to attempt to access and assert on the appended DOM components. This will likely become natively supported when React natively supports Portals, which is expected to land with Fiber. -If the third party solution lets you attach a `ref`, that would be the ideal scenario. With a `ref` you can then get that element from Enzyme. +If the third party solution lets you attach a `ref`, that would be the ideal scenario. With a `ref` you can then get that element from enzyme. example diff --git a/docs/future.md b/docs/future.md index 9ce5c318d..091f649ea 100644 --- a/docs/future.md +++ b/docs/future.md @@ -3,22 +3,10 @@ Discussion of additional features and support for enzyme should be initiated by opening a [Github issue](https://github.com/airbnb/enzyme/issues). -There are several things we'd like to address with Enzyme that often get asked. Here are a couple +There are several things we'd like to address with enzyme that often get asked. Here are a couple of projects that we plan on addressing in the near future: -### Enzyme Adapter & Compatibility - -[See the full proposal](future/compatibility.md) - - -#### Improved CSS Selector Support - -Currently, "hierarchical" CSS selectors are not supported in Enzyme. That means that the only CSS -selectors that can be used right now are those that operate on a specific node. As time progresses -we would like to provide a more complete subset of all valid CSS selectors. - - #### Improved event simulation and propagation support Event simulation is limited for Shallow rendering. Event propagation is not supported, and one must @@ -29,5 +17,5 @@ interaction. ### Improved Keyboard + Mouse Simulation Many react components involve simulating form input or complex mouse interaction. Simulating this -using the event simulation API that Enzyme provides is cumbersome and impractical. We are looking for +using the event simulation API that enzyme provides is cumbersome and impractical. We are looking for an expressive way to solve this problem, even if it is a library that lives outside of enzyme. diff --git a/docs/future/compatibility.md b/docs/future/compatibility.md deleted file mode 100644 index a6788f7aa..000000000 --- a/docs/future/compatibility.md +++ /dev/null @@ -1,184 +0,0 @@ -# Enzyme Adapter & Compatibility Proposal - - -## Motivation - -This proposal is attempting to address a handful of pain points that Enzyme has been -subject to for quite a while. This proposal has resulted mostly [#715](https://github.com/airbnb/enzyme/issues/715), -and a resulting discussion among core maintainers of this project. - -The desired results of this proposal are the following: - -1. Cleaner code, easier maintenance, less bug prone. - -By standardizing on a single tree specification, the implementation of Enzyme would no longer have -to take into account the matrix of supported structures and nuanced differences between different -versions of React, as well as to some extent the differences between `mount` and `shallow`. - -2. Additional libraries can provide compatible adapters - -React API-compatible libraries such as `preact` and `inferno` would be able to provide adapters to Enzyme -for their corresponding libraries, and be able to take full advantage of Enzyme's APIs. - -3. Better user experience (ie, bundlers won't complain about missing deps) - -Enzyme has had a long-standing issue with static-analysis bundlers such as Webpack and Browserify because -of our usage of internal React APIs. With this change, this would be minimized if not removed entirely, -since these things can be localized into the adapter modules, and users will only install the ones they need. - -Additionally, we can even attempt to remove the use of internal react APIs by lobbying for react-maintained packages -such as `react-test-renderer` to utilize the React Standard Tree (RST) format (details below). - -4. Standardization and interopability with other tools - -If we can agree on the tree format (specified below as "React Standard Tree"), other tools can start to use and -understand this format as well. Standardization is a good thing, and could allow tools to be built that maybe -don't even exist yet. - - -## Proposal - - -### React Standard Tree (RST) - -This proposal hinges on a standard tree specification. Keep in mind that this tree needs to account for more -than what is currently satisfied by the output of something like `react-test-renderer`, which is currently -only outputting the "host" nodes (ie, HTML elements). We need a tree format that allows for expressing a full -react component tree, including composite components. - -``` -// Strings and Numbers are rendered as literals. -type LiteralValue = string | number - -// A "node" in an RST is either a LiteralValue, or an RSTNode -type Node = LiteralValue | RSTNode - -// if node.type -type RenderedNode = RSTNode | [Node] - -type SourceLocation = {| - fileName: string - lineNumber: number -|} - -type NodeType = 'class' | 'function' | 'host'; - -// An RSTNode has this specific shape -type RSTNode = {| - // Either a string or a function. A string is considered a "host" node, and - // a function would be a composite component. It would be the component constructor or - // an SFC in the case of a function. - type: string | function; - - // This node's type - nodeType: NodeType; - - // The props object passed to the node, which will include `children` in its raw form, - // exactly as it was passed to the component. - props: object; - - // The backing instance to the node. Can be null in the case of "host" nodes and SFCs. - // Enzyme will expect instances to have the _public interface_ of a React Component, as would - // be expected in the corresponding React release returned by `getTargetVersion` of the - // renderer. Alternative React libraries can choose to provide an object here that implements - // the same interface, and Enzyme functionality that uses this will continue to work (An example - // of this would be the `setState()` prototype method). - instance: ComponentInstance?; - - // For a given node, this corresponds roughly to the result of the `render` function with the - // provided props, but transformed into an RST. For "host" nodes, this will always be `null` or - // an Array. For "composite" nodes, this will always be `null` or an `RSTNode`. - rendered: RenderedNode?; - - // an optional property with source information (useful in debug messages) that would be provided - // by this babel transform: https://babeljs.io/docs/plugins/transform-react-jsx-source/ - __source?: SourceLocation; -|} -``` - -### Enzyme Adapter Protocol - -**Definitions:** - -An `Element` is considered to be whatever data structure is returned by the JSX pragma being used. In the -react case, this would be the data structure returned from `React.createElement` - - -``` -type RendererOptions = { - // An optional predicate function that takes in an `Element` and returns - // whether or not the underlying Renderer should treat it as a "Host" node - // or not. This function should only be called with elements that are - // not required to be considered "host" nodes (ie, with a string `type`), - // so the default implementation of `isHost` is just a function that returns - // false. - ?isHost(Element): boolean; -} - -type EnzymeAdapter = { - // This is a method that will return a semver version string for the _react_ version that - // it expects enzyme to target. This will allow enzyme to know what to expect in the `instance` - // that it finds on an RSTNode, as well as intelligently toggle behavior across react versions - // etc. For react adapters, this will likely just be `() => React.Version`, but for other - // adapters for libraries like inferno or preact, it will allow those libraries to specify - // a version of the API that they are committing to. - getTargetApiVersion(): string; - - // Provided a bag of options, return an `EnzymeRenderer`. Some options can be implementation - // specific, like `attach` etc. for React, but not part of this interface explicitly. - createRenderer(?options: RendererOptions): EnzymeRenderer; - - // converts an RSTNode to the corresponding JSX Pragma Element. This will be needed - // in order to implement the `Wrapper.mount()` and `Wrapper.shallow()` methods, but should - // be pretty straightforward for people to implement. - nodeToElement(RSTNode): Element; -} - -type EnzymeRenderer = { - // both initial render and updates for the renderer. - render(Element): void; - - // retrieve a frozen-in-time copy of the RST. - getNode(): RSTNode?; -} -``` - - -### Using different adapters with Enzyme - -At the top level, Enzyme would expose a `configure` method, which would allow for an `adapter` -option to be specified and globally configure Enzyme's adapter preference: - -``` -import Enzyme from 'enzyme'; -import ThirdPartyEnzymeAdapter from 'third-party-enzyme-adapter'; - -Enzyme.configure({ adapter: ThirdPartyEnzymeAdapter }); - -``` - -Additionally, each wrapper Enzyme exposes will allow for an overriding `adapter` option that will use a -given adapter for just that wrapper: - -``` -import { shallow } from 'enzyme'; -import ThirdPartyEnzymeAdapter from 'third-party-enzyme-adapter'; - -shallow(, { adapter: ThirdPartyEnzymeAdapter }); -``` - -Enzyme will build adapters for all major versions of React since React 0.13, though will deprecate -adapters as usage of a particular major version fades. - -``` -import React13Adapter from 'enzyme-adapter-react-13'; -import React14Adapter from 'enzyme-adapter-react-14'; -import React15Adapter from 'enzyme-adapter-react-15'; -// ... -``` - -### Validation - -Enzyme will provide an `validate(node): Error?` method that will traverse down a provided `RSTNode` and -return an `Error` if any deviations from the spec are encountered, and `null` otherwise. This will -provide a way for implementors of the adapters to determine whether or not they are in compliance or not. diff --git a/docs/future/rst_examples.js b/docs/future/rst_examples.js deleted file mode 100644 index af2ea2ed7..000000000 --- a/docs/future/rst_examples.js +++ /dev/null @@ -1,253 +0,0 @@ -import Adapter from 'enzyme-adapter-foo'; - -let renderer; -let tree; - -// Example Components -// ================================================== - -// Composite returning host. no children props. -const Qoo = () => ( - Hello World! -); - -// composite returning host. passes through children. -const Foo = ({ className, children }) => ( -
- Literal - {children} -
-); - -// composite returning composite. passes through children. -const Bar = ({ special, children }) => ( - - {children} - -); - -// composite return composite. no children props. -const Bam = () => ( - - - -); - -// Examples -// ================================================== - -// IMPORTANT NOTE: -// in these examples i'm excluding `children` from `props` so that -// it's easier to read the tree output. In reality, `children` will -// be present and unaltered in the props, however enzyme will -// not use it for traversal. - -renderer = Adapter.createRenderer({ - // this would be the default as well. - isHost: el => typeof el.type === 'string', -}); - -// Simple Example -renderer.render(); -// => - -// Expected HTML output: -// -// Hello World! - -// Conceptual debug output: -// -// -// Hello World! -// - -// Expected tree output: -// tree = renderer.getNode(); -tree = { - type: Qoo, - nodeType: 'function', - props: {}, - rendered: { - type: 'span', - nodeType: 'host', - props: { className: 'Qoo' }, - rendered: ['Hello World!'], - }, -}; - -// Complex Example -renderer.render(); - -// Expected HTML output: -// -//
-// Literal -// Hello World! -//
- -// Conceptual debug output: -// -// -// -// -//
-// Literal -// -// Hello World! -// -//
-//
-//
-//
- -// Expected tree output -// tree = renderer.getNode(); -tree = { - type: Bam, - nodeType: 'function', - props: {}, - rendered: { - type: Bar, - nodeType: 'function', - props: { special: true }, - rendered: { - type: Foo, - nodeType: 'function', - props: { className: 'special' }, - rendered: { - type: 'div', - nodeType: 'host', - props: { className: 'Foo special' }, - rendered: [ - { - type: 'span', - nodeType: 'host', - props: { className: 'Foo2' }, - rendered: ['Literal'], - }, - { - type: Qoo, - nodeType: 'function', - props: {}, - rendered: { - type: 'span', - nodeType: 'host', - props: { className: 'Qoo' }, - rendered: ['Hello World!'], - }, - }, - ], - }, - }, - }, -}; - - -renderer = Adapter.createRenderer({ - // this is "shallow", but only if we specify - // not to call this on the root node... which - // is kind of strange. - isHost: () => true, -}); - -renderer.render(); - -// Conceptual debug output: -// -// -// -// -// -// - -// Expected tree output -// tree = renderer.getNode(); -tree = { - type: Bam, - nodeType: 'function', - props: {}, - rendered: { - type: Bar, - nodeType: 'host', - props: { special: true }, - rendered: [ - { - type: Qoo, - nodeType: 'host', - props: {}, - rendered: null, - }, - ], - }, -}; - -renderer.render( -
- -
-); - -// Conceptual debug output: -// -//
-// -// - -// Expected tree output -// tree = renderer.getNode(); -tree = { - type: 'div', - nodeType: 'host', - props: {}, - rendered: [ - { - type: Foo, - props: {}, - nodeType: 'host', - rendered: null, - }, - ], -}; - -renderer = Adapter.createRenderer({ - // In this case, we treat `Bar` as a host node - // but `Qoo` is not, so gets rendered - isHost: el => [Bar].includes(el.type), -}); - -renderer.render(); - -// Conceptual debug output: -// -// -// -// Hello World! -// -// -// - -// Expected tree output -// tree = renderer.getNode(); -tree = { - type: Bam, - nodeType: 'function', - props: {}, - rendered: { - type: Bar, - nodeType: 'host', - props: { special: true }, - rendered: [ - { - type: Qoo, - nodeType: 'function', - props: {}, - rendered: { - type: 'span', - nodeType: 'host', - props: { className: 'Qoo' }, - rendered: ['Hello World!'], - }, - }, - ], - }, -}; diff --git a/docs/guides.md b/docs/guides.md index 78db45470..5012fc7c2 100644 --- a/docs/guides.md +++ b/docs/guides.md @@ -1,11 +1,11 @@ -# Enzyme Guides +# enzyme Guides -- [*Using Enzyme with Browserify*](guides/browserify.md) -- [*Using Enzyme with WebPack*](guides/webpack.md) -- [*Using Enzyme with JSDOM*](guides/jsdom.md) -- [*Using Enzyme with Jest*](guides/jest.md) -- [*Using Enzyme with Karma*](guides/karma.md) -- [*Using Enzyme with Mocha*](guides/mocha.md) -- [*Using Enzyme with React Native*](guides/react-native.md) -- [*Using Enzyme with Lab*](guides/lab.md) -- [*Using Enzyme with Tape and AVA*](guides/tape-ava.md) +- [*Using enzyme with Browserify*](guides/browserify.md) +- [*Using enzyme with WebPack*](guides/webpack.md) +- [*Using enzyme with JSDOM*](guides/jsdom.md) +- [*Using enzyme with Jest*](guides/jest.md) +- [*Using enzyme with Karma*](guides/karma.md) +- [*Using enzyme with Mocha*](guides/mocha.md) +- [*Using enzyme with React Native*](guides/react-native.md) +- [*Using enzyme with Lab*](guides/lab.md) +- [*Using enzyme with Tape and AVA*](guides/tape-ava.md) diff --git a/docs/guides/browserify.md b/docs/guides/browserify.md index a92ce4667..75e351c96 100644 --- a/docs/guides/browserify.md +++ b/docs/guides/browserify.md @@ -1,52 +1,9 @@ -# Using Enzyme with Browserify +# Using enzyme with Browserify If you are using a test runner that runs code in a browser-based environment, you may be using -[browserify]() in order to bundle your React code. +[browserify](http://browserify.org/) in order to bundle your React code. -Browserify uses static analysis to create a dependency graph at build-time of your source code to -build a bundle. Enzyme has a hand full of conditional `require()` calls in it in order to remain -compatible with React 0.13 and React 0.14. - -Unfortunately, these conditional requires mean there is a bit of extra setup with bundlers like -browserify. - -In your browserify configuration, you simply need to make sure that the following files are -labeled as "external", which means they will be ignored: - -``` -react/addons -react/lib/ReactContext -react/lib/ExecutionEnvironment -``` - -Here is an example piece of configuration code marking these as external: - -```js -const browserify = require('browserify'); - -const b = browserify(); - -// make sure to mark these as external! -b.external('react/addons'); -b.external('react/lib/ReactContext'); -b.external('react/lib/ExecutionEnvironment'); - -// the rest of your browserify configuration -``` - - -## React 0.14 Compatibility - -If you are using React 0.14, the instructions above will be the same but with a different list of -externals: - -``` -react-dom -react-dom/server -react-addons-test-utils -``` - - -## Example Projects - -- [enzyme-example-karma](https://github.com/lelandrichardson/enzyme-example-karma) +Prior to enzyme 3.0 there were some issues with conditional requires that were used +to maintain backwards compatibility with React versions. With enzyme 3.0+, this +should no longer be an issue. If it is, please file a GitHub issue or make a PR +to this documentation with instructions on how to set it up. diff --git a/docs/guides/jest.md b/docs/guides/jest.md index ebd0ff950..fa4347dbb 100644 --- a/docs/guides/jest.md +++ b/docs/guides/jest.md @@ -1,10 +1,10 @@ -# Using Jest with Enzyme +# Using Jest with enzyme ## Jest version 15 and up -Starting with version 15, Jest [no longer mocks modules by default](https://facebook.github.io/jest/blog/2016/09/01/jest-15.html). Because of this, you no longer have to add _any_ special configuration for Jest to use it with Enzyme. +Starting with version 15, Jest [no longer mocks modules by default](https://facebook.github.io/jest/blog/2016/09/01/jest-15.html). Because of this, you no longer have to add _any_ special configuration for Jest to use it with enzyme. -Install Jest, and its Babel integrations, as recommended in the [Jest docs](https://facebook.github.io/jest/docs/getting-started.html). Install Enzyme. Then, simply require/import React, Enzyme functions, and your module at the top of a test file. +Install Jest, and its Babel integrations, as recommended in the [Jest docs](https://facebook.github.io/jest/docs/getting-started.html). Install enzyme. Then, simply require/import React, enzyme functions, and your module at the top of a test file. ```js import React from 'react'; @@ -21,7 +21,7 @@ You do **not** need to include Jest's own renderer, unless you want to use it _o ## Jest prior to version 15 -If you are using Jest 0.9 – 14.0 with Enzyme and using Jest's automocking feature, you will need to mark react and enzyme to be unmocked in your `package.json`: +If you are using Jest 0.9 – 14.0 with enzyme and using Jest's automocking feature, you will need to mark react and enzyme to be unmocked in your `package.json`: `package.json`: ```json @@ -36,7 +36,3 @@ If you are using Jest 0.9 – 14.0 with Enzyme and using Jest's automocking feat ``` If you are using a previous version of Jest together with npm3, you may need to unmock [more modules](https://github.com/airbnb/enzyme/blob/78febd90fe2fb184771b8b0356b0fcffbdad386e/docs/guides/jest.md). - -## Example Project for Jest prior to version 15 - -- [enzyme-example-jest](https://github.com/lelandrichardson/enzyme-example-jest) diff --git a/docs/guides/jsdom.md b/docs/guides/jsdom.md index 943c3926f..7f02d15c0 100644 --- a/docs/guides/jsdom.md +++ b/docs/guides/jsdom.md @@ -1,4 +1,4 @@ -# Using Enzyme with JSDOM +# Using enzyme with JSDOM [JSDOM](https://github.com/tmpvar/jsdom) is a JavaScript based headless browser that can be used to create a realistic testing environment. @@ -115,9 +115,3 @@ nvm use 0.12 ```bash nvm use 4 ``` - - - -## Example Projects - -- [enzyme-example-mocha](https://github.com/lelandrichardson/enzyme-example-mocha) diff --git a/docs/guides/karma.md b/docs/guides/karma.md index 682f4e878..748c2428f 100644 --- a/docs/guides/karma.md +++ b/docs/guides/karma.md @@ -1,13 +1,13 @@ -# Using Enzyme with Karma +# Using enzyme with Karma -Karma is a popular test runner that can run tests in browser environments. Enzyme is compatible with +Karma is a popular test runner that can run tests in browser environments. enzyme is compatible with Karma, but often requires a little bit of configuration. This configuration largely depends on which plugins you are using to bundle your JavaScript code. In the case of Browserify or Webpack, see the below documentation in order to get these up and running. -## Enzyme + Karma + Webpack +## enzyme + Karma + Webpack See the [webpack guide](webpack.md). @@ -28,20 +28,13 @@ module.exports = function karmaConfig(config) { }, }], }, - externals: { - cheerio: 'window', - 'react/addons': true, - 'react/lib/ExecutionEnvironment': true, - 'react/lib/ReactContext': true, - 'react-addons-test-utils': 'react-dom', - }, }, // ... }); }; ``` -## Enzyme + Karma + Browserify +## enzyme + Karma + Browserify See the [browserify guide](browserify.md). @@ -55,21 +48,8 @@ module.exports = function karmaConfig(config) { transform: [ ['babelify', { presets: ['airbnb'] }], ], - configure(bundle) { - bundle.on('prebundle', () => { - bundle.external('react/addons'); - bundle.external('react/lib/ReactContext'); - bundle.external('react/lib/ExecutionEnvironment'); - }); - }, }, // ... }); }; ``` - - -## Example Projects - -- [enzyme-example-karma](https://github.com/lelandrichardson/enzyme-example-karma) -- [enzyme-example-karma-webpack](https://github.com/lelandrichardson/enzyme-example-karma-webpack) diff --git a/docs/guides/lab.md b/docs/guides/lab.md index 6fe23f443..70ad4ab5a 100644 --- a/docs/guides/lab.md +++ b/docs/guides/lab.md @@ -1,9 +1,9 @@ -# Using Enzyme with Lab and Code +# Using enzyme with Lab and Code [Lab](https://github.com/hapijs/lab) is a simple test utility for node & part of the [Hapi.js](https://github.com/hapijs/hapi) framework universe. Lab's initial code borrowed heavily from [Mocha](https://github.com/mochajs/mocha). [Code](https://github.com/hapijs/code) is Lab's standard assertion library and was created as a direct rewrite of [Chai](https://github.com/chaijs). -# Example Test: Enzyme + Lab + Code +# Example Test: enzyme + Lab + Code ```jsx import { shallow, mount, render } from 'enzyme'; diff --git a/docs/future/migration.md b/docs/guides/migration-from-2-to-3.md similarity index 51% rename from docs/future/migration.md rename to docs/guides/migration-from-2-to-3.md index b56592862..388c93525 100644 --- a/docs/future/migration.md +++ b/docs/guides/migration-from-2-to-3.md @@ -1,39 +1,40 @@ -# Migration Guide for Enzyme v2.x to v3.x +# Migration Guide for enzyme v2.x to v3.x -The change from Enzyme v2.x to v3.x is a more significant change than in previous major releases, -due to the fact that the internal implementation has been almost completely rewritten. +The change from enzyme v2.x to v3.x is a more significant change than in previous major releases, +due to the fact that the internal implementation of enzyme has been almost completely rewritten. -The goal of this rewrite was to address a lot of the major issues that have plagued Enzyme since -its initial release. It was also to simultaneously remove a lot of the dependence that Enzyme has -on react internals, and to make enzyme more "pluggable", paving the way for Enzyme to be used +The goal of this rewrite was to address a lot of the major issues that have plagued enzyme since +its initial release. It was also to simultaneously remove a lot of the dependence that enzyme has +on react internals, and to make enzyme more "pluggable", paving the way for enzyme to be used with "React-like" libraries such as Preact and Inferno. -We have done our best to make Enzyme v3 as API compatible with v2.x as possible, however there are +We have done our best to make enzyme v3 as API compatible with v2.x as possible, however there are a hand full of breaking changes that we decided we needed to make, intentionally, in order to -support this new architecture. +support this new architecture and also improve the usability of the library long-term. -Airbnb has one of the largest Enzyme test suites, coming in at around 30,000 enzyme unit tests. -After upgrading Enzyme to v3.x in Airbnb's code base, 99.6% of these tests succeeded with no +Airbnb has one of the largest enzyme test suites, coming in at around 30,000 enzyme unit tests. +After upgrading enzyme to v3.x in Airbnb's code base, 99.6% of these tests succeeded with no modifications at all. Most of the tests that broke we found to be easy to fix, and some we found to actually be depending on what could arguably be considered a bug in v2.x, and the breakage was -desired. +actually desired. In this guide, we will go over a couple of the most common breakages that we ran into, and how to -fix them. Hopefully this will make your upgrade path that much easier. +fix them. Hopefully this will make your upgrade path that much easier. If during your upgrade you +find a breakage that doesn't seem to make sense to you, feel free to file an issue. ## Configuring your Adapter -Enzyme now has an "Adapter" system. This means that you now need to install Enzyme along with -another module that provides the Adapter that tells Enzyme how to work with your version of React +enzyme now has an "Adapter" system. This means that you now need to install enzyme along with +another module that provides the Adapter that tells enzyme how to work with your version of React (or whatever other react-like library you are using). -At the time of writing this, Enzyme publishes "officially supported" adapters for React 0.13.x, +At the time of writing this, enzyme publishes "officially supported" adapters for React 0.13.x, 0.14.x, 15.x, and 16.x. These adapters are npm packages of the form `enzyme-adapter-react-{{version}}`. -You will want to configure Enzyme with the adapter you'd like to use before using enzyme in your -tests. The way to do this is whith `Enzyme.configure(...)`. For example, if your project depends -on React 16, you would want to configure Enzyme this way: +You will want to configure enzyme with the adapter you'd like to use before using enzyme in your +tests. The way to do this is whith `enzyme.configure(...)`. For example, if your project depends +on React 16, you would want to configure enzyme this way: ```js import Enzyme from 'enzyme'; @@ -44,18 +45,20 @@ Enzyme.configure({ adapter: new Adapter() }); The list of adapter npm packages for React semver ranges are as follows: -- `enzyme-adapter-react-16` for `^16.0.0-0` -- `enzyme-adapter-react-15` for `^15.5.0` -- `enzyme-adapter-react-15.4` for `>= 15.0.0 && <15.5.0` -- `enzyme-adapter-react-14` for `^0.14.x` -- `enzyme-adapter-react-13` for `^0.13.x` +| enzyme Adapter Package | React semver compatibility | +| --- | --- | +| `enzyme-adapter-react-16` | `^16.0.0` | +| `enzyme-adapter-react-15` | `^15.5.0` | +| `enzyme-adapter-react-15.4` | `15.0.0-0 - 15.4.x` | +| `enzyme-adapter-react-14` | `^0.14.0` | +| `enzyme-adapter-react-13` | `^0.13.0` | ## Element referential identity is no longer preserved -Enzyme's new architecture means that the react "render tree" is transformed into an intermediate -representation that is common across all react versions so that Enzyme can properly traverse it -independent of React's internal representations. A side effect of this is that Enzyme no longer +enzyme's new architecture means that the react "render tree" is transformed into an intermediate +representation that is common across all react versions so that enzyme can properly traverse it +independent of React's internal representations. A side effect of this is that enzyme no longer has access to the actual object references that were returned from `render` in your React components. This normally isn't much of a problem, but can manifest as a test failure in some cases. @@ -88,16 +91,17 @@ const iconCount = wrapper.find(Icon).length; In v2.x, `iconCount` would be 1. In v3.x, it will be 2. This is because in v2.x it would find all of the elements matching the selector, and then remove any duplicates. Since `ICONS.success` is included twice in the render tree, but it's a constant that's reused, it would show up as a -duplicate in the eyes of Enzyme v2.x. In Enzyme v3, the elements that are traversed are +duplicate in the eyes of enzyme v2.x. In enzyme v3, the elements that are traversed are transformations of the underlying react elements, and are thus different references, resulting in two elements being found. Although this is a breaking change, I believe the new behavior is closer to what people would -actually expect and want. +actually expect and want. Having enzyme wrappers be immutable results in more deterministic tests +that are less prone to flakiness from external factors. ## `children()` now has slightly different meaning -Enzyme has a `.children()` method which is intended to return the rendered children of a wrapper. +enzyme has a `.children()` method which is intended to return the rendered children of a wrapper. When using `mount(...)`, it can sometimes be unclear exactly what this would mean. Consider for example the following react components: @@ -120,6 +124,38 @@ class Foo extends React.Component { } ``` +Now lets say we have a test which does something like: + +```js +const wrapper = mount(); +``` + +At this point, there is an ambiguity about what `wrapper.find(Box).children()` should return. +Although the `` element has a `children` prop of `
`, the actual +rendered children of the element that the box component renders is a `
...
` +element. + +Prior enzyme v3, we would observe the following behavior: + +```js +wrapper.find(Box).children().debug(); +// =>
+``` + +In enzyme v3, we now have `.children()` return the *rendered* children. In other words, it returns +the element that is returned from that component's `render` function. + +```js +wrapper.find(Box).children().debug(); +// => +//
+//
+//
+``` + +This may seem like a subtle difference, but making this change will be important for future APIs +we would like to introduce. + ## For `mount`, updates are sometimes required when they weren't before React applications are dynamic. When testing your react components, you often want to test them @@ -155,18 +191,18 @@ class CurrentTime extends React.Component { ``` In this code, there is a timer that continuously changes the rendered output of this component. This -might be a reasonable thing to do in your application. The thing is, Enzyme has no way of knowing -that these changes are taking place, and no way to automatically update the render tree. In Enzyme -v2, Enzyme operated *directly* on the in-memory representation of the render tree that React itself -had. This means that even though Enzyme couldn't know when the render tree was updated, updates +might be a reasonable thing to do in your application. The thing is, enzyme has no way of knowing +that these changes are taking place, and no way to automatically update the render tree. In enzyme +v2, enzyme operated *directly* on the in-memory representation of the render tree that React itself +had. This means that even though enzyme couldn't know when the render tree was updated, updates would be reflected anyway, since React *does* know. -Enzyme v3 architecturally created a layer where React would create an intermediate representation -of the render tree at an instance in time and pass that to Enzyme to traverse and inspect. This has +enzyme v3 architecturally created a layer where React would create an intermediate representation +of the render tree at an instance in time and pass that to enzyme to traverse and inspect. This has many advantages, but one of the side effects is that now the intermediate representation does not receive automatic updates. -Enzyme does attempt to automatically "update" the root wrapper in most common scenarios, but these +enzyme does attempt to automatically "update" the root wrapper in most common scenarios, but these are only the state changes that it knows about. For all other state changes, you may need to call `wrapper.update()` yourself. @@ -200,7 +236,7 @@ class Counter extends React.Component { This is a basic "counter" component in React. Here our resulting markup is a function of `this.state.count`, which can get updated by the `increment` and `decrement` functions. Let's take a -look at what some Enzyme tests with this component might look like, and when we do or don't have to +look at what some enzyme tests with this component might look like, and when we do or don't have to call `update()`. ```js @@ -223,7 +259,7 @@ wrapper.find('.dec').simulate('click'); wrapper.find('.count').text(); // => "Count: 1" ``` -In this case Enzyme will automatically check for updates after an event simulation takes place, as +In this case enzyme will automatically check for updates after an event simulation takes place, as it knows that this is a very common place for state changes to occur. In this case there is no difference between v2 and v3. @@ -240,11 +276,11 @@ wrapper.instance().decrement(); wrapper.find('.count').text(); // => "Count: 0" (would have been "Count: 1" in v2) ``` -The problem here is that once we grab the instance using `wrapper.instance()`, Enzyme has no way of +The problem here is that once we grab the instance using `wrapper.instance()`, enzyme has no way of knowing if you are going to execute something that will cause a state transition, and thus does not -know when to ask for an updated render tree from React. As a result, `.text()` never changes value. +know when to ask for an updated render tree from React. As a result, `.text()` never changes value. -The fix here is to use Enzyme's `wrapper.update()` method after a state change has occurred: +The fix here is to use enzyme's `wrapper.update()` method after a state change has occurred: ```js const wrapper = shallow(); @@ -261,15 +297,17 @@ wrapper.find('.count').text(); // => "Count: 1" ``` In practice we have found that this isn't actually needed that often, and when it is it is not -difficult to add. This breaking change was worth the architectural benefits of the new adapter -system in v3. +difficult to add. Additionally, having the enzyme wrapper automatically update alongside the real +render tree can result in flaky tests when writing asynchronous tests. This breaking change was +worth the architectural benefits of the new adapter system in v3, and we believe is a better choice +for an assertion library to take. ## `ref(refName)` now returns the actual ref instead of a wrapper -In Enzyme v2, the wrapper returned from `mount(...)` had a prototype method on it `ref(refName)` +In enzyme v2, the wrapper returned from `mount(...)` had a prototype method on it `ref(refName)` that returned a wrapper around the actual element of that ref. This has now been changed to -return the actual ref, which I believe is more intuitive. +return the actual ref, which we believe is a more intuitive API. Consider the following simple react component: @@ -288,16 +326,16 @@ share the same constructor: ```js const wrapper = mount(); -// this is what would happen with Enzyme v2 +// this is what would happen with enzyme v2 expect(wrapper.ref('abc')).toBeInstanceOf(wrapper.constructor); ``` In v3, the contract is slightly changed. The ref is exactly what React would assign as the ref. In -this case, it would be an DOM Element: +this case, it would be a DOM Element: ```js const wrapper = mount(); -// this is what would happen with Enzyme v2 +// this is what happens with enzyme v3 expect(wrapper.ref('abc')).toBeInstanceOf(Element); ``` @@ -318,55 +356,120 @@ const wrapper = mount(); expect(wrapper.ref('abc')).toBeInstanceOf(Box); ``` - In our experience, this is most often what people would actually want and expect out of the `.ref(...)` method. +## With `mount`, `.instance()` can be called at any level of the tree -# New Features in Enzyme v3 +enzyme now allows for you to grab the `instance()` of a wrapper at any level of the render tree, +not just at the root. This means that you can `.find(...)` a specific component, then grab its +instance and call `.setState(...)` or any other methods on the instance that you'd like. -## `instance()` can be called at any level of the tree +## With `mount`, `.getNode()` should not be used. `.instance()` does what it used to. -TODO: talk about this +For `mount` wrappers, the `.getNode()` method used to return the actual component instance. This +method no longer exists, but `.instance()` is functionally equivalent to what `.getNode()` used to +be. +## With `shallow`, `.getNode()` should be replaced with `getElement()` +For shallow wrappers, if you were previously using `.getNode()`, you will want to replace those +calls with `.getElement()`, which is now functionally equivalent to what `.getNode()` used to do. +One caveat is that previously `.getNode()` would return the actual element instance that was +created in the `render` function of the component you were testing, but now it will be a +structurally equal react element, but not referentially equal. Your tests will need to be updated to +account for this. +## Private properties and methods have been removed +There are several properties that are on an enzyme "wrapper" that were considered to be private and +were undocumented as a result. Despite being undocumented, people may haev been relying on them. In +an effort to make making changes less likely to be accidentally breaking in the future, we have +decided to make these properties properly "private". The following properties will no longer be +accessible on enzyme `shallow` or `mount` instances: +- `.node` +- `.nodes` +- `.renderer` +- `.unrendered` +- `.root` +- `.options` +## Cheerio has been updated, thus `render(...)` has been updated as well +enzyme's top level `render` API returns a [Cheerio](https://github.com/cheeriojs/cheerio) object. +The version of Cheerio that we use has been upgraded to 1.0.0. For debugging issues across enzyme +v2.x and v3.x with the `render` API, we recommend checking out [Cheerio's Changelog](https://github.com/cheeriojs/cheerio/blob/48eae25c93702a29b8cd0d09c4a2dce2f912d1f4/History.md) and +posting an issue on that repo instead of enzyme's unless you believe it is a bug in enzyme's use +of the library. +## CSS Selector +enzyme v3 now uses a real CSS selector parser rather than its own incomplete parser implementation. +This is done with [rst-selector-parser](https://github.com/aweary/rst-selector-parser) a fork of [scalpel](https://github.com/gajus/scalpel/) which is a CSS parser implemented with [nearley](https://nearley.js.org/). +We don't think this should cause any breakages across enzyme v2.x to v3.x, but if you believe you +have found something that did indeed break, please file an issue with us. Thank you to +[Brandon Dail](https://github.com/aweary) for making this happen! +## Node Equality now ignores `undefined` values +We have updated enzyme to consider node "equality" in a semantically identical way to how react +treats nodes. More specifically, we've updated enzyme's algorithms to treat `undefined` props as +equivalent to the absence of a prop. Consider the following example: + +```js +class Foo extends React.Component { + render() { + return
; + } +} +``` +With this component, the behavior in enzyme v2.x the behavior would have been like: +```js +const wrapper = shallow(); +wrapper.equals(
); // => false +wrapper.equals(
); // => true +``` +With enzyme v3, the behavior is now as follows: +```js +const wrapper = shallow(); +wrapper.equals(
); // => true +wrapper.equals(
); // => true +``` -# Migration Guide (for React 0.13 - React 15.x) +## Lifecycle methods +enzyme v2.x had an optional flag that could be passed in to all `shallow` calls which would make it +so that more of the component's lifecycle methods were called (such as `componentDidMount` and +`componentDidUpdate`). -## Root Wrapper +With enzyme v3, we have now turned on this mode by default, instead of making it opt-in. It is now +possible to *opt-out* instead. Additionally, you can now opt-out at a global level. -The initially returned wrapper used to be around the element passed -into the `mount` API, and for `shallow` it was around the root node of the rendered output of the element passed in. After the upgrade, the -two APIs are now symmetrical, starting off +If you'd like to opt out globally, you can run the following: - ```js -const x = 'x'; -const Foo = props =>
; -const wrapper = mount(); +import Enzyme from 'enzyme'; + +Enzyme.configure({ disableLifecycleMethods: true }); ``` +This will default enzyme back to the previous behavior globally. If instead you'd only like to opt +enzyme to the previous behavior for a specific test, you can do the following: + ```js -expect(wrapper.props()).to.deep.equal({ outer: x }); -``` +import { shallow } from 'enzyme'; -## for mount, getNode() should not be used. instance() does what it used to. +// ... + +const wrapper = shallow(, { disableLifecycleMethods: true }); +``` diff --git a/docs/guides/mocha.md b/docs/guides/mocha.md index 700dc6854..735936112 100644 --- a/docs/guides/mocha.md +++ b/docs/guides/mocha.md @@ -1,6 +1,6 @@ -# Using Enzyme with Mocha +# Using enzyme with Mocha -Enzyme was originally designed to work with Mocha, so getting it up and running with Mocha should +enzyme was originally designed to work with Mocha, so getting it up and running with Mocha should be no problem at all. Simply install it and start using it: ```bash @@ -24,9 +24,3 @@ describe('', () => { }); ``` - - -## Example Projects - -- [enzyme-example-mocha](https://github.com/lelandrichardson/enzyme-example-mocha) -- [enzyme-example-react-native](https://github.com/lelandrichardson/enzyme-example-react-native) diff --git a/docs/guides/react-native.md b/docs/guides/react-native.md index 580277e53..d2a927cd5 100644 --- a/docs/guides/react-native.md +++ b/docs/guides/react-native.md @@ -1,7 +1,7 @@ -# Using Enzyme to Test Components in React Native +# Using enzyme to Test Components in React Native As of v0.18, React Native uses React as a dependency rather than a forked version of the library, -which means it is now possible to use Enzyme's `shallow` with React Native components. +which means it is now possible to use enzyme's `shallow` with React Native components. Unfortunately, React Native has many environmental dependencies that can be hard to simulate without a host device. @@ -38,8 +38,3 @@ mocha --require react-native-mock/mock --recursive path/to/test/dir // This will mutate `react-native`'s require cache with `react-native-mock`'s. require('react-native-mock/mock'); // <-- side-effects!!! ``` - - -## Example Projects - -- [enzyme-example-react-native](https://github.com/lelandrichardson/enzyme-example-react-native) diff --git a/docs/guides/systemjs.md b/docs/guides/systemjs.md index f4c0ebb2e..e4bf45c4b 100644 --- a/docs/guides/systemjs.md +++ b/docs/guides/systemjs.md @@ -1,73 +1,9 @@ -# Using Enzyme with SystemJS +# Using enzyme with SystemJS If you are using a test runner that runs code in a browser-based environment, you may be using [SystemJS]() in order to bundle your React code. -SystemJS uses static analysis to create a dependency graph at build-time of -your source code to build a bundle. Enzyme has a hand full of conditional -`require()` calls in it in order to remain compatible with React 0.13 and -React 0.14. - -Unfortunately, these conditional requires mean there is a bit of extra setup -with bundlers like SystemJS. - -In your SystemJS configuration, you simply need to make sure that the -following files are labeled as "external", which means they will be ignored: - -``` -react/addons -react/lib/ReactContext -react/lib/ExecutionEnvironment -``` - -Here is an example piece of configuration code marking these as external: - -```json -/* package.json */ -{ - "jspm": { - "overrides": { - "npm:enzyme@2.3.0": { - "map": { - "react/addons": "@empty", - "react/lib/ExecutionEnvironment": "@empty", - "react/lib/ReactContext": "@empty" - } - } - } - } -} -``` - - -## React 0.14 Compatibility - -If you are using React 0.14, the instructions above will be the same but with a different list of -externals: - -``` -react-dom -react-dom/server -react-addons-test-utils -``` - -## React 15 Compatibility - -If you are using React 15, your config should include these externals: - -```json -/* package.json */ -{ - "jspm": { - "overrides": { - "npm:enzyme@2.3.0": { - "map": { - "react/addons": "@empty", - "react/lib/ExecutionEnvironment": "@empty", - "react/lib/ReactContext": "@empty" - } - } - } - } -} -``` +Prior to enzyme 3.0 there were some issues with conditional requires that were used +to maintain backwards compatibility with React versions. With enzyme 3.0+, this +should no longer be an issue. If it is, please file a GitHub issue or make a PR +to this documentation with instructions on how to set it up. diff --git a/docs/guides/tape-ava.md b/docs/guides/tape-ava.md index c2362b800..3c554555f 100644 --- a/docs/guides/tape-ava.md +++ b/docs/guides/tape-ava.md @@ -1,10 +1,10 @@ -# Using Enzyme with Tape and AVA +# Using enzyme with Tape and AVA -Enzyme works well with [Tape](https://github.com/substack/tape) and [AVA](https://github.com/avajs/ava). +enzyme works well with [Tape](https://github.com/substack/tape) and [AVA](https://github.com/avajs/ava). Simply install it and start using it: ```bash -npm i --save-dev enzyme +npm i --save-dev enzyme enzyme-adapter-react-16 ``` ## Tape @@ -12,10 +12,13 @@ npm i --save-dev enzyme ```jsx import test from 'tape'; import React from 'react'; -import { shallow, mount } from 'enzyme'; +import { shallow, mount, configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; import Foo from '../path/to/foo'; +configure({ adapter: new Adapter() }); + test('shallow', (t) => { const wrapper = shallow(); t.equal(wrapper.contains(Foo), true); @@ -34,10 +37,13 @@ test('mount', (t) => { ```jsx import test from 'ava'; import React from 'react'; -import { shallow, mount } from 'enzyme'; +import { shallow, mount, configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; import Foo from '../path/to/foo'; +configure({ adapter: new Adapter() }); + test('shallow', (t) => { const wrapper = shallow(); t.is(wrapper.contains(Foo), true); diff --git a/docs/guides/webpack.md b/docs/guides/webpack.md index de20b03ff..4895f515b 100644 --- a/docs/guides/webpack.md +++ b/docs/guides/webpack.md @@ -1,92 +1,9 @@ -# Using Enzyme with Webpack +# Using enzyme with Webpack If you are using a test runner that runs code in a browser-based environment, you may be using -[webpack]() in order to bundle your React code. +[webpack](https://webpack.js.org/) in order to bundle your React code. -Webpack uses static analysis to create a dependency graph at build-time of your source code to -build a bundle. Enzyme has a handful of conditional `require()` calls in it in order to remain -compatible with React 0.13 and React 0.14. - -Unfortunately, these conditional requires mean there is a bit of extra setup with bundlers like -webpack. - -In your webpack configuration, you simply need to make sure that the following files are -labeled as "external", which means they will be ignored: - -``` -cheerio -react/addons -react/lib/ReactContext -react/lib/ExecutionEnvironment -``` - -Depending on if you are using Webpack 1 or Webpack 2 you will need different configurations. - -### Webpack 1 - -```js -/* webpack.config.js */ -module.exports = { - // ... - externals: { - cheerio: 'window', - 'react/addons': true, - 'react/lib/ExecutionEnvironment': true, - 'react/lib/ReactContext': true, - 'react-addons-test-utils': 'react-dom', - }, - // ... -}; -``` - -### Webpack 2 - -```js -/* webpack.config.js */ -module.exports = { - // ... - externals: { - cheerio: 'window', - 'react/addons': 'react', - 'react/lib/ExecutionEnvironment': 'react', - 'react/lib/ReactContext': 'react', - 'react-addons-test-utils': 'react-dom', - }, - // ... -}; -``` - - -## React 0.14 Compatibility - -If you are using React 0.14, the instructions above will be the same but with a different list of -externals: - -``` -cheerio -react-dom -react-dom/server -react-addons-test-utils -``` - -## React 15 Compatibility - -If you are using React 15, your config should include these externals: - -```js -/* webpack.config.js */ -module.exports = { - // ... - externals: { - 'react/addons': true, - 'react/lib/ExecutionEnvironment': true, - 'react/lib/ReactContext': true, - 'react-addons-test-utils': 'react-dom', - }, - // ... -}; -``` - -## Example Projects - -- [enzyme-example-karma-webpack](https://github.com/lelandrichardson/enzyme-example-karma-webpack) +Prior to enzyme 3.0 there were some issues with conditional requires that were used +to maintain backwards compatibility with React versions. With enzyme 3.0+, this +should no longer be an issue. If it is, please file a GitHub issue or make a PR +to this documentation with instructions on how to set it up. diff --git a/docs/installation/README.md b/docs/installation/README.md index 6d2668cce..d2cd67de6 100644 --- a/docs/installation/README.md +++ b/docs/installation/README.md @@ -1,17 +1,19 @@ # Installation -Enzyme should be installed using npm: +enzyme should be installed using npm: ```bash npm i --save-dev enzyme ``` -Enzyme can be used with your test runner of choice. All examples in the documentation will be +enzyme can be used with your test runner of choice. All examples in the documentation will be provided using [mocha](https://mochajs.org/) and [BDD style chai](http://chaijs.com/api/bdd/), although neither library is a dependency of enzyme. -{% include "./react-013.md" %} +{% include "./react-16.md" %} + +{% include "./react-15.md" %} {% include "./react-014.md" %} -{% include "./react-15.md" %} +{% include "./react-013.md" %} diff --git a/docs/installation/react-013.md b/docs/installation/react-013.md index b89bd1fb0..c889d31ce 100644 --- a/docs/installation/react-013.md +++ b/docs/installation/react-013.md @@ -1,6 +1,6 @@ # Working with React 0.13 -If you are wanting to use Enzyme with React 0.13, but don't already have React 0.13 installed, you +If you are wanting to use enzyme with React 0.13, but don't already have React 0.13 installed, you should do so: ```bash @@ -10,13 +10,22 @@ npm i react@0.13 --save Next, to get started with enzyme, you can simply install it with npm: ```bash -npm i --save-dev enzyme +npm i --save-dev enzyme enzyme-adapter-react-13 ``` And then you're ready to go! In your test files you can simply `require` or `import` enzyme: ES6: ```js +// setup file +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-13'; + +configure({ adapter: new Adapter() }); +``` + +```js +// test file import { shallow, mount, render } from 'enzyme'; const wrapper = shallow(); @@ -25,8 +34,17 @@ const wrapper = shallow(); ES5: ```js +// setup file +var Enzyme = require('enzyme'); +var Adapter = require('enzyme-adapter-react-13'); + +configure({ adapter: new Adapter() }); +``` + + +```js +// test file var enzyme = require('enzyme'); var wrapper = enzyme.shallow(); ``` - diff --git a/docs/installation/react-014.md b/docs/installation/react-014.md index e7a92ec06..388509ea9 100644 --- a/docs/installation/react-014.md +++ b/docs/installation/react-014.md @@ -7,7 +7,7 @@ installed, you should do so: npm i --save react@0.14 react-dom@0.14 ``` -Further, enzyme requires the test utilities addon be installed: +Further, enzyme with React 0.14 requires the test utilities addon be installed: ```bash npm i --save-dev react-addons-test-utils@0.14 @@ -16,13 +16,22 @@ npm i --save-dev react-addons-test-utils@0.14 Next, to get started with enzyme, you can simply install it with npm: ```bash -npm i --save-dev enzyme +npm i --save-dev enzyme enzyme-adapter-react-14 ``` And then you're ready to go! In your test files you can simply `require` or `import` enzyme: ES6: ```js +// setup file +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-14'; + +configure({ adapter: new Adapter() }); +``` + +```js +// test file import { shallow, mount, render } from 'enzyme'; const wrapper = shallow(); @@ -31,9 +40,17 @@ const wrapper = shallow(); ES5: ```js -var enzyme = require('enzyme'); +// setup file +var Enzyme = require('enzyme'); +var Adapter = require('enzyme-adapter-react-14'); -var wrapper = enzyme.shallow(); +configure({ adapter: new Adapter() }); ``` + +```js +// test file +var enzyme = require('enzyme'); +var wrapper = enzyme.shallow(); +``` diff --git a/docs/installation/react-15.md b/docs/installation/react-15.md index 8418142c6..721fb1f14 100644 --- a/docs/installation/react-15.md +++ b/docs/installation/react-15.md @@ -4,25 +4,34 @@ If you are wanting to use Enzyme with React 15, but don't already have React 15 installed, you should do so: ```bash -npm i --save react@15.0.0-rc.2 react-dom@15.0.0-rc.2 +npm i --save react@15 react-dom@15 ``` Further, enzyme requires the test utilities addon be installed: ```bash -npm i --save-dev react-addons-test-utils@15.0.0-rc.2 +npm i --save-dev react-test-renderer@15 ``` Next, to get started with enzyme, you can simply install it with npm: ```bash -npm i --save-dev enzyme +npm i --save-dev enzyme enzyme-adapter-react-15 ``` And then you're ready to go! In your test files you can simply `require` or `import` enzyme: ES6: ```js +// setup file +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-15'; + +configure({ adapter: new Adapter() }); +``` + +```js +// test file import { shallow, mount, render } from 'enzyme'; const wrapper = shallow(); @@ -31,6 +40,16 @@ const wrapper = shallow(); ES5: ```js +// setup file +var Enzyme = require('enzyme'); +var Adapter = require('enzyme-adapter-react-15'); + +configure({ adapter: new Adapter() }); +``` + + +```js +// test file var enzyme = require('enzyme'); var wrapper = enzyme.shallow(); diff --git a/docs/installation/react-16.md b/docs/installation/react-16.md new file mode 100644 index 000000000..1cef870f0 --- /dev/null +++ b/docs/installation/react-16.md @@ -0,0 +1,56 @@ +# Working with React 16 + +If you are wanting to use enzyme with React 16, but don't already have React 16 and react-dom +installed, you should do so: + +```bash +npm i --save react@16 react-dom@16 +``` + +Further, enzyme requires the test utilities addon be installed: + +```bash +npm i --save-dev react-test-renderer@16 +``` + +Next, to get started with enzyme, you can simply install it with npm: + +```bash +npm i --save-dev enzyme enzyme-react-adapter-16 +``` + +And then you're ready to go! In your test files you can simply `require` or `import` enzyme: + +ES6: +```js +// setup file +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; + +configure({ adapter: new Adapter() }); +``` + +```js +// test file +import { shallow, mount, render } from 'enzyme'; + +const wrapper = shallow(); +``` + +ES5: + +```js +// setup file +var Enzyme = require('enzyme'); +var Adapter = require('enzyme-adapter-react-16'); + +configure({ adapter: new Adapter() }); +``` + + +```js +// test file +var enzyme = require('enzyme'); + +var wrapper = enzyme.shallow(); +``` diff --git a/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js b/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js index 19aef1359..564332de7 100644 --- a/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js +++ b/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js @@ -4,6 +4,7 @@ import ReactDOM from 'react-dom'; import ReactDOMServer from 'react-dom/server'; // eslint-disable-next-line import/no-unresolved, import/extensions import TestUtils from 'react-dom/test-utils'; +// eslint-disable-next-line import/no-unresolved, import/extensions import ShallowRenderer from 'react-test-renderer/shallow'; import values from 'object.values'; import { EnzymeAdapter } from 'enzyme';