storybookjs / test-runner

🚕 Turn stories into executable tests
https://storybook.js.org/docs/writing-tests/interaction-testing
MIT License
231 stars 72 forks source link

[FR] Support DOMElement based snapshotSerializers #135

Open FokkeZB opened 2 years ago

FokkeZB commented 2 years ago

Styling libraries like Emotion use hashes in their generated classnames. These change even if the change is just whitespace, and will also depend on whether you run start-storybook or build-storybook.

In our (Zapier's) case, this results in snapshots generated locally against start-storybook to differ from those that the CI generates against a Vercel deployed build-storybook.

Emotion has a solution for these, which also inline the styles so that they can be covered by the snapshots as well: https://emotion.sh/docs/@emotion/jest

Unfortunately, this expects toMatchSnapshot() to be called for a DOMElement, while AFAIK via Playwright we only have access to the HTML as a string, via innerHTML().

I've tried:

const jsdom = require("jsdom");
const innerHTML = await elementHandler.innerHTML();
const DOMElement = new jsdom.JSDOM(innerHTML);
expect(DOMElement).toMatchSnapshot();

but that fails with:

jest-emotion requires jsdom. See https://jestjs.io/docs/en/configuration#testenvironment-string for more information

It'd be great if we could somehow support this.

FokkeZB commented 2 years ago

How I worked around for now is use this in test-runner-jest.config.js:

  snapshotSerializers: [
    // Strip unstable Emotion classnames
    '<rootDir>/jest/snapshotSerializer.js',
    // Jest will use the first working one, but including default to be complete
    ...defaultConfig.snapshotSerializers,
  ],

With this as snapshotSerializer.js:

const jestSerializerHtml = require('jest-serializer-html');

const EMOTION_CLASS_PATTERN = /css-[\da-z]+(-[\w-]+)?/g;

module.exports = {
  serialize(val) {
    const withoutEmotion = val.replace(EMOTION_CLASS_PATTERN, 'removedUnstableClass');
    return jestSerializerHtml.print(withoutEmotion);
  },

  test(val) {
    return jestSerializerHtml.test(val);
  },
};
panvourtsis commented 11 months ago

Hi @FokkeZB i made a change on the above PR solution that solves this