From b12137c4cea10767d77bfd78ef39df4eb8475726 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Mon, 15 Jun 2026 20:10:00 +0200 Subject: [PATCH] test(10-04): add failing tests for toast store and ToastContainer - 6 toast store tests: show/dismiss/auto-dismiss/duration=0/stacking - 4 ToastContainer tests: empty/render count/click dismiss/accent class - uses vi.useFakeTimers() for timer-based assertions - Teleport and AppIcon stubbed for DOM-safe mounting --- .../ui/__tests__/ToastContainer.test.js | 57 +++++++++++++++++ frontend/src/stores/__tests__/toast.test.js | 64 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 frontend/src/components/ui/__tests__/ToastContainer.test.js create mode 100644 frontend/src/stores/__tests__/toast.test.js diff --git a/frontend/src/components/ui/__tests__/ToastContainer.test.js b/frontend/src/components/ui/__tests__/ToastContainer.test.js new file mode 100644 index 0000000..65fb5db --- /dev/null +++ b/frontend/src/components/ui/__tests__/ToastContainer.test.js @@ -0,0 +1,57 @@ +import { describe, it, expect, beforeEach } from 'vitest' +import { mount } from '@vue/test-utils' +import { setActivePinia, createPinia } from 'pinia' +import { useToastStore } from '../../../stores/toast.js' +import ToastContainer from '../ToastContainer.vue' + +beforeEach(() => { + setActivePinia(createPinia()) +}) + +describe('ToastContainer', () => { + it('renders nothing when store.toasts is empty', () => { + const wrapper = mount(ToastContainer, { + global: { + stubs: { AppIcon: true, Teleport: true }, + }, + }) + expect(wrapper.find('[data-test="toast"]').exists()).toBe(false) + }) + + it('renders one element per toast', () => { + const store = useToastStore() + store.show('First', 'success', 0) + store.show('Second', 'error', 0) + const wrapper = mount(ToastContainer, { + global: { + stubs: { AppIcon: true, Teleport: true }, + }, + }) + expect(wrapper.findAll('[data-test="toast"]').length).toBe(2) + }) + + it('clicking a toast calls dismiss(id)', async () => { + const store = useToastStore() + store.show('Click me', 'info', 0) + const wrapper = mount(ToastContainer, { + global: { + stubs: { AppIcon: true, Teleport: true }, + }, + }) + await wrapper.find('[data-test="toast"]').trigger('click') + expect(store.toasts.length).toBe(0) + }) + + it('accent class matches type (success -> bg-green-500)', () => { + const store = useToastStore() + store.show('Success toast', 'success', 0) + const wrapper = mount(ToastContainer, { + global: { + stubs: { AppIcon: true, Teleport: true }, + }, + }) + const toast = wrapper.find('[data-test="toast"]') + const accentBar = toast.find('.bg-green-500') + expect(accentBar.exists()).toBe(true) + }) +}) diff --git a/frontend/src/stores/__tests__/toast.test.js b/frontend/src/stores/__tests__/toast.test.js new file mode 100644 index 0000000..e661692 --- /dev/null +++ b/frontend/src/stores/__tests__/toast.test.js @@ -0,0 +1,64 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { setActivePinia, createPinia } from 'pinia' +import { useToastStore } from '../toast.js' + +beforeEach(() => { + setActivePinia(createPinia()) +}) + +afterEach(() => { + vi.useRealTimers() +}) + +describe('useToastStore', () => { + it('show(msg, type, duration) appends a toast with the given fields', () => { + const store = useToastStore() + store.show('Hi', 'success', 4000) + expect(store.toasts.length).toBe(1) + expect(store.toasts[0].message).toBe('Hi') + expect(store.toasts[0].type).toBe('success') + expect(store.toasts[0].duration).toBe(4000) + }) + + it('show defaults type to success and duration to 4000', () => { + const store = useToastStore() + store.show('Hi') + expect(store.toasts[0].type).toBe('success') + expect(store.toasts[0].duration).toBe(4000) + }) + + it('auto-dismisses after duration using fake timers', () => { + vi.useFakeTimers() + const store = useToastStore() + store.show('Hi', 'success', 4000) + expect(store.toasts.length).toBe(1) + vi.advanceTimersByTime(4000) + expect(store.toasts.length).toBe(0) + }) + + it('dismiss(id) removes the toast immediately', () => { + const store = useToastStore() + store.show('Hi', 'success', 0) + const id = store.toasts[0].id + store.dismiss(id) + expect(store.toasts.length).toBe(0) + }) + + it('duration=0 disables auto-dismiss', () => { + vi.useFakeTimers() + const store = useToastStore() + store.show('Hi', 'success', 0) + vi.advanceTimersByTime(10000) + expect(store.toasts.length).toBe(1) + }) + + it('multiple toasts stack with unique ids', () => { + const store = useToastStore() + store.show('A') + store.show('B') + store.show('C') + expect(store.toasts.length).toBe(3) + const ids = store.toasts.map(t => t.id) + expect(new Set(ids).size).toBe(3) + }) +})