GithubHelp home page GithubHelp logo

happo / happo.io Goto Github PK

View Code? Open in Web Editor NEW
196.0 5.0 26.0 2.43 MB

Happo is a cross-browser screenshot testing service

Home Page: https://happo.io

License: MIT License

JavaScript 89.91% Shell 9.04% CSS 0.08% HTML 0.96%
screenshot-testing visual-regression-testing ui-testing

happo.io's Introduction

Happo is a visual regression testing tool. It hooks into your CI environment to compare the visual appearance of UI 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.

See docs.happo.io for up-to-date documentation

happo.io's People

Contributors

allenwilliamson avatar brentertz avatar calleluks avatar dependabot[bot] avatar jamesgeorge007 avatar konstrybakov avatar lencioni avatar nicklee100 avatar parris avatar piperchester avatar sharmilajesupaul avatar trotzig avatar wokayme avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

happo.io's Issues

Move to ”happo” org

The ”enduire” organization doesn’t make sense to most people. The ”happo” name was already taken, so I had to get creative (”enduire” is short for ”end ui regressions”).

The @happo user has no activity except for a forked repo, so I thought I’d try to reach out with an at-mention here in the hopes that they would be willing to hand over the github handle. 🙏

Images created by `next/image` are not rendered in screenshots

Not quite sure what is causing it, if it's a bug or something that just needs to be documented.

Our Next.js application has a logo rendered using the Next.js Image component. Happo jobs are triggered by happo-cypress, and the image is present in the assets collected by Happo.

The screenshots, however, do not display the image, using the Chrome, Firefox, Edge, or Safari browser, only the alt text

Changing the Image to use an HTML img element fixes the issue, as shown in this happo report

[Feature request] Run Function After Comparison Request

I would like to be able to pass to happo config function which will be triggered after sending a synchronous request to the Happo repository.

Usecase

handle statistics about flaky/failing tests for internal needs.

Currently how I can do it

Currently, the thing I am getting back is a simple summary where I need on my own look for data and parse it. IMO it's quite dangerous as I don't know how API is going to change and this simple text can be changed.

Proposed solution

Add the opportunity to pass a function in .happo.js config file which will be run after comparison and get as an argument https://happo.io/docs/api#Comparison response object.

Example PR for better illustration.
I will be happy with the writing implementation, but before I would like to hear feedback:

#242

Script for `happo-ci-circleci` broken on custom base branch

It seems like there's an issue with happo-ci-circleci where it tries to compare BASE_BRANCH against itself after merging into the base branch. I have the BASE_BRANCH set to origin/main. Unfortunately, when that happens it seems like it fails because it tries to treat the base branch as a pull request instead of as a base branch?

I think the relevant error here is Failed to obtain temporary pull-request token. Any thoughts?

#!/bin/sh -eo pipefail
yarn run snapshot

yarn run v1.22.10
$ BASE_BRANCH=origin/main happo-ci-circleci
Using origin/main as the default branch (change this with the BASE_BRANCH environment variable)
Using the following ENV variables:
PREVIOUS_SHA: 2afb53786e3ee7c0781d61908641f56dc70090fa
CURRENT_SHA: 2afb53786e3ee7c0781d61908641f56dc70090fa
CHANGE_URL: https://github.com/company/design-system/commit/2afb53786e3ee7c0781d61908641f56dc70090fa
INSTALL_CMD: 
HAPPO_IS_ASYNC: 
HAPPO_GIT_COMMAND: git
HAPPO_COMMAND: node_modules/happo.io/build/cli.js
Detected yarn.lock - using yarn to install dependencies
We're not on a branch, so there's nothing to compare against.
Running a single happo run on 2afb53786e3ee7c0781d61908641f56dc70090fa
No `apiKey` or `apiSecret` found in config. Falling back to pull-request authentication.
Error: Failed to obtain temporary pull-request token
    at loadUserConfig (/root/project/node_modules/happo.io/build/loadUserConfig.js:89:13)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
Caused by:
StatusCodeError: 400 - "No pull request found"
    at new StatusCodeError (/root/project/node_modules/request-promise-core/lib/errors.js:32:15)
    at Request.plumbing.callback (/root/project/node_modules/request-promise-core/lib/plumbing.js:104:33)
    at Request.RP$callback [as _callback] (/root/project/node_modules/request-promise-core/lib/plumbing.js:46:31)
    at Request.self.callback (/root/project/node_modules/request/request.js:185:22)
    at Request.emit (events.js:314:20)
    at Request.<anonymous> (/root/project/node_modules/request/request.js:1154:10)
    at Request.emit (events.js:314:20)
    at IncomingMessage.<anonymous> (/root/project/node_modules/request/request.js:1076:12)
    at Object.onceWrapper (events.js:420:28)
    at IncomingMessage.emit (events.js:326:22)

/root/project/node_modules/happo.io/build/loadUserConfig.js:89
      throw new _WrappedError.default('Failed to obtain temporary pull-request token', e);

Error "undefined" in TypeScript projects

Cross-posting this issue here. It was reported in the Happo slack channel:

From @benbayard:

I am getting an odd error message when I try implementing the new wrapper module in happo:

./src/application-wrapper.tsx

import * as React from 'react'
import { ThemeProvider } from './core/theme/ThemeProvider'

export default (component: React.ReactNode) => (
    <ThemeProvider>{component}</ThemeProvider>
)

npm run happo run:

> happo "run"

No [sha] provided. A temporary one will be used in place: "dev-307915d3a6d8e9acee26".
Reading files... ✓ 10 found
Creating bundle... undefined
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! <redacted> happo: `happo "run"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the <redacted> happo script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!    <redacted>/2018-10-10T21_37_40_285Z-debug.log

Getting "Accessing nonexistent addons channel" error from storybook

If you are integrating Happo with Storybook and you are using addons, you might see this error:

Error: Uncaught [Error: Accessing nonexistent addons channel, see https://storybook.js.org/basics/faq/#why-is-there-no-addons-channel]

To work around this, you can add the following section to the top of your .storybook/config.js file:

import addons from '@storybook/addons';

if (!addons.getChannel()) {
  // Provide a mock channel to prevent "Accessing nonexistent addons channel"
  // errors from storybook when running with Happo.
  addons.setChannel({
    emit: () => null,
    on: () => null,
  });
}

Running happo with more than 1 test file.

Hey, I'm having some trouble running more than 1 test file.
For context, the two tests that I'm running together are trying to screenshot tall pages.

This is the error I get when I try to run 2 test files:
Screen Shot 2020-07-03 at 5 53 50 PM

Is there a reason for this? Not sure if this is an error on my end or vice versa.

Failures when running Happo

Hey, I sometimes run into some errors when running Happo where I'd get an error like this after seeing a 5 failed GET requests to the same url:
StatusCodeError: 500 - "An error occurred for browser-chrome: Error: connect ECONNREFUSED"

It doesn't happen frequently, but I've come across this error a couple of times, sometimes on consecutive runs too if unlucky.
At the moment, when this error happens, I just rerun Happo and hope that it doesn't run into this error again.

I was wondering if there's any recommendations on what can be done to minimize the chance of this error occurring or if there's a way to run Happo again via CircleCI if Happo couldn't run successfully due to a refused connection error?

CSS animations for opacity are not consistently frozen (at least for Chrome)

I have a number of examples where we are using CSS animations. It seems that the styles intended to freeze the animations are working for animations that affect styles like transform, but it is inconsistent for opacity. When looking at the view source in happo, I can tell that styles like animation-play-state: paused are being applied correctly and appropriately freezing these animations, but I still get diffs where only the opacity is different. I suspect that this could be a browser bug.

I wonder if it would help to add some styles like the following to the set of styles for freezing animations:

animation-delay: 999s !important;
animation-iteration-count: 0 !important;
transition-delay: 999s !important;

I included the transition styles here for completeness and consistency, not because I've seen any problems with these styles.

It might also be worth considering having a filtering step that removes problematic styles instead of trying to override them.

Feature request: add a way to configure targets to use the prefers reduced motion option

Happo is allergic to animations. Some animations, like from animated GIFs, are hard to disable using CSS. I think it would be nice to add an option to targets (or maybe just make it the default) to allow them to be run with the prefers reduced motion option enabled. This would give consumers an easy way to fix this class of spurious diffs while also making their product more accessible.

More info: https://developers.google.com/web/updates/2019/03/prefers-reduced-motion

Dynamic Imports do not work with TypeScript

Since we do not use babel w/ the happo-typescript-plugin the solution for #32 does not work. I still get this error:

This can be fixed in babel@7 with the typescript babel plugin. It might also be possible to use ts-loader to parse the module, then use babel to re-parse it.

Error: Could not load script: “http://localhost/92.happo-bundle-react-L1VzZXJzL2Jlbi9Qcm9qZWN0cy9naXRodWIuY29tL3BhdHJlb24vZGV2eC9zdHVkaW8=.js”
    at onErrorWrapped (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/browser/resources/per-document-resource-loader.js:39:19)
    at Object.check (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:58:23)
    at request.then.catch.err (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:104:14)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7) { RequestError: Error: connect ECONNREFUSED 127.0.0.1:80
    at new RequestError (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/request-promise-core/lib/errors.js:14:15)
    at Request.plumbing.callback (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/request-promise-core/lib/plumbing.js:87:29)
    at Request.RP$callback [as _callback] (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/request-promise-core/lib/plumbing.js:46:31)
    at self.callback (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/request/request.js:185:22)
    at emitOne (events.js:116:13)
    at Request.emit (events.js:211:7)
    at Request.onRequestError (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/request/request.js:881:8)
    at emitOne (events.js:116:13)
    at ClientRequest.emit (events.js:211:7)
    at Socket.socketErrorListener (_http_client.js:387:9)
  name: ‘RequestError’,
  message: ‘Error: connect ECONNREFUSED 127.0.0.1:80’,
  cause:
   { Error: connect ECONNREFUSED 127.0.0.1:80
    at Object._errnoException (util.js:1022:11)
    at _exceptionWithHostPort (util.js:1044:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1182:14)
     code: ‘ECONNREFUSED’,
     errno: ‘ECONNREFUSED’,
     syscall: ‘connect’,
     address: ‘127.0.0.1’,
     port: 80 },
  error:
   { Error: connect ECONNREFUSED 127.0.0.1:80
    at Object._errnoException (util.js:1022:11)
    at _exceptionWithHostPort (util.js:1044:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1182:14)
     code: ‘ECONNREFUSED’,
     errno: ‘ECONNREFUSED’,
     syscall: ‘connect’,
     address: ‘127.0.0.1’,
     port: 80 },
  options:
   { encoding: null,
     gzip: true,
     jar: RequestJar { _jar: [Object] },
     strictSSL: true,
     forever: true,
     headers:
      { ‘User-Agent’: ‘Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/12.0.0’,
        ‘Accept-Language’: ‘en’,
        Accept: ‘*/*’,
        referer: ‘http://localhost/’ },
     uri: ‘http://localhost/92.happo-bundle-react-L1VzZXJzL2Jlbi9Qcm9qZWN0cy9naXRodWIuY29tL3BhdHJlb24vZGV2eC9zdHVkaW8=.js’,
     callback: [Function: RP$callback],
     transform: undefined,
     simple: true,
     resolveWithFullResponse: false,
     transform2xxOnly: false },
  response: undefined }
Error: Loading chunk 92 failed.
    at HTMLScriptElement.onScriptComplete (file:///var/folders/80/lps10hxx28sfr5dkxj7vtkgr0000gn/T/happo-bundle-react-L1VzZXJzL2Jlbi9Qcm9qZWN0cy9naXRodWIuY29tL3BhdHJlb24vZGV2eC9zdHVkaW8=.js:99:24)
    at HTMLScriptElement.el.addEventListener.event (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js:33:32)
    at invokeEventListeners (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
    at HTMLScriptElementImpl._dispatch (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
    at HTMLScriptElementImpl.dispatchEvent (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
    at HTMLScriptElementImpl.dispatchEvent (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
    at onErrorWrapped (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/browser/resources/per-document-resource-loader.js:37:15)
    at Object.check (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:58:23)
    at request.then.catch.err (/Users/ben/Projects/github.com/patreon/devx/studio/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:104:14)
    at <anonymous>
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @patreon/[email protected] happo: `happo "run"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @patreon/[email protected] happo script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/ben/.npm/_logs/2018-10-15T22_35_50_116Z-debug.log

Posting status to non-Github instances

Hello,

I read through the section "Posting status back...", but only Github is ever mentioned here. I can't see any documentation, or if it is possible to integrate with any other repository management platform (e.g. Gitlab).

To support Gitlab CI, I guess being able to post to a webhook might be enough to be able to restart a failed job when new snapshots are accepted.

canvas error when running happo-ci

When I run the happo-ci command, I get this error:

happo.io@^3.17.0:
/home/circleci/project/node_modules/canvas/lib/context2d.js:13
bindings.CanvasRenderingContext2dInit(DOMMatrix, parseFont)
         ^

TypeError: bindings.CanvasRenderingContext2dInit is not a function
    at Object.<anonymous> (/home/circleci/project/node_modules/canvas/lib/context2d.js:13:10)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:690:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object.<anonymous> (/home/circleci/project/node_modules/canvas/lib/canvas.js:11:19)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
error Command failed with exit code 1.

here are my package versions:
node 10.15.3
"canvas": "^2.5.0"
"happo.io": "^3.17.0",

Possible to avoid rebuilding within happo and have it point to an already built bundle?

We're running happo in our CI for our PR builds and that process goes:

  1. Build
  2. Test
  3. Screenshot Tests (Happo)

Part of step 3 is having happo rebuild our bundles, I'm curious if some one can point me in the direction of enabling happo to use the output from step 1. I saw that there is a tmpdir option (https://github.com/happo/happo.io#tmpdir) but haven't been successful with it yet. This is what my current .happo.js looks like (prior to my failed attemps with tmpdir):

module.exports = {
  apiKey: process.env.HAPPO_API_KEY,
  apiSecret: process.env.HAPPO_API_SECRET,
  plugins: [
    happoPluginStorybook({
      configDir: 'docs/.storybook',
      outputDir: '.happo-out',
    }),
  ],
  targets: {
    chrome: new RemoteBrowserTarget('chrome', {
      viewport: '1280x1024',
    }),
  },
  type: 'react',
};

No warning shows up when an unknown command is passed in

Expected output

happo <junkcmd>

Invalid command a

Usage: happo [options]

Options:
  -V, --version            output the version number
  -c, --config <path>      set config path (default: "./.happo.js")
  -o, --only <component>   limit to one component
  -l, --link <url>         provide a link back to the commit
  -m, --message <message>  associate the run with a message (e.g. commit subject)
  -a, --author <email>     the author of the commit
  --debug-port <port>      the port where the debug server listens
  --debug-port <port>      the port where the debug server listens
  --dry-run                makes the `happo compare` call non-destructive when running with a `compareThreshold` config option
  -h, --help               output usage information

Commands:
  run [sha]                execute a full happo run
  dev                      start dev mode
  debug                    start a local server where you can debug happo examples
  has-report <sha>         check if there is a report for a specific sha
  empty <sha>              mark a report as empty
  compare <sha1> <sha2>    compare reports for two different shas
  start-job <sha1> <sha2>  start a job (used by happo-ci script)

Blank screenshots for components that are mostly rendered in portals

We have a component that renders everything inside a portal (created by using react-dom's createPortal). The screenshots for this component are always blank. Looking at the source in the happo report and most of the DOM nodes for these examples are missing.

I dug through the code, and I believe that our problem lies in this code:

// Grab the element that we add to the dom by default. This element will
// usually be the right element, at least in the react case.
const root = document.getElementById(ROOT_ELEMENT_ID);
if (!root) {
// The root element may very well have been overridden in the render method
// for an example. In that case, fall back to the <body> element.
return document.body;
}
if (root.innerHTML === '') {
// The root has no content. Which means we're potentially rendering to a
// portal element. Iterate through other root elements to see if any other
// has content.
for (const potentialRoot of document.body.children) {
if (potentialRoot.innerHTML !== '') {
return potentialRoot;
}
}
}
return root;

It looks like happo has some logic to account for examples that are rendered entirely inside a portal (checks to see if the root element is completely empty), however in our case there is some content that is rendered in the root element (a div or two, but with no styling) and most of the interesting bits are inside the portal.

Screenshots cutting off after some height

Hey, I'm having some trouble with getting the screenshots of all the components I want to capture in a single screenshot.

Running happo works fine, but the screenshot in the report renders the first few components perfectly fine, but the later half doesn't show at all (all blank space).

I tried rendering the components individually and the screenshot captured was exactly what I was expecting. But when I put the components together under the same test, the screenshot cuts off after showing the first few components and only parts of the next component before being followed by a bunch of blank space, I assume would be the area where the remaining components would've been placed.

This is the screenshot I'm getting now that gets cut off:

Screenshot 1

image

These are the components that was cut off that I wanted to see (these screenshots were also taken via running happo):

Screenshot 2

image

Screenshot 3

image

This is my current .happo.js file that I'm using:

// .happo.js
const { RemoteBrowserTarget } = require('happo.io');
const craWebpackConfig = require('react-scripts/config/webpack.config');
const path = require('path');

module.exports = {
  setupScript: path.resolve(__dirname, 'src/happoSetup.js'),
  renderWrapperModule: path.resolve(__dirname, 'src/tests/happo/happoWrapper.jsx'),
  prerender: false,

  apiKey: process.env.HAPPO_API_KEY,
  apiSecret: process.env.HAPPO_API_SECRET,

  customizeWebpackConfig: config => {
    // Use the built-in webpack config provided by create-react-app
    config.module = craWebpackConfig('development').module;
    config.module.rules.push({
      resolve: {
        alias: {
          shared: path.resolve(__dirname, 'src/shared'),
          "config.js": path.resolve(__dirname, 'src/config.js'),
          css: path.resolve(__dirname, 'src/css'),
          pages: path.resolve(__dirname, 'src/pages'),
        }
      },
    });
    return config;
  },

  targets: {
    'chrome-desktop': new RemoteBrowserTarget('chrome', {
      viewport: '1024x768',
      maxHeight: 10000,
      scrollStitch: true,
    }),
  },
};

This is also the test file I am running on:

import React from 'react';
import Home from 'pages/home';

export const homePage = () => <Home />;

I'm not sure if I'm putting too many components into a single screenshot test or if there's a screenshot cap that I'm not aware of.

Any help on this would be great, thanks!

Webpack v4 Support

Hi! So we recently upgraded to webpack v4. Webpack 4 changes how plugins work, and webpack packages some basic plugins in with it. This means old plugins in happo's copy of webpack are not compatible with webpack from our codebase. In addition, even if webpack didn't ship with plugins, npm doesn't allow installation of the same package at 2 separate versions.

Our workaround solution was to do the following in our happo config:

const webpack = require('happo.io/node_modules/webpack/lib/webpack');

plugins: [
   webpack.ModuleConatPlugin, // ... etc etc
]

Options:

  1. Maybe there is a way to setup webpack as a peer dependency? I dislike this because its great that happo works out of the box, and this would start going down the path of happo being dependent on some config.
  2. Pass webpack into the "customWebpackConfig" function as another argument. This means we can use the same version of webpack that happo uses. We will need to redefine the same configuration twice, but it at minimum prevents digging into happo's node_modules.
  3. Optionally, completely delegate control of webpack to the host application some how.
  4. Allow the host application to specify which webpack to use.

Or some combo of the above.

Thoughts?

ModuleBuildError: Module build failed (from ./node_modules/react-scripts/node_modules/babel-loader/lib/index.js): SyntaxError: Unexpected token

Hello, I'm having some trouble running the happo getting this error.
ModuleBuildError: Module build failed (from ./node_modules/react-scripts/node_modules/babel-loader/lib/index.js): SyntaxError: Unexpected token

Screen Shot 2020-06-30 at 3 02 29 PM

For context, this is the .happo.js file that I'm using:

// .happo.js
const { RemoteBrowserTarget } = require('happo.io');
const craWebpackConfig = require('react-scripts/config/webpack.config');
const path = require('path');

module.exports = {
  apiKey: process.env.HAPPO_API_KEY,
  apiSecret: process.env.HAPPO_API_SECRET,

  customizeWebpackConfig: config => {
    // Use the built-in webpack config provided by create-react-app
    config.module = craWebpackConfig('development').module;
    config.module.rules.push({
      resolve: {
        alias: {
          shared: path.resolve(__dirname, 'src/shared'),
        }
      },
    });
    return config;
  },

  targets: {
    'chrome-desktop': new RemoteBrowserTarget('chrome', {
      viewport: '1024x768',
      maxHeight: 5000,
      scrollStitch: true,
    }),
  },
};

And this is the happo test file I'm running happo on:

import React from 'react';
import Home from '../src/pages/home';

export const homePage = () => <Home />;

Any help would be great, thanks!

Environment variables in CircleCI run

Is it possible to set custom environment variables in CircleCI runs? I'm trying to define an environment variable IS_HAPPO_TEST=true when a Happo test is running.

Is it possible to tell at runtime (when the page is being screenshotted) whether the current request is from Happo within our React App?

Does this work with Rollup.js?

I have a component library that uses rollup.js and typescript. I've attempted to add WebPack to run Happo, but without any luck yet.

I've followed the install process for examples, but I get this error:

ModuleNotFoundError: Module not found: Error: Can't resolve 'src' in '/Users/.../src/components/Button'

I've also added a webpack.config.js:

const path = require("path");

module.exports = {
  entry: "./src/index.ts",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js", ".jsx"],
    modules: [path.resolve("./src"), path.resolve("./node_modules")],
  },
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
};

and tried adding babel-plugin-module-resolver

.babelrc

[
  "module-resolver",
  {
    "root": ["./src"]
  }
]

but none of these efforts have made a difference. This seems like a basic config issue, but it's been blocking me all day. This has me wondering if there is some fundamental incompatibility with Happo and a rollup based project. Is there an example of a working project with Happo and Rollup?

Happo seems like an exciting project, and I'd love to get it to work with my component library.

React-dom required when using `pages`

From a slack channel:

Question about the full-page screenshots. We are trying to use this in another repository, and Happo is failing with:

Cannot find module 'react-dom'

I'd think that with pages present it wouldn't care about type 🤷

Failure when running happo on safari browser

Generating screenshots works ok on other browsers (chrome, iPhone, ie11), but when I try to generate screenshots on safari 1024x768 I receive:

 - ie11-1200x900 ✓ (84928.3ms)
Failed GET https://happo.io/api/snap-requests/${id}. Retrying...
Failed GET https://happo.io/api/snap-requests/${id}. Retrying...
Failed GET https://happo.io/api/snap-requests/${id}. Retrying...
Failed GET https://happo.io/api/snap-requests/${id}. Retrying...
Failed GET https://happo.io/api/snap-requests/${id}. Retrying...
✗ (218767.5ms)
StatusCodeError: 500 - "An error occurred for browser-safari: Failed on worker"
    at new StatusCodeError (${projPath}/node_modules/request-promise-core/lib/errors.js:32:15)
    at Request.plumbing.callback (${projPath}/node_modules/request-promise-core/lib/plumbing.js:104:33)
    at Request.RP$callback [as _callback] (${projPath}/node_modules/request-promise-core/lib/plumbing.js:46:31)
    at Request.self.callback (${projPath}/node_modules/request/request.js:185:22)
    at Request.emit (events.js:315:20)
    at Request.<anonymous> (${projPath}/node_modules/request/request.js:1154:10)
    at Request.emit (events.js:315:20)
    at IncomingMessage.<anonymous> (${projPath}/node_modules/request/request.js:1076:12)
    at Object.onceWrapper (events.js:421:28)
    at IncomingMessage.emit (events.js:327:22)

${projPath}/node_modules/request-promise-core/lib/errors.js:32
        Error.captureStackTrace(this);
              ^
StatusCodeError: 500 - "An error occurred for browser-safari: Failed on worker"
    at new StatusCodeError (${projPath}/node_modules/request-promise-core/lib/errors.js:32:15)
    at Request.plumbing.callback (${projPath}/node_modules/request-promise-core/lib/plumbing.js:104:33)
    at Request.RP$callback [as _callback] (${projPath}/node_modules/request-promise-core/lib/plumbing.js:46:31)
    at Request.self.callback (${projPath}/node_modules/request/request.js:185:22)
    at Request.emit (events.js:315:20)
    at Request.<anonymous> (${projPath}/node_modules/request/request.js:1154:10)
    at Request.emit (events.js:315:20)
    at IncomingMessage.<anonymous> (${projPath}/node_modules/request/request.js:1076:12)
    at Object.onceWrapper (events.js:421:28)
    at IncomingMessage.emit (events.js:327:22)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! Exit status 1

Run browsers in modes that disable animations better

I know that Happo does some work to try to pause animations to help prevent spurious diffs, however the current techniques are not 100% effective--e.g. when rendering an animated GIF.

I wondered if Happo could run browsers in a way that disables more animations.

I poked around a bit and found this Animation Policy Chrome extension that Google released that does the trick. Trying it out locally, it does exactly what we want for animated GIFs when set to "Disable all image animation". Perhaps Happo can use this extension? It looks like extensions can be loaded with the --load-extension= cli arg, but I'm not entirely sure how to configure it. One option might be to copy the extension locally and modify it to have this behavior by default.

Firefox appears to have a setting image.animation_mode that can be set to 'none' to disable animations. It looks like this can be set in selenium via the FirefoxProfile, e.g.

profile = webdriver.FirefoxProfile();
profile.set_preference('image.animation_mode', 'none');

(There might be some other settings here to improve Firefox as well https://stackoverflow.com/a/21026372/18986)

I didn't spend any time looking into what might be possible in other browsers.

Possibility to replace config parameters in CLI with CLI args

For example, even if my .happo.js includes


  targets: {
    'chrome-desktop': new RemoteBrowserTarget('chrome', {
      viewport: '1200x768',
    }),
    // 'chrome-mobile': new RemoteBrowserTarget('chrome', {
    //   viewport: '320x640',
    // }),
    // 'safari-desktop': new RemoteBrowserTarget('safari', {
    //   viewport: '1200x768',
    // }),
    'safari-mobile': new RemoteBrowserTarget('safari', {
      viewport: '320x640',
    }),
    ie11: new RemoteBrowserTarget('internet explorer', {
      viewport: '1200x768',
    }),
    // Happo currently throws error 500 for ios-safari builds
    // TODO turn it back on when Happo team fixes it
    // iphone: new RemoteBrowserTarget('ios-safari', {
    //   viewport: '375x667',
    // }),
  },

if I could do happo run --targets ie11 or happo dev --targets safari-mobile , I could limit the targets to be enabled on this run only to the specified targets.

(Similarly as --only xxx works in happo dev mode.)

Wait for all async to settle before capturing HTML when prerendering

I'm not even totally sure this kind of thing would be useful, so this is more of just an idea.

I wonder if diffs could be made more stable by waiting for any in-flight async things (e.g. setTimeout) to settle before capturing the HTML when prerendering. Code that looks something like this could be used to handle setTimeout, for instance:

    const timers = new Set();
    originalSetTimeout = window.setTimeout;
    originalClearTimeout = window.clearTimeout;
    
    function deleteTimer(id) {
      timers.delete(id);
      if (timers.size === 0) {
        // Something here, like a callback maybe
      }
    }
    
    window.setTimeout = function(cb, delay) {
      const id = originalSetTimeout(() => {
        cb();
        
        deleteTimer(id);
      }, delay);
      
      timers.add(id);
      return id;
    }
    
    window.clearTimeout = function(id) {
      deleteTimer(id);      
      return originalClearTimeout(id);
    };

`happo dev` command not supported with some plugins

Currently, none of the happo-plugin-storybook and happo-plugin-gatsby plugins work when running happo dev. The dev command mostly makes sense when you have your own -happo.js files, so it might not make sense to fix support -- we might just have to bail with some useful error message.

Trouble using Limiting Targets

Hey, I'm getting trouble compiling happo test files that's set up for limiting targets. I'm following the example given here: https://docs.happo.io/docs/examples#limiting-targets

The version of happo I'm running right now is the rc version mentioned from #168.

Below is the happo config and test file:

// .happo.js
const { RemoteBrowserTarget } = require('happo.io');
const craWebpackConfig = require('react-scripts/config/webpack.config');
const path = require('path');
const MAX_HEIGHT = 10000;

module.exports = {
  renderWrapperModule: path.resolve(__dirname, 'src/tests/happo/happo-wrapper.jsx'),
  prerender: false,

  apiKey: process.env.HAPPO_API_KEY,
  apiSecret: process.env.HAPPO_API_SECRET,

  customizeWebpackConfig: config => {
    // Use the built-in webpack config provided by create-react-app
    config.module = craWebpackConfig('development').module;
    config.module.rules.push({
      resolve: {
        alias: {
          shared: path.resolve(__dirname, 'src/shared'),
          "config.js": path.resolve(__dirname, 'src/config.js'),
          css: path.resolve(__dirname, 'src/css'),
          pages: path.resolve(__dirname, 'src/pages'),
        }
      },
    });
    return config;
  },

  targets: {
    'chrome-small': new RemoteBrowserTarget('chrome', {
      viewport: '576x768',
      maxHeight: MAX_HEIGHT,
    }),
  },
};

Update: Updated the file to match the syntax from #76.

// about-happo.js
import React from 'react';
import About from 'pages/about';

export const aboutPage = {
  render: () => <About />,
  targets: ['chrome-small'],
};

This is the error I am getting:

ModuleError: Module Error (from ./node_modules/eslint-loader/index.js):
Line 7:10:  Parsing error: Unexpected token, expected ";"

I also have a similar problem using conditionally applied stylesheets because of this as well, so any help with this would be great!

Update: I realized I got the syntax wrong in my test file after looking at #76. I updated the file to match the syntax, but when I try running happo, it successfully runs, but the report afterwards says no screenshots were taken.

Silent failures when happo files are misconfigured

In a standard happo run (no plugins), this happo test file will not cause any errors:

export default () => <div>Foo</div>;
export const bar = () => <div>Foo</div>;
export const baz = <div>Foo</div>;

(notice how baz is not a function)

The end result will contain default and bar (baz will be ignored).

If we then apply the happo-plugin-puppeteer plugin, even weirder things happen. Suddenly none of the examples from the file are rendered, and there are no errors at all - nothing in the log (even with VERBOSE=true).

Failures running Happo - File Already Exists

Hey @trotzig, I'm currently running Happo Examples Integration and have been getting this error a couple times when Happo is ran via happo-ci-circleci:

StatusCodeError: 500 - "An error occurred for browser-safari: EEXIST: file already exists

Takes 1 or more reruns to get a successful Happo run without erroring out, any ideas for why this is occurring?

Run happo via a node script?

I'm trying to run happo via a node script, and I'm running into a lot of issues.

// scripts/happoScript.js
const executable = '<repo path>/node_modules/happo.io/build/cli.js'

const happo = spawn(
  executable,
  happoArgs,
  {
    stdio: 'inherit',
  }
)

If I spawn this off, there's a couple of issues. Firstly, there will be a bunch of un-exited processes after the build has completed, if you grep for them (ps -ef | grep happo).

The 2nd problem: The script will return a signal before it should. So in my cmd line, the script executes, completes and kicks you back to the command line, and then randomly outputs. It's also confusing as if you mash on ctrl + c, it won't do anything, as you're not actually inside a process.

Let me know if any of this makes any sense at all. Thanks.

Images with `decoding="async"` may cause spurious diffs

We have some img tags with decoding="async" where I see a higher number of spurious diffs, where sometimes the images are not rendered.

One way to fix this might be to have happo force decoding="sync" on all img tags.

Assets referenced by inline styles are not uploaded with the assets package

I have a snapshot that renders the following element:

<div class="background_3l6wgj-o_O-large_usp5di" style="background-image: url(19674e0d6b42a252d31d6290dc406e1b.png);" role="img" aria-label="image"></div>

When happo takes a screenshot of this element, the image is missing. When I view the snapshot source, I see the network request for this image 404.

image

I believe that when the assets package is prepared, it is looking at global CSS files, snap payloads, and things added to the public folders.

const assetsPackage = await prepareAssetsPackage({
globalCSS,
snapPayloads,
publicFolders,
});

The snapPayloads.assetPaths are added to the assets package here:

snapPayloads.forEach(({ assetPaths }) => {
assetPaths.forEach((assetPath) => {
paths[assetPath] = assetPath;
});
});

So I believe the relevant part here is how snapPayloads.assetPaths is generated. That happens in this loop:

while (await domProvider.next()) {
if (VERBOSE === 'true') {
console.log(`Viewport ${viewport}`);
}
const payload = await domProvider.processCurrent();
result.snapPayloads.push(payload);
}

Which calls this:

processCurrent() {
return this.dom.window.happoProcessor.processCurrent();
}

Which then calls this:

async processCurrent() {
const { component, fileName, variant, render } = this.flattenedExamples[
this.cursor
];
const exampleRenderFunc = getRenderFunc(render);
window.happoCleanup();
try {
window.verbose(`Rendering component ${component}, variant ${variant}`);
await renderExample(exampleRenderFunc);
} catch (e) {
return new WrappedError(
`Failed to render component "${component}", variant "${variant}" in ${fileName}`,
e,
);
}
const root =
(this.rootElementSelector &&
document.body.querySelector(this.rootElementSelector)) ||
findRoot();
const html = await this.waitForHTML(root);
const item = {
html,
css: '', // Can we remove this?
component,
variant,
assetPaths: findAssetPaths(),
};
const { stylesheets } = render;
if (stylesheets) {
item.stylesheets = stylesheets;
}
return item;
}

I think the key line here is the call to findAssetPaths():

assetPaths: findAssetPaths(),

Which is this function:

export default function findAssetPaths(doc = document) {
const imgPaths = Array.from(doc.querySelectorAll('img[src]')).map((img) =>
img.getAttribute('src'),
);
doc.querySelectorAll('img[srcset]').forEach((img) => {
Array.from(matchAll(img.getAttribute('srcset') || '', SRCSET_ITEM)).forEach((match) => {
imgPaths.push(match[1]);
});
});
return imgPaths.filter((url) => !isAbsoluteUrl(url) && url.trim().length);
}

It looks like findAssetPaths will look for img src or img srcset, but not other types of URLs, like in inline styles.

Ability to list reports

Instead of running Happo for every PR, we'd prefer to running it only once every night on a nightly CI build.

Thus, for example, we want to build a report every Monday-Friday night, that runs the happo for the current master, and compare it with the report from the previous day.

To do this, we need to query for all of the reports, in order to select with which report should we compare the new report.

How can we do it? In Happo API we don't see any way to list all reports atm.

TypeError: Cannot read property 'length' of undefined

A bug was introduced in [email protected] causing happo runs for happo-plugin-storybook to fail with the following error:

Generating screenshots in 3 targets...
✗
TypeError: Cannot read property 'length' of undefined
    at /Users/henrictrotzig/happo-circleci-webinar/node_modules/happo.io/build/RemoteBrowserTarget.js:84:42
    at Generator.next (<anonymous>)
    at step (/Users/henrictrotzig/happo-circleci-webinar/node_modules/happo.io/build/RemoteBrowserTarget.js:40:191)
    at /Users/henrictrotzig/happo-circleci-webinar/node_modules/happo.io/build/RemoteBrowserTarget.js:40:437
    at new Promise (<anonymous>)
    at /Users/henrictrotzig/happo-circleci-webinar/node_modules/happo.io/build/RemoteBrowserTarget.js:40:99
    at RemoteBrowserTarget.execute (/Users/henrictrotzig/happo-circleci-webinar/node_modules/happo.io/build/RemoteBrowserTarget.js:117:7)
    at /Users/henrictrotzig/happo-circleci-webinar/node_modules/happo.io/build/remoteRunner.js:38:46
    at Generator.next (<anonymous>)
    at step (/Users/henrictrotzig/happo-circleci-webinar/node_modules/happo.io/build/remoteRunner.js:21:191)
error Command failed with exit code 1.

Wrap every component in an application wrapper

hey folks! I was wondering if there was a way to wrap every happo example with another component.

Since we use styled-components + a theme we always wrap our happo examples in a theme provider:

export const medium = () => (
    <ThemeProvider>
        <MyComponent... />
    </ThemeProvider>
)

There are ways we can obfuscate this away ourselves, for instance:

export const medium = provideTheme(() => ...)

But I'd rather be able to configure happo to do this for us, so that all our components are wrapped behind the scenes.

Is this possible now? If not, I am open to taking up the OSS work required to make it work unless you have plans / API docs ready now.

Fail early when duplicates are found

If you for instance have a dist folder and that folder contains copies of the happo files found in src, there's a chance the happo run fails at the very last step with a validation error (duplicates found). We should detect this as early as possible.

Error with Dynamic imports

👋 hi everyone, when we try to dynamically import some icons using webpack 3+4 dynamic import syntax we get this error in CI for happo:

Error: Could not load script: "http://localhost/92.happo-bundle-react-L3Jvb3Qvc3R1ZGlv.js"
    at onErrorWrapped (/root/studio/node_modules/jsdom/lib/jsdom/browser/resources/per-document-resource-loader.js:39:19)
    at Object.check (/root/studio/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:58:23)
    at request.then.catch.err (/root/studio/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:104:14)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7) { RequestError: Error: connect ECONNREFUSED 127.0.0.1:80
    at new RequestError (/root/studio/node_modules/request-promise-core/lib/errors.js:14:15)
    at Request.plumbing.callback (/root/studio/node_modules/request-promise-core/lib/plumbing.js:87:29)
    at Request.RP$callback [as _callback] (/root/studio/node_modules/request-promise-core/lib/plumbing.js:46:31)
    at self.callback (/root/studio/node_modules/request/request.js:185:22)
    at emitOne (events.js:116:13)
    at Request.emit (events.js:211:7)
    at Request.onRequestError (/root/studio/node_modules/request/request.js:881:8)
    at emitOne (events.js:116:13)
    at ClientRequest.emit (events.js:211:7)
    at Socket.socketErrorListener (_http_client.js:387:9)
  name: 'RequestError',
  message: 'Error: connect ECONNREFUSED 127.0.0.1:80',
  cause: 
   { Error: connect ECONNREFUSED 127.0.0.1:80
    at Object._errnoException (util.js:1022:11)
    at _exceptionWithHostPort (util.js:1044:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1182:14)
     code: 'ECONNREFUSED',
     errno: 'ECONNREFUSED',
     syscall: 'connect',
     address: '127.0.0.1',
     port: 80 },
  error: 
   { Error: connect ECONNREFUSED 127.0.0.1:80
    at Object._errnoException (util.js:1022:11)
    at _exceptionWithHostPort (util.js:1044:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1182:14)
     code: 'ECONNREFUSED',
     errno: 'ECONNREFUSED',
     syscall: 'connect',
     address: '127.0.0.1',
     port: 80 },
  options: 
   { encoding: null,
     gzip: true,
     jar: RequestJar { _jar: [Object] },
     strictSSL: true,
     forever: true,
     headers: 
      { 'User-Agent': 'Mozilla/5.0 (linux) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/12.0.0',
        'Accept-Language': 'en',
        Accept: '*/*',
        referer: 'http://localhost/' },
     uri: 'http://localhost/92.happo-bundle-react-L3Jvb3Qvc3R1ZGlv.js',
     callback: [Function: RP$callback],
     transform: undefined,
     simple: true,
     resolveWithFullResponse: false,
     transform2xxOnly: false },
  response: undefined }
Error: Loading chunk 92 failed.
    at HTMLScriptElement.onScriptComplete (file:///tmp/happo-bundle-react-L3Jvb3Qvc3R1ZGlv.js:99:24)
    at HTMLScriptElement.el.addEventListener.event (/root/studio/node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js:33:32)
    at invokeEventListeners (/root/studio/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
    at HTMLScriptElementImpl._dispatch (/root/studio/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
    at HTMLScriptElementImpl.dispatchEvent (/root/studio/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
    at HTMLScriptElementImpl.dispatchEvent (/root/studio/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
    at onErrorWrapped (/root/studio/node_modules/jsdom/lib/jsdom/browser/resources/per-document-resource-loader.js:37:15)
    at Object.check (/root/studio/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:58:23)
    at request.then.catch.err (/root/studio/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:104:14)
    at <anonymous>

Here is our happo config for webpack customization:

    customizeWebpackConfig: config => {
        config.module.rules.push({
            test: /\.(ts|tsx)$/,
            use: [
                {
                    loader: 'ts-loader',
                },
            ],
        })
        config.resolve = Object.assign({}, config.resolve, {
            extensions: ['.tsx', '.ts', '.js'],
        }) // add in the existing config.resolve that happo needs

        return config
    },

Happo not updating GitHub check status on base branch

Hi, we started using Happo with the Github integration over at https://github.com/bcgov/cas-ciip-portal, using happo-cypress to trigger the Happo jobs. We love it so far!

Status checks are correctly reported to PRs, but on our base branch the status check remains "Pending — job started". The Happo job itself is completed (e.g. https://happo.io/a/336/jobs/29680).

The Happo jobs are triggered on CircleCI, and we set HAPPO_BASE_BRANCH to be origin/develop

Spurious diffs with iOS-safari

This happens to us a lot:

image

In particular, it seems like iOS-safari frequently has diffs related to icon assets not loading consistently.

Any advice?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.