@fmidev/smartmet-alert-client 4.4.19 → 4.7.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +2 -14
- package/.github/workflows/test.yaml +26 -0
- package/.nvmrc +1 -0
- package/dist/index.html +5 -0
- package/dist/index.js +105 -135
- package/dist/index.mjs +112 -135
- package/dist/locale-en-DCEKDw5G.js +8 -0
- package/dist/locale-fi-DPiOM1rB.js +8 -0
- package/dist/locale-sv-B0FlbgEF.js +8 -0
- package/dist/vendor-Cfkkvdz7.js +21 -0
- package/dist/vue/index.mjs +15245 -0
- package/dist/vue/style.css +1 -0
- package/dist/xml-parser-BiNO9kc-.js +13 -0
- package/package.json +60 -24
- package/src/AlertClientVue.vue +170 -0
- package/src/App.vue +55 -205
- package/src/assets/img/ui/arrow-down.svg +4 -11
- package/src/assets/img/ui/arrow-up.svg +4 -11
- package/src/assets/img/ui/clear.svg +7 -21
- package/src/assets/img/ui/close.svg +4 -15
- package/src/assets/img/ui/toggle-selected.svg +5 -6
- package/src/assets/img/ui/toggle-unselected.svg +5 -6
- package/src/assets/img/warning/cold-weather.svg +3 -6
- package/src/assets/img/warning/flood-level-3.svg +4 -7
- package/src/assets/img/warning/forest-fire-weather.svg +2 -6
- package/src/assets/img/warning/grass-fire-weather.svg +2 -6
- package/src/assets/img/warning/hot-weather.svg +3 -6
- package/src/assets/img/warning/pedestrian-safety.svg +3 -7
- package/src/assets/img/warning/rain.svg +2 -7
- package/src/assets/img/warning/sea-icing.svg +2 -6
- package/src/assets/img/warning/sea-thunder-storm.svg +2 -5
- package/src/assets/img/warning/sea-water-height-high-water.svg +3 -8
- package/src/assets/img/warning/sea-water-height-shallow-water.svg +3 -7
- package/src/assets/img/warning/sea-wave-height.svg +4 -7
- package/src/assets/img/warning/sea-wind-legend.svg +2 -5
- package/src/assets/img/warning/sea-wind.svg +2 -5
- package/src/assets/img/warning/several.svg +2 -5
- package/src/assets/img/warning/thunder-storm.svg +2 -5
- package/src/assets/img/warning/traffic-weather.svg +2 -6
- package/src/assets/img/warning/uv-note.svg +2 -6
- package/src/assets/img/warning/wind.svg +2 -5
- package/src/components/AlertClient.vue +41 -19
- package/src/components/CollapsiblePanel.vue +284 -0
- package/src/components/DayLarge.vue +12 -7
- package/src/components/DaySmall.vue +16 -6
- package/src/components/Days.vue +76 -51
- package/src/components/DescriptionWarning.vue +15 -8
- package/src/components/GrayScaleToggle.vue +11 -6
- package/src/components/Legend.vue +36 -248
- package/src/components/MapLarge.vue +41 -42
- package/src/components/MapSmall.vue +44 -28
- package/src/components/PopupRow.vue +6 -3
- package/src/components/Region.vue +30 -15
- package/src/components/RegionWarning.vue +6 -5
- package/src/components/Regions.vue +50 -19
- package/src/components/Warning.vue +18 -10
- package/src/components/Warnings.vue +36 -21
- package/src/main.js +1 -0
- package/src/mixins/alertClientCore.js +210 -0
- package/src/mixins/config.js +262 -256
- package/src/mixins/utils.js +40 -26
- package/src/plugins/index.js +1 -1
- package/src/scss/_utilities.scss +193 -0
- package/src/scss/constants.scss +2 -1
- package/src/scss/warningImages.scss +8 -3
- package/src/vue.js +41 -0
- package/svgo.config.js +45 -0
- package/tests/README.md +430 -0
- package/tests/fixtures/mockWarningData.js +135 -0
- package/tests/integration/warning-flow.spec.js +452 -0
- package/tests/setup.js +41 -0
- package/tests/unit/components/AlertClient.spec.js +734 -0
- package/tests/unit/components/DayLarge.spec.js +281 -0
- package/tests/unit/components/DaySmall.spec.js +278 -0
- package/tests/unit/components/Days.spec.js +565 -0
- package/tests/unit/components/DescriptionWarning.spec.js +432 -0
- package/tests/unit/components/GrayScaleToggle.spec.js +311 -0
- package/tests/unit/components/Legend.spec.js +223 -0
- package/tests/unit/components/MapLarge.spec.js +276 -0
- package/tests/unit/components/MapSmall.spec.js +226 -0
- package/tests/unit/components/PopupRow.spec.js +261 -0
- package/tests/unit/components/Region.spec.js +430 -0
- package/tests/unit/components/RegionWarning.snapshot.spec.js +73 -0
- package/tests/unit/components/RegionWarning.spec.js +408 -0
- package/tests/unit/components/Regions.spec.js +335 -0
- package/tests/unit/components/Warning.snapshot.spec.js +107 -0
- package/tests/unit/components/Warning.spec.js +472 -0
- package/tests/unit/components/Warnings.spec.js +329 -0
- package/tests/unit/components/__snapshots__/RegionWarning.snapshot.spec.js.snap +21 -0
- package/tests/unit/components/__snapshots__/Warning.snapshot.spec.js.snap +199 -0
- package/tests/unit/mixins/config.spec.js +269 -0
- package/tests/unit/mixins/i18n.spec.js +115 -0
- package/tests/unit/mixins/keycodes.spec.js +37 -0
- package/tests/unit/mixins/utils.spec.js +624 -0
- package/vite.config.js +96 -26
- package/vitest.config.js +40 -0
- package/dist/index.mjs.map +0 -1
- package/dist/index.relative.html +0 -19
- package/dist/index.start.html +0 -20
- package/playwright.config.ts +0 -18
- package/public/index.relative.html +0 -19
- package/public/index.start.html +0 -20
- package/src/mixins/panzoom.js +0 -900
- package/test/snapshot.test.ts +0 -126
- package/vitest.config.ts +0 -6
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { mockWarningsData } from '../fixtures/mockWarningData'
|
|
4
|
+
import utils from '@/mixins/utils'
|
|
5
|
+
import config from '@/mixins/config'
|
|
6
|
+
import geojsonsvg from '@/mixins/geojsonsvg'
|
|
7
|
+
import i18n from '@/mixins/i18n'
|
|
8
|
+
|
|
9
|
+
const TestComponent = {
|
|
10
|
+
mixins: [utils, config, geojsonsvg, i18n],
|
|
11
|
+
template: '<div></div>',
|
|
12
|
+
props: {
|
|
13
|
+
currentTime: {
|
|
14
|
+
type: Number,
|
|
15
|
+
default: Date.now(),
|
|
16
|
+
},
|
|
17
|
+
geometryId: {
|
|
18
|
+
type: Number,
|
|
19
|
+
default: 2021,
|
|
20
|
+
},
|
|
21
|
+
language: {
|
|
22
|
+
type: String,
|
|
23
|
+
default: 'fi',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
data() {
|
|
27
|
+
return {
|
|
28
|
+
timeOffset: 0,
|
|
29
|
+
updatedAt: null,
|
|
30
|
+
warnings: {},
|
|
31
|
+
coverageRegions: {},
|
|
32
|
+
coverageWarnings: [],
|
|
33
|
+
index: 0,
|
|
34
|
+
size: 'Large',
|
|
35
|
+
strokeWidth: 1,
|
|
36
|
+
theme: 'light-theme',
|
|
37
|
+
visibleWarnings: [],
|
|
38
|
+
startFrom: '',
|
|
39
|
+
dailyWarningTypes: [],
|
|
40
|
+
staticDays: true,
|
|
41
|
+
errors: [],
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
methods: {
|
|
45
|
+
handleError(error) {
|
|
46
|
+
if (!this.errors.includes(error)) {
|
|
47
|
+
this.errors.push(error)
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
onDataError() {
|
|
51
|
+
// Mock error handler
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
describe('Warning data flow integration', () => {
|
|
57
|
+
let wrapper
|
|
58
|
+
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
wrapper = mount(TestComponent, {
|
|
61
|
+
props: {
|
|
62
|
+
currentTime: new Date('2025-10-31T12:00:00Z').getTime(),
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('Complete warning processing', () => {
|
|
68
|
+
it('should process full warning dataset', () => {
|
|
69
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
70
|
+
|
|
71
|
+
expect(result).toHaveProperty('warnings')
|
|
72
|
+
expect(result).toHaveProperty('days')
|
|
73
|
+
expect(result).toHaveProperty('regions')
|
|
74
|
+
expect(result).toHaveProperty('parents')
|
|
75
|
+
expect(result).toHaveProperty('legend')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('should create 5 days from warnings', () => {
|
|
79
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
80
|
+
|
|
81
|
+
expect(result.days).toHaveLength(5)
|
|
82
|
+
result.days.forEach((day) => {
|
|
83
|
+
expect(day).toHaveProperty('weekdayName')
|
|
84
|
+
expect(day).toHaveProperty('day')
|
|
85
|
+
expect(day).toHaveProperty('month')
|
|
86
|
+
expect(day).toHaveProperty('year')
|
|
87
|
+
expect(day).toHaveProperty('severity')
|
|
88
|
+
expect(day).toHaveProperty('updatedDate')
|
|
89
|
+
expect(day).toHaveProperty('updatedTime')
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should parse weather warnings correctly', () => {
|
|
94
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
95
|
+
|
|
96
|
+
const windWarning = result.warnings['test-warning-wind-1']
|
|
97
|
+
expect(windWarning).toBeDefined()
|
|
98
|
+
expect(windWarning.type).toBe('wind')
|
|
99
|
+
expect(windWarning.severity).toBe(3)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should parse flood warnings correctly', () => {
|
|
103
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
104
|
+
|
|
105
|
+
const floodWarning = result.warnings['test-warning-flood-1']
|
|
106
|
+
expect(floodWarning).toBeDefined()
|
|
107
|
+
expect(floodWarning.type).toBe('floodLevel')
|
|
108
|
+
expect(floodWarning.severity).toBe(3)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('should create legend sorted by severity', () => {
|
|
112
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
113
|
+
|
|
114
|
+
expect(result.legend.length).toBeGreaterThan(0)
|
|
115
|
+
|
|
116
|
+
// Check that legend is sorted by severity (descending)
|
|
117
|
+
for (let i = 0; i < result.legend.length - 1; i++) {
|
|
118
|
+
expect(result.legend[i].severity).toBeGreaterThanOrEqual(
|
|
119
|
+
result.legend[i + 1].severity
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should create regions with warnings', () => {
|
|
125
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
126
|
+
|
|
127
|
+
expect(result.regions).toHaveLength(5)
|
|
128
|
+
result.regions.forEach((day) => {
|
|
129
|
+
expect(day).toHaveProperty('land')
|
|
130
|
+
expect(day).toHaveProperty('sea')
|
|
131
|
+
expect(Array.isArray(day.land)).toBe(true)
|
|
132
|
+
expect(Array.isArray(day.sea)).toBe(true)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should handle missing weather data gracefully', () => {
|
|
137
|
+
const incompleteData = {
|
|
138
|
+
weather_update_time: mockWarningsData.weather_update_time,
|
|
139
|
+
flood_update_time: mockWarningsData.flood_update_time,
|
|
140
|
+
// Missing weather and flood warnings
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const result = wrapper.vm.handleMapWarnings(incompleteData)
|
|
144
|
+
expect(result).toBeDefined()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('should set updatedAt timestamp', () => {
|
|
148
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
149
|
+
|
|
150
|
+
expect(wrapper.vm.updatedAt).toBeDefined()
|
|
151
|
+
expect(typeof wrapper.vm.updatedAt).toBe('number')
|
|
152
|
+
expect(wrapper.vm.updatedAt).toBeGreaterThan(0)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('should filter out invalid warnings', () => {
|
|
156
|
+
const dataWithInvalid = {
|
|
157
|
+
...mockWarningsData,
|
|
158
|
+
weather_finland_active_all: {
|
|
159
|
+
features: [
|
|
160
|
+
...mockWarningsData.weather_finland_active_all.features,
|
|
161
|
+
{
|
|
162
|
+
type: 'Feature',
|
|
163
|
+
properties: {
|
|
164
|
+
identifier: 'invalid-warning',
|
|
165
|
+
warning_context: 'wind',
|
|
166
|
+
severity: 'level-1', // Invalid for wind
|
|
167
|
+
effective_from: '2025-10-31T12:00:00Z',
|
|
168
|
+
effective_until: '2025-11-01T12:00:00Z',
|
|
169
|
+
reference: 'fi-warning#county.1',
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const result = wrapper.vm.handleMapWarnings(dataWithInvalid)
|
|
177
|
+
|
|
178
|
+
expect(result.warnings).not.toHaveProperty('invalid-warning')
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
describe('Region hierarchy', () => {
|
|
183
|
+
it('should handle parent-child relationships', () => {
|
|
184
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
185
|
+
|
|
186
|
+
// Check that parents object is created
|
|
187
|
+
expect(result.parents).toBeDefined()
|
|
188
|
+
expect(typeof result.parents).toBe('object')
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('Coverage handling', () => {
|
|
193
|
+
it('should process coverage data when present', () => {
|
|
194
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
195
|
+
|
|
196
|
+
const coverageWarning = result.warnings['test-warning-coverage-1']
|
|
197
|
+
if (coverageWarning) {
|
|
198
|
+
expect(coverageWarning.covRegions).toBeInstanceOf(Map)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
describe('Multi-language support', () => {
|
|
204
|
+
it('should include info in all languages for weather warnings', () => {
|
|
205
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
206
|
+
|
|
207
|
+
const windWarning = result.warnings['test-warning-wind-1']
|
|
208
|
+
expect(windWarning.info).toHaveProperty('fi')
|
|
209
|
+
expect(windWarning.info).toHaveProperty('sv')
|
|
210
|
+
expect(windWarning.info).toHaveProperty('en')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('should decode HTML entities in warning info', () => {
|
|
214
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
215
|
+
|
|
216
|
+
const windWarning = result.warnings['test-warning-wind-1']
|
|
217
|
+
// HTML entities should be decoded by he.decode
|
|
218
|
+
expect(windWarning.info.fi).not.toContain('&')
|
|
219
|
+
expect(windWarning.info.fi).not.toContain('<')
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
describe('Time calculations', () => {
|
|
224
|
+
it('should calculate effective days correctly', () => {
|
|
225
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
226
|
+
|
|
227
|
+
const windWarning = result.warnings['test-warning-wind-1']
|
|
228
|
+
expect(windWarning.effectiveDays).toHaveLength(5)
|
|
229
|
+
expect(windWarning.effectiveDays.some((day) => day === true)).toBe(true)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('should format valid intervals correctly', () => {
|
|
233
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
234
|
+
|
|
235
|
+
const windWarning = result.warnings['test-warning-wind-1']
|
|
236
|
+
expect(windWarning.validInterval).toContain('–')
|
|
237
|
+
expect(windWarning.validInterval).toMatch(/\d{1,2}\.\d{1,2}\./)
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
describe('Severity calculations', () => {
|
|
242
|
+
it('should calculate day severities from warnings', () => {
|
|
243
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
244
|
+
|
|
245
|
+
const firstDay = result.days[0]
|
|
246
|
+
// Should have severity based on active warnings
|
|
247
|
+
expect(typeof firstDay.severity).toBe('number')
|
|
248
|
+
expect(firstDay.severity).toBeGreaterThanOrEqual(0)
|
|
249
|
+
expect(firstDay.severity).toBeLessThanOrEqual(4)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('should show highest severity per day', () => {
|
|
253
|
+
const result = wrapper.vm.handleMapWarnings(mockWarningsData)
|
|
254
|
+
|
|
255
|
+
// Check that severity is the max of all warnings for that day
|
|
256
|
+
result.days.forEach((day, dayIndex) => {
|
|
257
|
+
const warningsForDay = Object.values(result.warnings).filter(
|
|
258
|
+
(warning) => warning.effectiveDays[dayIndex]
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if (warningsForDay.length > 0) {
|
|
262
|
+
const maxSeverity = Math.max(...warningsForDay.map((w) => w.severity))
|
|
263
|
+
expect(day.severity).toBe(maxSeverity)
|
|
264
|
+
} else {
|
|
265
|
+
expect(day.severity).toBe(0)
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
describe('Edge cases and error handling', () => {
|
|
272
|
+
it('should handle warnings with some missing properties', () => {
|
|
273
|
+
const incompleteData = {
|
|
274
|
+
weather_update_time: '2025-10-31T12:00:00Z',
|
|
275
|
+
weather_finland_active_all: {
|
|
276
|
+
features: [
|
|
277
|
+
{
|
|
278
|
+
type: 'Feature',
|
|
279
|
+
properties: {
|
|
280
|
+
identifier: 'incomplete-warning',
|
|
281
|
+
warning_context: 'wind',
|
|
282
|
+
severity: 'level-2',
|
|
283
|
+
effective_from: '2025-10-31T12:00:00Z',
|
|
284
|
+
effective_until: '2025-11-01T12:00:00Z',
|
|
285
|
+
reference: 'fi-warning#county.1',
|
|
286
|
+
// Missing descriptions, but has essential properties
|
|
287
|
+
},
|
|
288
|
+
geometry: {
|
|
289
|
+
type: 'Point',
|
|
290
|
+
coordinates: [25.0, 60.0],
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const result = wrapper.vm.handleMapWarnings(incompleteData)
|
|
298
|
+
|
|
299
|
+
// Should process the warning even with missing descriptions
|
|
300
|
+
expect(result).toBeDefined()
|
|
301
|
+
expect(result.warnings).toBeDefined()
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
it('should handle malformed timestamps', () => {
|
|
305
|
+
const badTimestampData = {
|
|
306
|
+
weather_update_time: 'invalid-timestamp',
|
|
307
|
+
flood_update_time: 'also-invalid',
|
|
308
|
+
weather_finland_active_all: { features: [] },
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const result = wrapper.vm.handleMapWarnings(badTimestampData)
|
|
312
|
+
expect(result).toBeDefined()
|
|
313
|
+
expect(result.days).toHaveLength(5)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it('should handle circular references in region hierarchy', () => {
|
|
317
|
+
const dataWithCircular = {
|
|
318
|
+
...mockWarningsData,
|
|
319
|
+
weather_finland_active_all: {
|
|
320
|
+
features: mockWarningsData.weather_finland_active_all.features.map(
|
|
321
|
+
(f) => ({
|
|
322
|
+
...f,
|
|
323
|
+
properties: {
|
|
324
|
+
...f.properties,
|
|
325
|
+
// Create potential circular reference
|
|
326
|
+
reference: f.properties.identifier,
|
|
327
|
+
},
|
|
328
|
+
})
|
|
329
|
+
),
|
|
330
|
+
},
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
expect(() => {
|
|
334
|
+
wrapper.vm.handleMapWarnings(dataWithCircular)
|
|
335
|
+
}).not.toThrow()
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it('should handle very long warning descriptions', () => {
|
|
339
|
+
const longDescription = 'A'.repeat(10000)
|
|
340
|
+
const dataWithLongText = {
|
|
341
|
+
...mockWarningsData,
|
|
342
|
+
weather_finland_active_all: {
|
|
343
|
+
features: [
|
|
344
|
+
{
|
|
345
|
+
...mockWarningsData.weather_finland_active_all.features[0],
|
|
346
|
+
properties: {
|
|
347
|
+
...mockWarningsData.weather_finland_active_all.features[0]
|
|
348
|
+
.properties,
|
|
349
|
+
description_fi: longDescription,
|
|
350
|
+
description_sv: longDescription,
|
|
351
|
+
description_en: longDescription,
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const result = wrapper.vm.handleMapWarnings(dataWithLongText)
|
|
359
|
+
expect(result).toBeDefined()
|
|
360
|
+
expect(result.warnings).toBeDefined()
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
it('should handle warnings spanning across midnight', () => {
|
|
364
|
+
const midnightData = {
|
|
365
|
+
weather_update_time: '2025-10-31T23:00:00Z',
|
|
366
|
+
weather_finland_active_all: {
|
|
367
|
+
features: [
|
|
368
|
+
{
|
|
369
|
+
type: 'Feature',
|
|
370
|
+
properties: {
|
|
371
|
+
identifier: 'midnight-warning',
|
|
372
|
+
warning_context: 'wind',
|
|
373
|
+
severity: 'level-2',
|
|
374
|
+
effective_from: '2025-10-31T23:30:00Z',
|
|
375
|
+
effective_until: '2025-11-01T01:30:00Z',
|
|
376
|
+
reference: 'fi-warning#county.1',
|
|
377
|
+
description_fi: 'Kova tuuli',
|
|
378
|
+
description_sv: 'Hårt väder',
|
|
379
|
+
description_en: 'Strong wind',
|
|
380
|
+
},
|
|
381
|
+
geometry: {
|
|
382
|
+
type: 'Point',
|
|
383
|
+
coordinates: [25.0, 60.0],
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
},
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const result = wrapper.vm.handleMapWarnings(midnightData)
|
|
391
|
+
expect(result.warnings['midnight-warning']).toBeDefined()
|
|
392
|
+
expect(result.warnings['midnight-warning'].effectiveDays).toBeDefined()
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('should handle duplicate warning identifiers', () => {
|
|
396
|
+
const duplicateData = {
|
|
397
|
+
weather_update_time: '2025-10-31T12:00:00Z',
|
|
398
|
+
weather_finland_active_all: {
|
|
399
|
+
features: [
|
|
400
|
+
mockWarningsData.weather_finland_active_all.features[0],
|
|
401
|
+
mockWarningsData.weather_finland_active_all.features[0], // Duplicate
|
|
402
|
+
],
|
|
403
|
+
},
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const result = wrapper.vm.handleMapWarnings(duplicateData)
|
|
407
|
+
|
|
408
|
+
// Should handle duplicates gracefully
|
|
409
|
+
expect(result).toBeDefined()
|
|
410
|
+
expect(Object.keys(result.warnings).length).toBeGreaterThan(0)
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
it('should handle special characters in warning text', () => {
|
|
414
|
+
const specialCharsData = {
|
|
415
|
+
weather_update_time: '2025-10-31T12:00:00Z',
|
|
416
|
+
weather_finland_active_all: {
|
|
417
|
+
features: [
|
|
418
|
+
{
|
|
419
|
+
type: 'Feature',
|
|
420
|
+
properties: {
|
|
421
|
+
identifier: 'special-chars-warning',
|
|
422
|
+
warning_context: 'wind',
|
|
423
|
+
severity: 'level-2',
|
|
424
|
+
effective_from: '2025-10-31T12:00:00Z',
|
|
425
|
+
effective_until: '2025-11-01T12:00:00Z',
|
|
426
|
+
reference: 'fi-warning#county.1',
|
|
427
|
+
description_fi:
|
|
428
|
+
'Test <script>alert("xss")</script> & special chars',
|
|
429
|
+
description_sv:
|
|
430
|
+
'Test <script>alert("xss")</script> & special chars',
|
|
431
|
+
description_en:
|
|
432
|
+
'Test <script>alert("xss")</script> & special chars',
|
|
433
|
+
},
|
|
434
|
+
geometry: {
|
|
435
|
+
type: 'Point',
|
|
436
|
+
coordinates: [25.0, 60.0],
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
},
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const result = wrapper.vm.handleMapWarnings(specialCharsData)
|
|
444
|
+
const warning = result.warnings['special-chars-warning']
|
|
445
|
+
|
|
446
|
+
// HTML entities should be decoded
|
|
447
|
+
expect(warning.info.fi).not.toContain('<')
|
|
448
|
+
expect(warning.info.fi).not.toContain('>')
|
|
449
|
+
expect(warning.info.fi).not.toContain('&')
|
|
450
|
+
})
|
|
451
|
+
})
|
|
452
|
+
})
|
package/tests/setup.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { vi } from 'vitest'
|
|
2
|
+
import '@testing-library/jest-dom'
|
|
3
|
+
import { config } from '@vue/test-utils'
|
|
4
|
+
|
|
5
|
+
// Mock environment variables
|
|
6
|
+
process.env.VITE_LANGUAGE = 'fi'
|
|
7
|
+
|
|
8
|
+
// Configure global stubs for components
|
|
9
|
+
config.global.stubs = {
|
|
10
|
+
GrayScaleToggle: true,
|
|
11
|
+
CollapsiblePanel: true,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Mock window.getComputedStyle
|
|
15
|
+
global.window.getComputedStyle = vi.fn(() => ({
|
|
16
|
+
fontSize: '16px',
|
|
17
|
+
}))
|
|
18
|
+
|
|
19
|
+
// Mock IntersectionObserver
|
|
20
|
+
global.IntersectionObserver = class IntersectionObserver {
|
|
21
|
+
constructor() {}
|
|
22
|
+
disconnect() {}
|
|
23
|
+
observe() {}
|
|
24
|
+
takeRecords() {
|
|
25
|
+
return []
|
|
26
|
+
}
|
|
27
|
+
unobserve() {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Mock ResizeObserver
|
|
31
|
+
global.ResizeObserver = class ResizeObserver {
|
|
32
|
+
constructor() {}
|
|
33
|
+
disconnect() {}
|
|
34
|
+
observe() {}
|
|
35
|
+
unobserve() {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Mock fetch if not available
|
|
39
|
+
if (!global.fetch) {
|
|
40
|
+
global.fetch = vi.fn()
|
|
41
|
+
}
|