@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,624 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import utils from '@/mixins/utils'
|
|
4
|
+
import config from '@/mixins/config'
|
|
5
|
+
import geojsonsvg from '@/mixins/geojsonsvg'
|
|
6
|
+
import i18n from '@/mixins/i18n'
|
|
7
|
+
import {
|
|
8
|
+
mockWeatherWarning,
|
|
9
|
+
mockThunderStormWarning,
|
|
10
|
+
mockSeaWindWarning,
|
|
11
|
+
mockFloodWarning,
|
|
12
|
+
} from '../../fixtures/mockWarningData'
|
|
13
|
+
|
|
14
|
+
// Helper component to test mixins
|
|
15
|
+
const TestComponent = {
|
|
16
|
+
mixins: [utils, config, geojsonsvg, i18n],
|
|
17
|
+
template: '<div></div>',
|
|
18
|
+
props: {
|
|
19
|
+
currentTime: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: Date.now(),
|
|
22
|
+
},
|
|
23
|
+
startFrom: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: '',
|
|
26
|
+
},
|
|
27
|
+
dailyWarningTypes: {
|
|
28
|
+
type: Array,
|
|
29
|
+
default: () => [],
|
|
30
|
+
},
|
|
31
|
+
geometryId: {
|
|
32
|
+
type: Number,
|
|
33
|
+
default: 2021,
|
|
34
|
+
},
|
|
35
|
+
visibleWarnings: {
|
|
36
|
+
type: Array,
|
|
37
|
+
default: () => [],
|
|
38
|
+
},
|
|
39
|
+
language: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: 'fi',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
data() {
|
|
45
|
+
return {
|
|
46
|
+
timeOffset: 0,
|
|
47
|
+
updatedAt: null,
|
|
48
|
+
warnings: {},
|
|
49
|
+
coverageRegions: {},
|
|
50
|
+
coverageWarnings: [],
|
|
51
|
+
index: 0,
|
|
52
|
+
size: 'Large',
|
|
53
|
+
strokeWidth: 1,
|
|
54
|
+
theme: 'light-theme',
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe('utils mixin', () => {
|
|
60
|
+
let wrapper
|
|
61
|
+
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
wrapper = mount(TestComponent, {
|
|
64
|
+
props: {
|
|
65
|
+
currentTime: new Date('2025-10-31T12:00:00Z').getTime(),
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('uncapitalize', () => {
|
|
71
|
+
it('should uncapitalize first letter', () => {
|
|
72
|
+
expect(wrapper.vm.uncapitalize('HelloWorld')).toBe('helloWorld')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should handle empty string', () => {
|
|
76
|
+
expect(wrapper.vm.uncapitalize('')).toBe('')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should handle null', () => {
|
|
80
|
+
expect(wrapper.vm.uncapitalize(null)).toBe('')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should handle single character', () => {
|
|
84
|
+
expect(wrapper.vm.uncapitalize('A')).toBe('a')
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('warningType', () => {
|
|
89
|
+
it('should parse thunderStorm type correctly', () => {
|
|
90
|
+
const result = wrapper.vm.warningType({
|
|
91
|
+
warning_context: 'thunder-storm',
|
|
92
|
+
})
|
|
93
|
+
expect(result).toBe('thunderStorm')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should parse wind type correctly', () => {
|
|
97
|
+
const result = wrapper.vm.warningType({
|
|
98
|
+
warning_context: 'wind',
|
|
99
|
+
})
|
|
100
|
+
expect(result).toBe('wind')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('should handle sea-wind with extension', () => {
|
|
104
|
+
const result = wrapper.vm.warningType({
|
|
105
|
+
warning_context: 'sea-wind',
|
|
106
|
+
})
|
|
107
|
+
expect(result).toBe('seaWind')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should handle context with extension', () => {
|
|
111
|
+
const result = wrapper.vm.warningType({
|
|
112
|
+
warning_context: 'sea-water-height',
|
|
113
|
+
context_extension: 'high-water',
|
|
114
|
+
})
|
|
115
|
+
expect(result).toBe('seaWaterHeightHighWater')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('should handle multi-word contexts', () => {
|
|
119
|
+
const result = wrapper.vm.warningType({
|
|
120
|
+
warning_context: 'forest-fire-weather',
|
|
121
|
+
})
|
|
122
|
+
expect(result).toBe('forestFireWeather')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('regionFromReference', () => {
|
|
127
|
+
it('should parse single region reference', () => {
|
|
128
|
+
const result = wrapper.vm.regionFromReference('fi-warning#county.1')
|
|
129
|
+
expect(result).toBe('county.1')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should parse merged region reference', () => {
|
|
133
|
+
const result = wrapper.vm.regionFromReference(
|
|
134
|
+
'fi-warning#county.1,fi-warning#county.2'
|
|
135
|
+
)
|
|
136
|
+
expect(result).toBe('county_1.2')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should parse multiple merged regions', () => {
|
|
140
|
+
const result = wrapper.vm.regionFromReference(
|
|
141
|
+
'fi-warning#county.1,fi-warning#county.2,fi-warning#county.3'
|
|
142
|
+
)
|
|
143
|
+
expect(result).toBe('county_1_2.3')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('should handle Saimaa special case (lake region)', () => {
|
|
147
|
+
const result = wrapper.vm.regionFromReference(
|
|
148
|
+
'fi-warning#sea_region_south.FI-115978'
|
|
149
|
+
)
|
|
150
|
+
expect(result).toBe('sea_region_south.FI-115978')
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
describe('relativeCoverageFromReference', () => {
|
|
155
|
+
it('should extract coverage from reference URL', () => {
|
|
156
|
+
const result = wrapper.vm.relativeCoverageFromReference(
|
|
157
|
+
'fi-warning#county.1?c=75'
|
|
158
|
+
)
|
|
159
|
+
expect(result).toBe(75)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should return 0 when no coverage parameter', () => {
|
|
163
|
+
const result = wrapper.vm.relativeCoverageFromReference(
|
|
164
|
+
'fi-warning#county.1'
|
|
165
|
+
)
|
|
166
|
+
expect(result).toBe(0)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should return 0 when no query string', () => {
|
|
170
|
+
const result = wrapper.vm.relativeCoverageFromReference('county.1')
|
|
171
|
+
expect(result).toBe(0)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should return 0 for null reference', () => {
|
|
175
|
+
const result = wrapper.vm.relativeCoverageFromReference(null)
|
|
176
|
+
expect(result).toBe(0)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('should handle URL with hash fragment', () => {
|
|
180
|
+
const result = wrapper.vm.relativeCoverageFromReference(
|
|
181
|
+
'fi-warning#county.1?c=50#fragment'
|
|
182
|
+
)
|
|
183
|
+
expect(result).toBe(50)
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
describe('twoDigits', () => {
|
|
188
|
+
it('should pad single digit with zero', () => {
|
|
189
|
+
expect(wrapper.vm.twoDigits(5)).toBe('05')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('should not pad double digit', () => {
|
|
193
|
+
expect(wrapper.vm.twoDigits(15)).toBe('15')
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('should handle zero', () => {
|
|
197
|
+
expect(wrapper.vm.twoDigits(0)).toBe('00')
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
describe('text', () => {
|
|
202
|
+
it('should return physical value for sea-wind', () => {
|
|
203
|
+
const result = wrapper.vm.text({
|
|
204
|
+
warning_context: 'sea-wind',
|
|
205
|
+
physical_value: '15',
|
|
206
|
+
})
|
|
207
|
+
expect(result).toBe('15')
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('should return empty string for other contexts', () => {
|
|
211
|
+
const result = wrapper.vm.text({
|
|
212
|
+
warning_context: 'wind',
|
|
213
|
+
physical_value: '20',
|
|
214
|
+
})
|
|
215
|
+
expect(result).toBe('')
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
describe('toTimeZone', () => {
|
|
220
|
+
it('should convert UTC to Helsinki timezone', () => {
|
|
221
|
+
const date = new Date('2025-10-31T12:00:00Z')
|
|
222
|
+
const result = wrapper.vm.toTimeZone(date)
|
|
223
|
+
|
|
224
|
+
expect(result.timeZone).toBe('Europe/Helsinki')
|
|
225
|
+
expect(result.year).toBe(2025)
|
|
226
|
+
expect(result.month).toBe(10)
|
|
227
|
+
expect(result.day).toBe(31)
|
|
228
|
+
// Helsinki is UTC+2 during DST (summer) or UTC+3 during winter
|
|
229
|
+
// Oct 31 is after DST ends, so it should be UTC+2
|
|
230
|
+
expect(result.hour).toBe(14)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('should handle different date formats', () => {
|
|
234
|
+
const date = '2025-10-31T12:00:00Z'
|
|
235
|
+
const result = wrapper.vm.toTimeZone(date)
|
|
236
|
+
|
|
237
|
+
expect(result.year).toBe(2025)
|
|
238
|
+
expect(result.month).toBe(10)
|
|
239
|
+
expect(result.day).toBe(31)
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
describe('msSinceStartOfDay', () => {
|
|
244
|
+
it('should calculate milliseconds since start of day', () => {
|
|
245
|
+
// 12:00:00 = 12 * 60 * 60 * 1000 = 43200000 ms
|
|
246
|
+
const timestamp = new Date('2025-10-31T12:00:00Z').getTime()
|
|
247
|
+
const result = wrapper.vm.msSinceStartOfDay(timestamp)
|
|
248
|
+
|
|
249
|
+
// In Helsinki timezone (UTC+2), 12:00 UTC = 14:00 local
|
|
250
|
+
// 14 * 60 * 60 * 1000 = 50400000 ms
|
|
251
|
+
expect(result).toBeGreaterThan(40000000) // At least 11+ hours
|
|
252
|
+
expect(result).toBeLessThan(60000000) // Less than 17 hours
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('should handle midnight', () => {
|
|
256
|
+
const timestamp = new Date('2025-10-31T00:00:00Z').getTime()
|
|
257
|
+
const result = wrapper.vm.msSinceStartOfDay(timestamp)
|
|
258
|
+
|
|
259
|
+
expect(result).toBeGreaterThanOrEqual(0)
|
|
260
|
+
expect(result).toBeLessThan(24 * 60 * 60 * 1000)
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
describe('validInterval', () => {
|
|
265
|
+
it('should format time interval correctly', () => {
|
|
266
|
+
const start = '2025-10-31T12:00:00Z'
|
|
267
|
+
const end = '2025-11-01T18:00:00Z'
|
|
268
|
+
const result = wrapper.vm.validInterval(start, end)
|
|
269
|
+
|
|
270
|
+
// Should contain both dates and times
|
|
271
|
+
expect(result).toContain('31.10.')
|
|
272
|
+
expect(result).toContain('1.11.')
|
|
273
|
+
expect(result).toContain('–') // en-dash separator
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('should include time in HH:MM format', () => {
|
|
277
|
+
const start = '2025-10-31T12:00:00Z'
|
|
278
|
+
const end = '2025-10-31T18:00:00Z'
|
|
279
|
+
const result = wrapper.vm.validInterval(start, end)
|
|
280
|
+
|
|
281
|
+
expect(result).toMatch(/\d{2}:\d{2}/)
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
describe('effectiveDays', () => {
|
|
286
|
+
it('should return array of 5 boolean values', () => {
|
|
287
|
+
const start = '2025-10-31T12:00:00Z'
|
|
288
|
+
const end = '2025-11-01T12:00:00Z'
|
|
289
|
+
const result = wrapper.vm.effectiveDays(start, end, false)
|
|
290
|
+
|
|
291
|
+
expect(result).toHaveLength(5)
|
|
292
|
+
expect(result.every((val) => typeof val === 'boolean')).toBe(true)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('should mark first day as effective for current warning', () => {
|
|
296
|
+
const now = new Date('2025-10-31T12:00:00Z').getTime()
|
|
297
|
+
wrapper.vm.updatedAt = now
|
|
298
|
+
|
|
299
|
+
const start = '2025-10-31T08:00:00Z'
|
|
300
|
+
const end = '2025-10-31T20:00:00Z'
|
|
301
|
+
const result = wrapper.vm.effectiveDays(start, end, false)
|
|
302
|
+
|
|
303
|
+
expect(result[0]).toBe(true)
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('should mark no days for past warning', () => {
|
|
307
|
+
const now = new Date('2025-10-31T12:00:00Z').getTime()
|
|
308
|
+
wrapper.vm.updatedAt = now
|
|
309
|
+
|
|
310
|
+
const start = '2025-10-29T08:00:00Z'
|
|
311
|
+
const end = '2025-10-29T20:00:00Z'
|
|
312
|
+
const result = wrapper.vm.effectiveDays(start, end, false)
|
|
313
|
+
|
|
314
|
+
expect(result.every((val) => val === false)).toBe(true)
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it('should mark multiple days for long warning', () => {
|
|
318
|
+
const now = new Date('2025-10-31T12:00:00Z').getTime()
|
|
319
|
+
wrapper.vm.updatedAt = now
|
|
320
|
+
|
|
321
|
+
const start = '2025-10-31T08:00:00Z'
|
|
322
|
+
const end = '2025-11-03T20:00:00Z'
|
|
323
|
+
const result = wrapper.vm.effectiveDays(start, end, false)
|
|
324
|
+
|
|
325
|
+
const effectiveDays = result.filter((val) => val === true)
|
|
326
|
+
expect(effectiveDays.length).toBeGreaterThan(1)
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
describe('createWeatherWarning', () => {
|
|
331
|
+
it('should create wind warning object', () => {
|
|
332
|
+
const result = wrapper.vm.createWeatherWarning(mockWeatherWarning)
|
|
333
|
+
|
|
334
|
+
expect(result).toMatchObject({
|
|
335
|
+
type: 'wind',
|
|
336
|
+
id: 'test-warning-wind-1',
|
|
337
|
+
severity: 3,
|
|
338
|
+
direction: 180, // 270 - 90 for wind
|
|
339
|
+
value: 25,
|
|
340
|
+
})
|
|
341
|
+
expect(result.effectiveDays).toHaveLength(5)
|
|
342
|
+
expect(result.regions).toHaveProperty('county.1')
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
it('should create thunder storm warning object', () => {
|
|
346
|
+
const result = wrapper.vm.createWeatherWarning(mockThunderStormWarning)
|
|
347
|
+
|
|
348
|
+
expect(result).toMatchObject({
|
|
349
|
+
type: 'thunderStorm',
|
|
350
|
+
id: 'test-warning-thunder-1',
|
|
351
|
+
severity: 4,
|
|
352
|
+
direction: 0,
|
|
353
|
+
})
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
it('should create sea wind warning with adjusted severity', () => {
|
|
357
|
+
const levelOneSeaWind = {
|
|
358
|
+
...mockSeaWindWarning,
|
|
359
|
+
properties: {
|
|
360
|
+
...mockSeaWindWarning.properties,
|
|
361
|
+
severity: 'level-1',
|
|
362
|
+
},
|
|
363
|
+
}
|
|
364
|
+
const result = wrapper.vm.createWeatherWarning(levelOneSeaWind)
|
|
365
|
+
|
|
366
|
+
// Level-1 sea wind gets severity bumped by 1
|
|
367
|
+
expect(result.severity).toBe(2)
|
|
368
|
+
expect(result.type).toBe('seaWind')
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
it('should set text for sea wind warnings', () => {
|
|
372
|
+
const result = wrapper.vm.createWeatherWarning(mockSeaWindWarning)
|
|
373
|
+
|
|
374
|
+
expect(result.text).toBe('15')
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
it('should include info in all languages', () => {
|
|
378
|
+
const result = wrapper.vm.createWeatherWarning(mockWeatherWarning)
|
|
379
|
+
|
|
380
|
+
expect(result.info).toHaveProperty('fi')
|
|
381
|
+
expect(result.info).toHaveProperty('sv')
|
|
382
|
+
expect(result.info).toHaveProperty('en')
|
|
383
|
+
expect(result.info.fi).toBe('Kovaa tuulta')
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
it('should initialize empty coverages', () => {
|
|
387
|
+
const result = wrapper.vm.createWeatherWarning(mockWeatherWarning)
|
|
388
|
+
|
|
389
|
+
expect(result.coveragesLarge).toEqual([])
|
|
390
|
+
expect(result.coveragesSmall).toEqual([])
|
|
391
|
+
expect(result.covRegions).toBeInstanceOf(Map)
|
|
392
|
+
expect(result.covRegions.size).toBe(0)
|
|
393
|
+
})
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
describe('createFloodWarning', () => {
|
|
397
|
+
it('should create flood warning object', () => {
|
|
398
|
+
const result = wrapper.vm.createFloodWarning(mockFloodWarning)
|
|
399
|
+
|
|
400
|
+
expect(result).toMatchObject({
|
|
401
|
+
type: 'floodLevel',
|
|
402
|
+
id: 'test-warning-flood-1',
|
|
403
|
+
severity: 3, // Severe = 3
|
|
404
|
+
direction: 0,
|
|
405
|
+
value: 0,
|
|
406
|
+
text: '',
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
it('should parse encoded description', () => {
|
|
411
|
+
const result = wrapper.vm.createFloodWarning(mockFloodWarning)
|
|
412
|
+
|
|
413
|
+
expect(result.info).toHaveProperty('fi')
|
|
414
|
+
expect(result.info.fi).toBe('Tulvavaara')
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it('should extract language from properties', () => {
|
|
418
|
+
const result = wrapper.vm.createFloodWarning(mockFloodWarning)
|
|
419
|
+
|
|
420
|
+
expect(result.info).toHaveProperty('fi')
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
it('should handle different severity levels', () => {
|
|
424
|
+
const minorFlood = {
|
|
425
|
+
...mockFloodWarning,
|
|
426
|
+
properties: {
|
|
427
|
+
...mockFloodWarning.properties,
|
|
428
|
+
severity: 'Minor',
|
|
429
|
+
},
|
|
430
|
+
}
|
|
431
|
+
const result = wrapper.vm.createFloodWarning(minorFlood)
|
|
432
|
+
|
|
433
|
+
expect(result.severity).toBe(1)
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
it('should include flood link information', () => {
|
|
437
|
+
const result = wrapper.vm.createFloodWarning(mockFloodWarning)
|
|
438
|
+
|
|
439
|
+
expect(result.link).toBeDefined()
|
|
440
|
+
expect(result.linkText).toBeDefined()
|
|
441
|
+
})
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
describe('isValid', () => {
|
|
445
|
+
it('should validate weather warning with level-2 severity', () => {
|
|
446
|
+
const result = wrapper.vm.isValid(mockWeatherWarning)
|
|
447
|
+
expect(result).toBe(true)
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
it('should validate sea wind with level-1 severity', () => {
|
|
451
|
+
const result = wrapper.vm.isValid(mockSeaWindWarning)
|
|
452
|
+
expect(result).toBe(true)
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
it('should validate flood warning', () => {
|
|
456
|
+
const result = wrapper.vm.isValid(mockFloodWarning)
|
|
457
|
+
expect(result).toBe(true)
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
it('should reject warning without properties', () => {
|
|
461
|
+
const result = wrapper.vm.isValid({ type: 'Feature' })
|
|
462
|
+
expect(result).toBe(false)
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
it('should reject warning with invalid region', () => {
|
|
466
|
+
const invalidWarning = {
|
|
467
|
+
...mockWeatherWarning,
|
|
468
|
+
properties: {
|
|
469
|
+
...mockWeatherWarning.properties,
|
|
470
|
+
reference: 'fi-warning#nonexistent.999',
|
|
471
|
+
},
|
|
472
|
+
}
|
|
473
|
+
const result = wrapper.vm.isValid(invalidWarning)
|
|
474
|
+
expect(result).toBe(false)
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
it('should reject warning with level-1 severity (except sea-wind)', () => {
|
|
478
|
+
const level1Warning = {
|
|
479
|
+
...mockWeatherWarning,
|
|
480
|
+
properties: {
|
|
481
|
+
...mockWeatherWarning.properties,
|
|
482
|
+
severity: 'level-1',
|
|
483
|
+
},
|
|
484
|
+
}
|
|
485
|
+
const result = wrapper.vm.isValid(level1Warning)
|
|
486
|
+
expect(result).toBe(false)
|
|
487
|
+
})
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
describe('createDays', () => {
|
|
491
|
+
it('should create 5 days', () => {
|
|
492
|
+
wrapper.vm.updatedAt = new Date('2025-10-31T12:00:00Z').getTime()
|
|
493
|
+
const result = wrapper.vm.createDays({})
|
|
494
|
+
|
|
495
|
+
expect(result).toHaveLength(5)
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
it('should include date information', () => {
|
|
499
|
+
wrapper.vm.updatedAt = new Date('2025-10-31T12:00:00Z').getTime()
|
|
500
|
+
const result = wrapper.vm.createDays({})
|
|
501
|
+
|
|
502
|
+
const firstDay = result[0]
|
|
503
|
+
expect(firstDay).toHaveProperty('weekdayName')
|
|
504
|
+
expect(firstDay).toHaveProperty('day')
|
|
505
|
+
expect(firstDay).toHaveProperty('month')
|
|
506
|
+
expect(firstDay).toHaveProperty('year')
|
|
507
|
+
expect(firstDay.day).toBe(31)
|
|
508
|
+
expect(firstDay.month).toBe(10)
|
|
509
|
+
expect(firstDay.year).toBe(2025)
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
it('should include updated time', () => {
|
|
513
|
+
wrapper.vm.updatedAt = new Date('2025-10-31T12:00:00Z').getTime()
|
|
514
|
+
const result = wrapper.vm.createDays({})
|
|
515
|
+
|
|
516
|
+
const firstDay = result[0]
|
|
517
|
+
expect(firstDay.updatedDate).toMatch(/31\.10\.2025/)
|
|
518
|
+
expect(firstDay.updatedTime).toMatch(/\d{2}:\d{2}/)
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
it('should calculate maximum severity per day', () => {
|
|
522
|
+
wrapper.vm.updatedAt = new Date('2025-10-31T12:00:00Z').getTime()
|
|
523
|
+
|
|
524
|
+
const warnings = {
|
|
525
|
+
'warning-1': {
|
|
526
|
+
effectiveDays: [true, true, false, false, false],
|
|
527
|
+
severity: 3,
|
|
528
|
+
},
|
|
529
|
+
'warning-2': {
|
|
530
|
+
effectiveDays: [true, false, false, false, false],
|
|
531
|
+
severity: 4,
|
|
532
|
+
},
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const result = wrapper.vm.createDays(warnings)
|
|
536
|
+
|
|
537
|
+
expect(result[0].severity).toBe(4) // Max of 3 and 4
|
|
538
|
+
expect(result[1].severity).toBe(3)
|
|
539
|
+
expect(result[2].severity).toBe(0)
|
|
540
|
+
})
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
describe('getMaxSeverities', () => {
|
|
544
|
+
it('should return max severity per warning type', () => {
|
|
545
|
+
const warnings = {
|
|
546
|
+
'warning-1': {
|
|
547
|
+
type: 'wind',
|
|
548
|
+
severity: 3,
|
|
549
|
+
effectiveDays: [true, false, false, false, false],
|
|
550
|
+
},
|
|
551
|
+
'warning-2': {
|
|
552
|
+
type: 'wind',
|
|
553
|
+
severity: 2,
|
|
554
|
+
effectiveDays: [true, false, false, false, false],
|
|
555
|
+
},
|
|
556
|
+
'warning-3': {
|
|
557
|
+
type: 'thunderStorm',
|
|
558
|
+
severity: 4,
|
|
559
|
+
effectiveDays: [true, false, false, false, false],
|
|
560
|
+
},
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const result = wrapper.vm.getMaxSeverities(warnings)
|
|
564
|
+
|
|
565
|
+
expect(result).toEqual({
|
|
566
|
+
wind: 3,
|
|
567
|
+
thunderStorm: 4,
|
|
568
|
+
})
|
|
569
|
+
})
|
|
570
|
+
|
|
571
|
+
it('should ignore warnings not effective any day', () => {
|
|
572
|
+
const warnings = {
|
|
573
|
+
'warning-1': {
|
|
574
|
+
type: 'wind',
|
|
575
|
+
severity: 3,
|
|
576
|
+
effectiveDays: [false, false, false, false, false],
|
|
577
|
+
},
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const result = wrapper.vm.getMaxSeverities(warnings)
|
|
581
|
+
|
|
582
|
+
expect(result).toEqual({})
|
|
583
|
+
})
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
describe('createLegend', () => {
|
|
587
|
+
it('should create legend sorted by severity', () => {
|
|
588
|
+
const severities = {
|
|
589
|
+
wind: 2,
|
|
590
|
+
thunderStorm: 4,
|
|
591
|
+
rain: 3,
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const result = wrapper.vm.createLegend(severities)
|
|
595
|
+
|
|
596
|
+
expect(result).toHaveLength(3)
|
|
597
|
+
expect(result[0].severity).toBe(4)
|
|
598
|
+
expect(result[1].severity).toBe(3)
|
|
599
|
+
expect(result[2].severity).toBe(2)
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
it('should set all warnings as visible', () => {
|
|
603
|
+
const severities = {
|
|
604
|
+
wind: 3,
|
|
605
|
+
rain: 2,
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const result = wrapper.vm.createLegend(severities)
|
|
609
|
+
|
|
610
|
+
expect(result.every((item) => item.visible === true)).toBe(true)
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
it('should include warning type', () => {
|
|
614
|
+
const severities = {
|
|
615
|
+
wind: 3,
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const result = wrapper.vm.createLegend(severities)
|
|
619
|
+
|
|
620
|
+
expect(result[0]).toHaveProperty('type', 'wind')
|
|
621
|
+
expect(result[0]).toHaveProperty('severity', 3)
|
|
622
|
+
})
|
|
623
|
+
})
|
|
624
|
+
})
|