diff --git a/web/frontend/src/views/ProjectView.vue b/web/frontend/src/views/ProjectView.vue index 3613496..5f13d48 100644 --- a/web/frontend/src/views/ProjectView.vue +++ b/web/frontend/src/views/ProjectView.vue @@ -1098,7 +1098,6 @@ async function addDecision() { diff --git a/web/frontend/src/views/__tests__/date-filter.test.ts b/web/frontend/src/views/__tests__/date-filter.test.ts deleted file mode 100644 index 201d875..0000000 --- a/web/frontend/src/views/__tests__/date-filter.test.ts +++ /dev/null @@ -1,225 +0,0 @@ -/** - * KIN-UI-016: Тесты фильтра дат для завершённых задач - * - * Проверяет: - * 1. Блок фильтра дат виден при статусе 'done' в selectedStatuses - * 2. Блок фильтра дат скрыт при отсутствии 'done' в selectedStatuses - * 3. Инпуты date-from и date-to присутствуют в блоке фильтра - * 4. Кнопка сброса скрыта, если dateFrom и dateTo не заданы - * 5. Кнопка сброса появляется при заполнении dateFrom - * 6. Кнопка сброса появляется при заполнении dateTo - * 7. Кнопка сброса имеет data-testid='date-reset-btn' (не хрупкий текстовый селектор) - * 8. Клик по кнопке сброса очищает оба поля dateFrom и dateTo - */ - -import { describe, it, expect, vi, beforeEach } from 'vitest' -import { mount, flushPromises } from '@vue/test-utils' -import { createRouter, createMemoryHistory } from 'vue-router' -import ProjectView from '../ProjectView.vue' - -vi.mock('../../api', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - api: { - project: vi.fn(), - projects: vi.fn(), - getPhases: vi.fn(), - environments: vi.fn(), - projectLinks: vi.fn(), - patchProject: vi.fn(), - syncObsidian: vi.fn(), - }, - } -}) - -import { api } from '../../api' - -const localStorageMock = (() => { - let store: Record = {} - return { - getItem: (k: string) => store[k] ?? null, - setItem: (k: string, v: string) => { store[k] = v }, - removeItem: (k: string) => { delete store[k] }, - clear: () => { store = {} }, - } -})() -Object.defineProperty(globalThis, 'localStorage', { value: localStorageMock, configurable: true }) - -const BASE_PROJECT_DETAIL = { - id: 'proj-1', - name: 'Test Project', - path: '/projects/test', - status: 'active', - priority: 5, - tech_stack: ['python'], - execution_mode: 'review', - autocommit_enabled: 0, - auto_test_enabled: 0, - worktrees_enabled: 0, - obsidian_vault_path: '', - deploy_command: '', - test_command: '', - deploy_host: '', - deploy_path: '', - deploy_runtime: '', - deploy_restart_cmd: '', - created_at: '2024-01-01', - total_tasks: 0, - done_tasks: 0, - active_tasks: 0, - blocked_tasks: 0, - review_tasks: 0, - project_type: 'development', - ssh_host: '', - ssh_user: '', - ssh_key_path: '', - ssh_proxy_jump: '', - description: null, - tasks: [], - modules: [], - decisions: [], -} - -function makeRouter() { - return createRouter({ - history: createMemoryHistory(), - routes: [ - { path: '/project/:id', component: ProjectView, props: true }, - ], - }) -} - -beforeEach(() => { - localStorageMock.clear() - vi.clearAllMocks() - vi.mocked(api.project).mockResolvedValue(BASE_PROJECT_DETAIL as any) - vi.mocked(api.projects).mockResolvedValue([]) - vi.mocked(api.getPhases).mockResolvedValue([]) - vi.mocked(api.environments).mockResolvedValue([]) - vi.mocked(api.projectLinks).mockResolvedValue([]) - vi.mocked(api.patchProject).mockResolvedValue(BASE_PROJECT_DETAIL as any) -}) - -describe('ProjectView — фильтр дат завершённых задач', () => { - it('блок фильтра дат виден когда выбран статус done', async () => { - const router = makeRouter() - await router.push('/project/proj-1?status=done') - const wrapper = mount(ProjectView, { - props: { id: 'proj-1' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const dateFromInput = wrapper.find('[data-testid="date-from"]') - expect(dateFromInput.exists()).toBe(true) - }) - - it('блок фильтра дат скрыт когда done не выбран', async () => { - const router = makeRouter() - await router.push('/project/proj-1?status=pending') - const wrapper = mount(ProjectView, { - props: { id: 'proj-1' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const dateFromInput = wrapper.find('[data-testid="date-from"]') - expect(dateFromInput.exists()).toBe(false) - }) - - it('инпут date-from присутствует в блоке фильтра при статусе done', async () => { - const router = makeRouter() - await router.push('/project/proj-1?status=done') - const wrapper = mount(ProjectView, { - props: { id: 'proj-1' }, - global: { plugins: [router] }, - }) - await flushPromises() - - expect(wrapper.find('[data-testid="date-from"]').exists()).toBe(true) - }) - - it('инпут date-to присутствует в блоке фильтра при статусе done', async () => { - const router = makeRouter() - await router.push('/project/proj-1?status=done') - const wrapper = mount(ProjectView, { - props: { id: 'proj-1' }, - global: { plugins: [router] }, - }) - await flushPromises() - - expect(wrapper.find('[data-testid="date-to"]').exists()).toBe(true) - }) - - it('кнопка сброса скрыта когда dateFrom и dateTo не заданы', async () => { - const router = makeRouter() - await router.push('/project/proj-1?status=done') - const wrapper = mount(ProjectView, { - props: { id: 'proj-1' }, - global: { plugins: [router] }, - }) - await flushPromises() - - expect(wrapper.find('[data-testid="date-reset-btn"]').exists()).toBe(false) - }) - - it('кнопка сброса появляется после ввода значения в dateFrom', async () => { - const router = makeRouter() - await router.push('/project/proj-1?status=done') - const wrapper = mount(ProjectView, { - props: { id: 'proj-1' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const dateFrom = wrapper.find('[data-testid="date-from"]') - await dateFrom.setValue('2024-01-01') - await wrapper.vm.$nextTick() - - expect(wrapper.find('[data-testid="date-reset-btn"]').exists()).toBe(true) - }) - - it('кнопка сброса имеет data-testid="date-reset-btn" (не хрупкий текстовый селектор)', async () => { - const router = makeRouter() - await router.push('/project/proj-1?status=done') - const wrapper = mount(ProjectView, { - props: { id: 'proj-1' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const dateTo = wrapper.find('[data-testid="date-to"]') - await dateTo.setValue('2024-12-31') - await wrapper.vm.$nextTick() - - const resetBtn = wrapper.find('[data-testid="date-reset-btn"]') - expect(resetBtn.exists()).toBe(true) - expect(resetBtn.attributes('data-testid')).toBe('date-reset-btn') - }) - - it('клик по кнопке сброса очищает dateFrom и dateTo', async () => { - const router = makeRouter() - await router.push('/project/proj-1?status=done') - const wrapper = mount(ProjectView, { - props: { id: 'proj-1' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const dateFrom = wrapper.find('[data-testid="date-from"]') - const dateTo = wrapper.find('[data-testid="date-to"]') - await dateFrom.setValue('2024-01-01') - await dateTo.setValue('2024-12-31') - await wrapper.vm.$nextTick() - - const resetBtn = wrapper.find('[data-testid="date-reset-btn"]') - expect(resetBtn.exists()).toBe(true) - await resetBtn.trigger('click') - await wrapper.vm.$nextTick() - - expect((dateFrom.element as HTMLInputElement).value).toBe('') - expect((dateTo.element as HTMLInputElement).value).toBe('') - expect(wrapper.find('[data-testid="date-reset-btn"]').exists()).toBe(false) - }) -})