kin: auto-commit after pipeline
This commit is contained in:
parent
d532f26f5a
commit
a22cf738b7
1 changed files with 225 additions and 0 deletions
225
web/frontend/src/views/__tests__/date-filter.test.ts
Normal file
225
web/frontend/src/views/__tests__/date-filter.test.ts
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
/**
|
||||||
|
* 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<typeof import('../../api')>()
|
||||||
|
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<string, string> = {}
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Add table
Add a link
Reference in a new issue