Enzyme: Importing named component does not have name in render

Created on 1 Mar 2018  ·  8Comments  ·  Source: enzymejs/enzyme

Current behavior

If I use a default exported component I can see and select it by name in both shallow rendered and mounted tests.

Default import Header in the App.js

import React, { Component } from 'react';
import Header from './components/Header';

class App extends Component {
  render() {
    return (
      <div>
        <Header />
      </div>
    );
  }
}

export default App;

components/Header.js with default export

import React from 'react';

const Header = () => (<h1>Test</h1>)

export default Header // I have default export here

App.test.js

import React from 'react';
import { shallow } from 'enzyme';
import App from './App';

it('renders without crashing', () => {
  const wrapper = shallow(<App />);

  console.log(wrapper.debug())
});

Test output

 PASS  src/App.test.js
  ✓ renders without crashing (1ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.536s, estimated 1s
Ran all test suites.

  console.log src/App.test.js:8
    <div>
      <Header />
    </div>

But if I use named export on the same component, the shallow render and mount just shows it as <Component /> instead of showing their name.

components/Header.js with named export

import React from 'react';

export const Header = () => (<h1>Test</h1>)  // I have named export here

Named import in App.js

import React, { Component } from 'react';
import { Header } from './components/Header';

class App extends Component {
  render() {
    return (
      <div>
        <Header />
      </div>
    );
  }
}

export default App;

Test output:

 PASS  src/App.test.js
  ✓ renders without crashing (1ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.547s, estimated 1s
Ran all test suites.

  console.log src/App.test.js:8
    <div>
      <Component />
    </div>

Expected behavior

I would see the name of the component for named exported components too. So in both cases the output should show:

<div>
  <Header />
</div>

Your environment

API

  • [x] shallow
  • [x] mount
  • [ ] render

Version

| library | version
| ---------------- | -------
| Enzyme | 3.3.0
| React | 16.2.0

Adapter

  • [x] enzyme-adapter-react-16
question

Most helpful comment

This is because arrow functions don’t have names, so you’re relying on name inference. Use normal named functions for SFCs and you won’t have any name problems.

All 8 comments

This is because arrow functions don’t have names, so you’re relying on name inference. Use normal named functions for SFCs and you won’t have any name problems.

@ljharb Anonymous function name inference is part of the specs so that should work.

On the other note, perhaps this could be caused by transpilers - how they handle modules.
For example, depending on the TS config, this:

export const Foo = () => {};

could be transpiled into:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Foo = () => { };

and Foo would loose it's name. 🤷‍♂️

While babel would handle that correctly and preserve the name:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Foo = void 0;
var Foo = function Foo() {};
exports.Foo = Foo;

p.s. re TS this is already reported as a bug, you can read more here
p.s.s. perhaps this should do the trick:

const foo = () => ({ foo: 'foo' })
export { foo }

The point is that it’s unintuitive and not exhaustive when inference will happen; I’m quite aware it’s in the spec.

Use displayName attribute

I came across an interesting alternative to writing functions. Its possible to assign a display name attribute to your components like so.

const MyComp = () => <div>my comp</div> 
MyComp.displayName = 'MyComp';
export { MyComp };

When running jest now you will get the display name.

Indeed. The better alternative, rather than using displayName to hack around having an unnamed function, is to use a normal named function.

@ljharb I didn't realise it was a hacky solution. The project I'm working on has strict rules for declaring components. Cheers for the info.

Also, on a side note, I'm a big fan of your posts here on github. They have taught me so much.
Thanks!!

@joeLloyd

const MyComp = () => <div>my comp</div>
MyComp.displayName = 'MyComp';
export { MyComp };

Would work even without disaplyName, as you declare the function prior exporting it would have correctly inferred name.

@grundmanise cheers for the tip. didn't realise that would would also work.

Was this page helpful?
0 / 5 - 0 ratings