import { describe, it, expect, vi, beforeEach } from 'vitest'; import { mount, flushPromises } from '@vue/test-utils'; import { createVuetify } from 'vuetify'; import { createRouter, createMemoryHistory } from 'vue-router'; import AdminBillingView from '../../resources/js/views/admin/AdminBillingView.vue'; import { ADMIN_BILLING_TENANTS, ADMIN_BILLING_SUMMARY } from '../../resources/js/composables/mockAdmin'; vi.mock('../../resources/js/api/admin', async (importOriginal) => { const orig = await importOriginal(); return { ...orig, listAdminBilling: vi.fn(), }; }); const adminApi = await import('../../resources/js/api/admin'); beforeEach(() => { vi.clearAllMocks(); vi.mocked(adminApi.listAdminBilling).mockResolvedValue({ tenants: ADMIN_BILLING_TENANTS.map((r) => ({ id: r.id, subdomain: `tenant${r.id}`, organization_name: r.name, contact_email: `t${r.id}@test.io`, status: r.status === 'overdue' ? 'active' : r.status, balance_rub: String(r.balance_rub), tariff_id: 1, tariff_name: { start: 'Старт', basic: 'Базовый', pro: 'Команда', enterprise: 'Enterprise' }[r.tariff] ?? r.tariff, mrr_rub: String(r.mrr_rub), monthly_topups_rub: String(r.monthly_topups_rub), monthly_charges_rub: String(r.monthly_charges_rub), last_payment_at: r.last_payment_at, chargeback_unrecovered_rub: r.status === 'overdue' ? '1.00' : '0.00', })), summary: { total_mrr_rub: String(ADMIN_BILLING_SUMMARY.total_mrr_rub), monthly_revenue_rub: String(ADMIN_BILLING_SUMMARY.monthly_revenue_rub), overdue_count: ADMIN_BILLING_SUMMARY.overdue_count, refunds_count_30d: ADMIN_BILLING_SUMMARY.refunds_count_30d, }, }); }); const mountView = async () => { const router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/admin/billing', component: AdminBillingView }], }); await router.push('/admin/billing'); await router.isReady(); const wrapper = mount(AdminBillingView, { global: { plugins: [createVuetify(), router] }, }); await flushPromises(); return wrapper; }; describe('AdminBillingView.vue', () => { it('монтируется и содержит заголовок «Биллинг»', async () => { const wrapper = await mountView(); expect(wrapper.text()).toContain('Биллинг'); }); it('содержит 4 stats: MRR / Выручка / Просрочка / Возвраты', async () => { const wrapper = await mountView(); const text = wrapper.text(); expect(text).toContain('MRR'); expect(text).toContain('Выручка за месяц'); expect(text).toContain('Просрочка'); expect(text).toContain('Возвраты за 30 дн'); }); it('содержит таблицу тенантов с tariff/balance/MRR/status', async () => { const wrapper = await mountView(); const text = wrapper.text(); expect(text).toContain('Окна Москва'); expect(text).toContain('BigCorp'); expect(text).toContain('Команда'); expect(text).toContain('Активен'); expect(text).toContain('Просрочка'); expect(text).toContain('Заблокирован'); }); });