Files
portal/app/tests/Frontend/FieldCompetitorScreenProposalsBulk.spec.ts
T
Дмитрий 5c8b344bcf fix(autopodbor): разворот глубина 1 по умолчанию + массовое удаление предложений
Разворот по телефонам 2ГИС и Яндекс больше не уходит цепочкой «до талого»:
по умолчанию берём только фирмы с ЛИЧНЫМ номером фирмы — глубина 1. Именно
неограниченная цепочка уводила в чужие фирмы, давая мусорные сайты в
предложениях холдинга. Многошаговый разворот сохранён за параметром maxDepth,
по умолчанию 1. Изолировано: работает только под флагом phone_reverse, основной
пул сбора не затрагивается. Бэкенд автоподбора 430 из 430.

Фронт: на вкладке «Предложения» добавлена кнопка массового удаления выбранных
источников — bulkDeleteProps через removeSource, покрыта тестами.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 15:33:31 +03:00

104 lines
4.5 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { setActivePinia, createPinia } from 'pinia';
vi.mock('../../resources/js/api/autopodbor', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../resources/js/api/autopodbor')>();
return { ...actual, fetchCompetitor: vi.fn() };
});
import * as api from '../../resources/js/api/autopodbor';
import { useAutopodborStore } from '../../resources/js/stores/autopodborStore';
import FieldCompetitorScreen from '../../resources/js/views/autopodbor/screens/FieldCompetitorScreen.vue';
const makeNav = (competitorId = 10) => ({
go: vi.fn(),
ctx: { competitorId, editProjectId: null, selectedSourceIds: [] },
screen: { value: 'field-competitor' },
});
// Источник-предложение (box 'proposal', без проекта).
const makeProposal = (id: number) => ({
id,
competitor_id: 10,
signal_type: 'site' as const,
identifier: `prop-${id}.ru`,
phone_kind: null,
phone_type: null,
box: 'proposal' as const,
provenance_url: null,
provenance_label: null,
created_project_id: null,
project: null,
});
async function mountWithProposals() {
(api.fetchCompetitor as ReturnType<typeof vi.fn>).mockResolvedValue({
competitor: { id: 10, name: 'Окна', is_federal: false, relevance_pct: 90, origin: 'auto', box: 'field', site_url: 'okna.ru', directory_urls: [], studied_at: '2026-01-01', study_run_id: 1, search_run_id: 1, description: '' },
sources: [makeProposal(1), makeProposal(2)],
});
const store = useAutopodborStore();
await store.loadCompetitor(10);
const wrapper = mount(FieldCompetitorScreen, {
global: { provide: { autopodborNav: makeNav() }, stubs: { Transition: false } },
});
await new Promise((r) => setTimeout(r, 50));
return { wrapper, store };
}
beforeEach(() => {
setActivePinia(createPinia());
vi.clearAllMocks();
});
describe('FieldCompetitorScreen — массовые действия на вкладке «Предложения» (Баг 1)', () => {
it('при 2+ выбранных предложениях показывается панель массового переноса', async () => {
const { wrapper } = await mountWithProposals();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
vm.switchTab('sugg');
vm.selected = [1, 2];
await wrapper.vm.$nextTick();
expect(wrapper.find('.ld-bulkbar').exists()).toBe(true);
expect(wrapper.text()).toContain('Перенести выбранные в источники');
});
it('bulkMoveToWork переносит каждый выбранный источник в box field', async () => {
const { wrapper, store } = await mountWithProposals();
const spy = vi.spyOn(store, 'moveSourceToBox').mockResolvedValue();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
vm.switchTab('sugg');
vm.selected = [1, 2];
await wrapper.vm.$nextTick();
await vm.bulkMoveToWork();
expect(spy).toHaveBeenCalledWith(10, 1, 'field');
expect(spy).toHaveBeenCalledWith(10, 2, 'field');
expect(spy).toHaveBeenCalledTimes(2);
});
it('в панели массовых действий предложений есть кнопка «Удалить»', async () => {
const { wrapper } = await mountWithProposals();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
vm.switchTab('sugg');
vm.selected = [1, 2];
await wrapper.vm.$nextTick();
const delBtn = wrapper.findAll('.ld-bulkbar button').find((b) => b.text().includes('Удалить'));
expect(delBtn).toBeTruthy();
});
it('bulkDeleteProps удаляет каждый выбранный источник через removeSource', async () => {
const { wrapper, store } = await mountWithProposals();
const spy = vi.spyOn(store, 'removeSource').mockResolvedValue();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
vm.switchTab('sugg');
vm.selected = [1, 2];
await wrapper.vm.$nextTick();
await vm.bulkDeleteProps();
expect(spy).toHaveBeenCalledWith(10, 1);
expect(spy).toHaveBeenCalledWith(10, 2);
expect(spy).toHaveBeenCalledTimes(2);
});
});