Setting up visual regression testing in 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Setting Up Visual Regression Testing for 1C-Bitrix

A CSS update in the template, a component change, a jQuery update — and the "Buy" button shifts by 3 pixels, the price block overlaps the gallery on mobile, the product card font becomes 2px smaller. You cannot catch this by eye. Visual regression testing takes a screenshot of a page before and after changes and compares them pixel by pixel.

Tools

Playwright + built-in snapshot tests — the most integrated option if Playwright is already used for E2E. Stores reference screenshots in the repository and compares them on every run.

Percy (BrowserStack) — a SaaS service with a visual dashboard, convenient review system, and responsive design support. Requires a paid subscription above 5,000 snapshots/month.

Chromatic — similar to Percy, optimized for Storybook. A good choice if the project has a component library.

For most 1C-Bitrix projects, Playwright with built-in snapshot tests is sufficient.

Setting Up Snapshot Tests in Playwright

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  snapshotPathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}{ext}',
  expect: {
    toHaveScreenshot: {
      maxDiffPixels: 50,      // tolerance: 50 pixel difference
      threshold: 0.01,         // 1% per-pixel difference
      animations: 'disabled',  // disable CSS animations
    },
  },
});

Basic Visual Tests for 1C-Bitrix

// tests/visual/catalog.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Catalog visual', () => {

  test('catalog section page', async ({ page }) => {
    await page.goto('/catalog/electronics/');
    // Wait for images to load
    await page.waitForLoadState('networkidle');
    // Hide dynamic elements (time-based sale labels, counters)
    await page.evaluate(() => {
      document.querySelectorAll('.catalog-item-label-sale').forEach(el => {
        (el as HTMLElement).style.visibility = 'hidden';
      });
    });
    await expect(page).toHaveScreenshot('catalog-section.png');
  });

  test('product card', async ({ page }) => {
    await page.goto('/catalog/electronics/headphones/model-x100/');
    await page.waitForLoadState('networkidle');
    // Hide sale timer if present
    await page.evaluate(() => {
      const timer = document.querySelector('.sale-timer');
      if (timer) (timer as HTMLElement).style.display = 'none';
    });
    await expect(page).toHaveScreenshot('product-card.png', { fullPage: false });
  });

  test('cart page', async ({ page }) => {
    // Log in and add a product
    await page.request.post('/local/ajax/cart-add.php', {
      data: { product_id: 123, quantity: 1 }
    });
    await page.goto('/personal/cart/');
    await page.waitForLoadState('networkidle');
    await expect(page).toHaveScreenshot('cart.png');
  });

});

Mobile Viewport

A separate project in the config for mobile view:

// playwright.config.ts
projects: [
  {
    name: 'desktop-chrome',
    use: { viewport: { width: 1440, height: 900 } },
  },
  {
    name: 'mobile-iphone',
    use: {
      ...devices['iPhone 14'],
      viewport: { width: 390, height: 844 },
    },
    testMatch: '**/visual/**',
  },
],

Updating Reference Screenshots

When an intentional design change is made, update the baselines with:

npx playwright test --update-snapshots tests/visual/

The updated screenshots are committed to the repository as part of a PR. The reviewer sees not only code changes in the diff but also visual changes.

Masking Dynamic Elements

1C-Bitrix pages contain elements that change on every load: visitor counters, sale timers, "Viewed today" widgets, and similar. These must be masked:

await expect(page).toHaveScreenshot('homepage.png', {
  mask: [
    page.locator('.bx-visitor-counter'),
    page.locator('.product-views-count'),
    page.locator('.sale-countdown-timer'),
    page.locator('.personal-greeting'), // "Good afternoon, Ivan"
  ],
});

CI/CD Integration

# .github/workflows/visual.yml
visual-tests:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: npm ci
    - run: npx playwright install chromium
    - run: npx playwright test tests/visual/
      env:
        TEST_BASE_URL: ${{ secrets.STAGING_URL }}
    - uses: actions/upload-artifact@v4
      if: failure()
      with:
        name: visual-diff
        path: test-results/

When a test fails, test-results/ will contain three files: the baseline, the current snapshot, and a diff with highlighted differences.

Implementation Strategy

Stage What to do Timeline
Baseline snapshots 10–15 key pages on desktop and mobile 1–2 days
CI integration Run on PR to staging 0.5 days
Expand coverage Catalog components, cart, checkout 2–3 days
Mobile profile Separate tests for 375px, 768px 1 day

Start with the homepage, catalog page, product card, and cart — these account for 80% of what breaks during template updates.