import { describe, it, expect, vi } from 'vitest'; import { mount } from '@vue/test-utils'; import { createVuetify } from 'vuetify'; import { createRouter, createMemoryHistory } from 'vue-router'; import ProjectLimitOverloadDialog from '../../resources/js/components/projects/ProjectLimitOverloadDialog.vue'; /** * Task 12 — переделка диалога «перегрузки лимита» в «запуск отложен». * Billing v2 Spec C §6.2, план 2026-07-02-launch-gate-balance.md. * Новое поле payload: topup_rub (сумма пополнения в рублях). */ const payload = { current_balance_rub: '200.00', current_capacity_leads: 4, would_be_required_leads: 12, deficit_leads: 8, topup_rub: '400.00', }; const mountDialog = (props?: Record) => mount(ProjectLimitOverloadDialog, { props: { modelValue: true, payload, ...props }, global: { plugins: [createVuetify()], stubs: { VDialog: { template: '
' } }, }, }); describe('ProjectLimitOverloadDialog — Task 12 (запуск отложен)', () => { it('монтируется без ошибок', () => { const w = mountDialog(); expect(w.exists()).toBe(true); }); it('показывает текст «не запущен»', () => { const w = mountDialog(); expect(w.text().toLowerCase()).toContain('не запущен'); }); it('показывает сумму пополнения из topup_rub', () => { const w = mountDialog(); expect(w.text()).toContain('400'); }); it('содержит кнопку [data-test="topup"]', () => { const w = mountDialog(); expect(w.find('[data-test="topup"]').exists()).toBe(true); }); it('содержит кнопку [data-test="reduce"]', () => { const w = mountDialog(); expect(w.find('[data-test="reduce"]').exists()).toBe(true); }); it('содержит кнопку закрытия [data-test="close"]', () => { const w = mountDialog(); expect(w.find('[data-test="close"]').exists()).toBe(true); }); it('[data-test="topup"] закрывает диалог (update:modelValue=false)', async () => { const w = mountDialog(); await w.find('[data-test="topup"]').trigger('click'); const emitted = w.emitted('update:modelValue') as unknown[][]; expect(emitted).toBeTruthy(); expect(emitted[0][0]).toBe(false); }); it('[data-test="topup"] ведёт в /billing через router.push', async () => { const router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/billing', component: { template: '
billing
' } }], }); const push = vi.spyOn(router, 'push'); const w = mount(ProjectLimitOverloadDialog, { props: { modelValue: true, payload }, global: { plugins: [createVuetify(), router], stubs: { VDialog: { template: '
' } } }, }); await w.find('[data-test="topup"]').trigger('click'); expect(push).toHaveBeenCalledWith('/billing'); }); it('[data-test="reduce"] эмитит set-zero', async () => { const w = mountDialog(); await w.find('[data-test="reduce"]').trigger('click'); expect(w.emitted('set-zero')).toBeTruthy(); }); it('[data-test="close"] закрывает диалог (update:modelValue=false)', async () => { const w = mountDialog(); await w.find('[data-test="close"]').trigger('click'); const emitted = w.emitted('update:modelValue') as unknown[][]; expect(emitted).toBeTruthy(); expect(emitted[0][0]).toBe(false); }); it('payload=null — без падения, карточка не рендерится', () => { const w = mount(ProjectLimitOverloadDialog, { props: { modelValue: true, payload: null }, global: { plugins: [createVuetify()], stubs: { VDialog: { template: '
' } } }, }); expect(w.find('[data-testid="overload-dialog"]').exists()).toBe(false); }); it('текст на «вы», без «ты»', () => { const w = mountDialog(); const text = w.text(); expect(text).not.toContain('У тебя'); expect(text).not.toContain('пополни '); expect(text).not.toContain('поставь '); }); });