
Research
Supply Chain Attack on Axios Pulls Malicious Dependency from npm
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.
describe-react-component
Advanced tools
**This is an EXPERIMENTAL package which is not yet fully functional and doesn't yet have much test coverage. If you're interested in getting involved, take a look at the TODO list at the bottom of page. All contributions are welcome.**
This is an EXPERIMENTAL package which is not yet fully functional and doesn't yet have much test coverage. If you're interested in getting involved, take a look at the TODO list at the bottom of page. All contributions are welcome.
An opinionated TDD framework for React components, built for the Jest test runner. Its purpose is to remove all of the boilerplate necessary for writing effective tests. Using this library will help you produce clear, simple tests that focus on behavior and data rather than test setup. That means your tests will be shorter, more consistent, less brittle and better at pinpointing errors.
The "catch" (if you can call it that) is that you'll need to write your tests the way the library wants you to write them. If you're not used to this style of testing then it may be a bit of a culture shock.
In particular, the library does away with shallow rendering and replaces it standard spying and stubbing as you may have practiced in other languages and environments.
Examples are a little bit further below.
describeReactComponent block tests exactly one component.ReactDOM.render. There's no shallow testing (see the final bullet point). Each test gets its own container (using JSDOM), with a freshly mounted component. That way your tests remain independent.First, install the package.
npm install --save-dev describe-react-component
Then modify your Babel configuration if you want spy and stub support (you probably do want this), as shown below.
.babelrcSpying and stubbing works by telling the Babel JSX transform plugin to use createMockableElement function instead of React.createElement. This is a straightforward approach to mocking that looks in a global registry of spies and stubs before forwarding the call to React.createElement.
To get this behaviour in your test environment, you'll need to set up your .babelrc file as shown below.
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties"],
"env": {
"test": {
"plugins": [
[ "@babel/plugin-transform-react-jsx", {
"pragma": "createMockableElement",
"pragmaFrag": "React.Fragment"
}] ]
}
}
}
It's important that the @babel/plugin-transform-react-jsx plugin is configured for the test environment only; you don't want to this code running in production.
You'll need to replace your calls to React.createElement with calls to createMockableElement instead.
import { createMockableElement } from 'describe-react-component/mockableComponent'
Imagine you have this component defined in src/A.js:
const A = () => <div><p>Hello, world!</p></div>
You can write a test suite for that like this:
import { describeReactComponent } from 'describe-react-component'
describeReactComponent(A, () => {
it('renders a Hello, world message', () => {
mount()
expect(container().textContent).toEqual('Hello, world!')
})
})
The first argument to describeReactComponent is not a string like with describe, but instead is the component type. This is type that each of your tests will instrument.
The mount call is optional: if you don't call it, the call to container will do it for you (and so will any of the assertion helper functions--see the table below). We don't recommend one way or the other, but since each of your tests must mount the component at least once you might prefer the concise version:
import { describeReactComponent } from 'describe-react-component'
describeReactComponent(A, () => {
it('renders a _Hello, world_ message', () => {
expect(container().textContent).toEqual('Hello, world!')
})
})
Now let's add in some props to A:
const A2 = ({ firstName }) => <div><p>Hello, {firstName}!</p></div>
There are two ways to specify props. The first is with an explicit call to mountWithProps:
describeReactComponent(A2, () => {
it('renders a _Hello, Jack_ message', () => {
mountWithProps({ firstName: 'Jack' })
expect(container().textContent).toEqual('Hello, Jack!')
})
})
The second way is to call withProps. This allows you to set the default set of props for a set of tests.
describeReactComponent(A2, () => {
withProps({ firstName: 'Jack' })
it('renders a _Hello, Jack_ message', () => {
expect(container().textContent).toEqual('Hello, Jack!')
})
})
Now let's say you have a component F that calls fetch to pull in some data.
const F = () => {
useEffect(() => {
window.fetch('/myapi', { mode: 'origin' })
}, [])
return <div />
}
We want to test that window.fetch is called.
describeReactComponent(F, () => {
withSpy(window, 'fetch')
it('calls window.fetch with /myapi', () => {
mount()
expect(window.fetch).toHaveBeenCalled()
})
})
What if we want to test that the response is used? We can use the fetchResponseOk or fetchResponseError helpers for that.
const F2 = () => {
const [ message, setMessage ] = useState('')
useEffect(() => {
const fetchData = async () => {
const response = await window.fetch('/myapi', { mode: 'origin' })
setMessage(await response.json())
}
fetchData()
}, [])
return <div>{message}</div>
}
Now we can use withStub, which is a spy but with a stubbed response. Since window.fetch asynchronous we need to mount and then wait for our response.
describeReactComponent(F2, () => {
const message = 'Hello, world!'
withStub(window, 'fetch', fetchResponseOk(message))
it('renders the message returned from the window.fetch call', async () => {
await mountAndWait()
expect(container().textContent).toEqual(message)
})
})
Finally, let's assume F is used in a parent component called P. We want to test that P renders F but we don't want F to make that call to window.fetch. So we spy on F:
const P = () => <><F /></>
You can test that using withComponentSpy and the toHaveBeenRendered matcher. Note that P becomes a normal Jest mock, so you could just use the standard Jest matchers here if you wanted. toHaveBeenRendered (and toHaveBeenRenderedWithProps, below) are just 'nicer' versions for React components: they both check that the component is in the tree and that it was called.
describeReactComponent(P, () => {
withComponentSpy(P)
it('renders P', () => {
expect(P).toHaveBeenRendered()
})
})
What about if F had some props? Then you use toHaveBeenRenderedWithProps instead.
const P2 = () => <><F randomProp={123} /></>
describeReactComponent(P2, () => {
withComponentSpy(P)
it('renders P', () => {
expect(P).toHaveBeenRenderedWithProps({ randomProp: 123 })
})
})
Further options: (these could do with better documentation)
withMounted - this is just an extra context that gives you a nice description of "when initially mounted" so you don't have to repeat that in your test descriptions.
describeReactComponent splurts out a bunch of functions onto the global object. Since Jest gives you a fresh Node worker per test suite, we believe this to be fine.
We agree that this is ugly, but we are also pragmatists, and you're running with the context of a test suite, where clarity trumps all else. Rules are made to be broken!
npm run build
# TODO
withSpy are a bit yuk. So we name one withComponentSpy?elementWithClass.querySelectorAll helpers.createMockableElement is installed globally by describeReactComponent. If you aren't using this function for all of your React component testing then JSX transforms will break, so this library probably needs a way to either apply that function when Jest launches.withProps support - it'd probably be nice to merge these with parent? Or have a way to get at the parent context props if you want to do an explicit mergeit.auto for common test names (e.g. renders A, fetches data when button clicked, etc)FAQs
**This is an EXPERIMENTAL package which is not yet fully functional and doesn't yet have much test coverage. If you're interested in getting involved, take a look at the TODO list at the bottom of page. All contributions are welcome.**
We found that describe-react-component demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.