233 lines
8.3 KiB
TypeScript
233 lines
8.3 KiB
TypeScript
|
|
/**
|
|||
|
|
* Task 13: при ответе создания с launch.deferred=1
|
|||
|
|
* показывается диалог перегрузки (проект считается созданным).
|
|||
|
|
*
|
|||
|
|
* Контракт POST /api/projects → 201:
|
|||
|
|
* { data: {...}, launch: { launched: 0|1, deferred: 0|1, balance: OverloadPayload | null } }
|
|||
|
|
*
|
|||
|
|
* Если launch.deferred === 1:
|
|||
|
|
* - форма закрывается (emit saved)
|
|||
|
|
* - открывается ProjectLimitOverloadDialog с payload = launch.balance
|
|||
|
|
*
|
|||
|
|
* Если launch.deferred === 0: обычный успех, диалога нет.
|
|||
|
|
*/
|
|||
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|||
|
|
import { mount, flushPromises } from '@vue/test-utils';
|
|||
|
|
import { createPinia, setActivePinia } from 'pinia';
|
|||
|
|
import { createVuetify } from 'vuetify';
|
|||
|
|
|
|||
|
|
vi.mock('../../resources/js/api/client', () => ({
|
|||
|
|
apiClient: {
|
|||
|
|
post: vi.fn(),
|
|||
|
|
patch: vi.fn().mockResolvedValue({ data: {} }),
|
|||
|
|
},
|
|||
|
|
ensureCsrfCookie: vi.fn().mockResolvedValue(undefined),
|
|||
|
|
extractErrorMessage: vi.fn(() => 'Произошла ошибка.'),
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
vi.mock('../../resources/js/api/requisites', () => ({
|
|||
|
|
getRequisites: vi.fn().mockResolvedValue({
|
|||
|
|
subject_type: 'individual',
|
|||
|
|
contact_name: 'Иван',
|
|||
|
|
contact_phone: '+79161234567',
|
|||
|
|
inn: null,
|
|||
|
|
}),
|
|||
|
|
updateRequisites: vi.fn().mockResolvedValue({}),
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
// vue-router: stub для ProjectLimitOverloadDialog.goTopup
|
|||
|
|
vi.mock('vue-router', () => ({
|
|||
|
|
useRouter: vi.fn(() => ({ push: vi.fn() })),
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
import { apiClient } from '../../resources/js/api/client';
|
|||
|
|
import NewProjectDialog from '../../resources/js/views/projects/NewProjectDialog.vue';
|
|||
|
|
import type { Project } from '../../resources/js/stores/projectsStore';
|
|||
|
|
|
|||
|
|
const factory = (
|
|||
|
|
props: { modelValue: boolean; mode?: 'create' | 'edit'; project?: Project | null } = {
|
|||
|
|
modelValue: true,
|
|||
|
|
mode: 'create',
|
|||
|
|
},
|
|||
|
|
) =>
|
|||
|
|
mount(NewProjectDialog, {
|
|||
|
|
props,
|
|||
|
|
global: {
|
|||
|
|
plugins: [createVuetify()],
|
|||
|
|
stubs: {
|
|||
|
|
VDialog: {
|
|||
|
|
template: '<div class="dialog-stub" v-if="modelValue"><slot /></div>',
|
|||
|
|
props: ['modelValue'],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
beforeEach(() => {
|
|||
|
|
setActivePinia(createPinia());
|
|||
|
|
vi.clearAllMocks();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('NewProjectDialog — launch.deferred (Task 13)', () => {
|
|||
|
|
it('при launch.deferred=1 открывается диалог перегрузки и эмитится saved', async () => {
|
|||
|
|
// Бэкенд вернул 201 с deferred=1
|
|||
|
|
(apiClient.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|||
|
|
data: {
|
|||
|
|
data: { id: 42, name: 'Тест' },
|
|||
|
|
launch: {
|
|||
|
|
launched: 0,
|
|||
|
|
deferred: 1,
|
|||
|
|
balance: {
|
|||
|
|
current_balance_rub: '500.00',
|
|||
|
|
current_capacity_leads: 15,
|
|||
|
|
would_be_required_leads: 50,
|
|||
|
|
deficit_leads: 35,
|
|||
|
|
topup_rub: '1050.00',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const wrapper = factory();
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
const vm = wrapper.vm as unknown as {
|
|||
|
|
confirmVsyaRf: () => void;
|
|||
|
|
form: { name: string; signal_identifier: string };
|
|||
|
|
submit: () => Promise<void>;
|
|||
|
|
};
|
|||
|
|
vm.confirmVsyaRf();
|
|||
|
|
vm.form.name = 'Тест';
|
|||
|
|
vm.form.signal_identifier = 'test.ru';
|
|||
|
|
await vm.submit();
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
// Диалог перегрузки должен быть открыт
|
|||
|
|
const overload = wrapper.find('[data-testid="overload-dialog"]');
|
|||
|
|
expect(overload.exists()).toBe(true);
|
|||
|
|
|
|||
|
|
// Payload пробрасывается — показывается сумма пополнения
|
|||
|
|
expect(overload.text()).toContain('1050.00');
|
|||
|
|
|
|||
|
|
// saved должен был эмититься (форма считается созданной)
|
|||
|
|
expect(wrapper.emitted('saved')).toBeTruthy();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('при launch.deferred=0 диалога перегрузки нет, эмитится saved', async () => {
|
|||
|
|
(apiClient.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|||
|
|
data: {
|
|||
|
|
data: { id: 43, name: 'Тест2' },
|
|||
|
|
launch: {
|
|||
|
|
launched: 1,
|
|||
|
|
deferred: 0,
|
|||
|
|
balance: null,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const wrapper = factory();
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
const vm = wrapper.vm as unknown as {
|
|||
|
|
confirmVsyaRf: () => void;
|
|||
|
|
form: { name: string; signal_identifier: string };
|
|||
|
|
submit: () => Promise<void>;
|
|||
|
|
};
|
|||
|
|
vm.confirmVsyaRf();
|
|||
|
|
vm.form.name = 'Тест2';
|
|||
|
|
vm.form.signal_identifier = 'test2.ru';
|
|||
|
|
await vm.submit();
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
// Диалога перегрузки быть не должно
|
|||
|
|
const overload = wrapper.find('[data-testid="overload-dialog"]');
|
|||
|
|
expect(overload.exists()).toBe(false);
|
|||
|
|
|
|||
|
|
// saved эмититься должен
|
|||
|
|
expect(wrapper.emitted('saved')).toBeTruthy();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('при launch.deferred=1 баннер не показывает ошибку в форме', async () => {
|
|||
|
|
(apiClient.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|||
|
|
data: {
|
|||
|
|
data: { id: 44 },
|
|||
|
|
launch: {
|
|||
|
|
launched: 0,
|
|||
|
|
deferred: 1,
|
|||
|
|
balance: {
|
|||
|
|
current_balance_rub: '0.00',
|
|||
|
|
current_capacity_leads: 0,
|
|||
|
|
would_be_required_leads: 50,
|
|||
|
|
deficit_leads: 50,
|
|||
|
|
topup_rub: '1500.00',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const wrapper = factory();
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
const vm = wrapper.vm as unknown as {
|
|||
|
|
confirmVsyaRf: () => void;
|
|||
|
|
form: { name: string; signal_identifier: string };
|
|||
|
|
submit: () => Promise<void>;
|
|||
|
|
};
|
|||
|
|
vm.confirmVsyaRf();
|
|||
|
|
vm.form.name = 'Тест3';
|
|||
|
|
vm.form.signal_identifier = 'test3.ru';
|
|||
|
|
await vm.submit();
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
// В форме нет общей ошибки
|
|||
|
|
const errorAlert = wrapper.find('.dialog-stub .v-alert--type-error');
|
|||
|
|
expect(errorAlert.exists()).toBe(false);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('update (edit) 409 balance_insufficient читает payload из err.response.data.balance', async () => {
|
|||
|
|
// В edit-режиме PATCH всё ещё может вернуть 409 — payload вложен в .balance
|
|||
|
|
(apiClient.patch as ReturnType<typeof vi.fn>).mockRejectedValueOnce({
|
|||
|
|
response: {
|
|||
|
|
status: 409,
|
|||
|
|
data: {
|
|||
|
|
error: 'balance_insufficient',
|
|||
|
|
balance: {
|
|||
|
|
current_balance_rub: '200.00',
|
|||
|
|
current_capacity_leads: 6,
|
|||
|
|
would_be_required_leads: 80,
|
|||
|
|
deficit_leads: 74,
|
|||
|
|
topup_rub: '2220.00',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const project = {
|
|||
|
|
id: 5,
|
|||
|
|
name: 'Проект',
|
|||
|
|
signal_type: 'site' as const,
|
|||
|
|
signal_identifier: 'edit.ru',
|
|||
|
|
daily_limit_target: 10,
|
|||
|
|
delivered_today: 0,
|
|||
|
|
is_active: true,
|
|||
|
|
regions: [77],
|
|||
|
|
delivery_days_mask: 127,
|
|||
|
|
sync_status: 'ok' as const,
|
|||
|
|
region_mask: 0,
|
|||
|
|
region_mode: 'include',
|
|||
|
|
} as unknown as Project;
|
|||
|
|
|
|||
|
|
const wrapper = factory({ modelValue: true, mode: 'edit', project });
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
const vm = wrapper.vm as unknown as { submit: () => Promise<void> };
|
|||
|
|
await vm.submit();
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
const overload = wrapper.find('[data-testid="overload-dialog"]');
|
|||
|
|
expect(overload.exists()).toBe(true);
|
|||
|
|
// Проверяем что payload из .balance дошёл (сумма 2220)
|
|||
|
|
expect(overload.text()).toContain('2220');
|
|||
|
|
});
|
|||
|
|
});
|