From 6e35193f3b1bd2feeea264e0ae691c8bdb1be2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Sat, 16 May 2026 11:25:49 +0300 Subject: [PATCH] =?UTF-8?q?fix(deals):=20router=20=D0=B2=20DealsViewRedesi?= =?UTF-8?q?gn.spec=20+=20idempotency=20guard=20+=20watch-test=20(C8/F3=20r?= =?UTF-8?q?eview=20fixup)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- app/resources/js/views/DealsView.vue | 7 ++--- app/tests/Frontend/DealsView.spec.ts | 12 +++++++++ app/tests/Frontend/DealsViewRedesign.spec.ts | 27 +++++++++----------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/app/resources/js/views/DealsView.vue b/app/resources/js/views/DealsView.vue index 32997ef4..ea386db6 100644 --- a/app/resources/js/views/DealsView.vue +++ b/app/resources/js/views/DealsView.vue @@ -174,7 +174,7 @@ onMounted(async () => { }); watch( - () => route?.query?.openId, + () => route.query.openId, () => openDealFromQuery(), ); @@ -228,12 +228,10 @@ function openDeal(deal: MockDeal) { /** Audit C8/F3: deep-link — открыть drawer сделки по ?openId= из URL. */ function openDealFromQuery(): void { - // Guard: route may be undefined when component is mounted without a router - // (e.g. isolated unit tests in DealsViewRedesign.spec.ts). - if (!route) return; const raw = route.query.openId; const id = Number(Array.isArray(raw) ? raw[0] : raw); if (!Number.isInteger(id) || id <= 0) return; + if (selectedDeal.value?.id === id) return; const deal = dealsState.find((d) => d.id === id); if (deal) openDeal(deal); } @@ -432,7 +430,6 @@ defineExpose({ toggleManagerDraft, drawerOpen, selectedDeal, - openDealFromQuery, }); const leadStatuses = computed(() => leadStatusesStore.statuses); diff --git a/app/tests/Frontend/DealsView.spec.ts b/app/tests/Frontend/DealsView.spec.ts index 41cfc90d..f2334e78 100644 --- a/app/tests/Frontend/DealsView.spec.ts +++ b/app/tests/Frontend/DealsView.spec.ts @@ -305,4 +305,16 @@ describe('DealsView.vue', () => { const vm = wrapper.vm as unknown as { drawerOpen: boolean }; expect(vm.drawerOpen).toBe(false); }); + + it('навигация на /deals?openId= в смонтированном view открывает drawer (watch)', async () => { + const openId = MOCK_DEALS[0].id; + const wrapper = await mountDealsViewAt('/deals'); + await flushPromises(); + const vm = wrapper.vm as unknown as { drawerOpen: boolean; selectedDeal: { id: number } | null }; + expect(vm.drawerOpen).toBe(false); + await wrapper.vm.$router.push(`/deals?openId=${openId}`); + await flushPromises(); + expect(vm.drawerOpen).toBe(true); + expect(vm.selectedDeal?.id).toBe(openId); + }); }); diff --git a/app/tests/Frontend/DealsViewRedesign.spec.ts b/app/tests/Frontend/DealsViewRedesign.spec.ts index 98d13e14..7bff341e 100644 --- a/app/tests/Frontend/DealsViewRedesign.spec.ts +++ b/app/tests/Frontend/DealsViewRedesign.spec.ts @@ -56,13 +56,20 @@ describe('DealsView — redesigned', () => { }); describe('FilterChip popovers (Sprint 1 C2)', () => { - it('clicking Project chip toggles projectMenuOpen ref to true', async () => { - const wrapper = mount(DealsView, { + function setupWithRouter() { + setActivePinia(createPinia()); + const router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/deals', component: DealsView }] }); + router.push('/deals'); + return mount(DealsView, { global: { - plugins: [createPinia(), createVuetify()], + plugins: [router, createVuetify()], stubs: { DealDetailDrawer: true, NewDealDialog: true, VMenu: { template: '
' } }, }, }); + } + + it('clicking Project chip toggles projectMenuOpen ref to true', async () => { + const wrapper = setupWithRouter(); await new Promise((r) => setTimeout(r, 50)); // eslint-disable-next-line @typescript-eslint/no-explicit-any const vm = wrapper.vm as any; @@ -75,12 +82,7 @@ describe('FilterChip popovers (Sprint 1 C2)', () => { }); it('clicking Manager chip toggles managerMenuOpen ref to true', async () => { - const wrapper = mount(DealsView, { - global: { - plugins: [createPinia(), createVuetify()], - stubs: { DealDetailDrawer: true, NewDealDialog: true, VMenu: { template: '
' } }, - }, - }); + const wrapper = setupWithRouter(); await new Promise((r) => setTimeout(r, 50)); // eslint-disable-next-line @typescript-eslint/no-explicit-any const vm = wrapper.vm as any; @@ -93,12 +95,7 @@ describe('FilterChip popovers (Sprint 1 C2)', () => { }); it('applying project selection updates filterProjects and closes menu', async () => { - const wrapper = mount(DealsView, { - global: { - plugins: [createPinia(), createVuetify()], - stubs: { DealDetailDrawer: true, NewDealDialog: true, VMenu: { template: '
' } }, - }, - }); + const wrapper = setupWithRouter(); await new Promise((r) => setTimeout(r, 50)); // eslint-disable-next-line @typescript-eslint/no-explicit-any const vm = wrapper.vm as any;