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.41k stars 393 forks source link

`not.toHaveClass()` works only for the first class #471

Open probil opened 2 years ago

probil commented 2 years ago

Relevant code or config:

import { render, screen } from '@testing-library/vue';
import Tag from './Tag';

const renderComponent = (text, { props } = {}) =>
  render(Tag, {
    props,
    slots: { default: text },
  });

it('renders default tag named with proper classes', () => {
  const textInsideTag = 'alice';
  renderComponent(textInsideTag);

  expect(screen.getByText(textInsideTag)).toHaveClass('info', 'medium');
  expect(screen.getByText(textInsideTag)).not.toHaveClass('123', 'medium');
});

What you did:

I was trying to test whether my component doesn't have a list of given classes

What happened:

not.toHaveClass() seems to be ignoring second and other classes. The test above passes ⬆️ . Also passes when I use single string syntax:

expect(screen.getByText(textInsideTag)).not.toHaveClass('123 medium'); // `'medium'` class is present

But fails (as expected) when the class to test is the first one / only one:

expect(screen.getByText(textInsideTag)).not.toHaveClass('medium'); // `'medium'` class is present

Reproduction:

Couldn't fine codesandbox for vue 2 + latest testing library so here's github repo reproducing the issue: https://github.com/probil/testing-library-vue-not-tohave-class-issue

Problem description:

Currently .not.toHaveClass(...classes) works fine only for the first class

Suggested solution:

Check other classes similarly to the first one.

jadeye commented 1 year ago

I'm experiencing the same issue

rpie3 commented 1 year ago

What I am seeing is that the class you are expecting not.toHaveClass for has to be the only argument - if the class that should trigger failure is the first of an array of classes, I am seeing incorrect test results.

tnyo43 commented 1 year ago

I think this is an expected behavior. expect(element).not.toHaveclass(...classes) doesn't mean "none of the classes is had by the element" but "the element does not have all of the classes".

You can find that your supposition is not correct by checking the following assertion passes.

expect(screen.getByText(textInsideTag)).not.toHaveClass('medium', '123');

Basically, expect(x).not.toXxx passes if and only if expect(x).toXxx fails. expect(screen.getByText(textInsideTag)).not.toHaveClass('123', 'medium'); passes because expect(screen.getByText(textInsideTag)).toHaveClass('123', 'medium'); fails since the element does not have "123" class.

You may be able to use exact option if you want to check "none of the classes is had by the element". The following test code checks that the classes of the element exactly matches the expected classes and no other classes like "123".

it("renders default tag with proper classes", () => {
  const textInsideTag = "alice";
  renderComponent(textInsideTag);

  expect(screen.getByText(textInsideTag)).toHaveClass("tag", "info", "medium", { exact: true });
});

Otherwise, you can check each class one by one as link the following code.

it.each([['123'], ['medium']])(
    'renders default tag without %p classes',
    (className) => {
        const textInsideTag = 'alice';
        renderComponent(textInsideTag);

        // fails when `className` is `medium`
        expect(screen.getByText(textInsideTag)).not.toHaveClass(className);
    }
);