tss-react icon indicating copy to clipboard operation
tss-react copied to clipboard

Need help with unit testing generated classes

Open OlIvAl opened this issue 1 year ago • 3 comments

Hello there! I'm using your library in my project. Unfortunately, I couldn't find any examples of writing unit tests for classes generated by the useStyles hook (which returns makeStyles function). For example:

const MenuItem: FC<IProps> = ({
  label,
  url,
  iconSrc,
  active = false,
}) => {
  const { classes, cx } = useMenuItemStyles();
  return (
    <a
      href={url}
      target="_blank"
      rel="noreferrer"
      data-testid="menu-item-link"
      className={cx(
        сlasses.link,
        active && сlasses.active
      )}
    >
      {label}
    </a>
  );
};

it.skip("should render anchor with active class if active prop is true", () => {
  expect.hasAssertions();

  const { getByTestId } = render(
    <ThemeProvider theme={LayoutTheme}>
      <CollapsedMenuItem
        label={label}
        url={url}
        iconSrc={iconSrc}
        active={true}
      />
    </ThemeProvider>
  );

  expect(getByTestId("menu-item-link")).toHaveClass("active");
});

This test, or any other similar test, cannot pass because the active class does not exist. Instead, a class with a hash is generated. Is it possible to control or mock this generation process? In the past, MUI had a StylesProvider where you could set the class name generator using the generateClassName prop.

For example:

const generateClassName: StylesOptions['generateClassName'] = (
  rule,
  sheet
): string => `${sheet?.options.classNamePrefix}-${rule.key}`;

export const provideTheme = (ui: ReactElement): ReactElement => {
  return (
    <ThemeProvider theme={theme}>
      <StylesProvider generateClassName={generateClassName}>
        {ui}
      </StylesProvider>
    </ThemeProvider>
  );
};

I couldn't find similar mechanisms in your library.

OlIvAl avatar Mar 09 '23 08:03 OlIvAl

HI @OlIvAl,

I'll see what I can do but allow me to challange your testing approach.
Why testing for classNames instead of testing for poperties like MUI does?

Because the main strengh of TSS (and Emotion) is to be able to generate the styles dynamically and get rid of the class witching logic.

const MenuItem: FC<IProps> = ({
  label,
  url,
  iconSrc,
  active = false,
}) => {
  const { classes, cx } = useMenuItemStyles({ active });
  return (
    <a
      href={url}
      target="_blank"
      rel="noreferrer"
      data-testid="menu-item-link"
      className={сlasses.link}
    >
      {label}
    </a>
  );
};

const useMenuItemStyles = makeStyles<{ active: boolean; }>()((theme, { active }) => ({
   link: {
      // generate the style dynamically using active
   }
}));

If I wand to provide a mock I have to be able to generate a fingerprint of the params and the theme state, wich isn't trivial to do safely.

garronej avatar Mar 09 '23 12:03 garronej

Afterthough:
Under the hood of TSS it's emotion.
Let's see what they have in store.
Can you test with this and tell me if it work for you: https://emotion.sh/docs/testing

garronej avatar Mar 09 '23 12:03 garronej

Well, I checked and Emotion does snapshot testing. This comes at a cost because you have to maintain your code AND the generated snapshots. I know there's convenience to regenerate the snapshots on almost any test engine, but I'd rather monkey patch the makeStyles method. Does someone know how this could be done or approached?

Regardless of my preference for monkey patching, it would be great to have some examples on the docs for unit testing, or testing in general.

Thanks in advance!

julianinsua avatar Apr 30 '24 10:04 julianinsua