Skip to content

Commit

Permalink
feat: integrate visual testing (#925)
Browse files Browse the repository at this point in the history
* chore: update storybook config to allow arguments on the URL survive after refresh

* feat: update stories to new format

* feat: add default data test id on images

* Add dataTestid on overlay

* Add dataTestid on paddedContainer

* refactor: migrate checkbox story

* feat: add default data test id to checkbox

* refactor(floater): remove extra line from argTypes

* refactor(hero): use static image to avoid error on image comparison on visual testing

* refactor(price): add data-testId on wrapper

* refactor: update text property from dataQaId to dataTestId

* feat: add dataTestId on progress circle

* feat: add default dataTestId on tooltip

* refactor: update dataTestIds from textarea

* refactor: update dataTestIds from toggle

* refactor: update dataTestIds from select and update story

* chore: update storybook config to allow arguments on the URL survive after refresh

* feat: update stories to new format

* feat: add default data test id on images

* refactor: migrate checkbox story

* feat: add default data test id to checkbox

* Add dataTestid on overlay

* Add dataTestid on paddedContainer

* refactor(floater): remove extra line from argTypes

* refactor(hero): use static image to avoid error on image comparison on visual testing

* refactor(price): add data-testId on wrapper

* refactor: update text property from dataQaId to dataTestId

* feat: add dataTestId on progress circle

* feat: add default dataTestId on tooltip

* refactor: update dataTestIds from textarea

* refactor: update dataTestIds from toggle

* refactor: update dataTestIds from select and update story

* chore: update foregroudColor to use storybook mapping to be able to receive parameters from URL

* chore: bump node to version 18

* refactor: update components to pass dataTestId to Text

* ci: force rebuild

* style: run formatter

* test: include visual test

* wip: trying to create aliased paths for cypress

* refactor: replace js files for ts

* fix: use correct generic type on chainable from cypress to avoid unnecessary any

* chore: enable linting, disable conflicting rules for cypress files and create alias to cypress

* ci: force rebuild

* ci: set node to correct latest

* ci: add dumb visual testing workflow

* ci: add quotes and manual dispatch

* ci: add repository dispatch

* ci: force rebuild

* ci: add dockerfile to run tests locally

* chore: bump cypress to 13:6.4 version

* chore: add script to update images

* ci: use cypress image to run visual testing

* chore: bump node to 20.11

* ci: remove workflow run

* ci: list repository

* Test visual testing PR number

* Test PR-Name

* Test PR-Name v2

* Check title

* Check amplify link

* Run visual testing with amplify link

* Remove -it from docker command

* Use js file extension instead on cypress config

* chore: add type module on package

* ci: install packages before running cypress

* ci: set image on a key

* ci: add step to install deps using bit

* ci: fix environment variable name

* ci: use npm bin to get cypress path

* ci: use bit to install deps

* ci: add bit compile and reuse command on package.json

* ci: add matrix to paralelize runs

* ci: split the jobs into install and testing

* ci: downgrade upload and download action versions

actions/upload-artifact#485

* ci: tar files before upload

* ci: use upload and download action on the same version

* chore: replace every test ot use getFullPageWithVisibleTarget

* feat: wait for fonts to load

* feat: create script to move images from actual to base folder

* feat: update progress circle test and baseline

* revert: rollback base url variable

* Remove useless StyledOption from input phone

* feat: run tests on full screen

* chore: remove `-it` flag

* refactor: remove it.each

* chore: add timer on getFullPageWithVisibleTarget

* refactor: set timer to only 1 ms

* ci: prepare workflow for mobile validation in a different container

* chore: rename base snapshot to force failure

* ci: add steps to upload logs and snapshots when ci fails

* Revert "chore: rename base snapshot to force failure"

This reverts commit 704f2e8.

* chore: delete primary button image to force ci to fail

* Revert "chore: delete primary button image to force ci to fail"

This reverts commit 574c0f5.

* chore: set timer to 100ms

* ci: define retention days

* ci: remove step that upload logs as no log is written

* ci: include wait-on utility to wait for amplify link to be generated

* ci: include a timeout

* refactor: use args to send dataTestId

* chore: update lock file

* test: include negative validation

* Update yarn.lock

* Update yarn.lock with new node version

* Check bit and bvm versions

* Move bit version

* Restrict bvm version

* Rollback yarn.lock file

* Update yarn.lock with older version

---------

Co-authored-by: Sergio Vanacloig <sergiovanacloig97@gmail.com>
  • Loading branch information
samuelsilvadev and sergiovanacloig authored Mar 27, 2024
1 parent 6cd9822 commit 35dfc1c
Show file tree
Hide file tree
Showing 205 changed files with 14,669 additions and 20,798 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BASE_URL=http://localhost:6006
15 changes: 15 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@
"rules": {
"no-console": "off"
}
},
{
"files": ["./cypress/**/*.ts"],
"globals": {
"Cypress": "readonly",
"cy": "readonly"
},
"rules": {
"jest/expect-expect": "off",
"import/extensions": "off",
"testing-library/prefer-screen-queries": "off",
"testing-library/await-async-utils": "off",
"import/no-unresolved": ["error", { "ignore": ["@/cypress"] }],
"import/order": "off"
}
}
]
}
2 changes: 1 addition & 1 deletion .github/workflows/bit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- uses: actions/setup-node@v3
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
with:
node-version: '16'
node-version: '20.11.0'

- name: Install Bit CLI
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

- uses: actions/setup-node@v3
with:
node-version: '16'
node-version: '20.11.0'

- name: Install Bit CLI
run: |
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/visual-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Visual testing

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
repository_dispatch:
types: [run_visual_testing]

jobs:
prepare-visual-testing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install Bit CLI
run: |
npm i -g @teambit/bvm
bvm install ${BIT_VERSION}
env:
BIT_VERSION: ${{ secrets.BIT_VERSION }}

- name: Add bvm bin folder to path
run: |
echo "$HOME/bin" >> $GITHUB_PATH
echo "PATH=$HOME/bin:$PATH" >> $GITHUB_ENV
- name: Set up bit config
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
echo "Adding bit.dev to npm registry"
export PATH=${{ env.PATH }}
npm config set @bit:registry https://node.bit.dev
npm config set @carlsberggroup:registry=https://node.bit.dev
npm config set @teambit:registry=https://node.bit.dev
bit config set analytics_reporting false
bit config set anonymous_reporting false
bit config set user.token ${BIT_TOKEN}
echo "Completed adding bit.dev to npm registry"
env:
BIT_TOKEN: ${{ secrets.BIT_TOKEN }}

- name: Bit Deps Install & Compile
run: |
export PATH=${{ env.PATH }}
bit install
bit compile
shell: bash

- name: 'Tar files'
run: tar -cvf malty.tar .

- name: Save packages folder
uses: actions/upload-artifact@v3
with:
name: malty-artifact
if-no-files-found: error
path: malty.tar
retention-days: 1

visual-testing:
runs-on: ubuntu-latest
needs: prepare-visual-testing
env:
BASE_URL: https://pr-${{ github.event.number }}.d3acoh2jwy8aw9.amplifyapp.com
strategy:
fail-fast: false
matrix:
containers: [1, 2]
container:
image: cypress/included:cypress-13.6.4-node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
env:
BASE_URL: ${{ env.BASE_URL }}
steps:
- uses: actions/checkout@v3

- name: Download the build folder
uses: actions/download-artifact@v3
with:
name: malty-artifact

- name: 'Untar files'
run: tar -xvf malty.tar

- name: Run visual testing in desktop
if: matrix.containers == 1
# TODO: As soon as the ticket https://carlsberggbs.atlassian.net/browse/DEVOPS-3179 is finished we can remove the npx wait-on utility.
run: npx wait-on ${{ env.BASE_URL }} --interval 300000 --timeout 15m && yarn cypress:ci:desktop

- name: Run visual testing in mobile
if: matrix.containers == 2
run: echo 'Not implemented yet, moving on...'.
shell: bash

- name: Upload Cypress Screenshots
uses: actions/upload-artifact@v3
if: failure()
with:
name: cypress-snapshots-${{ matrix.containers }}
path: cypress/snapshots
retention-days: 1
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ public/bit/*
**/install-state.gz
docs/*
coverage/*
.env

# Cypress
cypress/videos/
cypress/snapshots/actual/
cypress/snapshots/diff/
cypress/downloads/
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.11.0
12 changes: 5 additions & 7 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ function getAbsolutePath(value) {

export default {
stories: ['../malty/!(icons)/!(IconWrapper)/*.stories.@(mdx|js|jsx|ts|tsx)'],
addons: [
{
name: '@storybook/addon-links',
name: '@storybook/addon-essentials'
}
],
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
typescript: {
check: false,
checkOptions: {},
reactDocgen: 'react-docgen-typescript',
/**
* @see https://github.com/storybookjs/storybook/issues/14508
*/
reactDocgen: 'react-docgen',
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: false,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true)
Expand Down
25 changes: 25 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { defineConfig } from 'cypress';
import { configureVisualRegression } from 'cypress-visual-regression/dist/plugin';
import 'dotenv/config';

export default defineConfig({
e2e: {
baseUrl: process.env.BASE_URL,
viewportWidth: 1024,
viewportHeight: 768,
env: {
visualRegressionType: 'regression'
},
screenshotsFolder: './cypress/snapshots/actual',
setupNodeEvents(on) {
on('before:browser:launch', (_browser, launchOptions) => {
launchOptions.args.push('--start-fullscreen');
launchOptions.args.push('--force-device-scale-factor=1');

return launchOptions;
});
configureVisualRegression(on);
}
},
video: false
});
29 changes: 29 additions & 0 deletions cypress/e2e/Button.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { buildSnapshotName, visit } from '@/cypress/support/utils';

const dataTestId = 'button';

describe(`<Button />`, () => {
it('Primary', () => {
const page = visit({ args: { dataTestId }, storyId: 'forms-button--base' });

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});

it('Secondary', () => {
const page = visit({ args: { dataTestId }, storyId: 'forms-button--secondary' });

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});

it('Transparent', () => {
const page = visit({ args: { dataTestId }, storyId: 'forms-button--transparent' });

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});

it('Negative', () => {
const page = visit({ args: { dataTestId, negative: 'true' }, storyId: 'forms-button--base' });

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});
});
46 changes: 46 additions & 0 deletions cypress/e2e/Card.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { buildSnapshotName, expectImageIsVisible, visit } from '@/cypress/support/utils';

const dataTestId = 'card';
const imageDataTestId = 'image';

describe('<Card />', () => {
it('Base', () => {
const page = visit({ args: { dataTestId }, storyId: 'cards-card--base' });

expectImageIsVisible(imageDataTestId);

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});

it('Shadowed', () => {
const page = visit({ args: { dataTestId }, storyId: 'cards-card--shadowed' });

expectImageIsVisible(imageDataTestId);

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});

it('Landscape', () => {
const page = visit({ args: { dataTestId }, storyId: 'cards-card--landscape' });

expectImageIsVisible(imageDataTestId);

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});

it('Selected', () => {
const page = visit({ args: { dataTestId }, storyId: 'cards-card--selected' });

expectImageIsVisible(imageDataTestId);

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});

it('On click', () => {
const page = visit({ args: { dataTestId }, storyId: 'cards-card--on-click' });

expectImageIsVisible(imageDataTestId);

page.getFullPageWithVisibleTarget(dataTestId).compareSnapshot(buildSnapshotName());
});
});
93 changes: 93 additions & 0 deletions cypress/e2e/Checkbox.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { buildSnapshotName, visit } from '@/cypress/support/utils';

const dataTestId = 'checkbox';
const wrapperDataTestId = `${dataTestId}-wrapper`;

describe('<Checkbox />', () => {
it('Base', () => {
const page = visit({ args: { dataTestId }, storyId: 'forms-checkbox--base' });

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('Checked', () => {
const page = visit({
args: { dataTestId },
storyId: 'forms-checkbox--checked'
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('Not Checked', () => {
const page = visit({
args: { dataTestId },
storyId: 'forms-checkbox--unchecked'
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('Required', () => {
const page = visit({
args: { dataTestId },
storyId: 'forms-checkbox--required'
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('Disabled', () => {
const page = visit({
args: { dataTestId },
storyId: 'forms-checkbox--disabled'
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('Disabled - Not checked', () => {
const page = visit({
storyId: 'forms-checkbox--disabled',
args: { checked: '!false', dataTestId }
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('Readonly', () => {
const page = visit({
args: { dataTestId },
storyId: 'forms-checkbox--read-only'
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('Readonly - Not checked', () => {
const page = visit({
storyId: 'forms-checkbox--read-only',
args: { checked: '!false', dataTestId }
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('Indeterminate', () => {
const page = visit({
args: { dataTestId },
storyId: 'forms-checkbox--undetermined'
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});

it('With Error Message', () => {
const page = visit({
args: { dataTestId },
storyId: 'forms-checkbox--errored'
});

page.getFullPageWithVisibleTarget(wrapperDataTestId).compareSnapshot(buildSnapshotName());
});
});
Loading

0 comments on commit 35dfc1c

Please sign in to comment.