At one point in time, the server-side code was responsible for generating and serving every page in our application. Nowadays, Single Page Apps (SPA) are taking a bigger slice of the pie, as more clients are leaning towards building their applications as an SPA.
Developers spend most of their time debugging a SPA inside the browser, by debugging the JavaScript files, CSS styles and the DOM, hoping to find the root cause of any visual bug appearing in their application.
Applitools realizes the complexity of visually debugging an application and introduced the Root Cause Analysis (RCA) module on their Test Manager Dashboard to help quickly identify the root cause behind visual UI changes.
In this article, I will demonstrate the Applitools RCA feature by walking you through a complete step by step guide to visually test a Vue.js app with Storybook and Applitools.
If you’re not already familiar, Applitools is an automated visual regression testing framework. It focuses on the visual aspects of your app and plays a major role in exposing the visual differences between baseline snapshots and both current and future snapshots.
Applitools integrates with dozens of development and testing frameworks, such as Cypress.io, Storybook and Selenium. It provides SDKs for use in your projects to seamlessly communicate and interact with Applitools, in order to send both baseline and test screenshots.
So, get started with visual UI testing. Your visual testing project will depend on React, Storybook and Applitools. You will also need the node.js tools. So, firstly, make sure you know the following software tools and install on your machine:
Before we immerse ourselves in writing code, let’s look at how Applitools and Storybook work together.
How Applitools Works With Storybook
Whether you use Vue.js, React or Angular, Storybook doesn’t offer any extension points for third-party frameworks, such as Applitools, to integrate with. The Applitools team worked around this limitation by providing the Applitools Eyes Storybook SDK.
Once installed, this SDK collects all stories in the application, runs them all via the Storybook engine, generates DOM snapshots for each story and uploads these snapshots to the Applitools Eyes server. Here’s a visual of this flow:
The Applitools server runs the tests generates image snapshots. Applitools compares each snapshot to a baseline snapshot (if one exists) and displays the results on the Test Manager Dashboard.
This kind of integration provides a smooth experience in terms of writing Storybook stories. Nothing changes in the way you write your Vue.js apps.
Automated Root Cause Analysis
The concept of Root Cause Analysis stems from the world of management, where it’s defined as a method of problem solving, used for identifying the root causes of faults or problems.
So far, Applitools has been focusing on visual testing by comparing the baseline and the current test run snapshot through their AI engine. It finds the visual differences in order to map them graphically. Previously, there was no way to search your codebase for a reason behind any visual testing bug. As you may know, searching through code is nightmarish, time-consuming and tedious!
This is where Applitools steps in with RCA, showing you the exact DOM and CSS changes between the baseline snapshot and the current test run snapshot.
Why is this so important for both developers and QA testers? Let’s take a look.
Step By Step Guide
For this demonstration of visual testing a Vue.js application with Storybook and Applitools, I’ve chosen the Bootstrap Vue open-source library hosted on GitHub under this repo. This library, with over 40 plugins and more than 75 custom components, provides one of the most comprehensive implementations of Bootstrap v4 components and grid system for Vue.js. With extensive and automated WAI-ARIA accessibility markup.
The plan is to start by installing Storybook onto the Bootstrap Vue project. Write and run a few Storybook stories, install the Applitools Eyes SDK for Storybook and finally demonstrate the RCA feature.
Now, let’s get on with it and start coding …
Step 1: Clone the Bootstrap Vue GitHub repo locally on your machine by running this command:
git clone git@github.com:bootstrap-vue/bootstrap-vue.git
Step 2: Install all NPM package dependencies by running this command:
yarn
Step 3: Install the Storybook NPM package as a dev-dependency by issuing this command:
yarn add @storybook/vue -D
Step 4: Install all peer dependencies required by Storybook to function properly by issuing this command:
yarn add vue-loader babel-loader babel-preset-vue -D
Step 5: Install css-related NPM packages in order to load CSS styles inside your Storybook stories by issuing this command:
yarn add css-loader style-loader -D
Step 6: Add the following NPM script to your package.json file in order to start the Storybook:
{ "scripts": { "storybook": "start-storybook" } }
Step 7: Create the Storybook config.js file inside a .storybook folder located at the root of the solution folder as follows:
import { configure } from '@storybook/vue'; function loadStories() { const req = require.context('../stories', true, /\.stories\.js$/); req.keys().forEach(filename => req(filename)); } configure(loadStories, module);
Step 8: Create a scss-loader.scss file inside the .storybook folder to include all the styles required by Bootstrap Vue library components to load properly inside our Storybook stories:
@import ‘../node_modules/bootstrap/scss/bootstrap.scss’ @import ‘../src/index.scss’
Then import this file inside the config.js file as follows:
import { configure } from '@storybook/vue'; import ‘!style-loader!css-loader!sass-loader!./scss-loader.scss’ function loadStories() { const req = require.context('../stories', true, /\.stories\.js$/); req.keys().forEach(filename => req(filename)); }
Step 9: Create the navbar.stories.js file inside the stories folder, located at the root of the solution folder, to hold our Storybook stories for the NavBar component:
import { storiesOf } from '@storybook/vue' const navbarStories = storiesOf('BNavBar', module) navbarStories.add('Full Navbar', () => ({ template: `<div> <b-navbar toggleable="lg" type="dark" variant="info"> <b-navbar-brand href="#">NavBar</b-navbar-brand> <b-navbar-toggle target="nav-collapse"></b-navbar-toggle> <b-collapse id="nav-collapse" is-nav> <b-navbar-nav> <b-nav-item href="#"><span>Link</span></b-nav-item> <b-nav-item href="#" disabled>Disabled</b-nav-item> </b-navbar-nav> <b-navbar-nav class="ml-auto"> <b-nav-form> <b-form-input size="sm" class="mr-sm-2" placeholder="Search"></b-form-input> <b-button size="sm" class="my-2 my-sm-0" type="submit">Search</b-button> </b-nav-form> <b-nav-item-dropdown text="Lang" right> <b-dropdown-item href="#">EN</b-dropdown-item> <b-dropdown-item href="#">ES</b-dropdown-item> <b-dropdown-item href="#">RU</b-dropdown-item> <b-dropdown-item href="#">FA</b-dropdown-item> </b-nav-item-dropdown> <b-nav-item-dropdown right> <template slot="button-content"><em>User</em></template> <b-dropdown-item href="#">Profile</b-dropdown-item> <b-dropdown-item href="#">Sign Out</b-dropdown-item> </b-nav-item-dropdown> </b-navbar-nav> </b-collapse> </b-navbar> </div>` }))
The story groups all NavBar stories under the name BNavBar and assigns the first story the name of Full Navbar.
There are two ways to tell Storybook stories about your Vue.js components. Either define all used components in a components object property on the story itself or define the components globally inside .storybook/config.js file so that they are available to all running stories in your solution.
Let’s follow the latter option and define all components needed by NavBar story inside .storybook/config.js file using the Vue.component() helper method:
import { configure } from '@storybook/vue' import Vue from 'vue' import '!style-loader!css-loader!sass-loader!./scss-loader.scss' import { BNavbar, BNavbarNav, BNavbarBrand, BNavbarToggle, BButton, BCollapse, BNavItem, BFormInput, BNavForm, BNavItemDropdown, BDropdownItem } from '../src' Vue.component('b-navbar', BNavbar) Vue.component('b-navbar-nav', BNavbarNav) Vue.component('b-navbar-brand', BNavbarBrand) Vue.component('b-navbar-toggle', BNavbarToggle) Vue.component('b-button', BButton) Vue.component('b-collapse', BCollapse) Vue.component('b-nav-item', BNavItem) Vue.component('b-form-input', BFormInput) Vue.component('b-nav-form', BNavForm) Vue.component('b-nav-item-dropdown', BNavItemDropdown) Vue.component('b-dropdown-item', BDropdownItem) function loadStories() { const req = require.context('../stories', true, /\.stories\.js$/) req.keys().forEach(filename => req(filename)) } configure(loadStories, module)
Step 10: Now the story is ready, let’s run it with Storybook using this command:
yarn storybook
The Storybook engine runs successfully and renders our Navbar story.
Now let’s install the Applitools Eyes SDK for Storybook.
Step 11: Install the Applitools Eyes SDK for Storybook NPM package as a dev-dependency by issuing this command:
yarn add @applitools/eyes-storybook -D
In order to authenticate via the Applitools server, you need to supply the Eyes-Storybook SDK with the API key you got from Applitools. Read more about how to obtain the API key here.
To do this, set the environment variable APPLITOOLS_API_KEY to the API key before running your tests. For example, on Linux/Mac:
export APPLITOOLS_API_KEY=<your_key>
And on Windows:
set APPLITOOLS_API_KEY=<your_key>
Step 12: Let’s run the first cycle of the visual UI testing by issuing this command:
npx eyes-storybook
Once the tests are done running, let’s check back with the Applitools Test Manager Dashboard and verify the results.
The left side of the Dashboard lists all batch test runs that you have previously performed. Currently, there is a single test run with a status of Passed. This is the first test run and so Applitools engine automatically passes this test and all snapshots thereafter as baseline snapshots.
Click on the single batch test to view the snapshots available. For each and every Storybook story, a snapshot is generated by Applitools.
Expand the BNavBar: Full Navbar test run and click the snapshot to view it in a bigger scale:
As you can tell, the test run snapshot is simply an image snapshot for the story that runs inside the Storybook playground application.
At this moment of time, Applitools stores all the generated snapshots as baseline snapshots. The future test runs would generate a new set of snapshots and Applitools would compare them to the baseline ones and report the results accordingly.
You have noticed that Applitools Eyes SDK for Storybook is prepackaged with some default settings allowing you to do visual testing for your stories without having to specify any test configuration settings. However, that doesn’t mean that Applitools doesn’t offer you the means to customize your tests by specifying test configuration settings either through environment variables or storing your settings inside the applitools.config.js configuration file.
Let’s switch gears and explore a unique gem of Applitools that is the Regression UI testing. The traditional regression testing verifies recent source code changes or additions and that the functionality of the software has not changed or been affected in any way.
Applitools allows you to perform visual UI Regression testing. You can run multiple cycles of test runs and all visual changes collected from one to another, are detected by Applitools servers and logged into the Dashboard.
Step 13: Let’s introduce a visual change in the Navbar component by including the following CSS selector inside .storybook/scss-loader.scss file and run our tests again.
@import "../node_modules/bootstrap/scss/bootstrap.scss"; @import "../src/index.scss"; a { background-color: yellow; }
The change causes all the hyperlinks on the Navbar component to render with a background color of yellow.
Step 14: Let’s run the second cycle of the visual UI testing by issuing this command:
npx eyes-storybook
Notice the logs on the terminal windows after running the command:
The test failed and that’s exactly what we expect. Applitools detected the change we introduced to the background color of the hyperlinks and reported it.
Let’s switch back to the Applitools Test Manager Dashboard to check the results.
The new test run is marked as Unresolved. Expand on the Navbar test run to view the visual differences highlighted to you by Applitools:
Make sure both snapshots are selected so that you can easily view the differences. If not, locate the following menu and choose the option Both:
The snapshot above highlights all the hyperlinks, indicating that those hyperlinks have changed.
Step 15: Click on the RCA tool icon (highlighted in the red rectangle) to get a full analysis of the source of this visual bug.
Next, you select the visual difference you want RCA to assess.
I’ve selected to click on the Lang hyperlink on the Navbar. Notice how the RCA tool detects a css style change on the <a> tag represented by the Yellow background.
The RCA tool runs and shows the root cause of this visual difference, which is a change in the CSS Rules.
Step 16: The RCA tool can also detect Attribute changes. Let’s change the tag of the Search box to have a size of lg as follows and run the test once more.
<b-form-input size="lg" class="mr-sm-2" placeholder="Search"></b-form-input>
The RCA tool detects a change in the attributes of the Search box.
It also shows the change of CSS rules as follows:
Step 17: Another category RCA detects is the DOM changes category. Let’s wrap the Search button text with a paragraph tag as follows and run the test again:
<b-button size="sm" class="my-2 my-sm-0" type="submit"><p>Search</p></b-button>
The RCA tool detects a change in the text inside the Search button. Instead of rendering the text as is, the new test snapshot renders the text inside a <p> tag.
Any test run with status Unresolved requires someone’s attention and manual intervention to accept or reject the change. In this case, you may accept the new change(s) and thus update the baseline snapshots.
On the other hand, if the change detected is undesirable, then this implies a bug in the source code and you can reject this new test run to keep the original baseline snapshot without any changes.
In Conclusion
Applitools RCA is both powerful and convenient. It can pinpoint the exact DOM/CSS change. If you did not intend the change, you can identify the code the responsible code quickly and make appropriate modifications to pass your tests.
RCA supports a set of DOM/CSS categories of change, including:
- Textual changes
- CSS Property changes
- Attributes changes
- Bounding Box changes
- Tag changes (for example, when you replace a button with a hyperlink)
Happy Testing!
Video Demonstration
More about Applitools