testing-library / jest-dom

:owl: Custom jest matchers to test the state of the DOM
https://testing-library.com/docs/ecosystem-jest-dom
MIT License
4.44k stars 401 forks source link

.toHaveStyle() does not get font-size w/ css variable #322

Open KOREAN139 opened 3 years ago

KOREAN139 commented 3 years ago

Relevant code or config:

  :root {
    --font-size-xlarge: 64px;
    --font-weight-bold: 700;
  }

  .title {
    font-size: var(--font-size-xlarge);
    font-weight: var(--font-weight-bold);
    margin: auto 0;
  }
    <div data-testid="title" class="title">Title</div>
    const search = getByTestId('search');
    expect(title).toHaveStyle({
      'font-size': 'var(--font-size-xlarge)',
      'font-weight': 'var(--font-weight-bold)',
    });

What you did:

Test font style w/ @testing-library/jest-dom and @testing-library/svelete-testing-library

What happened:

Test should fail if i set font-size not as var(--font-size-xlarge), but test still passes. But works charm about font-weight.

    expect(element).toHaveStyle()

    - Expected

    - font-size: ;
    - font-weight: var(--font-weight-light);
    + font-weight: var(--font-weight-bold);

      33 |     const settings = getByTestId('settings');
      34 | 
    > 35 |     expect(title).toHaveStyle({
         |                   ^
      36 |       'font-size': 'var(--font-size-xlarge)',
      37 |       'font-weight': 'var(--font-weight-light)',
      38 |     });

Reproduction:

from this commit, install w/ yarn then run test w/ yarn test

Problem description:

Seems like cannot recognize font-size with css variable, as we can see the result of getComputedStyle(title) below

      CSSStyleDeclaration {
        '0': 'display',
        '1': 'font-weight',
        '2': 'margin',
        '3': 'visibility',
        _values: {
          display: 'block',
          'font-weight': 'var(--font-weight-bold)',
          margin: 'auto 0px',
          'margin-top': 'auto',
          'margin-right': '0px',
          'margin-bottom': 'auto',
          'margin-left': '0px',
          visibility: 'visible'
        },
        _importants: {
          display: '',
          'font-size': '',
          'font-weight': '',
          margin: '',
          visibility: undefined
        },
        _length: 4,
        _onChange: [Function (anonymous)]
      }
gnapse commented 3 years ago

Hi, thanks for taking the time to report this.

I am having no luck reproducing it though. I just added the following test to the test module for toHaveStyle and it passes:

  it('handles CSS custom properties', () => {
    const {container, queryByTestId} = render(`
      <div data-testid="title" class="title">Title</div>
    `)

    const style = document.createElement('style')
    style.innerHTML = `
      :root {
        --font-size-xlarge: 64px;
        --font-weight-bold: 700;
      }
      .title {
        font-size: var(--font-size-xlarge);
        font-weight: var(--font-weight-bold);
        margin: auto 0;
      }
    `
    document.body.appendChild(style)
    document.body.appendChild(container)

    const title = queryByTestId('title')
    expect(title).toHaveStyle({
      'font-size': 'var(--font-size-xlarge)',
      'font-weight': 'var(--font-weight-bold)',
    })
  })

Any ideas on what I may be missing? Maybe if you can provide a minimal repo where the issue can be reproduced, that'd be great.

yjcyun commented 3 years ago

Hi, I'm also having a similiar problem with toHaveStyle.

"@nrwl/jest": "^12.0.6" "@testing-library/jest-dom": "^5.11.10" "@testing-library/react": "^11.2.6" "@nrwl/node": "^12.0.6" "styled-components": "^5.2.3", "jest": "^26.6.3"

In my case, the test always passes when css variable is used within Styled Components. I've created a code sandbox here.

The following test should fail because the color is actually set to var(--clr-blue) and also because var(--clr-red) doesn't exist. Am I using toHaveStyle correctly? Is there an alternative for testing the style using jest-dom?

  expect(getByTestId("message-cont")).toHaveStyle(`
    color: var(--clr-red)
  `);

Any help would be greatly appreciated. Thanks.

gnapse commented 3 years ago

As mentioned before, a minimal repository where this can be reproduced would go a long way towards figuring this out.

solimant commented 3 years ago

Any ideas on what I may be missing? Maybe if you can provide a minimal repo where the issue can be reproduced, that'd be great.

@gnapse - have you tried changing your test to something like this?

expect(title).toHaveStyle({
  'font-size': 'var(--font-size-small)', // this should cause a failure, but it doesn't
  'font-weight': 'var(--font-weight-bold)',
})

The reported issue is that the test still passes.

gregmalcolm commented 3 years ago

I just encountered the kind of problem (false positive on a toHaveStyle() assertion) when I was selecting through screen.findByText. Applying a console.log to the returned element told me I was getting a HTMLDivElement which I think was from a wrapper, not the desired component (a <td />). Switching to a more cell specific selector (getByRole('cell', text: /something/) allowed me to use .toHaveStyle() successfully:

 expect(element).toHaveStyle()

    - Expected

    - height: ;
    + height: 56px;

I would guess from all this that toHaveStyle gives surprising false successes if the wrong root element is accidentally selected (in my case the div height was probably inherited).

Note: I know the original complaint was for a getByTestID() selection, but I'm guessing a wrapped selection could still be happening that way

jeongnaehyeok commented 3 years ago

Hey guys, Did you import CSS variable and use it?. I encountered the same problem and realized that the Css variable was not defined. And I tried to use variable using css file at css-in-js.

hughes-ch commented 2 years ago

Just ran into this problem today. Seems like it's related to the jsdom dependency. Based on what I found, it's either due to a bug parsing the :root selector or due to how cascaded property values are implemented. See here.

I'll continue to investigate because this seems like a feature that's needed.

Edit Nov-29-2021: After some more investigation, I don't think there's a workaround, but there seems to be a fix in the pipeline. In the original comment in this thread, the reason for the false positive is that --font-size-xlarge evaluates to the empty string, which means that

expect(title).toHaveStyle({
      'font-size': 'var(--font-size-xlarge)',
      'font-weight': 'var(--font-weight-bold)',
    });

really evaluates to:

expect(title).toHaveStyle({
      'font-size': '', // Not valid - CSS parser will remove this rule
      'font-weight': 'var(--font-weight-bold)',
    });

This particular issue is not actually a problem with jest-dom, but with its JSDOM dependency. It looks like the problem will be fixed with this pull request.

I've included my original suggestion for a workaround below (not sure if it's valid though):

Since it seems to be related to the CSS selectors instead of the variables, a (really ugly) workaround is to put the variables directly in the most specific selector for the unit test:

  it('handles CSS custom properties', () => {
    const {container, queryByTestId} = render(`
      <div data-testid="title" class="title">Title</div>
    `)

    const style = document.createElement('style')
    style.innerHTML = `
      .title {
        --font-size-xlarge: 64px;
        --font-weight-bold: 700;
        font-size: var(--font-size-xlarge);
        font-weight: var(--font-weight-bold);
        margin: auto 0;
      }
    `
    document.body.appendChild(style)
    document.body.appendChild(container)

    const title = queryByTestId('title')
    expect(title).toHaveStyle({
      'font-size': 'var(--font-size-xlarge)',
      'font-weight': 'var(--font-weight-bold)',
    })
  })

Since you are just testing that the correct variables are being loaded by your components, it still meets the spirit of the test, though it's not quite DRY...

hughes-ch commented 2 years ago

I've submitted two pull requests for the JSDOM project to get this feature working: