Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Visual Testing: Cypress + Percy #419

Merged
merged 13 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Cypress Percy Tests

on:
schedule:
# 8:10 UTC time on Mondays
- cron: '10 8 * * 1'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: Install npm 8
run: npm install -g npm@8
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build && npm run build:dev
- name: Install Percy
run: cd packages/dev; npm install --save-dev @percy/cli @percy/cypress
- name: Start server
run: cd packages/dev; npm run serve & sleep 5
- name: Run tests
run: cd packages/dev; npm test
env:
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
- name: Stop server
run: pkill -f "serve dist -s -p 9501"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,6 @@ licences.temp
# System Files
.DS_Store
Thumbs.db

# Cypress
packages/*/cypress/screenshots/
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions packages/dev/.percy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"version": 2,
"snapshot": {
"percy-css": "",
"widths": [1280]
}
}
19 changes: 19 additions & 0 deletions packages/dev/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defineConfig } from 'cypress'

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:9501',
setupNodeEvents (on, config) {
// implement node event listeners here
},
},

component: {
devServer: {
framework: 'react',
bundler: 'webpack',
},
},

hosts: { localhost: '127.0.0.1' },
})
22 changes: 22 additions & 0 deletions packages/dev/cypress/e2e/unovis.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { urls } from '../urls'
const scopeSelector = '.exampleViewer'

describe('Unovis Test', () => {
it('Load homepage', () => {
cy.visit('/')
cy.percySnapshot('Homepage test')
})

urls.forEach((test) => {
it(test.title, () => {
cy.visit(test.url, {
qs: {
duration: test.duration,
},
})
cy.wait(test.duration > 100 ? test.duration : 100)
cy.percySnapshot(test.title, { scope: scopeSelector })
})
})
})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can try generating this file automatically based on the example files. Technically they have the required information, like the description text.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can try generating this file automatically based on the example files. Technically they have the required information, like the description text.

I suppose we could, but some page require additional settings. For example the API Endpoints Tree requires a bigger minHeight, we'd have to manually set things like that for specific test cases. Do you have a workaround for cases like this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can have a map of additional settings in the testing file to apply on the go if needed. I can explain more if you want, just let me know.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean to have an array or load a separate file which contains

testingArray = [
  {name: 'Single Container', duration:3000, additionalParams: {width: [1300]}},
  {name: 'Sankey', duration:3000},  ...
]

And iterate through the array inside the unovis.cy.ts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at unovis/packages/dev/src/examples/index.ts, I was thinking about something similar to generate this test file automatically. You'll need to "compile" it using Webpack but I think it will be a reasonable trade-off.

So the script will:

  • Batch import all the index.tsx files in the examples folder;
  • You'll be able to get the required information from the imports (titles, ...);
  • You can define additional testing configuration in the unovis.cy.ts file and match the corresponding examples by name or path. Or you can define it in the index.tsx files of the examples themsevles.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rokotyan I will give it a try. Is this PR getting too big? Should we have a separate PR for this and the duration params?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you. I would do it here, but I don't see any problem with merging this PR first and having another one

37 changes: 37 additions & 0 deletions packages/dev/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/// <reference types="cypress" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this file at all?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out we do need this file. Without it Cypress can't resolve /commands. See here

// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
12 changes: 12 additions & 0 deletions packages/dev/cypress/support/component-index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Components App</title>
</head>
<body>
<div data-cy-root></div>
</body>
</html>
42 changes: 42 additions & 0 deletions packages/dev/cypress/support/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// ***********************************************************
// This example support/component.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************


// Alternatively you can use CommonJS syntax:
// require('./commands')

import { mount } from 'cypress/react18'

// Import commands.js using ES2015 syntax:
import './commands'


// Augment the Cypress namespace to include type definitions for
// your custom command.
// Alternatively, can be defined in cypress/support/component.d.ts
// with a <reference path="./component" /> at the top of your spec.
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
mount: typeof mount;
}
}
}

Cypress.Commands.add('mount', mount)

// Example use:
// cy.mount(<MyComponent />)
20 changes: 20 additions & 0 deletions packages/dev/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
import '@percy/cypress'
// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')
Loading
Loading