import { describe, it, expect, beforeEach } from 'vitest'; import { mount, type VueWrapper } from '@vue/test-utils'; import { createMemoryHistory, createRouter, type Router } from 'vue-router'; import { createPinia, setActivePinia } from 'pinia'; import { createVuetify } from 'vuetify'; import AppSidebar from '../../resources/js/components/layout/AppSidebar.vue'; import { useDealsCountStore } from '../../resources/js/stores/dealsCount'; // VDialog телепортит контент в body → стаб рендерит слот инлайн, чтобы искать // тело диалога в wrapper (тот же квирк, что в AdminSupplierProjectsView.spec). async function setup(initialRoute = '/deals'): Promise<{ wrapper: VueWrapper; router: Router }> { setActivePinia(createPinia()); useDealsCountStore().count = 5; const router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/deals', component: { template: '
' } }], }); await router.push(initialRoute); await router.isReady(); const vuetify = createVuetify(); const wrapper = mount(AppSidebar, { global: { plugins: [router, vuetify], stubs: { // Яндекс Аудитория и Реклама Телеграм — реальные RouterLink, поэтому стаб // должен пробрасывать остальные атрибуты (data-testid и т.п.), не только class. RouterLink: { props: ['to'], template: '', inheritAttrs: false, }, VDialog: { template: '
' }, }, }, }); return { wrapper, router }; } const AD_ITEMS = [ { testid: 'ad-nav-ai-callcenter', title: 'ИИ колцентр' }, { testid: 'ad-nav-sms', title: 'Рассылка СМС' }, { testid: 'ad-nav-yandex-audience', title: 'Яндекс Аудитория' }, { testid: 'ad-nav-vk', title: 'VK Реклама' }, { testid: 'ad-nav-telegram', title: 'Реклама Телеграм' }, ]; // Каналы-заглушки: клик открывает «В разработке». Яндекс Аудитория и Реклама Телеграм // уже ведут на реальные экраны — в этот список они не входят. const STUB_ITEMS = AD_ITEMS.filter( (i) => i.testid !== 'ad-nav-yandex-audience' && i.testid !== 'ad-nav-telegram', ); describe('AppSidebar — раздел «Рекламные возможности»', () => { beforeEach(() => localStorage.clear()); it('показывает группу «Рекламные возможности» сразу после «Работа»', async () => { const { wrapper } = await setup(); const eyebrows = wrapper.findAll('.ld-nav-group__eyebrow').map((e) => e.text()); const workIdx = eyebrows.indexOf('Работа'); const adIdx = eyebrows.indexOf('Рекламные возможности'); expect(workIdx).toBeGreaterThanOrEqual(0); expect(adIdx).toBe(workIdx + 1); }); it('содержит 5 рекламных пунктов с нужными названиями', async () => { const { wrapper } = await setup(); for (const item of AD_ITEMS) { const el = wrapper.find(`[data-testid="${item.testid}"]`); expect(el.exists(), `нет пункта ${item.testid}`).toBe(true); expect(el.text()).toContain(item.title); } }); it('пока не кликнули — окно «В разработке» не показано', async () => { const { wrapper } = await setup(); expect(wrapper.find('[data-testid="ad-stub-dialog"]').exists()).toBe(false); }); it.each(STUB_ITEMS)('клик по «$title» открывает окно с текстом релиза', async ({ testid }) => { const { wrapper } = await setup(); await wrapper.find(`[data-testid="${testid}"]`).trigger('click'); const dialog = wrapper.find('[data-testid="ad-stub-dialog"]'); expect(dialog.exists()).toBe(true); expect(dialog.text()).toContain('В разработке'); expect(dialog.text()).toContain('01.09.2026'); }); it('«Реклама Телеграм» — активный пункт: ведёт на /advertising/telegram, не на заглушку', async () => { const { wrapper } = await setup(); const item = wrapper.find('[data-testid="ad-nav-telegram"]'); expect(item.attributes('href') ?? item.attributes('to')).toContain('/advertising/telegram'); await item.trigger('click'); expect(wrapper.find('[data-testid="ad-stub-dialog"]').exists()).toBe(false); }); it('кнопка «Понятно» закрывает окно', async () => { const { wrapper } = await setup(); await wrapper.find('[data-testid="ad-nav-sms"]').trigger('click'); expect(wrapper.find('[data-testid="ad-stub-dialog"]').exists()).toBe(true); await wrapper.find('[data-testid="ad-stub-ok"]').trigger('click'); expect(wrapper.find('[data-testid="ad-stub-dialog"]').exists()).toBe(false); }); // B2-2: Яндекс Аудитория — теперь реальный переход, не заглушка. it('пункт «Яндекс Аудитория» — ссылка на /advertising/yandex, клик НЕ открывает заглушку', async () => { const { wrapper } = await setup(); const link = wrapper.find('[data-testid="ad-nav-yandex-audience"]'); expect(link.attributes('href')).toBe('/advertising/yandex'); await link.trigger('click'); expect(wrapper.find('[data-testid="ad-stub-dialog"]').exists()).toBe(false); }); // T19: «Рекламный кошелёк» — отдельная ссылка (НЕ канал-заглушка), без числа баланса в меню. it('пункт «Рекламный кошелёк» — ссылка на /advertising/wallet, без суммы баланса в подписи', async () => { const { wrapper } = await setup(); const link = wrapper.find('[data-testid="ad-nav-wallet"]'); expect(link.exists()).toBe(true); expect(link.attributes('href')).toBe('/advertising/wallet'); expect(link.text()).toBe('Рекламный кошелёк'); await link.trigger('click'); expect(wrapper.find('[data-testid="ad-stub-dialog"]').exists()).toBe(false); }); });