All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 25s
- Implemented PendingRewardDialog for handling pending reward requests. - Created RewardConfirmDialog for confirming reward redemption. - Developed TaskConfirmDialog for task confirmation with child name display. test: add unit tests for ChildView and ParentView components - Added comprehensive tests for ChildView including task triggering and SSE event handling. - Implemented tests for ParentView focusing on override modal and SSE event management. test: add ScrollingList component tests - Created tests for ScrollingList to verify item fetching, loading states, and custom item classes. - Included tests for two-step click interactions and edit button display logic. - Moved toward hashed passwords.
209 lines
7.1 KiB
TypeScript
209 lines
7.1 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
import { mount, VueWrapper } from '@vue/test-utils'
|
|
import { nextTick } from 'vue'
|
|
import OverrideEditModal from '../OverrideEditModal.vue'
|
|
|
|
// Mock API functions
|
|
vi.mock('@/common/api', () => ({
|
|
setChildOverride: vi.fn(),
|
|
parseErrorResponse: vi.fn(() => ({ msg: 'Test error', code: 'TEST_ERROR' })),
|
|
}))
|
|
|
|
import { setChildOverride } from '@/common/api'
|
|
|
|
global.alert = vi.fn()
|
|
|
|
describe('OverrideEditModal', () => {
|
|
let wrapper: VueWrapper<any>
|
|
|
|
const defaultProps = {
|
|
isOpen: true,
|
|
childId: 'child-123',
|
|
entityId: 'task-456',
|
|
entityType: 'task' as 'task' | 'reward',
|
|
entityName: 'Test Task',
|
|
defaultValue: 100,
|
|
currentOverride: undefined,
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
afterEach(() => {
|
|
if (wrapper) {
|
|
wrapper.unmount()
|
|
}
|
|
})
|
|
|
|
describe('Modal Display', () => {
|
|
it('renders when isOpen is true', () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
expect(wrapper.find('.modal-backdrop').exists()).toBe(true)
|
|
expect(wrapper.text()).toContain('Test Task')
|
|
})
|
|
|
|
it('does not render when isOpen is false', () => {
|
|
wrapper = mount(OverrideEditModal, { props: { ...defaultProps, isOpen: false } })
|
|
expect(wrapper.find('.modal-backdrop').exists()).toBe(false)
|
|
})
|
|
|
|
it('displays entity information correctly for tasks', () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
expect(wrapper.text()).toContain('Test Task')
|
|
expect(wrapper.text()).toContain('New Points')
|
|
})
|
|
|
|
it('displays entity information correctly for rewards', () => {
|
|
wrapper = mount(OverrideEditModal, {
|
|
props: { ...defaultProps, entityType: 'reward', entityName: 'Test Reward' },
|
|
})
|
|
expect(wrapper.text()).toContain('Test Reward')
|
|
expect(wrapper.text()).toContain('New Cost')
|
|
})
|
|
})
|
|
|
|
describe('Input Validation', () => {
|
|
it('initializes with default value when no override exists', async () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
await nextTick()
|
|
const input = wrapper.find('input[type="number"]')
|
|
expect((input.element as HTMLInputElement).value).toBe('100')
|
|
})
|
|
|
|
it('initializes with current override value when it exists', async () => {
|
|
wrapper = mount(OverrideEditModal, {
|
|
props: { ...defaultProps, currentOverride: 150 },
|
|
})
|
|
await nextTick()
|
|
const input = wrapper.find('input[type="number"]')
|
|
expect((input.element as HTMLInputElement).value).toBe('150')
|
|
})
|
|
|
|
it('validates input within range (0-10000)', async () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
const input = wrapper.find('input[type="number"]')
|
|
const saveButton = wrapper.findAll('button').find((btn) => btn.text() === 'Save')
|
|
|
|
// Valid value
|
|
await input.setValue(5000)
|
|
await nextTick()
|
|
expect(saveButton?.attributes('disabled')).toBeUndefined()
|
|
|
|
// Zero is valid
|
|
await input.setValue(0)
|
|
await nextTick()
|
|
expect(saveButton?.attributes('disabled')).toBeUndefined()
|
|
|
|
// Max is valid
|
|
await input.setValue(10000)
|
|
await nextTick()
|
|
expect(saveButton?.attributes('disabled')).toBeUndefined()
|
|
})
|
|
|
|
it('shows error for values outside range', async () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
const input = wrapper.find('input[type="number"]')
|
|
|
|
// Above max
|
|
await input.setValue(10001)
|
|
await nextTick()
|
|
expect(wrapper.text()).toContain('Value must be between 0 and 10000')
|
|
const saveButton = wrapper.findAll('button').find((btn) => btn.text() === 'Save')
|
|
expect(saveButton?.attributes('disabled')).toBeDefined()
|
|
})
|
|
})
|
|
|
|
describe('User Interactions', () => {
|
|
it('emits close event when Cancel is clicked', async () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
const cancelButton = wrapper.findAll('button').find((btn) => btn.text() === 'Cancel')
|
|
await cancelButton?.trigger('click')
|
|
expect(wrapper.emitted('close')).toBeTruthy()
|
|
})
|
|
|
|
it('emits close event when clicking backdrop', async () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
await wrapper.find('.modal-backdrop').trigger('click')
|
|
expect(wrapper.emitted('close')).toBeTruthy()
|
|
})
|
|
|
|
it('does not close when clicking modal dialog', async () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
await wrapper.find('.modal-dialog').trigger('click')
|
|
expect(wrapper.emitted('close')).toBeFalsy()
|
|
})
|
|
|
|
it('calls API and emits events on successful save', async () => {
|
|
;(setChildOverride as any).mockResolvedValue({ ok: true })
|
|
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
const input = wrapper.find('input[type="number"]')
|
|
await input.setValue(250)
|
|
await nextTick()
|
|
|
|
const saveButton = wrapper.findAll('button').find((btn) => btn.text() === 'Save')
|
|
await saveButton?.trigger('click')
|
|
await nextTick()
|
|
|
|
expect(setChildOverride).toHaveBeenCalledWith('child-123', 'task-456', 'task', 250)
|
|
expect(wrapper.emitted('saved')).toBeTruthy()
|
|
expect(wrapper.emitted('close')).toBeTruthy()
|
|
})
|
|
|
|
it('shows alert on API error', async () => {
|
|
;(setChildOverride as any).mockResolvedValue({ ok: false, status: 400 })
|
|
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
const saveButton = wrapper.findAll('button').find((btn) => btn.text() === 'Save')
|
|
await saveButton?.trigger('click')
|
|
await nextTick()
|
|
|
|
expect(global.alert).toHaveBeenCalledWith('Error: Test error')
|
|
expect(wrapper.emitted('saved')).toBeFalsy()
|
|
})
|
|
|
|
it('does not save when validation fails', async () => {
|
|
wrapper = mount(OverrideEditModal, { props: defaultProps })
|
|
const input = wrapper.find('input[type="number"]')
|
|
await input.setValue(20000)
|
|
await nextTick()
|
|
|
|
const saveButton = wrapper.findAll('button').find((btn) => btn.text() === 'Save')
|
|
await saveButton?.trigger('click')
|
|
await nextTick()
|
|
|
|
expect(setChildOverride).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('Modal State Updates', () => {
|
|
it('reinitializes value when modal reopens', async () => {
|
|
wrapper = mount(OverrideEditModal, { props: { ...defaultProps, isOpen: false } })
|
|
await nextTick()
|
|
|
|
await wrapper.setProps({ isOpen: true })
|
|
await nextTick()
|
|
|
|
const input = wrapper.find('input[type="number"]')
|
|
expect((input.element as HTMLInputElement).value).toBe('100')
|
|
})
|
|
|
|
it('uses updated currentOverride when modal reopens', async () => {
|
|
wrapper = mount(OverrideEditModal, {
|
|
props: { ...defaultProps, isOpen: true, currentOverride: 200 },
|
|
})
|
|
await nextTick()
|
|
|
|
await wrapper.setProps({ isOpen: false })
|
|
await nextTick()
|
|
|
|
await wrapper.setProps({ isOpen: true, currentOverride: 300 })
|
|
await nextTick()
|
|
|
|
const input = wrapper.find('input[type="number"]')
|
|
expect((input.element as HTMLInputElement).value).toBe('300')
|
|
})
|
|
})
|
|
})
|