Files
portal/app/tests/Frontend/TopupDialog.spec.ts
T
Дмитрий c9672e81e6 fix(billing): TopupDialog NaN-guard + state reset on open (Task 5 review)
Code-quality review fixups: Number.isFinite-guard в amountError/canSubmit
(очищенное number-поле не должно включать кнопку); watch(model) сбрасывает
amount/errorMsg при открытии (паттерн ReminderDialog, нет префилла/race);
комментарий про намеренный refetch в onTopupSuccess; flushPromises в spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 08:29:05 +03:00

70 lines
2.8 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import TopupDialog from '../../resources/js/components/billing/TopupDialog.vue';
import * as billingApi from '../../resources/js/api/billing';
vi.mock('../../resources/js/api/billing');
const vuetify = createVuetify();
const factory = () =>
mount(TopupDialog, {
global: { plugins: [vuetify] },
props: { modelValue: true },
});
describe('TopupDialog.vue', () => {
beforeEach(() => {
vi.mocked(billingApi.topup).mockResolvedValue({
transaction: {
id: 1,
type: 'topup',
amount_rub: '5000.00',
balance_rub_after: '5000.00',
created_at: '2026-05-16T00:00:00Z',
},
balance_rub: '5000.00',
});
});
it('блокирует submit при сумме ниже 100 ₽', async () => {
const wrapper = factory();
(wrapper.vm as unknown as { amount: number | null }).amount = 50;
await flushPromises();
expect((wrapper.vm as unknown as { canSubmit: boolean }).canSubmit).toBe(false);
});
it('разрешает submit при валидной сумме', async () => {
const wrapper = factory();
(wrapper.vm as unknown as { amount: number | null }).amount = 5000;
await flushPromises();
expect((wrapper.vm as unknown as { canSubmit: boolean }).canSubmit).toBe(true);
});
it('submit вызывает topup и эмитит success с новым балансом', async () => {
const wrapper = factory();
(wrapper.vm as unknown as { amount: number | null }).amount = 5000;
await (wrapper.vm as unknown as { submit: () => Promise<void> }).submit();
await flushPromises();
expect(billingApi.topup).toHaveBeenCalledWith(5000);
expect(wrapper.emitted('success')?.[0]).toEqual(['5000.00']);
});
it('блокирует submit при нечисловом значении (очищенное поле)', async () => {
const wrapper = factory();
(wrapper.vm as unknown as { amount: number }).amount = NaN;
await flushPromises();
expect((wrapper.vm as unknown as { canSubmit: boolean }).canSubmit).toBe(false);
});
it('показывает ошибку при отказе backend', async () => {
vi.mocked(billingApi.topup).mockRejectedValue(new Error('fail'));
const wrapper = factory();
(wrapper.vm as unknown as { amount: number | null }).amount = 5000;
await (wrapper.vm as unknown as { submit: () => Promise<void> }).submit();
await flushPromises();
expect((wrapper.vm as unknown as { errorMsg: string | null }).errorMsg).not.toBeNull();
});
});