fix: update ParentView tests to use mount options and improve mock implementations
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m18s

This commit is contained in:
2026-03-24 16:38:06 -04:00
parent e9f4343426
commit ac29ee9bd0
2 changed files with 81 additions and 24 deletions

View File

@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { mount, VueWrapper } from '@vue/test-utils' import { mount, VueWrapper } from '@vue/test-utils'
import { nextTick } from 'vue' import { nextTick, defineComponent } from 'vue'
import ParentView from '../ParentView.vue' import ParentView from '../ParentView.vue'
import { eventBus } from '@/common/eventBus' import { eventBus } from '@/common/eventBus'
@@ -8,6 +8,7 @@ import { eventBus } from '@/common/eventBus'
vi.mock('vue-router', () => ({ vi.mock('vue-router', () => ({
useRoute: vi.fn(() => ({ useRoute: vi.fn(() => ({
params: { id: 'child-123' }, params: { id: 'child-123' },
query: {},
})), })),
useRouter: vi.fn(() => ({ useRouter: vi.fn(() => ({
push: vi.fn(), push: vi.fn(),
@@ -17,6 +18,15 @@ vi.mock('vue-router', () => ({
vi.mock('@/common/api', () => ({ vi.mock('@/common/api', () => ({
setChildOverride: vi.fn(), setChildOverride: vi.fn(),
parseErrorResponse: vi.fn(() => ({ msg: 'Test error', code: 'TEST_ERROR' })), parseErrorResponse: vi.fn(() => ({ msg: 'Test error', code: 'TEST_ERROR' })),
extendChoreTime: vi.fn(),
approveChore: vi.fn(),
rejectChore: vi.fn(),
resetChore: vi.fn(),
}))
vi.mock('@/common/imageCache', () => ({
getCachedImageUrl: vi.fn(() => Promise.resolve('/mock-image.png')),
revokeAllImageUrls: vi.fn(),
})) }))
global.fetch = vi.fn() global.fetch = vi.fn()
@@ -24,6 +34,51 @@ global.alert = vi.fn()
import { setChildOverride, parseErrorResponse } from '@/common/api' import { setChildOverride, parseErrorResponse } from '@/common/api'
// Stub for ScrollingList that exposes the same interface
const ScrollingListStub = defineComponent({
name: 'ScrollingList',
props: [
'title',
'fetchBaseUrl',
'ids',
'itemKey',
'imageField',
'imageFields',
'enableEdit',
'childId',
'readyItemId',
'isParentAuthenticated',
'filterFn',
'sortFn',
'getItemClass',
],
emits: ['trigger-item', 'edit-item', 'item-ready'],
setup(_, { expose }) {
const refresh = vi.fn()
const scrollToItem = vi.fn()
const centerItem = vi.fn()
expose({ refresh, items: [], centerItem, scrollToItem })
return { refresh, scrollToItem, centerItem }
},
template: '<div class="scrolling-list-stub"><slot /></div>',
})
const mountOptions = {
global: {
stubs: {
ScrollingList: ScrollingListStub,
ChildDetailCard: { template: '<div class="child-detail-stub" />' },
StatusMessage: { template: '<div class="status-stub" />' },
ModalDialog: { template: '<div class="modal-stub"><slot /></div>' },
ScheduleModal: { template: '<div class="schedule-stub" />' },
PendingRewardDialog: { template: '<div class="pending-reward-stub" />' },
TaskConfirmDialog: { template: '<div class="task-confirm-stub" />' },
RewardConfirmDialog: { template: '<div class="reward-confirm-stub" />' },
ChoreApproveDialog: { template: '<div class="chore-approve-stub" />' },
},
},
}
describe('ParentView', () => { describe('ParentView', () => {
let wrapper: VueWrapper<any> let wrapper: VueWrapper<any>
@@ -91,7 +146,7 @@ describe('ParentView', () => {
describe('Component Mounting', () => { describe('Component Mounting', () => {
it('loads and displays child data on mount', async () => { it('loads and displays child data on mount', async () => {
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
await new Promise((resolve) => setTimeout(resolve, 50)) await new Promise((resolve) => setTimeout(resolve, 50))
@@ -100,7 +155,7 @@ describe('ParentView', () => {
it('registers SSE event listeners on mount', async () => { it('registers SSE event listeners on mount', async () => {
const onSpy = vi.spyOn(eventBus, 'on') const onSpy = vi.spyOn(eventBus, 'on')
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
expect(onSpy).toHaveBeenCalledWith('child_task_triggered', expect.any(Function)) expect(onSpy).toHaveBeenCalledWith('child_task_triggered', expect.any(Function))
@@ -111,7 +166,7 @@ describe('ParentView', () => {
it('unregisters SSE event listeners on unmount', async () => { it('unregisters SSE event listeners on unmount', async () => {
const offSpy = vi.spyOn(eventBus, 'off') const offSpy = vi.spyOn(eventBus, 'off')
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
wrapper.unmount() wrapper.unmount()
@@ -123,7 +178,7 @@ describe('ParentView', () => {
describe('Override Modal', () => { describe('Override Modal', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
await new Promise((resolve) => setTimeout(resolve, 50)) await new Promise((resolve) => setTimeout(resolve, 50))
}) })
@@ -184,7 +239,7 @@ describe('ParentView', () => {
describe('Save Override', () => { describe('Save Override', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
await new Promise((resolve) => setTimeout(resolve, 50)) await new Promise((resolve) => setTimeout(resolve, 50))
}) })
@@ -229,7 +284,7 @@ describe('ParentView', () => {
describe('SSE Event Handlers', () => { describe('SSE Event Handlers', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
await new Promise((resolve) => setTimeout(resolve, 50)) await new Promise((resolve) => setTimeout(resolve, 50))
}) })
@@ -248,13 +303,15 @@ describe('ParentView', () => {
}) })
it('handles child_override_set event and refreshes appropriate lists', async () => { it('handles child_override_set event and refreshes appropriate lists', async () => {
const mockChoreRefresh = vi.fn() const mockChoreRefresh = vi.fn().mockResolvedValue(undefined)
const mockPenaltyRefresh = vi.fn() const mockPenaltyRefresh = vi.fn().mockResolvedValue(undefined)
const mockRewardRefresh = vi.fn() const mockRewardRefresh = vi.fn().mockResolvedValue(undefined)
const mockKindnessRefresh = vi.fn().mockResolvedValue(undefined)
wrapper.vm.childChoreListRef = { refresh: mockChoreRefresh } wrapper.vm.childChoreListRef = { refresh: mockChoreRefresh, scrollToItem: vi.fn() }
wrapper.vm.childPenaltyListRef = { refresh: mockPenaltyRefresh } wrapper.vm.childKindnessListRef = { refresh: mockKindnessRefresh, scrollToItem: vi.fn() }
wrapper.vm.childRewardListRef = { refresh: mockRewardRefresh } wrapper.vm.childPenaltyListRef = { refresh: mockPenaltyRefresh, scrollToItem: vi.fn() }
wrapper.vm.childRewardListRef = { refresh: mockRewardRefresh, scrollToItem: vi.fn() }
// Test task override // Test task override
wrapper.vm.handleOverrideSet({ wrapper.vm.handleOverrideSet({
@@ -318,7 +375,7 @@ describe('ParentView', () => {
describe('Ready Item State Management', () => { describe('Ready Item State Management', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
await new Promise((resolve) => setTimeout(resolve, 50)) await new Promise((resolve) => setTimeout(resolve, 50))
}) })
@@ -339,7 +396,7 @@ describe('ParentView', () => {
describe('Penalty Display', () => { describe('Penalty Display', () => {
it('displays penalty values as negative in template', async () => { it('displays penalty values as negative in template', async () => {
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
// The template should show -custom_value or -points for penalties // The template should show -custom_value or -points for penalties
@@ -361,7 +418,7 @@ describe('ParentView', () => {
} }
beforeEach(async () => { beforeEach(async () => {
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
await new Promise((resolve) => setTimeout(resolve, 50)) await new Promise((resolve) => setTimeout(resolve, 50))
}) })
@@ -453,7 +510,7 @@ describe('ParentView', () => {
describe('Chore Card Selection (selectedChoreId)', () => { describe('Chore Card Selection (selectedChoreId)', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper = mount(ParentView) wrapper = mount(ParentView, mountOptions)
await nextTick() await nextTick()
await new Promise((resolve) => setTimeout(resolve, 50)) await new Promise((resolve) => setTimeout(resolve, 50))
}) })

View File

@@ -89,13 +89,6 @@ async function refresh() {
loading.value = false loading.value = false
} }
defineExpose({
refresh,
items,
centerItem,
scrollToItem,
})
const scrollToItem = async (itemId: string) => { const scrollToItem = async (itemId: string) => {
await nextTick() await nextTick()
const card = itemRefs.value[itemId] const card = itemRefs.value[itemId]
@@ -123,6 +116,13 @@ const centerItem = async (itemId: string) => {
} }
} }
defineExpose({
refresh,
items,
centerItem,
scrollToItem,
})
const handleClicked = async (item: any) => { const handleClicked = async (item: any) => {
await nextTick() await nextTick()
const wrapper = scrollWrapper.value const wrapper = scrollWrapper.value