Files
dodo/frontend/src/lib/task-utils.test.ts
T
bboysoul 988d131d9b
ci / docker (push) Successful in 3m33s
feat: add task and habit reordering
2026-09-08 06:51:31 +08:00

65 lines
2.5 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { filterTasks, groupTaskTree, moveItemWithinScope, renderMarkdown, toDateTimeLocal } from './task-utils'
type SearchTask = {
id: string
title: string
description?: string
parent_id?: string | null
completed?: boolean
list_name?: string
}
const tasks: SearchTask[] = [
{ id: '1', title: 'Write release notes', description: 'mention **API**', parent_id: null, completed: false },
{ id: '2', title: 'Check links', description: '', parent_id: '1', completed: false },
{ id: '3', title: 'Buy milk', description: '', parent_id: null, completed: true },
]
describe('task utilities', () => {
it('filters task titles and markdown descriptions case-insensitively', () => {
expect(filterTasks(tasks, 'api').map((task) => task.id)).toEqual(['1'])
expect(filterTasks(tasks, 'WRITE').map((task) => task.id)).toEqual(['1'])
})
it('keeps a one-level subtask tree without duplicating children', () => {
expect(groupTaskTree(tasks)).toEqual([{ task: tasks[0], subtasks: [tasks[1]] }, { task: tasks[2], subtasks: [] }])
})
it('moves an item before or after another item without changing other scopes', () => {
const rows = [
{ id: 'a', parent_id: null },
{ id: 'a1', parent_id: 'a' },
{ id: 'b', parent_id: null },
{ id: 'c', parent_id: null },
]
expect(moveItemWithinScope(rows, 'c', 'a', 'before').map((row) => row.id)).toEqual(['c', 'a1', 'a', 'b'])
expect(moveItemWithinScope(rows, 'a', 'c', 'after').map((row) => row.id)).toEqual(['b', 'a1', 'c', 'a'])
expect(moveItemWithinScope(rows, 'a1', 'b', 'before')).toEqual(rows)
})
it('renders safe basic markdown and strips unsafe html', () => {
const html = renderMarkdown('# Plan\n**bold** [link](https://example.com)\n<script>alert(1)</script>')
expect(html).toContain('<h1>Plan</h1>')
expect(html).toContain('<strong>bold</strong>')
expect(html).toContain('rel="noopener noreferrer"')
expect(html).not.toContain('<script>')
})
it('filters titles, descriptions, and list names', () => {
const searchable: SearchTask[] = [
...tasks,
{ id: '4', title: 'Plan', list_name: '工作清单' },
]
expect(filterTasks(searchable, '工作').map((task) => task.id)).toEqual(['4'])
})
it('formats API dates in local wall-clock time', () => {
const original = process.env.TZ
process.env.TZ = 'Asia/Shanghai'
expect(toDateTimeLocal('2026-09-05T12:30:00Z')).toBe('2026-09-05T20:30')
process.env.TZ = original
expect(toDateTimeLocal(null)).toBe('')
})
})