import { describe, it, expect } from 'vitest'; import { mount } from '@vue/test-utils'; import { createVuetify } from 'vuetify'; import { createRouter, createMemoryHistory } from 'vue-router'; import ErrorView from '../../resources/js/views/errors/ErrorView.vue'; // ErrorView читает route.meta.errorCode для конфигурации экрана. const mountErrorView = async (errorCode: '404' | '403' | '500') => { const router = createRouter({ history: createMemoryHistory(), routes: [ { path: '/error', component: ErrorView, meta: { errorCode } }, { path: '/dashboard', component: { template: '
dashboard stub
' } }, ], }); await router.push('/error'); await router.isReady(); return mount(ErrorView, { global: { plugins: [createVuetify(), router], // ErrorView содержит v-app + v-main — те же layout-проблемы что у DealDetailDrawer. // Stub'им VApp/VMain как passthrough. stubs: { VApp: { template: '
' }, VMain: { template: '
' }, }, }, }); }; describe('ErrorView.vue', () => { it('по умолчанию (errorCode=404) показывает «404 / Страница не найдена»', async () => { const wrapper = await mountErrorView('404'); const text = wrapper.text(); expect(wrapper.find('.err-code').text()).toBe('404'); expect(text).toContain('Страница не найдена'); expect(text).toContain('Все рабочие экраны Лидерра доступны через дашборд'); }); it('errorCode=403 показывает «403 / У вас нет доступа» + RequestId', async () => { const wrapper = await mountErrorView('403'); const text = wrapper.text(); expect(wrapper.find('.err-code').text()).toBe('403'); expect(text).toContain('У вас нет доступа'); expect(text).toContain('REQ-3F8A2-0007'); expect(text).toContain('Запрос'); }); it('errorCode=500 показывает «500 / Что-то пошло не так» + IncidentId + status-list', async () => { const wrapper = await mountErrorView('500'); const text = wrapper.text(); expect(wrapper.find('.err-code').text()).toBe('500'); expect(text).toContain('Что-то пошло не так'); expect(text).toContain('INC-2026-0507-0034'); expect(text).toContain('Инцидент'); // status-list только на 500. expect(text).toContain('API · OK'); expect(text).toContain('Telegram · деградация'); }); it('404 содержит «На дашборд» primary + «Назад» secondary', async () => { const wrapper = await mountErrorView('404'); const text = wrapper.text(); expect(text).toContain('На дашборд'); expect(text).toContain('Назад'); }); it('403 содержит «На дашборд» + «Написать в поддержку» (mailto)', async () => { const wrapper = await mountErrorView('403'); const text = wrapper.text(); expect(text).toContain('На дашборд'); expect(text).toContain('Написать в поддержку'); const mailtoLink = wrapper.find('a[href^="mailto:"]'); expect(mailtoLink.exists()).toBe(true); expect(mailtoLink.attributes('href')).toBe('mailto:support@liderra.app'); }); it('500 содержит «Попробовать снова» + «Статус сервиса» (external link)', async () => { const wrapper = await mountErrorView('500'); const text = wrapper.text(); expect(text).toContain('Попробовать снова'); expect(text).toContain('Статус сервиса'); const statusLink = wrapper.find('a[href^="https://status."]'); expect(statusLink.exists()).toBe(true); }); it('содержит брендовый блок «Лидерра.» в шапке', async () => { const wrapper = await mountErrorView('404'); const brand = wrapper.find('.top-brand'); expect(brand.exists()).toBe(true); expect(brand.text()).toContain('Лидерра'); }); it('404 НЕ содержит RequestId или status-list', async () => { const wrapper = await mountErrorView('404'); const text = wrapper.text(); expect(text).not.toContain('REQ-'); expect(text).not.toContain('INC-'); expect(text).not.toContain('API · OK'); }); });