fix: align task due dates to the right
This commit is contained in:
@@ -32,12 +32,50 @@ describe('unified task due display', () => {
|
||||
expect(overdue).not.toContain('swipe-bg')
|
||||
})
|
||||
|
||||
it('uses semantic due colors and preserves task content and action tracks', () => {
|
||||
it('places every due display in a right tail before stable actions', () => {
|
||||
expect(app.match(/<span v-if="[^\"]+\.due_at" class="task-tail"><TaskDueDisplay/g)).toHaveLength(4)
|
||||
expect(app).not.toContain('class="meta"><TaskDueDisplay')
|
||||
expect(app).toContain('</div><span v-if="node.task.due_at" class="task-tail"><TaskDueDisplay')
|
||||
expect(app).toContain('</span><span v-if="activeView===\'trash\'" class="task-actions">')
|
||||
const ordinaryChildren = app.slice(app.indexOf('<article v-if="!collapsedTaskIds.has(node.task.id)"'), app.indexOf('</template>', app.indexOf('<article v-if="!collapsedTaskIds.has(node.task.id)"')))
|
||||
expect(ordinaryChildren).toContain('v-if="activeView!==\'trash\'" class="drag-handle task-drag-handle"')
|
||||
expect(ordinaryChildren).toContain('v-if="activeView!==\'trash\'" class="task-check"')
|
||||
expect(ordinaryChildren).not.toContain('restoreTask(subtask)')
|
||||
expect(ordinaryChildren).not.toContain('purgeTask(subtask)')
|
||||
expect(ordinaryChildren).not.toContain('v-if="activeView===\'trash\'" class="task-actions"')
|
||||
expect(app).toContain('<span v-else class="task-actions"><button class="icon ghost"')
|
||||
expect(app).toContain('<span v-if="node.task.priority" class="priority"')
|
||||
expect(app).toContain('v-if="node.subtasks.length" class="meta"')
|
||||
expect(app).toContain('<span class="meta-item"><ListChecks/>')
|
||||
})
|
||||
|
||||
it('uses a right-aligned desktop tail and a compact two-line mobile tail without empty slots', () => {
|
||||
expect(css).toContain('.task-tail{flex:0 1 auto;min-width:0;max-width:')
|
||||
expect(css).toContain('text-align:right;white-space:nowrap')
|
||||
expect(css).toContain('.task-actions{display:flex;align-items:center;flex:0 0 auto}')
|
||||
expect(css).toContain('.task-row>.drag-handle,.task-row>.task-check,.task-actions>*{flex-shrink:0}')
|
||||
expect(css).toContain('@media(max-width:930px){.task-tail{flex:0 1 76px;max-width:76px;')
|
||||
expect(css).toContain('.task-row:has(>.task-tail){height:66px;max-height:68px;}')
|
||||
expect(css).toContain('.task-main{padding:4px 0}')
|
||||
expect(css).toContain('.task-due__text{display:grid;justify-items:end;overflow:hidden;text-overflow:ellipsis;')
|
||||
expect(css).toContain('.task-due__absolute,.task-due__relative{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}')
|
||||
expect(css).toContain('.task-due__separator{display:none}')
|
||||
expect(css).not.toContain('.task-main .meta{display:flex;flex-wrap:wrap;')
|
||||
expect(css).toContain('.task-check{width:44px;height:44px;')
|
||||
expect(css).toContain('.icon,.ghost{min-width:44px;min-height:44px;')
|
||||
expect(css).toMatch(/\.restore\{[^}]*min-height:44px/)
|
||||
})
|
||||
|
||||
it('scopes content metadata and keeps date semantics without a visible calendar icon', () => {
|
||||
expect(css).toContain('.task-due--overdue{color:var(--danger)}')
|
||||
expect(css).toContain('.task-due--neutral,.task-due--completed{color:var(--muted)}')
|
||||
expect(css).toContain('.task-main{border:0;background:transparent;flex:1;min-width:0;')
|
||||
expect(css).toContain('.icon,.ghost{min-width:44px;min-height:44px;')
|
||||
expect(css).toContain('@media(max-width:930px){.task-main .meta{display:flex;flex-wrap:wrap;')
|
||||
expect(css).toContain('.meta-item{display:flex;align-items:center;gap:3px}')
|
||||
expect(css).not.toContain('.meta span{display:flex;')
|
||||
expect(taskDueDisplay).not.toContain('CalendarDays')
|
||||
expect(taskDueDisplay).toContain('<time')
|
||||
expect(taskDueDisplay).toContain(':aria-label="presentation.fullText"')
|
||||
expect(taskDueDisplay).toContain('class="task-due__separator"')
|
||||
expect(css).not.toContain('.overdue-task .meta')
|
||||
expect(css).not.toContain('.overdue-badge')
|
||||
})
|
||||
@@ -396,6 +434,24 @@ describe('task and habit row decoration', () => {
|
||||
expect(css).toMatch(/\.restore\{[^}]*min-height:44px/)
|
||||
})
|
||||
|
||||
it('reconciles only top-level Trash mutations and distinguishes refresh failure', () => {
|
||||
const loadBlock = app.slice(app.indexOf('async function loadTrash()'), app.indexOf('async function switchView'))
|
||||
const mutationBlock = app.slice(app.indexOf('async function mutateTrashTask'), app.indexOf('async function restoreTask'))
|
||||
const restoreBlock = app.slice(app.indexOf('async function restoreTask'), app.indexOf('async function purgeTask'))
|
||||
const purgeBlock = app.slice(app.indexOf('async function purgeTask'), app.indexOf('async function addSubtask'))
|
||||
expect(loadBlock).toContain('return await runLatestRequest')
|
||||
expect(mutationBlock).toContain('const result = await performTrashMutation(')
|
||||
expect(mutationBlock).toContain('trash.value = trash.value.filter((item) => item.id !== task.id)')
|
||||
expect(mutationBlock).toContain('totalTasks.value = nextTotalAfterLocalTaskRemoval(totalTasks.value)')
|
||||
expect(mutationBlock).toContain('if (page.value > totalPages.value) page.value = totalPages.value')
|
||||
expect(mutationBlock).toContain('if (!result.mutated)')
|
||||
expect(mutationBlock).toContain('fail(result.error)')
|
||||
expect(mutationBlock).toContain('if (!result.refreshed)')
|
||||
expect(mutationBlock).toContain('请重试刷新')
|
||||
expect(restoreBlock).toContain("await mutateTrashTask(task, () => api(`/tasks/${task.id}/restore`")
|
||||
expect(purgeBlock).toContain("await mutateTrashTask(task, () => api(`/trash/${task.id}`")
|
||||
})
|
||||
|
||||
it('supports pointer dragging for desktop task and subtask completion', () => {
|
||||
expect(app.match(/@pointerdown="startTaskPointer/g)?.length).toBe(3)
|
||||
expect(app.match(/@pointermove="moveTaskPointer/g)?.length).toBe(3)
|
||||
|
||||
Reference in New Issue
Block a user