2026-05-14 14:41:28 +03:00
import { describe , it , expect , beforeEach , vi } from 'vitest' ;
2026-05-14 17:51:56 +03:00
import { mount , config } from '@vue/test-utils' ;
2026-05-14 14:13:52 +03:00
import { createPinia , setActivePinia } from 'pinia' ;
2026-05-14 17:51:56 +03:00
import { createVuetify } from 'vuetify' ;
2026-05-14 14:41:28 +03:00
import axios from 'axios' ;
2026-05-14 14:13:52 +03:00
import ProjectDetailsDrawer from '../../resources/js/components/projects/ProjectDetailsDrawer.vue' ;
2026-05-14 14:21:07 +03:00
import type { Project } from '../../resources/js/stores/projectsStore' ;
2026-05-14 14:48:24 +03:00
import { useProjectsStore } from '../../resources/js/stores/projectsStore' ;
2026-05-14 14:21:07 +03:00
2026-05-14 14:41:28 +03:00
vi . mock ( 'axios' ) ;
2026-05-14 17:51:56 +03:00
// VAutocomplete (added in regions field task) requires Vuetify defaults provide context.
// Register Vuetify plugin globally for all mount() calls in this file.
config . global . plugins = [ createVuetify ( ) ] ;
2026-05-14 14:21:07 +03:00
const sampleProject : Project = {
id : 42 ,
name : 'Натяжные потолки' ,
signal_type : 'call' ,
signal_identifier : '79161112233' ,
daily_limit_target : 30 ,
delivered_today : 0 ,
is_active : true ,
region_mask : 0 ,
region_mode : 'include' ,
2026-05-15 05:54:05 +03:00
regions : [ ] ,
2026-05-14 14:21:07 +03:00
delivery_days_mask : 31 , // Mon-Fri
sync_status : 'pending' ,
} ;
2026-05-14 14:13:52 +03:00
describe ( 'ProjectDetailsDrawer' , ( ) = > {
beforeEach ( ( ) = > setActivePinia ( createPinia ( ) ) ) ;
it ( 'does not have .open class when project=null' , ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : null } } ) ;
const aside = wrapper . find ( 'aside.project-details-drawer' ) ;
expect ( aside . exists ( ) ) . toBe ( true ) ;
expect ( aside . classes ( ) ) . not . toContain ( 'open' ) ;
} ) ;
2026-05-14 14:21:07 +03:00
it ( 'renders project name + daily_limit_target + delivery_days' , ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
const aside = wrapper . find ( 'aside.project-details-drawer' ) ;
expect ( aside . classes ( ) ) . toContain ( 'open' ) ;
expect ( wrapper . text ( ) ) . toContain ( 'Натяжные потолки' ) ;
const nameInput = wrapper . get ( 'input[data-testid="pdd-name"]' ) ;
expect ( ( nameInput . element as HTMLInputElement ) . value ) . toBe ( 'Натяжные потолки' ) ;
const limitInput = wrapper . get ( 'input[data-testid="pdd-limit"]' ) ;
expect ( ( limitInput . element as HTMLInputElement ) . value ) . toBe ( '30' ) ;
// Days mask 31 = bits 0..4 = Mon..Fri (5 days active)
const dayBtns = wrapper . findAll ( 'button[data-testid^="pdd-day-"]' ) ;
expect ( dayBtns . length ) . toBe ( 7 ) ;
2026-05-15 05:54:05 +03:00
const activeBtns = dayBtns . filter ( ( b ) = > b . classes ( ) . includes ( 'active' ) ) ;
2026-05-14 14:21:07 +03:00
expect ( activeBtns . length ) . toBe ( 5 ) ;
} ) ;
2026-05-14 14:28:49 +03:00
it ( 'emits close on X button click' , async ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
await wrapper . get ( '[data-testid="pdd-close"]' ) . trigger ( 'click' ) ;
expect ( wrapper . emitted ( 'close' ) ) . toHaveLength ( 1 ) ;
} ) ;
it ( 'emits close on Cancel button click' , async ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
await wrapper . get ( '[data-testid="pdd-cancel"]' ) . trigger ( 'click' ) ;
expect ( wrapper . emitted ( 'close' ) ) . toHaveLength ( 1 ) ;
} ) ;
it ( 'emits close on ESC keydown when project is set' , async ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } , attachTo : document.body } ) ;
document . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Escape' } ) ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( wrapper . emitted ( 'close' ) ) . toHaveLength ( 1 ) ;
wrapper . unmount ( ) ;
} ) ;
it ( 'does NOT emit close on ESC when project=null' , async ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : null } , attachTo : document.body } ) ;
document . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Escape' } ) ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( wrapper . emitted ( 'close' ) ) . toBeUndefined ( ) ;
wrapper . unmount ( ) ;
} ) ;
2026-05-14 14:35:54 +03:00
it ( 'reseeds form when project.id changes' , async ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
const nameInputBefore = wrapper . get ( 'input[data-testid="pdd-name"]' ) . element as HTMLInputElement ;
expect ( nameInputBefore . value ) . toBe ( 'Натяжные потолки' ) ;
const other : Project = { . . . sampleProject , id : 99 , name : 'Окна СПб' , daily_limit_target : 50 } ;
await wrapper . setProps ( { project : other } ) ;
const nameInputAfter = wrapper . get ( 'input[data-testid="pdd-name"]' ) . element as HTMLInputElement ;
expect ( nameInputAfter . value ) . toBe ( 'Окна СПб' ) ;
expect ( ( wrapper . get ( 'input[data-testid="pdd-limit"]' ) . element as HTMLInputElement ) . value ) . toBe ( '50' ) ;
} ) ;
2026-05-14 14:41:28 +03:00
it ( 'Save: PATCH /api/projects/{id} + emits saved on 200' , async ( ) = > {
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockResolvedValueOnce ( { data : { data : sampleProject } } ) ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
await wrapper . get ( '[data-testid="pdd-name"]' ) . setValue ( 'Новое имя' ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( axios . patch ) . toHaveBeenCalledWith (
'/api/projects/42' ,
expect . objectContaining ( { name : 'Новое имя' , daily_limit_target : 30 } ) ,
) ;
expect ( wrapper . emitted ( 'saved' ) ) . toHaveLength ( 1 ) ;
} ) ;
it ( 'Save: 422 → errors reactive проставляется, no saved emit' , async ( ) = > {
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockRejectedValueOnce ( {
response : { status : 422 , data : { errors : { name : [ 'Имя обязательно' ] } } } ,
} ) ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( wrapper . emitted ( 'saved' ) ) . toBeUndefined ( ) ;
expect ( wrapper . text ( ) ) . toContain ( 'Имя обязательно' ) ;
} ) ;
2026-05-14 14:48:24 +03:00
2026-06-23 11:44:06 +03:00
it ( 'Save: 409 balance_insufficient → показывает блок, no saved emit' , async ( ) = > {
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockRejectedValueOnce ( {
response : {
status : 409 ,
data : {
error : 'balance_insufficient' ,
current_balance_rub : '5000.00' ,
current_capacity_leads : 10 ,
would_be_required_leads : 50 ,
deficit_leads : 40 ,
} ,
} ,
} ) ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( wrapper . emitted ( 'saved' ) ) . toBeUndefined ( ) ;
expect ( wrapper . text ( ) . toLowerCase ( ) ) . toContain ( 'баланс' ) ;
} ) ;
2026-05-14 14:48:24 +03:00
it ( 'Pause button calls store.toggleActive' , async ( ) = > {
2026-05-15 05:54:05 +03:00
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockResolvedValueOnce ( {
data : { data : { . . . sampleProject , is_active : false } } ,
} ) ;
( axios . get as unknown as ReturnType < typeof vi.fn > | undefined ) ? . mockResolvedValue ? . ( {
data : { data : [ ] , meta : { total : 0 } } ,
} ) ;
2026-05-14 14:48:24 +03:00
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
const store = useProjectsStore ( ) ;
const spy = vi . spyOn ( store , 'toggleActive' ) . mockResolvedValueOnce ( undefined ) ;
await wrapper . get ( '[data-testid="pdd-pause"]' ) . trigger ( 'click' ) ;
expect ( spy ) . toHaveBeenCalledWith ( sampleProject ) ;
} ) ;
it ( 'Pause button label is "Возобновить" when is_active=false' , ( ) = > {
const paused : Project = { . . . sampleProject , is_active : false } ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : paused } } ) ;
expect ( wrapper . get ( '[data-testid="pdd-pause"]' ) . text ( ) ) . toContain ( 'Возобновить' ) ;
} ) ;
it ( 'Pause button label is "Приостановить" when is_active=true' , ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
expect ( wrapper . get ( '[data-testid="pdd-pause"]' ) . text ( ) ) . toContain ( 'Приостановить' ) ;
} ) ;
2026-05-14 14:54:55 +03:00
2026-05-21 08:37:26 +03:00
it ( 'Delete: confirm=true → del + close emit' , async ( ) = > {
2026-05-14 14:54:55 +03:00
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
const store = useProjectsStore ( ) ;
2026-05-21 08:37:26 +03:00
const spy = vi . spyOn ( store , 'del' ) . mockResolvedValueOnce ( undefined ) ;
2026-05-14 14:54:55 +03:00
vi . stubGlobal ( 'confirm' , ( ) = > true ) ;
await wrapper . get ( '[data-testid="pdd-delete"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( spy ) . toHaveBeenCalledWith ( 42 ) ;
expect ( wrapper . emitted ( 'close' ) ) . toBeDefined ( ) ;
vi . unstubAllGlobals ( ) ;
} ) ;
2026-05-21 08:37:26 +03:00
it ( 'Delete: confirm=false → no del, no close' , async ( ) = > {
2026-05-14 14:54:55 +03:00
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
const store = useProjectsStore ( ) ;
2026-05-21 08:37:26 +03:00
const spy = vi . spyOn ( store , 'del' ) . mockResolvedValueOnce ( undefined ) ;
2026-05-14 14:54:55 +03:00
vi . stubGlobal ( 'confirm' , ( ) = > false ) ;
await wrapper . get ( '[data-testid="pdd-delete"]' ) . trigger ( 'click' ) ;
expect ( spy ) . not . toHaveBeenCalled ( ) ;
expect ( wrapper . emitted ( 'close' ) ) . toBeUndefined ( ) ;
vi . unstubAllGlobals ( ) ;
} ) ;
2026-05-14 17:51:56 +03:00
2026-05-26 12:33:18 +03:00
it ( 'Delete: 422 errors.project → drawer не закрывается, текст показан' , async ( ) = > {
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
const store = useProjectsStore ( ) ;
2026-06-17 05:17:12 +03:00
const message =
'Мы уже начали сбор лидов по этому проекту на завтра. Пока поставьте на паузу — мы увидим это сегодня в 18:00 и завтра не будем запускать сбор лидов по этому проекту. Удалить можно будет послезавтра.' ;
2026-05-26 12:33:18 +03:00
vi . spyOn ( store , 'del' ) . mockRejectedValueOnce ( {
response : { status : 422 , data : { errors : { project : [ message ] } } } ,
} ) ;
vi . stubGlobal ( 'confirm' , ( ) = > true ) ;
await wrapper . get ( '[data-testid="pdd-delete"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( wrapper . emitted ( 'close' ) ) . toBeUndefined ( ) ;
expect ( wrapper . text ( ) ) . toContain ( 'Мы уже начали сбор лидов' ) ;
vi . unstubAllGlobals ( ) ;
} ) ;
it ( 'Save: 422 errors.project → текст показан' , async ( ) = > {
// Сброс предыдущих очередей mock'ов, чтобы наш reject точно был первым в queue.
vi . mocked ( axios . patch ) . mockReset ( ) ;
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockRejectedValueOnce ( {
response : {
status : 422 ,
2026-06-17 05:17:12 +03:00
data : {
errors : {
project : [
'Мы уже начали сбор лидов по этому проекту на завтра. Изменить источник можно будет послезавтра.' ,
] ,
} ,
} ,
2026-05-26 12:33:18 +03:00
} ,
} ) ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( wrapper . emitted ( 'saved' ) ) . toBeUndefined ( ) ;
expect ( wrapper . text ( ) ) . toContain ( 'Изменить источник можно будет послезавтра' ) ;
} ) ;
2026-05-15 05:54:05 +03:00
it ( 'renders region chips for project.regions = [1, 2]' , async ( ) = > {
const withRegions : Project = { . . . sampleProject , regions : [ 1 , 2 ] } ;
2026-05-14 17:51:56 +03:00
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : withRegions } } ) ;
await wrapper . vm . $nextTick ( ) ;
const text = wrapper . text ( ) ;
2026-05-15 05:54:05 +03:00
expect ( text ) . toContain ( 'Адыгея' ) ; // code 1
expect ( text ) . toContain ( 'Алтай' ) ; // code 2 (Республика Алтай)
2026-05-14 17:51:56 +03:00
} ) ;
2026-05-15 05:54:05 +03:00
it ( 'selecting regions adds to regions array (no bitmask conversion)' , async ( ) = > {
2026-05-14 17:51:56 +03:00
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockResolvedValueOnce ( { data : { data : sampleProject } } ) ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : sampleProject } } ) ;
const autocomplete = wrapper . getComponent ( { name : 'VAutocomplete' } ) ;
2026-05-15 05:54:05 +03:00
await autocomplete . vm . $emit ( 'update:model-value' , [ 82 , 83 ] ) ;
2026-05-14 17:51:56 +03:00
await wrapper . vm . $nextTick ( ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
2026-05-15 05:54:05 +03:00
expect ( axios . patch ) . toHaveBeenCalledWith ( '/api/projects/42' , expect . objectContaining ( { regions : [ 82 , 83 ] } ) ) ;
2026-05-14 17:51:56 +03:00
} ) ;
2026-05-15 05:54:05 +03:00
it ( 'clearing all regions sets regions=[] on save' , async ( ) = > {
2026-05-14 17:51:56 +03:00
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockResolvedValueOnce ( { data : { data : sampleProject } } ) ;
2026-05-15 05:54:05 +03:00
const withRegions : Project = { . . . sampleProject , regions : [ 82 , 83 ] } ;
2026-05-14 17:51:56 +03:00
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : withRegions } } ) ;
const autocomplete = wrapper . getComponent ( { name : 'VAutocomplete' } ) ;
await autocomplete . vm . $emit ( 'update:model-value' , [ ] ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
2026-05-15 05:54:05 +03:00
expect ( axios . patch ) . toHaveBeenCalledWith ( '/api/projects/42' , expect . objectContaining ( { regions : [ ] } ) ) ;
2026-05-14 17:51:56 +03:00
} ) ;
2026-05-23 10:21:10 +03:00
it ( 'region autocomplete has closable-chips so a single region can be removed' , ( ) = > {
const withRegions : Project = { . . . sampleProject , regions : [ 1 , 2 ] } ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : withRegions } } ) ;
const autocomplete = wrapper . getComponent ( { name : 'VAutocomplete' } ) ;
expect ( autocomplete . props ( 'closableChips' ) ) . toBe ( true ) ;
} ) ;
2026-06-22 20:59:02 +03:00
2026-06-25 17:48:34 +03:00
// --- Эпик 3 (редизайн): замок заменён на редактируемые поля + объявление + подтверждение ---
it ( 'source_locked НЕ блокирует поле источника (можно редактировать)' , ( ) = > {
2026-06-22 20:59:02 +03:00
const locked : Project = {
. . . sampleProject ,
signal_type : 'call' ,
source_locked : true ,
2026-06-25 17:48:34 +03:00
source_unlock_at : '2026-06-27T21:00:00+03:00' ,
2026-06-22 20:59:02 +03:00
source_unlock_projected : true ,
} ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : locked } } ) ;
const src = wrapper . get ( 'input[data-testid="pdd-signal-identifier"]' ) ;
2026-06-25 17:48:34 +03:00
expect ( ( src . element as HTMLInputElement ) . disabled ) . toBe ( false ) ;
2026-06-22 20:59:02 +03:00
} ) ;
2026-06-25 17:48:34 +03:00
it ( 'показывает объявление о вступлении изменений, не блокируя поле лимита' , async ( ) = > {
const locked : Project = {
. . . sampleProject ,
source_locked : true ,
source_unlock_at : '2026-06-27T21:00:00+03:00' ,
} ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : locked } } ) ;
// Меняем лимит — появляется объявление «вступит в силу позже».
await wrapper . get ( '[data-testid="pdd-limit"]' ) . setValue ( '20' ) ;
expect ( wrapper . find ( '[data-testid="pdd-applies-from-banner"]' ) . exists ( ) ) . toBe ( true ) ;
// Поле лимита НЕ заблокировано.
expect ( ( wrapper . get ( 'input[data-testid="pdd-limit"]' ) . element as HTMLInputElement ) . disabled ) . toBe ( false ) ;
} ) ;
it ( 'объявление о вступлении НЕ показывается на незалоченном проекте' , async ( ) = > {
2026-06-22 20:59:02 +03:00
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : { . . . sampleProject , source_locked : false } } } ) ;
2026-06-25 17:48:34 +03:00
await wrapper . get ( '[data-testid="pdd-limit"]' ) . setValue ( '20' ) ;
expect ( wrapper . find ( '[data-testid="pdd-applies-from-banner"]' ) . exists ( ) ) . toBe ( false ) ;
2026-06-22 20:59:02 +03:00
} ) ;
2026-06-25 17:48:34 +03:00
it ( 'смена источника на залоченном проекте требует подтверждения (save только после confirm)' , async ( ) = > {
vi . mocked ( axios . patch ) . mockReset ( ) ;
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockResolvedValue ( { data : { data : sampleProject } } ) ;
2026-06-22 20:59:02 +03:00
const locked : Project = {
. . . sampleProject ,
signal_type : 'call' ,
2026-06-25 17:48:34 +03:00
signal_identifier : '79161112233' ,
2026-06-22 20:59:02 +03:00
source_locked : true ,
2026-06-25 17:48:34 +03:00
source_unlock_at : '2026-06-27T21:00:00+03:00' ,
2026-06-22 20:59:02 +03:00
} ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : locked } } ) ;
2026-06-25 17:48:34 +03:00
// Меняем источник и жмём Сохранить → должен открыться диалог подтверждения, PATCH ещё нет.
await wrapper . get ( '[data-testid="pdd-signal-identifier"]' ) . setValue ( '79990001122' ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( wrapper . find ( '[data-testid="pdd-source-change-confirm"]' ) . exists ( ) ) . toBe ( true ) ;
expect ( axios . patch ) . not . toHaveBeenCalled ( ) ;
// Подтверждаем → PATCH уходит с новым источником.
await wrapper . get ( '[data-testid="pdd-source-confirm-yes"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( axios . patch ) . toHaveBeenCalledWith (
'/api/projects/42' ,
expect . objectContaining ( { signal_identifier : '79990001122' } ) ,
) ;
} ) ;
2026-06-25 19:08:47 +03:00
it ( 'диалог подтверждения берёт текст правила из source_change_message (единый источник)' , async ( ) = > {
vi . mocked ( axios . patch ) . mockReset ( ) ;
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockResolvedValue ( { data : { data : sampleProject } } ) ;
const locked : Project = {
. . . sampleProject ,
signal_type : 'call' ,
signal_identifier : '79161112233' ,
source_locked : true ,
source_change_message : 'Лиды по старому источнику придут до 27 июня, дальше — по новому.' ,
} ;
const wrapper = mount ( ProjectDetailsDrawer , { props : { project : locked } } ) ;
await wrapper . get ( '[data-testid="pdd-signal-identifier"]' ) . setValue ( '79990001122' ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
const dialog = wrapper . find ( '[data-testid="pdd-source-change-confirm"]' ) ;
expect ( dialog . exists ( ) ) . toBe ( true ) ;
expect ( dialog . text ( ) ) . toContain ( '27 июня' ) ; // дата из API-текста, не из JS-хардкода
} ) ;
2026-06-25 17:48:34 +03:00
it ( 'смена источника на незалоченном проекте сохраняется без подтверждения' , async ( ) = > {
vi . mocked ( axios . patch ) . mockReset ( ) ;
( axios . patch as unknown as ReturnType < typeof vi.fn > ) . mockResolvedValue ( { data : { data : sampleProject } } ) ;
const wrapper = mount ( ProjectDetailsDrawer , {
props : { project : { . . . sampleProject , signal_type : 'call' , source_locked : false } } ,
} ) ;
await wrapper . get ( '[data-testid="pdd-signal-identifier"]' ) . setValue ( '79990001122' ) ;
await wrapper . get ( '[data-testid="pdd-save"]' ) . trigger ( 'click' ) ;
await wrapper . vm . $nextTick ( ) ;
await wrapper . vm . $nextTick ( ) ;
expect ( wrapper . find ( '[data-testid="pdd-source-change-confirm"]' ) . exists ( ) ) . toBe ( false ) ;
expect ( axios . patch ) . toHaveBeenCalled ( ) ;
2026-06-22 20:59:02 +03:00
} ) ;
2026-05-14 14:13:52 +03:00
} ) ;