简体   繁体   中英

BackstopJS - Set common selector for all scenarios

I'm using BackstopJS to run some visual regression tests on some React components. I have all of my components displayed on individual Storybook pages inside a "common" wrapper.

For example, each story in Storybook is set up to display the following:

<div key="my_unique_key" id="component_preview">
  <MyReactComponentHere />
</div>

Since all of my components are displayed on separate pages in isolation inside that common container with the ID component_preview , I'd like to set a selector in BackstopJS for all test suites so as this is the focus of the screen capture for each test (ie. this is so as I avoid capturing any markdown or prop tables displayed with the component on each page).

I know I can individually set this in each scenario as follows:

scenarios: [
  {
    ...
    selectors: [
      'div[id="component_preview"]'
    ],
    ...
  }
],

But given I may have a large number of scenarios (this is an ever growing project so I've no idea as to how many components I will want to capture in isolation in the future), I'd like to be able set this as a general rule for all scenarios and not have to individually set this for every individual scenario.

I've tried setting a selectors array outside of the scenarios configuration but it didn't have any effect.

Is it possible to set a common selector like this for all scenarios without having to set it individually on each scenario?

It's no big deal if I have to set this individually on each scenario (just means more work / duplication of the same configuration) but I'd like to avoid doing that if possible.

OK so I've been doing some work on this and have come up with this solution, which works for what I want, without the need to set a common selector to be captured with every single scenario.

The initial goal was to capture my React components, displayed on Storybook, in isolation (ie. without the markdown or prop tables getting in the way).

Just for everyones reference, these are the relevant dependencies and versions that I'm using (copied and pasted from my projects package.json file):

"@storybook/addon-actions": "^3.4.8",
"@storybook/addon-info": "^3.4.8",
"@storybook/addon-links": "^3.4.8",
"@storybook/addon-options": "^3.4.8",
"@storybook/addons": "^3.4.8",
"@storybook/react": "^3.4.8",
"backstopjs": "^3.2.19",
"prop-types": "^15.6.2",
"react": "^16.4.1",
"react-dom": "^16.4.1"

As a further note, I'm using puppeteer with backstopjs .

The first issue I had to get around was the fact that Storybook displays your component, markdown and prop-tables inside an inner <iframe> element on each page. This caused an issue with backstopjs since the CSS scope had no concept of the inner document inside that inner <iframe> . If my component was bigger than what was visible in the immediate UI then it wouldn't realise that the inner document was longer than the outer one. In addition to this, I wasn't able to set any hideSelectors or removeSelectors for any components inside that inner <iframe> since it was out of scope.

So the first major discovery that helped to isolate that inner <iframe> on its own page was to add iframe.html to the URL as follows (for example - suppose you're running Storybook on your localhost at the default port):

http://localhost:6006/iframe.html?selectedKind=...

This isolates that previously inner <iframe> on its own page without the left menu panel appearing. So, from here, I could now hide and remove selectors as I wanted to since everything was now in scope. The Storybook markdown and prop-tables that are displayed on the page are, conveniently, inside a single <div> element. The unique CSS selector that I used to point to this <div> element is as follows:

div[id="root"] > div > div > div[style*="font-family: -apple-system"]

So what I decided to do, instead of setting up a common selector to be captured with each scenario, was to invoke a common onReadyScript in my backstop.json configuration file as follows:

{
  "id": "suite_name",
  "viewports": [
    ...
  ],
  "onReadyScript": "my-on-ready-script.js",
  "scenarios": [
    ...
  ],
  ...
}

My script, then, was set up to remove the markdown and prop-tables <div> element as follows:

module.exports = async function (puppeteer) {
  /* Remove the markdown and prop tables from the Storybook preview panel */
  await puppeteer
    .$eval('div[id="root"] > div > div > div[style*="font-family: -apple-system"]', (markdownAndPropTables) => {
      markdownAndPropTables.parentNode.remove();
    });
};

This leaves my component displayed completely in isolation on each page and backstopjs , then, can capture that component all on its own.

This is the best solution I've been able to find to achieve my goals with this. I'm putting this out there as a potential solution for everyone else, too. Hopefully there's something in this that will help someone else out there looking to do the same thing that I wanted to!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM