Enzyme: Trouble with compound selector

Created on 25 May 2016  ·  8Comments  ·  Source: enzymejs/enzyme

I use css-modules and have trouble with this selector:

.PlaybackBar__minimize___Psnl-.PlaybackBar__button___mzBxL
const wrapper = shallow(<div><button className="PlaybackBar__minimize___Psnl- PlaybackBar__button___mzBxL"></button></div>);
expect(wrapper.find('.PlaybackBar__minimize___Psnl-.PlaybackBar__button___mzBxL')).to.have.length(1);

Problem here https://github.com/nfpiche/enzyme/blob/master/src/Utils.js#L179.
I suggest to supplement the regex.

bug

Most helpful comment

@ljharb I just verified that rst-selector-parser correctly parses class names with leading _ or -. So this should be resolved in v3!

All 8 comments

Hey @fresk-nc, thanks for the issue! I was able to reproduce a simplified case. You can see it here: Aweary/enzyme-test-repo/blob/issue-413/test.js. The issue is the dash after the first class name. You can see in the test case that by adding a trailing dash:

const firstClass = 'foo'
const secondClass = 'bar'

const transformedFirstClass = firstClass + '-'
const transformedSecondClass = secondClass

It causes the query to fail. I did verify that a trailing dash works fine in an actual DOM environment:

div = document.createElement('div')
div.classList.add('foo-')
div.classList.add('bar')
document.body.appendChild(div)
document.querySelector('.foo-.bar')
// > <div class=​"foo- bar">​</div>​

So we should definitely fix this so it matches querySelector's behavior. We'll look into this as soon as we can 👍

I'm going to use this issue to document any other valid compound selectors that aren't being identified correctly. I'm testing these against what document.querySelector allows in the latest version of Chrome, FWIW.

Leading attribute queries

'[foo="true"].bar'
'[foo="true"][bar="true"]'

IDs with leading underscore

.foo#_bar

Also broken,

test.only('weird-but-not-that-weird input names', t => {
  const $ = mount(<input type="checkbox" name="hours[]" value="0" />);
  t.is($.find('[name="hours[]"]').length, 1);
});

Compound selector is unable to handle if className starts with _ or - it seems. I just hit this one..

it.only('Selector Bug', () => {
        const wrapper = shallow(
            <div className='abc'>
                <div className='_startsWithUnderscore'/>
                <div className='nounderscore'/>
            </div>           
        );
        should(wrapper.find('div.nounderscore').length).be.exactly(1); //passes
        should(wrapper.find('.nounderscore').length).be.exactly(1); //passes
        should(wrapper.find('._startsWithUnderscore').length).be.exactly(1); //passes
         //below fails
        should(wrapper.find('div._startsWithUnderscore').length).be.exactly(1);

    });

a quick look this seems to be where the issue is https://github.com/airbnb/enzyme/blob/master/src/Utils.js#L179

Just discovered this same issue attempting to test CSS Modules also.

The regex here only covers selectors beginning and ending with an alpha character
https://github.com/airbnb/enzyme/blob/master/src/Utils.js#L179

However the spec allows for much more than that.

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance, the identifier "B&W?" may be written as "B&W\?" or "B\26 W\3F".

https://www.w3.org/TR/CSS2/syndata.html#value-def-identifier

Unrealistic to cover the spec as it's not really "regular". But perhaps we can widen the gamut? Happy to submit PR if anyone has suggestions on what a nice middle ground to cover is?

As a side note, those hoping to test cssmodules, a workaround is to generate a valid local scope string. I'm using css-modules-require-hook in my test-setup.js

import hook from 'css-modules-require-hook';
import sass from 'node-sass';

hook({
  extensions: [ '.scss' ],
  preprocessCss: data => sass.renderSync({ data }).css,
  generateScopedName: '[name]__[local]',
});

I've run into an issue with not being able to find compound selectors. I thought it might be similar to @dannyshaw's issue since I'm using a setup similar to his, but after removing the underscores from the class names I'm still running into the issue.

If my component looks like this:

<button>
  <i className={ classNames(styles.a, styles.b, styles.c) } />
</button>

I can't select it:

expect(button().find(`.${styles.icon}`).length).to.eq(1);

I've been able to get around this by using findWhere.

button().findWhere(n => n.hasClass(styles.icon))

@aweary is this covered by v3?

@ljharb I just verified that rst-selector-parser correctly parses class names with leading _ or -. So this should be resolved in v3!

Was this page helpful?
0 / 5 - 0 ratings