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

enduire-happo-test

v0.1.0-beta.9

Published

Visual diffing for UI components

Downloads

4

Readme

Happo.io

Happo is a visual regression testing tool for React. It hooks into your CI environment to compare the visual appearance of your components before and after a change. Screenshots are taken in different browsers and across different screen sizes to ensure consistent cross-browser and responsive styling of your application.

Installation

npm install happo.io --save-dev

Getting started

Before you can run happo, you need to define one or more component example files. Let's assume there's a <Button> component that we're adding examples for. First, create a file called Button-happo.jsx and save it next to your Button.jsx file (if this doesn't match your naming scheme you can use the include option). Add a few exports to this file (yes, you can use ES6 here):

export const primary = () => <Button type="primary">Primary</Button>;
export const secondary = () => <Button type="secondary">Secondary</Button>;

Then, we need to add some configuration. API tokens are used to authenticate you with the remote happo.io service: apiKey and apiSecret. These can be found on your account page at https://happo.io/me. You also need to tell happo what browsers you want to target. In this example, we're using two Firefox targets. One at 1024 x 768 screen ("desktop") and one on a 320 x 640 screen ("mobile").

const { RemoteBrowserTarget } = require('happo');

module.exports = {
  // It's good practice to never store API tokens directly in the config file.
  // Here, we're using environment variables.
  apiKey: process.env.HAPPO_API_KEY,
  apiSecret: process.env.HAPPO_API_SECRET,

  targets: {
    'firefox-desktop': new RemoteBrowserTarget('firefox', {
      viewport: '1024x768',
    }),
    'firefox-mobile': new RemoteBrowserTarget('firefox', {
      viewport: '320x640',
    }),
  },
};

Once we're done with the configuration it's time to try things out. Before we do that, let's add a script to our package.json file so that it's easier to invoke commands:

{
  "scripts": {
    "happo": "happo"
  }
}

This will expose a happo script we can run with

npm run happo run

Go ahead and run that command now.

If things are successful, you'll see something like this at the end of the run:

Uploading report for 07f7bd689025c8e
Done 07f7bd689025c8e

This first run will serve as our baseline. But now we need something to compare that baseline with. A good way to test the whole flow is to make a change to a component example and verify that happo will catch that difference. Open one of your *-happo.jsx files and make some changes, e.g.

export const primary = () => <Button type="primary">PRIMARY</Button>;
export const secondary = () => <Button type="secondary">Secondary</Button>;
export const tertiary = () => <Button type="tertiary">Tertiary</Button>;

Here, we made primary button have ALL CAPS and added a tertiary variant.

Let's run happo a second time:

npm run happo run

This time, we'll get a different hash:

Uploading report for 07f8a31ec5f24...
Done 07f8a31ec5f24

Once the second run is done, we can compare the two runs by passing both hashes to the happo compare action:

$ npm run happo compare 07f7bd689025c8e 07f8a31ec5f24
The two reports are different. View full report at
https://happo.io/compare?q=07f7bd689025c8e..07f8a31ec5f24

If you open this URL in a browser, you'll see something like this: Happo report page

We've now covered the most important steps and commands involved in making a full happo run. Normally, you won't run all these commands locally. Instead, you'll configure your CI environment to do it for you, on every PR/commit/branch pushed. Let's look at how you can do that next.

Defining examples

The default way of defining happo examples for a component is through a ComponentName-happo.jsx file, with an ES export for each variant you are looking to test:

export const primary = () => <Button type="primary">Primary</Button>;
export const secondary = () => <Button type="secondary">Secondary</Button>;

If you are more comfortable with CommonJS syntax, you can export an object instead:

module.exports = {
  primary: () => <Button type="primary">Primary</Button>,
  secondary: () => <Button type="secondary">Secondary</Button>,
};

Happo will infer the component name from the file. In the example above, if the file is named Button-happo.jsx, the inferred name will be Button.

If you want to group multiple components in one file you can export an array instead, with objects defining the component and its variants. This can be handy if you for some reason want to auto-generate happo examples from another source (e.g. a style-guide, component-gallery).

export default [
  {
    component: 'Button',
    variants: {
      primary: () => <Button type="primary">Primary</Button>,
      secondary: () => <Button type="secondary">Secondary</Button>,
    },
  },
  {
    component: 'Icon',
    variants: {
      small: () => <Icon size="small" />,
      large: () => <Icon size="large" />,
    },
  },
]

Integrating with your CI environment

Once you've gone through the Getting Started guide, you should have a good understanding of what commands are involved in making a full, two-pass, happo run. Happo works by running twice. Once to create a baseline, and a second time to compare against this baseline. If you are using a pull-request model to push code to your repo, you can run the baseline run on the commit that the PR/branch was based off of. If you have a commit-by-commit model, you can compare against the previous commit. Since multiple changes might be pushed at the same time, using the same commit as base, there's a happo command you can use to check if the baseline even has to be created:

happo has-report <sha>

If this command exits successfully (exit code 0), the report already exists. In this case, you don't have to checkout that commit and run happo on it. If it exits with a non-zero exit code, the report hasn't been created and you need to do git checkout and run happo.

When you run happo compare <sha1> <sha2>, a message will be printed to stdout containing a link to the page where you can view changes. You can use this message to post a comment on the PR/commit. Additionally, the exit code will tell you whether there was any diffs or not. Exit code zero means no diffs. Any other exit code means there was one or more diffs (including added and removed examples).

CSS Loading Strategies

Happo works best when CSS code is co-located with the components. In some cases, you'll get away with zero configuration to get this working. But in many cases, you'll have to add a little webpack config to the mix. Happo uses webpack under the hood when generating browser-executable javascript. The customizeWebpackConfig config option will let you inject things like webpack loaders to the happo run. E.g.

module.exports = {
  customizeWebpackConfig: (config) => {
    config.module = {
      rules: [
        { test: /\.css$/, use: [{ loader: cssLoader }] },
      ],
    };
   // it's important that we return the modified config
    return config;
  },
}

Configuration

Happo will look for configuration in a .happo.js file in the current working folder. You can override the path to this file through the --config CLI option. This file isn't subject to babel transpilation, so it's best to stay with good old CommonJS syntax unless you're on the very latest Node version.

include

Controls what files happo will grab examples from. The default is '**/@(*-happo|happo).@(js|jsx)'. This option is useful if you want to apply a different naming scheme, e.g. **/*-examples.js.

stylesheets

If you rely on externally loaded stylesheets, list their URLs in this config option, e.g. ['http://cdn/style.css'].

targets

This is where you specify the browsers you want to be part of your happo run. E.g.

module.exports = {
  targets: {
    'firefox-desktop': new RemoteBrowserTarget('firefox', {
      viewport: '1024x768',
    }),
    'firefox-mobile': new RemoteBrowserTarget('firefox', {
      viewport: '320x640',
    }),
    'chrome': new RemoteBrowserTarget('chrome', {
      viewport: '800x600',
    }),
  },
};

Currently supported browsers:

  • firefox
  • chrome

customizeWebpackConfig

A function you can use to override the default webpack config used internally by happo during a run. Make sure to always return the passed in config. E.g.

module.exports = {
  customizeWebpackConfig: (config) => {
    config.module = {
      rules: [
        { test: /\.css$/, use: [{ loader: cssLoader }] },
      ],
    };
   // it's important that we return the modified config
    return config;
  },
}