opsdash-app/opsdash/test/DeckCardsWidget.test.ts
2026-04-02 20:31:52 +07:00

434 lines
11 KiB
TypeScript

import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import DeckCardsWidget from '../src/components/widgets/deck/DeckCardsWidget.vue'
const stubs = {
NcEmptyContent: { template: '<div class="stub-empty"></div>' },
NcLoadingIcon: { template: '<div class="stub-loading"></div>' },
}
describe('DeckCardsWidget', () => {
const now = Date.now()
const dayMs = 24 * 60 * 60 * 1000
const rangeFrom = new Date(now - 7 * dayMs).toISOString().slice(0, 10)
const rangeTo = new Date(now + 7 * dayMs).toISOString().slice(0, 10)
const cards = [
{
id: 1,
title: 'Open mine today',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [{ uid: 'me' }],
labels: [],
createdTs: now,
},
{
id: 2,
title: 'Done other',
boardId: 2,
boardTitle: 'B',
status: 'done',
match: 'completed',
assignees: [{ uid: 'other' }],
labels: [],
createdTs: now,
},
{
id: 3,
title: 'Archived other',
boardId: 1,
boardTitle: 'A',
status: 'archived',
match: 'due',
assignees: [{ uid: 'me' }],
labels: [],
createdTs: now,
},
]
it('filters by created_today_mine and boardIds', async () => {
const wrapper = mount(DeckCardsWidget, {
props: {
cards,
rangeLabel: 'This week',
uid: 'me',
filters: ['created_today_mine', 'all'],
defaultFilter: 'created_today_mine',
boardIds: [1],
},
global: { stubs },
})
expect(wrapper.findAll('.deck-card').length).toBe(1)
expect(wrapper.find('.deck-card__title').text()).toBe('Open mine today')
})
it('filters cards by stackIds before applying the active filter', () => {
const withStacks = cards.map((card, index) => ({
...card,
stackId: index === 0 ? 101 : 102,
stackTitle: index === 0 ? 'Doing' : 'Done',
}))
const wrapper = mount(DeckCardsWidget, {
props: {
cards: withStacks,
rangeLabel: 'This week',
filters: ['all'],
defaultFilter: 'all',
stackIds: [101],
},
global: { stubs },
})
expect(wrapper.findAll('.deck-card').length).toBe(1)
expect(wrapper.find('.deck-card__title').text()).toBe('Open mine today')
})
it('adds auto tag filters with counts', () => {
const taggedCards = [
{
id: 10,
title: 'Urgent card',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }],
createdTs: now,
},
{
id: 11,
title: 'Second urgent',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }, { id: 12, title: 'Backlog' }],
createdTs: now,
},
]
const wrapper = mount(DeckCardsWidget, {
props: {
cards: taggedCards,
rangeLabel: 'This week',
filters: ['all'],
defaultFilter: 'all',
autoTagsEnabled: true,
},
global: { stubs },
})
const buttons = wrapper.findAll('.deck-filter-btn')
const urgent = buttons.find((btn) => btn.text().includes('Urgent'))
expect(urgent).toBeTruthy()
expect(urgent?.text()).toContain('2')
})
it('respects tag filter selection', () => {
const taggedCards = [
{
id: 10,
title: 'Urgent card',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }],
createdTs: now,
},
{
id: 11,
title: 'Backlog card',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 12, title: 'Backlog' }],
createdTs: now,
},
]
const wrapper = mount(DeckCardsWidget, {
props: {
cards: taggedCards,
rangeLabel: 'This week',
filters: ['all'],
defaultFilter: 'all',
autoTagsEnabled: true,
autoTagSelection: ['tag_11'],
},
global: { stubs },
})
const labels = wrapper.findAll('.deck-filter-btn').map((btn) => btn.text())
expect(labels.some((text) => text.includes('Urgent'))).toBe(true)
expect(labels.some((text) => text.includes('Backlog'))).toBe(false)
})
it('filters by tag when selected', () => {
const taggedCards = [
{
id: 10,
title: 'Urgent card',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }],
createdTs: now,
},
{
id: 11,
title: 'Backlog card',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 12, title: 'Backlog' }],
createdTs: now,
},
]
const wrapper = mount(DeckCardsWidget, {
props: {
cards: taggedCards,
rangeLabel: 'This week',
filters: ['all'],
defaultFilter: 'tag_11',
autoTagsEnabled: true,
},
global: { stubs },
})
const cardsShown = wrapper.findAll('.deck-card')
expect(cardsShown.length).toBe(1)
expect(cardsShown[0].find('.deck-card__title').text()).toBe('Urgent card')
})
it('skips auto tag filters when disabled', () => {
const taggedCards = [
{
id: 10,
title: 'Urgent card',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }],
createdTs: now,
},
]
const wrapper = mount(DeckCardsWidget, {
props: {
cards: taggedCards,
rangeLabel: 'This week',
filters: ['all'],
defaultFilter: 'all',
autoTagsEnabled: false,
},
global: { stubs },
})
const labels = wrapper.findAll('.deck-filter-btn').map((btn) => btn.text())
expect(labels.some((text) => text.includes('Urgent'))).toBe(false)
})
it('counts tags using cleaned cards', () => {
const taggedCards = [
{
id: 10,
title: 'Active urgent',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }],
createdTs: now,
},
{
id: 11,
title: 'Done urgent',
boardId: 1,
boardTitle: 'A',
status: 'done',
match: 'completed',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }],
createdTs: now,
},
{
id: 12,
title: 'Archived urgent',
boardId: 1,
boardTitle: 'A',
status: 'archived',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }],
createdTs: now,
},
]
const wrapper = mount(DeckCardsWidget, {
props: {
cards: taggedCards,
rangeLabel: 'This week',
filters: ['all'],
defaultFilter: 'all',
includeArchived: false,
includeCompleted: false,
},
global: { stubs },
})
const buttons = wrapper.findAll('.deck-filter-btn')
const urgent = buttons.find((btn) => btn.text().includes('Urgent'))
expect(urgent).toBeTruthy()
expect(urgent?.text()).toContain('1')
})
it('limits tag filters to selected boards', () => {
const taggedCards = [
{
id: 10,
title: 'Board A tag',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Alpha' }],
createdTs: now,
},
{
id: 11,
title: 'Board B tag',
boardId: 2,
boardTitle: 'B',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 12, title: 'Beta' }],
createdTs: now,
},
]
const wrapper = mount(DeckCardsWidget, {
props: {
cards: taggedCards,
rangeLabel: 'This week',
filters: ['all'],
defaultFilter: 'all',
boardIds: [1],
autoTagsEnabled: true,
},
global: { stubs },
})
const labels = wrapper.findAll('.deck-filter-btn').map((btn) => btn.text())
expect(labels.some((text) => text.includes('Alpha'))).toBe(true)
expect(labels.some((text) => text.includes('Beta'))).toBe(false)
})
it('falls back to default filters when tag filter is unavailable', () => {
const taggedCards = [
{
id: 10,
title: 'Tag one',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 11, title: 'Urgent' }],
createdTs: now,
},
{
id: 11,
title: 'Tag two',
boardId: 1,
boardTitle: 'A',
status: 'active',
match: 'due',
assignees: [],
labels: [{ id: 12, title: 'Backlog' }],
createdTs: now,
},
]
const wrapper = mount(DeckCardsWidget, {
props: {
cards: taggedCards,
rangeLabel: 'This week',
filters: ['all'],
defaultFilter: 'tag_11',
autoTagsEnabled: false,
},
global: { stubs },
})
expect(wrapper.findAll('.deck-card').length).toBe(2)
})
it('renders a compact list when enabled', () => {
const wrapper = mount(DeckCardsWidget, {
props: {
cards,
rangeLabel: 'This week',
compactList: true,
},
global: { stubs },
})
expect(wrapper.find('.deck-panel--compact').exists()).toBe(true)
expect(wrapper.find('.deck-card__row').exists()).toBe(true)
})
it('hides filters below minFilterCount and falls back from hidden default filter', () => {
const noThreshold = mount(DeckCardsWidget, {
props: {
cards,
rangeLabel: 'This week',
filters: ['open_all', 'due_all', 'done_all', 'archived_all'],
defaultFilter: 'done_all',
includeCompleted: false,
includeArchived: false,
minFilterCount: 0,
},
global: { stubs },
})
const wrapper = mount(DeckCardsWidget, {
props: {
cards,
rangeLabel: 'This week',
filters: ['open_all', 'due_all', 'done_all', 'archived_all'],
defaultFilter: 'done_all',
includeCompleted: false,
includeArchived: false,
minFilterCount: 1,
},
global: { stubs },
})
const noThresholdLabels = noThreshold.findAll('.deck-filter-btn').map((btn) => btn.text())
const labels = wrapper.findAll('.deck-filter-btn').map((btn) => btn.text())
expect(noThresholdLabels.some((text) => text.includes('Done · All'))).toBe(true)
expect(labels.some((text) => text.includes('Done · All'))).toBe(false)
expect(labels.some((text) => text.includes('Archived · All'))).toBe(false)
expect(wrapper.findAll('.deck-card').length).toBe(1)
})
it('hydrates created_range filters without runtime initialization errors', () => {
const wrapper = mount(DeckCardsWidget, {
props: {
cards,
rangeLabel: 'This week',
filters: ['created_range_all', 'all'],
defaultFilter: 'created_range_all',
from: rangeFrom,
to: rangeTo,
},
global: { stubs },
})
expect(wrapper.exists()).toBe(true)
expect(wrapper.findAll('.deck-card').length).toBeGreaterThan(0)
})
})