4387333118
Фича «Конкурентное поле» на 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>
69 lines
3.5 KiB
TypeScript
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 проекта создано…»
|
|
});
|
|
});
|