Настройка Jest-тестов для Vue/React компонентов 1С-Битрикс

Наша компания занимается разработкой, поддержкой и обслуживанием решений на Битрикс и Битрикс24 любой сложности. От простых одностраничных сайтов до сложных интернет магазинов, CRM систем с интеграцией 1С и телефонии. Опыт разработчиков подтвержден сертификатами от вендора.
Предлагаемые услуги
Показано 1 из 1 услугВсе 1626 услуг
Настройка Jest-тестов для Vue/React компонентов 1С-Битрикс
Простая
~1 рабочий день
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1167
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    563
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    743
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    976

Настройка Jest-тестов для Vue/React компонентов 1С-Битрикс

Vue и React компоненты в Битрикс-проектах — часть, которую проще всего протестировать изолированно. Компонент принимает пропсы, отдаёт DOM — чистая функция. Jest с Testing Library даёт возможность тестировать компоненты без браузера, быстро, в Node.js-окружении. Для Битрикс-проекта это означает: тесты фронтенда запускаются за секунды, не требуют запущенного сервера, интегрируются в CI без Docker.

Настройка Jest-тестов для Vue/React компонентов 1С-Битрикс

Структура тестов в Битрикс-проекте

/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__/: при рефакторинге перемещение компонента и его тестов происходит вместе.

jest.config.ts для Vue + TypeScript

// jest.config.ts
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 — мокируем
        '^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;

Мок объекта BX и BX24

// 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(),
};

Тест Vue-компонента карточки товара

// src/components/catalog/ProductCard.test.ts
import { mount }           from '@vue/test-utils';
import { describe, it, expect, vi, beforeEach } from 'vitest'; // или jest.fn()
import ProductCard         from './ProductCard.vue';
import * as cartApi        from '@/api/cart';

const mockProduct = {
    id:       '42',
    name:     'Дрель Bosch GSB 21-2 RCT',
    price:    '8990',
    currency: 'RUB',
    img:      '/upload/test.jpg',
    inStock:  true,
};

describe('ProductCard', () => {
    it('отображает название и цену товара', () => {
        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('показывает кнопку «В корзину» для товара в наличии', () => {
        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('скрывает кнопку «В корзину» для товара не в наличии', () => {
        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('вызывает API корзины при клике «В корзину»', 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-компонента (TSX)

// 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:     'Перфоратор Makita HR2630',
    price:    12490,
    quantity: 2,
    img:      null,
};

describe('CartItem', () => {
    it('отображает название и суммарную стоимость', () => {
        render(<CartItem item={mockItem} onRemove={jest.fn()} onQuantityChange={jest.fn()} />);

        expect(screen.getByText('Перфоратор Makita HR2630')).toBeInTheDocument();
        expect(screen.getByText('24 980 ₽')).toBeInTheDocument(); // 12490 * 2
    });

    it('вызывает onQuantityChange при изменении количества', 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);
    });
});

Тест composable (Vue Composition API)

// src/composables/useCart.test.ts
import { setActivePinia, createPinia } from 'pinia';
import { useCart }   from './useCart';
import * as cartApi  from '@/api/cart';

vi.mock('@/api/cart');

describe('useCart', () => {
    beforeEach(() => {
        setActivePinia(createPinia());
    });

    it('инициализируется с пустой корзиной', () => {
        const { items, totalCount, totalPrice } = useCart();
        expect(items.value).toEqual([]);
        expect(totalCount.value).toBe(0);
        expect(totalPrice.value).toBe(0);
    });

    it('добавляет товар и обновляет состояние', async () => {
        vi.mocked(cartApi.addToCart).mockResolvedValue({
            items:      [{ id: 1, name: 'Test', price: 100, quantity: 1, img: null }],
            totalPrice: 100,
            totalCount: 1,
            currency:   'RUB',
        });

        const { addItem, totalCount } = useCart();
        await addItem(42, 1);

        expect(totalCount.value).toBe(1);
    });
});

Запуск тестов

# Все тесты
npx jest

# С покрытием
npx jest --coverage

# Watch-mode при разработке
npx jest --watch

# Конкретный файл
npx jest ProductCard

Сроки

Задача Сроки
Настройка Jest + @vue/test-utils или @testing-library/react 4–8 часов
Базовые тесты для 5–10 компонентов 1–2 дня
Тесты composables / хуков с Pinia/Zustand 1 день
Интеграция в CI, конфигурация coverage-репорта 4 часа