kin/web/frontend/src/views/__tests__/SettingsView.worktrees.test.ts

150 lines
4.9 KiB
TypeScript
Raw Normal View History

2026-03-17 20:32:49 +02:00
/**
2026-03-18 15:39:54 +02:00
* KIN-UI-012: Тесты SettingsView навигатор по настройкам проектов
2026-03-18 14:30:36 +02:00
*
* После рефакторинга SettingsView стал навигатором:
* показывает список проектов и ссылки на /project/{id}?tab=settings.
* Детальные настройки каждого проекта переехали в ProjectView вкладка Settings.
2026-03-17 20:32:49 +02:00
*
* Проверяет:
2026-03-18 14:30:36 +02:00
* 1. Загрузка и отображение списка проектов
* 2. Имя и id проекта видны
* 3. Ссылки ведут на /project/{id}?tab=settings
* 4. execution_mode отображается если задан
2026-03-17 20:32:49 +02:00
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
2026-03-18 14:30:36 +02:00
import { createRouter, createMemoryHistory } from 'vue-router'
2026-03-17 20:32:49 +02:00
import SettingsView from '../SettingsView.vue'
vi.mock('../../api', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../api')>()
return {
...actual,
api: {
projects: vi.fn(),
},
}
})
import { api } from '../../api'
const BASE_PROJECT = {
id: 'proj-1',
name: 'Test Project',
path: '/projects/test',
status: 'active',
priority: 5,
tech_stack: ['python'],
2026-03-18 14:30:36 +02:00
execution_mode: null as string | null,
2026-03-17 20:32:49 +02:00
autocommit_enabled: null,
auto_test_enabled: null,
worktrees_enabled: null as number | null,
obsidian_vault_path: null,
deploy_command: null,
test_command: null,
deploy_host: null,
deploy_path: null,
deploy_runtime: null,
deploy_restart_cmd: null,
created_at: '2024-01-01',
total_tasks: 0,
done_tasks: 0,
active_tasks: 0,
blocked_tasks: 0,
review_tasks: 0,
project_type: null,
ssh_host: null,
ssh_user: null,
ssh_key_path: null,
ssh_proxy_jump: null,
description: null,
}
2026-03-18 14:30:36 +02:00
function makeRouter() {
return createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/settings', component: SettingsView },
{ path: '/project/:id', component: { template: '<div />' } },
],
})
}
2026-03-17 20:32:49 +02:00
beforeEach(() => {
vi.clearAllMocks()
})
async function mountSettings(overrides: Partial<typeof BASE_PROJECT> = {}) {
const project = { ...BASE_PROJECT, ...overrides }
vi.mocked(api.projects).mockResolvedValue([project as any])
2026-03-18 14:30:36 +02:00
const router = makeRouter()
await router.push('/settings')
const wrapper = mount(SettingsView, { global: { plugins: [router] } })
2026-03-17 20:32:49 +02:00
await flushPromises()
return wrapper
}
2026-03-18 14:30:36 +02:00
describe('SettingsView — навигатор', () => {
2026-03-18 15:41:59 +02:00
it('таблица проектов рендерится', async () => {
const wrapper = await mountSettings()
expect(wrapper.find('table').exists()).toBe(true)
})
2026-03-18 14:30:36 +02:00
it('показывает имя проекта', async () => {
const wrapper = await mountSettings()
expect(wrapper.text()).toContain('Test Project')
2026-03-17 20:32:49 +02:00
})
2026-03-18 14:30:36 +02:00
it('показывает id проекта', async () => {
const wrapper = await mountSettings()
expect(wrapper.text()).toContain('proj-1')
2026-03-17 20:32:49 +02:00
})
2026-03-18 14:30:36 +02:00
it('содержит ссылку на страницу настроек проекта', async () => {
const wrapper = await mountSettings()
const links = wrapper.findAll('a')
expect(links.length).toBeGreaterThan(0)
const settingsLink = links.find(l => l.attributes('href')?.includes('proj-1'))
expect(settingsLink?.exists()).toBe(true)
expect(settingsLink?.attributes('href')).toContain('settings')
2026-03-17 20:32:49 +02:00
})
2026-03-18 14:30:36 +02:00
it('ссылка ведёт на /project/{id} с tab=settings', async () => {
const wrapper = await mountSettings()
const link = wrapper.find('a[href*="proj-1"]')
expect(link.exists()).toBe(true)
expect(link.attributes('href')).toMatch(/\/project\/proj-1/)
expect(link.attributes('href')).toContain('settings')
2026-03-17 20:32:49 +02:00
})
2026-03-18 14:30:36 +02:00
it('показывает execution_mode если задан', async () => {
const wrapper = await mountSettings({ execution_mode: 'auto_complete' })
expect(wrapper.text()).toContain('auto_complete')
2026-03-17 20:32:49 +02:00
})
2026-03-18 14:30:36 +02:00
it('не показывает execution_mode если null', async () => {
const wrapper = await mountSettings({ execution_mode: null })
expect(wrapper.text()).not.toContain('auto_complete')
2026-03-17 20:32:49 +02:00
})
2026-03-18 15:41:59 +02:00
it('для каждого проекта есть ссылка с ?tab=settings', async () => {
const projects = [
{ ...BASE_PROJECT, id: 'proj-1', name: 'Project One' },
{ ...BASE_PROJECT, id: 'proj-2', name: 'Project Two' },
{ ...BASE_PROJECT, id: 'proj-3', name: 'Project Three' },
]
vi.mocked(api.projects).mockResolvedValue(projects as any)
const router = makeRouter()
await router.push('/settings')
const wrapper = mount(SettingsView, { global: { plugins: [router] } })
await flushPromises()
for (const project of projects) {
const link = wrapper.find(`a[href*="${project.id}"]`)
expect(link.exists()).toBe(true)
expect(link.attributes('href')).toContain('tab=settings')
}
})
2026-03-17 20:32:49 +02:00
})