Jest Setup for Isolated Vue/React Components in 1C-Bitrix Projects
Picture this: you update a cart component, merge to develop, and an hour later production crashes—but the test environment worked fine. Sound familiar? Isolated tests catch such regressions in seconds, without a server or database. The official documentation recommends isolating component testing using mocks. 1С-Bitrix Developer's Guide We set up Jest for Vue/React components in 1C-Bitrix projects: mocks for BX/BX24, CI integration, coverage of key scenarios. We work under contract and guarantee results. By our estimates, introducing isolated tests reduces bug detection costs by 30–50%, which for an average Bitrix online store means savings of 200,000 to 500,000 rubles per year. Manual testing of one module typically costs the company 30,000–50,000 rubles per month with a full-time QA engineer. Tests run in milliseconds and can be executed in any environment. The investment for setting up such tests is usually around 100,000 rubles, quickly recovered by preventing regressions.
Problems That Isolated Testing Solves
- Regressions when templates change. For example, after updating the cart component, the total display breaks—the test catches it before deployment.
- Hidden bugs in composables/hooks. Cart logic, filtering, pagination—if not covered by tests, the error surfaces only in production.
- Slow manual testing. With every commit, QA manually checks dozens of scenarios.
- No CI checks. Without tests, you cannot guarantee that the frontend isn't broken after merging branches.
Additionally, a typical issue is incorrect work with the BX object after a CMS update. Mocks allow simulating the API without a real server, simplifying debugging.
How We Set Up Jest for a Bitrix Project
We are certified 1C-Bitrix specialists with 10+ years on the market and over 50 successful projects. The setup includes:
- Jest configuration with TypeScript, jsdom, coverage.
- Mocks for global objects
BX,BX24,BX.ajax. - Tests for 5–10 priority components (cart, product card, filter).
- Integration with GitLab CI / GitHub Actions.
- README with launch instructions and rules for adding new tests.
Typical test structure in a Bitrix project
/local/templates/my_site/ src/ components/ catalog/ ProductCard.vue ProductCard.test.ts cart/ CartItem.tsx CartItem.test.tsx composables/ useCart.ts useCart.test.ts jest.config.ts package.json Tests stay next to components—more convenient than a separate folder: when refactoring, we move them together.
Example jest.config.ts for Vue + TypeScript
import type { Config } from 'jest'; const config: Config = { testEnvironment: 'jsdom', transform: { '^.+\\.vue$': ['@vue/vue3-jest', { tsConfig: 'tsconfig.json' }], '^.+\\.(ts|tsx|js|jsx)$': ['ts-jest', { tsconfig: 'tsconfig.json' }], }, moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1', '\\.(css|scss|png|jpg|svg)$': '<rootDir>/src/__mocks__/fileMock.ts', '^bx-globals$': '<rootDir>/src/__mocks__/bx.ts', }, moduleFileExtensions: ['ts', 'tsx', 'vue', 'js', 'json'], coverageDirectory: 'coverage', collectCoverageFrom: [ 'src/components/**/*.{vue,ts,tsx}', 'src/composables/**/*.ts', '!src/**/*.test.{ts,tsx}', ], setupFilesAfterFramework: ['<rootDir>/src/test-setup.ts'], }; export default config; Mock of global BX and BX24 objects
// src/__mocks__/bx.ts global.BX = { bitrix_sessid: () => 'test-sessid-12345', message: (params: Record<string, string>) => params, bind: jest.fn(), Event: { add: jest.fn() }, }; global.BX24 = { init: (cb: () => void) => cb(), isAdmin: () => false, callMethod: jest.fn(), callBatch: jest.fn(), resizeWindow: jest.fn(), }; Why Test Isolated Rather Than Integrated?
Isolated tests don't need a database, server, or running Bitrix. They execute in milliseconds—a 1000x speed advantage over integration tests that take minutes—and are easy to run locally and in CI. Integration tests (e.g., via Selenium) are slower and more flaky. We recommend the pyramid: 70% unit tests, 20% component tests, 10% e2e.
| Parameter | Isolated tests | Integration tests |
|---|---|---|
| Speed | milliseconds | minutes |
| Dependencies | only Node.js | server, DB, browser |
| Reliability | high | medium (flaky) |
| CI execution | seamless | requires infrastructure |
Test Examples
Vue ProductCard component
// src/components/catalog/ProductCard.test.ts import { mount } from '@vue/test-utils'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import ProductCard from './ProductCard.vue'; import * as cartApi from '@/api/cart'; const mockProduct = { id: '42', name: 'Drill Bosch GSB 21-2 RCT', price: '8990', currency: 'RUB', img: '/upload/test.jpg', inStock: true, }; describe('ProductCard', () => { it('displays product name and price', () => { const wrapper = mount(ProductCard, { props: { product: mockProduct }, }); expect(wrapper.find('.product-name').text()).toBe(mockProduct.name); expect(wrapper.find('.product-price').text()).toContain('8 990'); }); it('shows "Add to cart" button for in-stock product', () => { const wrapper = mount(ProductCard, { props: { product: mockProduct }, }); expect(wrapper.find('[data-action="add-to-cart"]').exists()).toBe(true); expect(wrapper.find('.out-of-stock').exists()).toBe(false); }); it('hides "Add to cart" button for out-of-stock product', () => { const wrapper = mount(ProductCard, { props: { product: { ...mockProduct, inStock: false } }, }); expect(wrapper.find('[data-action="add-to-cart"]').exists()).toBe(false); expect(wrapper.find('.out-of-stock').exists()).toBe(true); }); it('calls cart API on "Add to cart" click', async () => { const addToCart = vi.spyOn(cartApi, 'addToCart').mockResolvedValue({ items: [], totalPrice: 8990, totalCount: 1, currency: 'RUB', }); const wrapper = mount(ProductCard, { props: { product: mockProduct }, }); await wrapper.find('[data-action="add-to-cart"]').trigger('click'); await wrapper.vm.$nextTick(); expect(addToCart).toHaveBeenCalledWith({ productId: 42, quantity: 1, }); }); }); React CartItem component
// src/components/cart/CartItem.test.tsx import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import CartItem from './CartItem'; import * as cartApi from '@/api/cart'; const mockItem = { id: 1, name: 'Hammer Drill Makita HR2630', price: 12490, quantity: 2, img: null, }; describe('CartItem', () => { it('displays name and total price', () => { render(<CartItem item={mockItem} onRemove={jest.fn()} onQuantityChange={jest.fn()} />); expect(screen.getByText('Hammer Drill Makita HR2630')).toBeInTheDocument(); expect(screen.getByText('24 980 ₽')).toBeInTheDocument(); }); it('calls onQuantityChange when quantity changes', async () => { const onQuantityChange = jest.fn(); const user = userEvent.setup(); render(<CartItem item={mockItem} onRemove={jest.fn()} onQuantityChange={onQuantityChange} />); const plusBtn = screen.getByRole('button', { name: '+' }); await user.click(plusBtn); expect(onQuantityChange).toHaveBeenCalledWith(mockItem.id, 3); }); }); How to Add Tests to CI?
- Configure a job in GitLab CI / GitHub Actions: install dependencies, run
jest --coverage. - Add a coverage threshold in jest.config.ts:
coverageThreshold. - Set up artifacts for the coverage report.
- On failed tests — block the merge request.
What's Included in the Work
| Stage | What We Do | Duration |
|---|---|---|
| Audit current frontend | Determine list of components, composables, API layers | 4–8 hours |
| Set up Jest + mocks | Configuration, setup files, mocks for BX/BX24/static assets | 1 day |
| Write tests for priority components | 5–10 components covering key scenarios | 1–2 days |
| Integrate into CI | Add job to GitLab/GitHub Actions, configure coverage report | 4 hours |
| Documentation and training | README with instructions, demo session for the team | 2–4 hours |
Checklist for test adoption
- Collect list of components, composables, and API layers. - Set up Jest with TypeScript and jsdom. - Create mocks for BX, BX24, static assets. - Write tests for top-5 components. - Integrate into CI with coverage threshold. - Conduct review with the team.Get a consultation on setting up Jest for your project—we will estimate the scope and timeline for free. Contact us—we will prepare a proposal and guarantee quality: all tests pass in CI, coverage meets the specified threshold.

