178 lines
6.8 KiB
TypeScript
178 lines
6.8 KiB
TypeScript
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|||
|
|
import { mount } from '@vue/test-utils';
|
|||
|
|
import { setActivePinia, createPinia } from 'pinia';
|
|||
|
|
import axios from 'axios';
|
|||
|
|
|
|||
|
|
vi.mock('axios');
|
|||
|
|
vi.mock('../../resources/js/api/autopodbor');
|
|||
|
|
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' },
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Фабрика источника с проектом
|
|||
|
|
const makeSource = (id: number, projectId: number, isActive: boolean) => ({
|
|||
|
|
id,
|
|||
|
|
competitor_id: 10,
|
|||
|
|
signal_type: 'site' as const,
|
|||
|
|
identifier: `site-${id}.ru`,
|
|||
|
|
phone_kind: null,
|
|||
|
|
phone_type: null,
|
|||
|
|
box: 'field' as const,
|
|||
|
|
provenance_url: null,
|
|||
|
|
provenance_label: null,
|
|||
|
|
created_project_id: projectId,
|
|||
|
|
project: {
|
|||
|
|
id: projectId,
|
|||
|
|
name: `Проект ${id}`,
|
|||
|
|
signal_identifier: `site-${id}.ru`,
|
|||
|
|
is_active: isActive,
|
|||
|
|
paused_at: null,
|
|||
|
|
preflight_blocked_at: null,
|
|||
|
|
daily_limit_target: 5,
|
|||
|
|
delivered_in_month: 10,
|
|||
|
|
delivery_days_mask: 127,
|
|||
|
|
regions: [16],
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
beforeEach(() => {
|
|||
|
|
setActivePinia(createPinia());
|
|||
|
|
vi.clearAllMocks();
|
|||
|
|
vi.mocked(axios.post).mockResolvedValue({ data: { updated: 2, skipped: [], warnings: [] } });
|
|||
|
|
vi.mocked(axios.get).mockResolvedValue({ data: { data: [], meta: { total: 0 } } });
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('FieldCompetitorScreen — bulk actions via /api/projects/bulk (Task 17b)', () => {
|
|||
|
|
it('bulkWork("pause") шлёт POST /api/projects/bulk с project_id выбранных источников', async () => {
|
|||
|
|
const nav = makeNav();
|
|||
|
|
|
|||
|
|
// Настраиваем store с двумя источниками с проектами
|
|||
|
|
(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: [makeSource(1, 101, true), makeSource(2, 102, true)],
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const store = useAutopodborStore();
|
|||
|
|
await store.loadCompetitor(10);
|
|||
|
|
|
|||
|
|
const wrapper = mount(FieldCompetitorScreen, {
|
|||
|
|
global: {
|
|||
|
|
provide: { autopodborNav: nav },
|
|||
|
|
stubs: { Transition: false },
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Ждём onMounted reload
|
|||
|
|
await new Promise((r) => setTimeout(r, 50));
|
|||
|
|
|
|||
|
|
// Выбираем оба источника
|
|||
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|||
|
|
const vm = wrapper.vm as any;
|
|||
|
|
vm.selected = [1, 2];
|
|||
|
|
await wrapper.vm.$nextTick();
|
|||
|
|
|
|||
|
|
// Вызываем bulkWork('pause') напрямую через exposed
|
|||
|
|
await vm.bulkWork('pause');
|
|||
|
|
await new Promise((r) => setTimeout(r, 30));
|
|||
|
|
|
|||
|
|
// Должен быть вызван POST /api/projects/bulk с ids проектов [101, 102]
|
|||
|
|
expect(vi.mocked(axios.post)).toHaveBeenCalledWith(
|
|||
|
|
'/api/projects/bulk',
|
|||
|
|
expect.objectContaining({
|
|||
|
|
action: 'pause',
|
|||
|
|
ids: expect.arrayContaining([101, 102]),
|
|||
|
|
}),
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('bulkWork("resume") шлёт POST /api/projects/bulk с project_id', async () => {
|
|||
|
|
const nav = makeNav();
|
|||
|
|
|
|||
|
|
(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: [makeSource(1, 101, false), makeSource(2, 102, false)],
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const store = useAutopodborStore();
|
|||
|
|
await store.loadCompetitor(10);
|
|||
|
|
|
|||
|
|
const wrapper = mount(FieldCompetitorScreen, {
|
|||
|
|
global: {
|
|||
|
|
provide: { autopodborNav: nav },
|
|||
|
|
stubs: { Transition: false },
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
await new Promise((r) => setTimeout(r, 50));
|
|||
|
|
|
|||
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|||
|
|
const vm = wrapper.vm as any;
|
|||
|
|
vm.selected = [1, 2];
|
|||
|
|
await wrapper.vm.$nextTick();
|
|||
|
|
|
|||
|
|
await vm.bulkWork('resume');
|
|||
|
|
await new Promise((r) => setTimeout(r, 30));
|
|||
|
|
|
|||
|
|
expect(vi.mocked(axios.post)).toHaveBeenCalledWith(
|
|||
|
|
'/api/projects/bulk',
|
|||
|
|
expect.objectContaining({
|
|||
|
|
action: 'resume',
|
|||
|
|
ids: expect.arrayContaining([101, 102]),
|
|||
|
|
}),
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('bulkWork пропускает источники без проекта', async () => {
|
|||
|
|
const nav = makeNav();
|
|||
|
|
|
|||
|
|
(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: [
|
|||
|
|
makeSource(1, 101, true),
|
|||
|
|
// Источник без проекта
|
|||
|
|
{
|
|||
|
|
id: 2, competitor_id: 10, signal_type: 'site' as const, identifier: 'b.ru',
|
|||
|
|
phone_kind: null, phone_type: null, box: 'field' as const,
|
|||
|
|
provenance_url: null, provenance_label: null, created_project_id: null,
|
|||
|
|
project: null,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const store = useAutopodborStore();
|
|||
|
|
await store.loadCompetitor(10);
|
|||
|
|
|
|||
|
|
const wrapper = mount(FieldCompetitorScreen, {
|
|||
|
|
global: {
|
|||
|
|
provide: { autopodborNav: nav },
|
|||
|
|
stubs: { Transition: false },
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
await new Promise((r) => setTimeout(r, 50));
|
|||
|
|
|
|||
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|||
|
|
const vm = wrapper.vm as any;
|
|||
|
|
vm.selected = [1, 2];
|
|||
|
|
await wrapper.vm.$nextTick();
|
|||
|
|
|
|||
|
|
await vm.bulkWork('pause');
|
|||
|
|
await new Promise((r) => setTimeout(r, 30));
|
|||
|
|
|
|||
|
|
// Должен отправить bulk только с id проекта источника 1 (101), источник 2 без проекта пропущен
|
|||
|
|
expect(vi.mocked(axios.post)).toHaveBeenCalledWith(
|
|||
|
|
'/api/projects/bulk',
|
|||
|
|
expect.objectContaining({
|
|||
|
|
action: 'pause',
|
|||
|
|
ids: [101],
|
|||
|
|
}),
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
});
|