npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@shopify/storybook-a11y-test

v1.2.1

Published

Test storybook pages with axe and puppeteer

Downloads

6,145

Readme

@shopify/storybook-a11y-test

Build Status License: MIT npm version npm bundle size (minified + gzip)

Test Storybook stories with axe® and Puppeteer.

Installation

Add this package to your project’s development dependencies.

yarn add @shopify/storybook-a11y-test --dev

This assumes you’ve installed and set up the Storybook accessibility addon.

In your project’s package.json, add the build-storybook and storybook-a11y-test scripts:

// package.json
"scripts": {
  "build-storybook": "build-storybook --static-dir=.storybook/public --output-dir=build/storybook/static",
  "storybook-a11y-test": "node ./scripts/storybook-a11y-test.js"
},

CI steps

Your CI steps should include:

  1. Building Storybook (yarn run build-storybook)
  2. Running the script (yarn run storybook-a11y-test)

For example:

steps:
  - label: ':storybook: Build Storybook and run accessibility tests'
    run:
      - yarn install
      - yarn run build-storybook
      - yarn run storybook-a11y-test

For optimal test performance, break the build and accessibility testing steps into two separate steps, as illustrated in 🔒 this example (only visible to Shopify employees).

Usage

Make sure you have built your Storybook that you can point the test towards.

const {A11yTestRunner} = require('@shopify/storybook-a11y-test');

(async () => {
  // Full path to your static storybook build
  const buildDir = path.join(__dirname, '../build/storybook/static');

  const testRunner = new A11yTestRunner(buildDir);

  try {
    // Grab all Story IDs
    const storyIds = await testRunner.collectEnabledStoryIdsFromIFrame();

    // Run tests on all stories in `storyIds`
    const results = await testRunner.testStories({
      storyIds,

      // Optional: maximum time in milliseconds to wait for the browser instance to start.
      // Defaults to 30000 (30 seconds). Pass 0 to disable timeout.
      timeout: 30000,
    });

    if (results.length) {
      console.error(`‼️  Accessibility violations found`);
      console.log(results.join('\n'));
      process.exitCode = 1;
    } else {
      console.log('🧚  Accessibility tests passed');
    }
  } finally {
    await testRunner.teardown();
  }
})();

Ignoring violations in a Story

When is it okay to ignore accessibility violations?

  • False positives
  • Work in progress, early stages of building a component
  • Playgrounds, prototypes, work in progress…
MyStory.parameters = {
  a11y: {
    // 🙅‍♀️ Don't do this!
    disable: true, // 💩💩💩
    // It disables all accessibility checks for the story,
    // and we won't know when we introduce regressions.
    //
    // 🙌 Instead, override single rules on specific elements.
    // 👇 see guidelines below

    // @see axe-core configParameter (https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#parameters-1)
    config: {
      rules: [
        {
          // False positives on specific elements
          //
          // You can exclude some elements from raising errors for a specific rule.
          id: 'failing-rule-id',
          selector: '*:not(<selector triggering violation>)',
        },
        {
          // False positive on an entire component
          //
          // In certain cases (like a disabled button), it's okay to disable a rule.
          id: 'failing-rule-id',
          enabled: false,
        },
        {
          // Temporary override (failure "needs review")
          //
          // `reviewOnFail: true` overrides the result of a rule to return
          // "Needs Review" rather than "Violation" if the rule fails.
          //
          // Useful when merging unfinished or early stage work.
          id: 'failing-rule-id',
          reviewOnFail: true,
        },
      ],
    },
  },
};

Ignoring violations: examples

AutocompleteField.parameters = {
  a11y: {
    config: {
      rules: [
        {
          // Add support for `autocomplete="nope"`, a workaround to prevent autocomplete in Chrome
          //
          // @link https://bugs.chromium.org/p/chromium/issues/detail?id=468153
          // @link https://development.shopify.io/engineering/developing_at_Shopify/accessibility/forms/autocomplete
          id: 'autocomplete-valid',
          selector: '*:not([autocomplete="nope"])',
        },
      ],
    },
  },
};
DisabledButton.parameters = {
  a11y: {
    config: {
      rules: [
        {
          // Color contrast ratio doesn't need to meet 4.5:1, as the element is disabled
          //
          // @link https://dequeuniversity.com/rules/axe/4.3/color-contrast
          id: 'color-contrast',
          enabled: false,
        },
      ],
    },
  },
};
PrototypeComponent.parameters = {
  a11y: {
    config: {
      rules: [
        {
          // Page-level semantics cause a violation and need to be reworked.
          // Currently discussing solutions with the accessibility team.
          //
          // @link https://github.com/Shopify/shopify/issues/123
          // @link https://dequeuniversity.com/rules/axe/4.3/landmark-complementary-is-top-level
          id: 'landmark-complementary-is-top-level',
          reviewOnFail: true,
        },
      ],
    },
  },
};

API for A11yTests class

constructor

buildPath string

The location of the built Storybook

collectStoryIdsFromStoriesJSON

Returns all the stories from Storybook the story book JSON. If you are using storyStoreV7 in your storybook config this method can provide speed gains.

collectEnabledStoryIdsFromIFrame(options)

Returns a filtered list of stories ids. This needs to load all the stories and could be slow depending on the amount of stories.

skippedStoryIds array (optional)

An array of Storybook Story IDs to skip.

testStories(options)

Returns the result of testing the stories. Stories that have a11y disabled are skipped.

storyIds

An array of Storybook IDs to run. These can be retrieved via the currentStoryIds() function.

concurrentCount number (optional)

The number of tabs to open in Chromium. The default option is based off the number of CPU cores available os.cpus().length.

timeout number (optional)

The goto timeout for the provided url. Defaults to 3000

waitUntil PuppeteerLifeCycleEvent (optional)

When to consider navigation succeeded. Defaults to load.

  • load : consider navigation to be finished when the load event is fired.
  • domcontentloaded : consider navigation to be finished when the DOMContentLoaded event is fired.
  • networkidle0 : consider navigation to be finished when there are no more than 0 network connections for at least 500 ms.
  • networkidle2 : consider navigation to be finished when there are no more than 2 network connections for at least 500 ms.