Files
portal/app/tests/Frontend/BalanceCard.spec.ts
T
Дмитрий 8cc09d4509
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
fix(ui): биллинг — «Запас» по живому заказу, убрать дубль и строку-чтение
Поправки после первой версии (по замечаниям владельца):
- «Запас» считаем по текущему заказу проектов (requiredLeadsPerDay), так же как
  BalanceCapacityIndicator: 0/день → ∞ (баланс не расходуется), N/день → лиды/N.
  Раньше брался бэкендный runway_days (историческая скорость за 30 дней) —
  давал «2192 дн.» рядом с «при 0 лидов в день», выглядело фейком/противоречиво.
- Убрана строка-чтение под рядом (дубль).
- Убран BalanceCapacityIndicator из BillingView: дублировал ряд и писал
  «по тарифу», хотя тарифов нет.
- Рамки трёх карточек выровнены по высоте (единый border/radius + flex-stretch,
  убран конфликтный height:100%).

Тесты BalanceCard/BillingView обновлены. vitest 19/19, vue-tsc и build — зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 13:20:14 +03:00

71 lines
3.0 KiB
TypeScript

import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import BalanceCard from '../../resources/js/components/billing/BalanceCard.vue';
import { createVuetify } from 'vuetify';
const vuetify = createVuetify();
const baseProps = {
walletRub: 5000,
affordableLeads: 46,
currentTierPriceRub: '120.00',
requiredLeadsPerDay: 4,
};
describe('BalanceCard (Billing v2 Spec A; ряд Деньги → Лиды → Запас)', () => {
it('shows wallet ₽ formatted with thousands separator', () => {
const w = mount(BalanceCard, { props: baseProps, global: { plugins: [vuetify] } });
// Intl.NumberFormat('ru-RU') uses U+00A0 (non-breaking space) — match any whitespace
expect(w.text()).toMatch(/5\s000/);
});
it('shows «≈ N лидов» using affordableLeads', () => {
const w = mount(BalanceCard, { props: baseProps, global: { plugins: [vuetify] } });
expect(w.text()).toContain('≈ 46');
expect(w.text()).toContain('лидов');
});
it('does NOT contain «(ГЦК)»', () => {
const w = mount(BalanceCard, { props: baseProps, global: { plugins: [vuetify] } });
expect(w.text()).not.toContain('ГЦК');
});
it('does NOT contain «округление вниз ₽→лиды»', () => {
const w = mount(BalanceCard, { props: baseProps, global: { plugins: [vuetify] } });
expect(w.text()).not.toContain('округление вниз');
});
it('renders «сейчас по X ₽/лид» subline', () => {
const w = mount(BalanceCard, { props: baseProps, global: { plugins: [vuetify] } });
expect(w.text()).toContain('120.00 ₽/лид');
});
it('does NOT contain the removed «Тариф» card', () => {
const w = mount(BalanceCard, { props: baseProps, global: { plugins: [vuetify] } });
expect(w.text()).not.toContain('Тариф');
});
it('Запас = affordableLeads / requiredLeadsPerDay (живой заказ): 46 / 4 → 11 дн.', () => {
const w = mount(BalanceCard, { props: baseProps, global: { plugins: [vuetify] } });
expect(w.text()).toContain('Запас');
expect(w.text()).toMatch(/11\s*дн\./);
expect(w.text()).toContain('при 4');
});
it('Запас = ∞ когда проекты не заказывают (requiredLeadsPerDay = 0)', () => {
const w = mount(BalanceCard, {
props: { ...baseProps, requiredLeadsPerDay: 0 },
global: { plugins: [vuetify] },
});
expect(w.text()).toContain('∞');
expect(w.text()).toContain('проекты не заказывают');
});
it('НЕ содержит строки-чтения (дубль убран)', () => {
const w = mount(BalanceCard, { props: baseProps, global: { plugins: [vuetify] } });
expect(w.text()).not.toContain('тыс ₽ =');
expect(w.text()).not.toContain('млн ₽ =');
});
});