chorus/frontend/tests/ItemModal.spec.js
pyr0ball 73fb67b3bf fix: cog re-registration, thread NotFound, follow-up date, and error/empty states
I1: move cog registration + tree.sync into ChorusBot.setup_hook (called once
before gateway connect) instead of on_ready (fires on every reconnect),
preventing duplicate cogs/listeners/poll loops after a gateway RESUME.

I2 (email_ingest.py, addressed alongside I1 for IMAP stability): none here,
handled separately.

I3/I4: TriageList.vue now renders a follow_up_date column so it actually
surfaces back to Donna. App.vue adds a "Show completed" checkbox bound to
includeDone, matching what docs/smoke-test.md step 7 already describes.

I5: add frontend/tests/ItemModal.spec.js covering stagesForType() options
for donation/other/unset types and the type-change stage reset regression.

I6: App.vue wraps loadItems/openItem in try/catch with a calm (no-panic)
error message, and shows calm empty-state copy when there are zero items.

Also fixes thread_ingest.py: standalone threads not started from a message
raise discord.NotFound on fetch_message; now caught and returns early.
2026-07-13 15:01:32 -07:00

56 lines
2 KiB
JavaScript

import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import ItemModal from '../src/components/ItemModal.vue'
vi.mock('../src/api.js', () => ({
updateItem: vi.fn(async (id, fields) => ({ id, ...fields })),
}))
function makeItem(overrides = {}) {
return {
id: 1,
modality: 'bh_email',
sender_id: 'jane@example.com',
raw_content: 'I have some books to donate.',
type: null,
stage: 'new',
notes: '',
follow_up_date: '',
...overrides,
}
}
describe('ItemModal', () => {
it('offers the donation stages when item.type is donation', () => {
const wrapper = mount(ItemModal, { props: { item: makeItem({ type: 'donation', stage: 'new' }) } })
const options = wrapper.findAll('select')[1].findAll('option').map((o) => o.element.value)
expect(options).toEqual([
'new', 'replied', 'pickup_scheduled', 'picked_up', 'logged_in_bookmark',
])
})
it('offers only new/done stages when item.type is other', () => {
const wrapper = mount(ItemModal, { props: { item: makeItem({ type: 'other', stage: 'new' }) } })
const options = wrapper.findAll('select')[1].findAll('option').map((o) => o.element.value)
expect(options).toEqual(['new', 'done'])
})
it('offers only the new stage when item.type is unset/null', () => {
const wrapper = mount(ItemModal, { props: { item: makeItem({ type: null, stage: 'new' }) } })
const options = wrapper.findAll('select')[1].findAll('option').map((o) => o.element.value)
expect(options).toEqual(['new'])
})
it('resets stage to new when type changes away from donation while stage is pickup_scheduled', async () => {
const wrapper = mount(ItemModal, {
props: { item: makeItem({ type: 'donation', stage: 'pickup_scheduled' }) },
})
const stageSelect = wrapper.findAll('select')[1]
expect(stageSelect.element.value).toBe('pickup_scheduled')
const typeSelect = wrapper.findAll('select')[0]
await typeSelect.setValue('other')
expect(wrapper.findAll('select')[1].element.value).toBe('new')
})
})