Files
portal/app/tests/Frontend/AutopodborFlowSmoke.spec.ts
T
Дмитрий 4387333118 feat(Конкурентное поле): рабочее место конкуренты→источники→проекты (поверх автоподбора)
Фича «Конкурентное поле» на dev до уровня прототипа 2026-06-29-konkurentnoe-pole-proto.html.

Данные: box (proposal|field) на competitors+sources; phone_type city/mobile/tollfree рядом
с phone_kind (вариант C). 3 миграции, дефолты тарифов 300/50.

API (AutopodborController): GET /field (+счётчики), GET /proposals, PATCH/DELETE competitors
и sources с гвардами активного проекта, переключение box, POST /competitors/manual (+directory_urls),
competitor(id) обогащён box+project-статусом; projectStatus отдаёт limit/delivered/days/regions.
Смена источника проекта = PATCH /api/projects/{id} (реальный гвард слепка §14.10).

Фронт: FieldWorkspaceScreen/FieldCompetitorScreen/FieldProposalsScreen/FieldManualCompetitorScreen
+ field-shared.css (Forest) + AutopodborServicesPanel в Биллинге. Дословно по прототипу: подзаголовки,
баннер предложений, баннер правил времени 18:00 МСК, Справочник 2ГИС·Яндекс, статус проекта
5/день·заявки, окна сбора с ценами 300/50 + «что известно», полные формы. Пункт меню «Конкурентное поле».

Тесты: backend автоподбор 80/80, фронт автоподбор 49/49. Движок шага 2 = заглушка FakeCompetitorAgent.
OmegaDemoFieldSeeder — только для визуальной проверки (НЕ на прод).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 04:18:46 +03:00

69 lines
3.5 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import { createVuetify } from 'vuetify';
import { nextTick } from 'vue';
// Mock vue-router (DoneScreen uses useRouter)
vi.mock('vue-router', () => ({
useRouter: () => ({ push: vi.fn() }),
useRoute: () => ({ params: {}, query: {} }),
}));
vi.mock('axios');
import axios from 'axios';
vi.mock('../../resources/js/api/autopodbor');
import * as api from '../../resources/js/api/autopodbor';
import AutopodborView from '../../resources/js/views/autopodbor/AutopodborView.vue';
const vuetify = createVuetify();
describe('Autopodbor сквозной smoke — все 9 экранов монтируются', () => {
beforeEach(() => {
setActivePinia(createPinia());
vi.clearAllMocks();
(api.fetchState as any).mockResolvedValue({ enabled: true, prices: { search: '500', study: '300' }, runs: [] });
(api.fetchField as any).mockResolvedValue([]);
(api.fetchRunCompetitors as any).mockResolvedValue([]);
(api.fetchCompetitor as any).mockResolvedValue({ competitor: { id: 3, name: 'Окна Комфорт', is_federal: false, relevance_pct: 100, origin: 'auto', site_url: 'okna.ru', directory_urls: [], description: 'd', studied_at: '2026-06-28', study_run_id: 9, search_run_id: 5 }, sources: [] });
(axios.get as any).mockResolvedValue({ data: { data: { id: 99, name: 'Окна Комфорт 🎭', regions: [16], daily_limit_target: 20, delivery_days_mask: 127 } } });
});
const screens = ['field', 'entry', 'autoform', 'manualform', 'loading', 'list', 'detail', 'editproject', 'create', 'done'] as const;
it('все экраны переключаются и монтируются без ошибок', async () => {
const w = mount(AutopodborView, { global: { plugins: [vuetify] } });
await new Promise(r => setTimeout(r, 0));
// предзаполним ctx, чтобы экраны, читающие ctx, не падали
const vm = w.vm as any;
vm.ctx.runId = 5; vm.ctx.competitorId = 3; vm.ctx.editProjectId = 99;
vm.ctx.selectedSourceIds = []; vm.ctx.createdCount = 2; vm.ctx.launched = true;
vm.ctx.loadMsg = 'Идёт работа…'; vm.ctx.loadSub = 'Подождите.';
for (const name of screens) {
vm.go(name);
await nextTick();
await new Promise(r => setTimeout(r, 0));
await nextTick();
// экран отрендерил хоть какой-то контент и не выбросил
expect(vm.screen).toBe(name);
expect(w.html().length).toBeGreaterThan(50);
}
expect(true).toBe(true);
});
it('entry показывает обе двери, done показывает итог', async () => {
const w = mount(AutopodborView, { global: { plugins: [vuetify] } });
await new Promise(r => setTimeout(r, 0));
const vm = w.vm as any;
vm.go('entry');
await nextTick(); await new Promise(r => setTimeout(r, 0));
expect(w.text()).toContain('Подобрать конкурентов');
expect(w.text()).toContain('Указать своего конкурента');
vm.ctx.createdCount = 3; vm.ctx.launched = true;
vm.go('done');
await nextTick(); await new Promise(r => setTimeout(r, 0));
expect(w.text()).toContain('проект'); // «3 проекта создано…»
});
});