9ada842bdc
UX-request 18.05.2026 (п.3):
- DealDetailHero: v-chip → v-menu со списком всех статусов из lead_statuses
store; форма и цвет chip'а не меняются
- DealDetailBody: emit 'status-changed' наверх (без мутации props.deal)
- DealDetailDrawer: forward события наружу
- DealsView: onDrawerStatusChanged → optimistic update dealsState + PATCH
/api/deals/{id} + rollback
- KanbanView: onDrawerStatusChanged → перенос карточки между колонками
dealsByStatus + transitionDeals + rollback на ошибку
Vue правило vue/no-mutating-props соблюдено (логика в parent'е, не в Body).
Vitest 5 файлов / 38 passed на затронутых; build 2.29s.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
2.5 KiB
TypeScript
52 lines
2.5 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { mount } from '@vue/test-utils';
|
|
import { createVuetify } from 'vuetify';
|
|
import DealDetailHero from '../../resources/js/components/deals/DealDetailHero.vue';
|
|
import type { MockDeal } from '../../resources/js/composables/mockDeals';
|
|
import type { LeadStatus } from '../../resources/js/composables/leadStatuses';
|
|
|
|
const vuetify = createVuetify();
|
|
|
|
const statuses: LeadStatus[] = [
|
|
{ slug: 'new', nameRu: 'Новая сделка', colorHex: '#5b2db2', order: 1 } as LeadStatus,
|
|
{ slug: 'viewed', nameRu: 'Просмотрено', colorHex: '#5a2db2', order: 2 } as LeadStatus,
|
|
{ slug: 'won', nameRu: 'Куплено', colorHex: '#00A36C', order: 3 } as LeadStatus,
|
|
];
|
|
|
|
function makeDeal(over: Partial<MockDeal> = {}): MockDeal {
|
|
return {
|
|
id: 1, name: '+79991234567', phone: '+79991234567', statusSlug: 'new',
|
|
project: 'p', manager: { initials: 'A', name: 'A' }, cost: 0,
|
|
receivedMinutesAgo: 1, ...over,
|
|
};
|
|
}
|
|
|
|
describe('DealDetailHero — inline status picker (18.05.2026)', () => {
|
|
it('рендерит статус-chip с триггером (data-testid="status-chip-trigger")', () => {
|
|
const w = mount(DealDetailHero, {
|
|
props: { deal: makeDeal(), status: statuses[0], allStatuses: statuses },
|
|
global: { plugins: [vuetify] },
|
|
});
|
|
expect(w.find('[data-testid="status-chip-trigger"]').exists()).toBe(true);
|
|
});
|
|
|
|
it('клик по chip открывает меню (data-testid="status-option-{slug}" появляются)', async () => {
|
|
const w = mount(DealDetailHero, {
|
|
props: { deal: makeDeal(), status: statuses[0], allStatuses: statuses },
|
|
global: { plugins: [vuetify], stubs: { teleport: false } },
|
|
attachTo: document.body,
|
|
});
|
|
await w.find('[data-testid="status-chip-trigger"]').trigger('click');
|
|
// Give v-menu time to mount (teleport target = body).
|
|
await new Promise((r) => setTimeout(r, 200));
|
|
const options = document.body.querySelectorAll('[data-testid^="status-option-"]');
|
|
expect(options.length).toBeGreaterThan(0);
|
|
const wonOption = document.body.querySelector('[data-testid="status-option-won"]') as HTMLElement | null;
|
|
expect(wonOption).not.toBeNull();
|
|
wonOption?.click();
|
|
await new Promise((r) => setTimeout(r, 30));
|
|
expect(w.emitted('change-status')?.[0]?.[0]).toBe('won');
|
|
w.unmount();
|
|
});
|
|
});
|