92f5973a95
api/billing.ts (getWallet) + BillingView тянет GET /api/billing/wallet на mount (шапка + BalanceCard, loading/error-state). BalanceCard на реальные props с nullable-тарифом. featureLabel для feature-слагов. Sprint 2 Plan C, audit E3 (frontend pt1). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { mount, flushPromises } from '@vue/test-utils';
|
|
import { createVuetify } from 'vuetify';
|
|
import BillingView from '../../resources/js/views/BillingView.vue';
|
|
import * as billingApi from '../../resources/js/api/billing';
|
|
import type { Wallet } from '../../resources/js/api/billing';
|
|
|
|
vi.mock('../../resources/js/api/billing');
|
|
|
|
const vuetify = createVuetify();
|
|
|
|
function makeWallet(): Wallet {
|
|
return {
|
|
balance_rub: '14250.00',
|
|
balance_leads: 285,
|
|
runway_days: 142,
|
|
tariff: {
|
|
code: 'pro',
|
|
name: 'Про',
|
|
price_monthly: '990.00',
|
|
billing_model: 'hybrid',
|
|
features: ['webhook', 'kanban', 'api'],
|
|
},
|
|
};
|
|
}
|
|
|
|
describe('BillingView.vue', () => {
|
|
beforeEach(() => {
|
|
vi.mocked(billingApi.getWallet).mockResolvedValue(makeWallet());
|
|
});
|
|
|
|
const factory = () =>
|
|
mount(BillingView, {
|
|
global: {
|
|
plugins: [vuetify],
|
|
stubs: { TransactionsTable: true, InvoicesTable: true, ChargesTab: true },
|
|
},
|
|
});
|
|
|
|
it('монтируется с заголовком «Биллинг и тарифы»', async () => {
|
|
const wrapper = factory();
|
|
await flushPromises();
|
|
expect(wrapper.find('h1').text()).toBe('Биллинг и тарифы');
|
|
});
|
|
|
|
it('загружает кошелёк и показывает баланс в шапке', async () => {
|
|
const wrapper = factory();
|
|
await flushPromises();
|
|
expect(billingApi.getWallet).toHaveBeenCalled();
|
|
const text = wrapper.text();
|
|
expect(text).toMatch(/14\s+250\s*₽/);
|
|
expect(text).toContain('285');
|
|
});
|
|
|
|
it('показывает тариф из API в BalanceCard', async () => {
|
|
const wrapper = factory();
|
|
await flushPromises();
|
|
const text = wrapper.text();
|
|
expect(text).toContain('Про');
|
|
expect(text).toContain('Канбан');
|
|
expect(text).toContain('Webhook');
|
|
});
|
|
|
|
it('показывает «Тариф не выбран» при tariff=null', async () => {
|
|
vi.mocked(billingApi.getWallet).mockResolvedValue({
|
|
balance_rub: '0.00',
|
|
balance_leads: 0,
|
|
runway_days: null,
|
|
tariff: null,
|
|
});
|
|
const wrapper = factory();
|
|
await flushPromises();
|
|
expect(wrapper.text()).toContain('Тариф не выбран');
|
|
});
|
|
|
|
it('показывает error-alert при сбое загрузки кошелька', async () => {
|
|
vi.mocked(billingApi.getWallet).mockRejectedValue(new Error('network'));
|
|
const wrapper = factory();
|
|
await flushPromises();
|
|
expect(wrapper.text()).toContain('Не удалось загрузить');
|
|
});
|
|
|
|
it('содержит табы Обзор / Списания', async () => {
|
|
const wrapper = factory();
|
|
await flushPromises();
|
|
const text = wrapper.text();
|
|
expect(text).toContain('Обзор');
|
|
expect(text).toContain('Списания');
|
|
});
|
|
});
|