UNPKG

@fmidev/smartmet-alert-client

Version:

Web application for viewing weather and flood alerts

375 lines (312 loc) 9.14 kB
import { describe, it, expect, afterEach } from 'vitest' import { mount, VueWrapper } from '@vue/test-utils' import Region from '@/components/Region.vue' import type { RegionWarningItem, WarningsMap, Warning, Theme, Language, } from '@/types' // eslint-disable-next-line @typescript-eslint/no-explicit-any type ComponentInstance = any const mockWarning: Warning = { type: 'wind', id: 'test-warning-1', regions: { 'county.1': true }, covRegions: new Map(), coveragesLarge: [], coveragesSmall: [], effectiveFrom: '2025-10-31T12:00:00Z', effectiveUntil: '2025-11-01T12:00:00Z', effectiveDays: [true, true, false, false, false], validInterval: '31.10.2025 14:00 – 1.11.2025 14:00', validIntervalAriaLabel: '31. lokakuuta 2025 14:00 – 1. marraskuuta 2025 14:00', severity: 3, direction: 270, value: 25, text: '25', info: { fi: 'Kovaa tuulta', sv: 'Hårt blåsväder', en: 'Strong wind', }, link: '', linkText: '', } const mockRegionWarnings: RegionWarningItem[] = [ { type: 'wind', identifiers: ['test-warning-1'], coverage: 100, }, ] const mockWarnings: WarningsMap = { 'test-warning-1': mockWarning, } describe('Region.vue', () => { let wrapper: VueWrapper | null = null afterEach(() => { if (wrapper) { wrapper.unmount() wrapper = null } }) describe('Component mounting', () => { it('should mount with required props', () => { wrapper = mount(Region, { props: { type: 'land', code: 'county.1', name: 'Uusimaa', input: mockRegionWarnings, warnings: mockWarnings, theme: 'light-theme' as Theme, language: 'fi' as Language, }, }) expect(wrapper.exists()).toBe(true) }) it('should mount with minimal props', () => { wrapper = mount(Region, { props: {}, }) expect(wrapper.exists()).toBe(true) }) }) describe('Computed properties', () => { it('should compute identifier from code', () => { wrapper = mount(Region, { props: { code: 'county.1', }, }) expect((wrapper.vm as ComponentInstance).identifier).toBe( 'accordion-item-county.1' ) }) it('should compute regionName from translations', () => { wrapper = mount(Region, { props: { name: 'Uusimaa', language: 'fi' as Language, }, }) expect(typeof (wrapper.vm as ComponentInstance).regionName).toBe('string') }) it('should compute warningsSummary from input and warnings', () => { wrapper = mount(Region, { props: { input: mockRegionWarnings, warnings: mockWarnings, }, }) const summary = (wrapper.vm as ComponentInstance).warningsSummary expect(Array.isArray(summary)).toBe(true) expect(summary.length).toBe(1) }) it('should return empty warningsSummary when no warnings match', () => { wrapper = mount(Region, { props: { input: mockRegionWarnings, warnings: {}, }, }) expect((wrapper.vm as ComponentInstance).warningsSummary).toEqual([]) }) it('should compute reducedWarnings from input and warnings', () => { wrapper = mount(Region, { props: { input: mockRegionWarnings, warnings: mockWarnings, }, }) const reduced = (wrapper.vm as ComponentInstance).reducedWarnings expect(Array.isArray(reduced)).toBe(true) }) it('should compute ariaButton from translations', () => { wrapper = mount(Region, { props: { name: 'Uusimaa', language: 'fi' as Language, }, }) expect(typeof (wrapper.vm as ComponentInstance).ariaButton).toBe('string') }) it('should compute ariaInfo from warnings', () => { wrapper = mount(Region, { props: { input: mockRegionWarnings, warnings: mockWarnings, language: 'fi' as Language, }, }) expect(Array.isArray((wrapper.vm as ComponentInstance).ariaInfo)).toBe( true ) }) }) describe('State management', () => { it('should start with open state as false', () => { wrapper = mount(Region, { props: {}, }) expect((wrapper.vm as ComponentInstance).open).toBe(false) }) it('should toggle open state on onRegionToggle', () => { wrapper = mount(Region, { props: {}, }) expect((wrapper.vm as ComponentInstance).open).toBe(false) ;(wrapper.vm as ComponentInstance).onRegionToggle() expect((wrapper.vm as ComponentInstance).open).toBe(true) ;(wrapper.vm as ComponentInstance).onRegionToggle() expect((wrapper.vm as ComponentInstance).open).toBe(false) }) }) describe('Props handling', () => { it('should accept type prop', () => { wrapper = mount(Region, { props: { type: 'sea', }, }) expect((wrapper.vm as ComponentInstance).type).toBe('sea') }) it('should accept code prop', () => { wrapper = mount(Region, { props: { code: 'county.2', }, }) expect((wrapper.vm as ComponentInstance).code).toBe('county.2') }) it('should accept name prop', () => { wrapper = mount(Region, { props: { name: 'Varsinais-Suomi', }, }) expect((wrapper.vm as ComponentInstance).name).toBe('Varsinais-Suomi') }) it('should accept input prop', () => { wrapper = mount(Region, { props: { input: mockRegionWarnings, }, }) expect((wrapper.vm as ComponentInstance).input).toEqual( mockRegionWarnings ) }) it('should accept warnings prop', () => { wrapper = mount(Region, { props: { warnings: mockWarnings, }, }) expect(wrapper.exists()).toBe(true) }) }) describe('Theme support', () => { it('should accept theme prop', () => { wrapper = mount(Region, { props: { theme: 'dark-theme' as Theme, }, }) expect((wrapper.vm as ComponentInstance).theme).toBe('dark-theme') }) it('should default to light-theme', () => { wrapper = mount(Region, { props: {}, }) expect((wrapper.vm as ComponentInstance).theme).toBe('light-theme') }) }) describe('Language support', () => { it('should support Finnish language', () => { wrapper = mount(Region, { props: { language: 'fi' as Language, }, }) expect(wrapper.exists()).toBe(true) }) it('should support Swedish language', () => { wrapper = mount(Region, { props: { language: 'sv' as Language, }, }) expect(wrapper.exists()).toBe(true) }) it('should support English language', () => { wrapper = mount(Region, { props: { language: 'en' as Language, }, }) expect(wrapper.exists()).toBe(true) }) }) describe('Accessibility', () => { it('should have accordion button with correct aria attributes', () => { wrapper = mount(Region, { props: { code: 'county.1', }, }) const button = wrapper.find('.accordion-trigger') expect(button.attributes('aria-expanded')).toBe('false') expect(button.attributes('aria-controls')).toBe( 'accordion-section-county.1' ) }) it('should update aria-expanded when toggled', async () => { wrapper = mount(Region, { props: { code: 'county.1', }, }) ;(wrapper.vm as ComponentInstance).onRegionToggle() await wrapper.vm.$nextTick() const button = wrapper.find('.accordion-trigger') expect(button.attributes('aria-expanded')).toBe('true') }) it('should have accordion panel with role region', () => { wrapper = mount(Region, { props: { code: 'county.1', }, }) const panel = wrapper.find('.accordion-panel') expect(panel.attributes('role')).toBe('region') }) }) describe('CSS classes', () => { it('should apply collapsed class when closed', () => { wrapper = mount(Region, { props: {}, }) const button = wrapper.find('.accordion-trigger') expect(button.classes()).toContain('collapsed') }) it('should remove collapsed class when open', async () => { wrapper = mount(Region, { props: {}, }) ;(wrapper.vm as ComponentInstance).onRegionToggle() await wrapper.vm.$nextTick() const button = wrapper.find('.accordion-trigger') expect(button.classes()).not.toContain('collapsed') }) it('should apply collapsed class to toggle icon when closed', () => { wrapper = mount(Region, { props: {}, }) const toggle = wrapper.find('.current-warning-toggle') expect(toggle.classes()).toContain('collapsed') }) }) })