Applitools Eyes: Getting Started with AI-Powered Visual Testing

Applitools Eyes: Getting Started with AI-Powered Visual Testing

Applitools Eyes is a visual testing platform that uses AI to compare screenshots intelligently. Instead of pixel-by-pixel diffing (which generates noise from anti-aliasing and rendering differences), it uses a Visual AI model trained on millions of UI screenshots to distinguish real visual regressions from irrelevant rendering variations.

This guide covers setup with Selenium, Playwright, and Cypress, plus the Ultrafast Grid for cross-browser visual testing.

How Applitools Visual AI Differs from Pixel Diffing

Standard screenshot diffing compares pixels. A 1-pixel anti-aliasing difference on a button edge is flagged as a failure — even though no human would notice it.

Applitools Visual AI understands UI semantics. It knows that:

  • A button that moved 1px due to font rendering isn't a regression
  • A button that changed colour from blue to red is a regression
  • Missing text in a heading is a regression
  • Slightly different shadow opacity isn't a regression

This dramatically reduces false positives, which is the primary reason visual testing suites get disabled — too many false alarms.

Setup

Get an API key

Sign up at applitools.com and get your API key from the dashboard. Every SDK call needs this key.

export APPLITOOLS_API_KEY=your_api_key_here

Choose your SDK

Applitools provides SDKs for every major testing framework:

# Selenium Java (Maven)
<span class="hljs-comment"># Add to pom.xml

<span class="hljs-comment"># Playwright Node
npm install --save-dev @applitools/eyes-playwright

<span class="hljs-comment"># Cypress
npm install --save-dev @applitools/eyes-cypress

<span class="hljs-comment"># Selenium Python
pip install eyes-selenium

<span class="hljs-comment"># WebdriverIO
npm install --save-dev @applitools/eyes-webdriverio

Getting Started: Playwright SDK

The Playwright SDK is the most modern integration. Install it:

npm install --save-dev @applitools/eyes-playwright @playwright/test

Basic test

// tests/visual/checkout.spec.js
import { test } from '@playwright/test';
import { Eyes, Target, Configuration, BatchInfo, BrowserType } from '@applitools/eyes-playwright';

const batch = new BatchInfo({ name: 'Checkout Flow Visual Tests' });

test.describe('Checkout Visual Tests', () => {
  let eyes;

  test.beforeEach(async ({ page }) => {
    eyes = new Eyes();
    const config = new Configuration();
    config.setBatch(batch);
    config.setApiKey(process.env.APPLITOOLS_API_KEY);
    eyes.setConfiguration(config);

    await eyes.open(page, 'My App', test.info().title, {
      width: 1280,
      height: 720
    });
  });

  test.afterEach(async () => {
    await eyes.closeAsync();
  });

  test('checkout form - empty state', async ({ page }) => {
    await page.goto('/checkout');
    await page.waitForLoadState('networkidle');

    // Check the full page
    await eyes.check('Checkout Form', Target.window().fully());
  });

  test('checkout form - shipping step', async ({ page }) => {
    await page.goto('/checkout');
    await page.fill('#name', 'Alice Smith');
    await page.fill('#address', '123 Main St');

    // Check just the form region
    await eyes.check('Shipping Step - Filled', Target.region(page.locator('.checkout-form')));
  });

  test('order confirmation page', async ({ page }) => {
    // Set up test order state via API
    await page.request.post('/api/test/create-order', {
      data: { orderId: 'TEST-001' }
    });

    await page.goto('/order/TEST-001/confirmation');
    await eyes.check('Order Confirmation', Target.window().fully());
  });
});

Closing the batch and getting results

// In afterAll, close the batch to get final status
test.afterAll(async () => {
  const runner = new ClassicRunner(); // or VisualGridRunner
  const results = await runner.getAllTestResults(false);
  console.log(results.toString());
});

Ultrafast Grid: Cross-Browser Visual Testing

The Ultrafast Grid is Applitools' rendering farm. Instead of running your tests in multiple browsers locally, you run them once and Percy renders the captured DOM in multiple browsers and screen sizes in parallel.

import {
  Eyes,
  Target,
  Configuration,
  BatchInfo,
  BrowserType,
  DeviceName,
  ScreenOrientation,
  VisualGridRunner
} from '@applitools/eyes-playwright';

// Use VisualGridRunner instead of ClassicRunner
const runner = new VisualGridRunner({ testConcurrency: 5 });

test.beforeEach(async ({ page }) => {
  eyes = new Eyes(runner);
  const config = new Configuration();
  config.setBatch(new BatchInfo({ name: 'Cross-Browser Suite' }));
  config.setApiKey(process.env.APPLITOOLS_API_KEY);

  // Configure browsers for the Ultrafast Grid
  config.addBrowser({ width: 1280, height: 800, name: BrowserType.CHROME });
  config.addBrowser({ width: 1280, height: 800, name: BrowserType.FIREFOX });
  config.addBrowser({ width: 1280, height: 800, name: BrowserType.SAFARI });
  config.addBrowser({ width: 1280, height: 800, name: BrowserType.EDGE_CHROMIUM });

  // Mobile devices
  config.addDeviceEmulation(DeviceName.iPhone_12, ScreenOrientation.PORTRAIT);
  config.addDeviceEmulation(DeviceName.Pixel_5, ScreenOrientation.PORTRAIT);
  config.addDeviceEmulation(DeviceName.iPad_Pro, ScreenOrientation.LANDSCAPE);

  eyes.setConfiguration(config);

  await eyes.open(page, 'My App', test.info().title);
});

With this configuration, each eyes.check() call sends one snapshot to the Ultrafast Grid, which renders it across all 7 configurations in parallel. You test cross-browser appearance with the speed of a single-browser run.

Selenium Java Integration

// pom.xml dependency
<dependency>
  <groupId>com.applitools</groupId>
  <artifactId>eyes-selenium-java5</artifactId>
  <version>5.64.0</version>
  <scope>test</scope>
</dependency>
import com.applitools.eyes.BatchInfo;
import com.applitools.eyes.RectangleSize;
import com.applitools.eyes.selenium.Eyes;
import com.applitools.eyes.selenium.fluent.Target;
import org.junit.jupiter.api.*;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

class CheckoutVisualTest {

    private static final BatchInfo BATCH = new BatchInfo("Checkout Visual Tests");
    private Eyes eyes;
    private WebDriver driver;

    @BeforeEach
    void setUp() {
        driver = new ChromeDriver();
        eyes = new Eyes();
        eyes.setApiKey(System.getenv("APPLITOOLS_API_KEY"));
        eyes.setBatch(BATCH);

        eyes.open(
            driver,
            "My App",           // app name
            "Checkout Form",    // test name
            new RectangleSize(1280, 800)
        );
    }

    @AfterEach
    void tearDown() throws Exception {
        eyes.closeAsync();
        driver.quit();
    }

    @Test
    void checkoutFormRendersCorrectly() {
        driver.get("https://your-app.com/checkout");

        // Full page screenshot
        eyes.check("Checkout - Full Page", Target.window().fully());
    }

    @Test
    void paymentSectionRendersCorrectly() {
        driver.get("https://your-app.com/checkout/payment");

        // Region-specific check
        eyes.check(
            "Payment Form",
            Target.region(driver.findElement(By.cssSelector(".payment-section")))
        );
    }
}

Cypress Integration

npm install --save-dev @applitools/eyes-cypress
npx eyes-setup  # adds Eyes commands to Cypress support file

Configure in cypress.config.js:

const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      require('@applitools/eyes-cypress')(on);
      return config;
    }
  }
});

Use in tests:

describe('Homepage Visual Tests', () => {
  beforeEach(() => {
    cy.eyesOpen({
      appName: 'My App',
      testName: Cypress.currentTest.title,
      browser: [
        { width: 1280, height: 800, name: 'chrome' },
        { width: 375, height: 812, name: 'chrome' },
      ]
    });
  });

  afterEach(() => {
    cy.eyesClose();
  });

  it('hero section renders correctly', () => {
    cy.visit('/');
    cy.get('.hero').should('be.visible');
    cy.eyesCheckWindow({
      tag: 'Homepage - Hero',
      target: 'region',
      selector: '.hero'
    });
  });

  it('navigation renders correctly', () => {
    cy.visit('/');
    cy.eyesCheckWindow({
      tag: 'Navigation Bar',
      target: 'region',
      selector: 'nav'
    });
  });
});

Match Levels

Applitools provides different sensitivity levels for comparison:

import { MatchLevel } from '@applitools/eyes-playwright';

// Strict — pixel-level differences matter (for images, icons)
await eyes.check('Logo', Target.region(logoLocator).matchLevel(MatchLevel.Strict));

// Content — layout differences matter, text changes matter, style changes don't
await eyes.check('Article', Target.region(articleLocator).matchLevel(MatchLevel.Content));

// Layout — structural changes matter, content changes don't (for CMS content)
await eyes.check('Page Structure', Target.window().matchLevel(MatchLevel.Layout));

// Exact — byte-for-byte identical (rarely useful)
await eyes.check('Reference Image', Target.image('reference.png').matchLevel(MatchLevel.Exact));

Use Layout match level for pages where the content changes legitimately (news feeds, user-generated content) but the structure should stay the same.

Baseline Management

Accepting baselines

On the first run, Applitools creates baselines automatically. Subsequent runs compare against these baselines.

In the Applitools dashboard:

  1. Click "Unreviewed" to see new diffs
  2. Click thumbs up to accept as new baseline
  3. Click thumbs down to reject (test will fail in CI)
  4. Use "Accept All" for bulk-accepting expected changes

Ignoring regions

Some regions contain dynamic content that will always differ:

await eyes.check('Dashboard', Target.window()
  .ignore(By.css('.live-clock'))
  .ignore(By.css('.ad-banner'))
  .ignore(By.css('.user-avatar'))
  .fully()
);

Floating regions

For elements that can move within bounds:

await eyes.check('Header', Target.window()
  .floating(By.css('.notification-badge'), 5, 5, 5, 5) // max 5px deviation in each direction
);

GitHub Actions CI

name: Visual Tests

on: [push, pull_request]

jobs:
  applitools-visual:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright
        run: npx playwright install chromium

      - name: Run Applitools visual tests
        run: npx playwright test tests/visual/
        env:
          APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }}
          # Ultrafast Grid concurrency
          APPLITOOLS_CONCURRENCY: 5

Applitools integrates with GitHub's status checks API. Failed visual tests block merges; approved tests auto-pass.

Cost Considerations

Applitools pricing is checkpoint-based. Each eyes.check() call is one checkpoint. With the Ultrafast Grid, each browser/device combination multiplies the count.

Optimise costs:

  • Use Target.region() for targeted checks rather than always capturing full pages
  • Group related checks in one test (one eyes.open() / eyes.close() pair)
  • Use Layout match level for high-churn content to reduce noise and re-baselines
  • Separate visual tests from functional tests so you can run them independently

When to Use Applitools

Applitools is best suited for:

  • Teams maintaining design systems across many components
  • Multi-browser visual consistency requirements
  • Applications with complex layouts (data grids, dashboards, reports)
  • Enterprises that need audit trails of visual approvals

If you have a small application and just need basic screenshot comparison, a simpler tool (Percy, Chromatic, or even Playwright's built-in screenshot + image comparison) may be sufficient.

For AI-powered visual testing with extensive cross-browser coverage and smart noise reduction, Applitools is the industry leader.

Read more

Testing Atlantis Terraform PR Automation: Workflows, Plan Verification, and Policy Enforcement

Testing Atlantis Terraform PR Automation: Workflows, Plan Verification, and Policy Enforcement

Atlantis automates Terraform plan and apply through pull requests. But Atlantis itself needs testing: workflow configuration, plan output validation, policy enforcement, and server health checks. This guide covers testing Atlantis workflows locally with atlantis-local, validating plan outputs with custom scripts, enforcing Terraform policies with OPA and Conftest, and monitoring Atlantis

By HelpMeTest