Files
portal/app/tests/Frontend/AppLayout.spec.ts
T
Дмитрий 1e233a70c8 phase2(dashboard): AppLayout + DashboardView - default-layout приложения
- AppLayout: v-navigation-drawer (теало-нуар sidebar 240px) + brand-block
  + nav-tree из 8 пунктов в 3 группах (Работа/Финансы/Команда), v-app-bar
  с crumb «Рабочая область → currentPage» + search ⌘K + bell + user-chip.
  Mobile (md<): drawer toggleable.
- DashboardView: page-head «Доброе утро, Иван» + page-meta + range-toggle
  4 опции (Сегодня/7д/30д/Период). KPI-row из 4 cards: 3 outlined (получено
  лидов/конверсия/активные проекты) + 1 hero balance с runway-bar 4/7
  заполненных сегментов teal #32C8A9.
- AppShell упрощён до layout-mapper (route.meta.layout 'app'/'auth').
- Маршрут /dashboard (meta.layout='app') в router + web.php.
- histoire.setup расширен 8 app-stub-маршрутами для AppLayout.
- Vitest +11 тестов: AppLayout 6 (brand+3 группы+8 пунктов+счётчики+crumb),
  DashboardView 5, AppShell.spec.ts переписан под layout-mapper.
- cspell-words.txt: JBM.

Регресс: lint+type-check+format OK; vitest 35/35 за 4.92s; vite build
DashboardView lazy-chunk 14.9KB; story:build 8/8 за 28.97s; Pest 48/48 за 4.88s.

CLAUDE.md v1.20->v1.21, реестр Открытых_вопросов v1.29->v1.30.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:21:19 +03:00

71 lines
3.1 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import { createRouter, createMemoryHistory } from 'vue-router';
import AppLayout from '../../resources/js/layouts/AppLayout.vue';
// AppLayout содержит sidebar (8 nav-items в 3 группах) + topbar (crumb/search/user) + RouterView.
const mountAppLayout = async (path = '/dashboard') => {
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/dashboard', component: { template: '<div>dashboard</div>' } },
{ path: '/deals', component: { template: '<div>deals</div>' } },
{ path: '/kanban', component: { template: '<div>kanban</div>' } },
{ path: '/reminders', component: { template: '<div>reminders</div>' } },
{ path: '/billing', component: { template: '<div>billing</div>' } },
{ path: '/reports', component: { template: '<div>reports</div>' } },
{ path: '/managers', component: { template: '<div>managers</div>' } },
{ path: '/settings', component: { template: '<div>settings</div>' } },
],
});
await router.push(path);
await router.isReady();
return mount(AppLayout, {
global: { plugins: [createVuetify(), router] },
});
};
describe('AppLayout.vue', () => {
it('монтируется без ошибок', async () => {
const wrapper = await mountAppLayout();
expect(wrapper.exists()).toBe(true);
});
it('содержит брендовый блок «Лидерра.» в sidebar', async () => {
const wrapper = await mountAppLayout();
expect(wrapper.text()).toContain('Лидерра');
});
it('содержит 3 nav-группы: Работа, Финансы, Команда', async () => {
const wrapper = await mountAppLayout();
const text = wrapper.text();
expect(text).toContain('Работа');
expect(text).toContain('Финансы');
expect(text).toContain('Команда');
});
it('содержит все 8 nav-пунктов', async () => {
const wrapper = await mountAppLayout();
const text = wrapper.text();
['Дашборд', 'Сделки', 'Канбан', 'Напоминания', 'Биллинг', 'Отчёты', 'Менеджеры', 'Настройки'].forEach((label) =>
expect(text).toContain(label),
);
});
it('показывает счётчики только у пунктов с count', async () => {
const wrapper = await mountAppLayout();
const text = wrapper.text();
expect(text).toContain('247'); // Сделки
expect(text).toContain('12'); // Напоминания
expect(text).toContain('4'); // Менеджеры
});
it('breadcrumb показывает текущую страницу', async () => {
const wrapper = await mountAppLayout('/dashboard');
expect(wrapper.text()).toContain('Рабочая область');
expect(wrapper.text()).toContain('Дашборд');
});
});