tsvesync 1.0.90 → 1.0.91
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/dist/__tests__/fans/vesyncAir131.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAir131.test.js +365 -0
- package/dist/__tests__/fans/vesyncAirBaseV2.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAirBaseV2.test.js +283 -0
- package/dist/__tests__/fans/vesyncAirBypass.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAirBypass.test.js +257 -0
- package/dist/__tests__/vesyncFan.test.d.ts +4 -0
- package/dist/__tests__/vesyncFan.test.js +263 -0
- package/dist/lib/fans/air131.js +5 -4
- package/dist/lib/fans/airBypass.js +12 -4
- package/dist/lib/vesyncFan.d.ts +30 -4
- package/dist/lib/vesyncFan.js +70 -5
- package/package.json +1 -1
- package/src/lib/fans/air131.ts +5 -4
- package/src/lib/fans/airBypass.ts +10 -4
- package/src/lib/vesyncFan.ts +79 -7
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for VeSyncAir131 air quality data parsing and filter life handling
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const air131_1 = require("../../lib/fans/air131");
|
|
7
|
+
// Mock VeSync manager
|
|
8
|
+
const mockManager = {
|
|
9
|
+
username: 'test@example.com',
|
|
10
|
+
password: 'testpass',
|
|
11
|
+
token: 'testtoken',
|
|
12
|
+
accountID: 'testaccount',
|
|
13
|
+
apiKey: 'testapikey',
|
|
14
|
+
apiBase: 'testbase',
|
|
15
|
+
timezone: 'UTC'
|
|
16
|
+
};
|
|
17
|
+
describe('VeSyncAir131 Air Quality Features', () => {
|
|
18
|
+
let device;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
const deviceDetails = {
|
|
21
|
+
deviceName: 'LV-PUR131S Test',
|
|
22
|
+
deviceType: 'LV-PUR131S',
|
|
23
|
+
cid: 'test-cid',
|
|
24
|
+
uuid: 'test-uuid',
|
|
25
|
+
deviceStatus: 'on',
|
|
26
|
+
deviceRegion: 'US',
|
|
27
|
+
configModule: 'VeSyncAir131',
|
|
28
|
+
macId: '00:11:22:33:44:55',
|
|
29
|
+
deviceCategory: 'fan',
|
|
30
|
+
connectionStatus: 'online'
|
|
31
|
+
};
|
|
32
|
+
device = new air131_1.VeSyncAir131(deviceDetails, mockManager);
|
|
33
|
+
});
|
|
34
|
+
describe('Air Quality Data Parsing', () => {
|
|
35
|
+
it('should parse air quality data with PM2.5 values', () => {
|
|
36
|
+
const mockResponse = {
|
|
37
|
+
result: {
|
|
38
|
+
code: 0,
|
|
39
|
+
result: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
mode: 'auto',
|
|
42
|
+
level: 2,
|
|
43
|
+
speed: 2,
|
|
44
|
+
display: true,
|
|
45
|
+
child_lock: false,
|
|
46
|
+
night_light: 'off',
|
|
47
|
+
air_quality: 2,
|
|
48
|
+
air_quality_value: 28,
|
|
49
|
+
filter_life: {
|
|
50
|
+
percent: 68,
|
|
51
|
+
replace_indicator: false
|
|
52
|
+
},
|
|
53
|
+
timer_remain: 0
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
device.details = mockResponse.result.result;
|
|
58
|
+
expect(device.airQualityLevel).toBe(2);
|
|
59
|
+
expect(device.airQualityValue).toBe(28);
|
|
60
|
+
expect(device.filterLife).toBe(68);
|
|
61
|
+
});
|
|
62
|
+
it('should handle air quality levels 1-5 mapping', () => {
|
|
63
|
+
const airQualityMappings = [
|
|
64
|
+
{ level: 1, pm25: 8, description: 'Excellent' },
|
|
65
|
+
{ level: 2, pm25: 22, description: 'Good' },
|
|
66
|
+
{ level: 3, pm25: 38, description: 'Fair' },
|
|
67
|
+
{ level: 4, pm25: 75, description: 'Poor' },
|
|
68
|
+
{ level: 5, pm25: 150, description: 'Very Poor' }
|
|
69
|
+
];
|
|
70
|
+
airQualityMappings.forEach(mapping => {
|
|
71
|
+
device.details = {
|
|
72
|
+
air_quality: mapping.level,
|
|
73
|
+
air_quality_value: mapping.pm25,
|
|
74
|
+
filter_life: { percent: 50 }
|
|
75
|
+
};
|
|
76
|
+
expect(device.airQualityLevel).toBe(mapping.level);
|
|
77
|
+
expect(device.airQualityValue).toBe(mapping.pm25);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
it('should handle missing air quality data', () => {
|
|
81
|
+
device.details = {
|
|
82
|
+
enabled: true,
|
|
83
|
+
mode: 'manual',
|
|
84
|
+
level: 3,
|
|
85
|
+
speed: 3,
|
|
86
|
+
filter_life: { percent: 45 }
|
|
87
|
+
// air_quality and air_quality_value missing
|
|
88
|
+
};
|
|
89
|
+
expect(device.airQualityLevel).toBe('');
|
|
90
|
+
expect(device.airQualityValue).toBe(0);
|
|
91
|
+
});
|
|
92
|
+
it('should handle zero PM2.5 readings (perfect air)', () => {
|
|
93
|
+
device.details = {
|
|
94
|
+
air_quality: 1,
|
|
95
|
+
air_quality_value: 0,
|
|
96
|
+
filter_life: { percent: 85 }
|
|
97
|
+
};
|
|
98
|
+
expect(device.airQualityLevel).toBe(1);
|
|
99
|
+
expect(device.airQualityValue).toBe(0);
|
|
100
|
+
});
|
|
101
|
+
it('should handle hazardous air quality levels', () => {
|
|
102
|
+
device.details = {
|
|
103
|
+
air_quality: 5,
|
|
104
|
+
air_quality_value: 500,
|
|
105
|
+
filter_life: { percent: 25 }
|
|
106
|
+
};
|
|
107
|
+
expect(device.airQualityLevel).toBe(5);
|
|
108
|
+
expect(device.airQualityValue).toBe(500);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('Filter Life Object Format', () => {
|
|
112
|
+
it('should parse filter life object with percent', () => {
|
|
113
|
+
device.details = {
|
|
114
|
+
filter_life: {
|
|
115
|
+
percent: 72,
|
|
116
|
+
replace_indicator: false
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
expect(device.filterLife).toBe(72);
|
|
120
|
+
});
|
|
121
|
+
it('should handle filter replacement indicator', () => {
|
|
122
|
+
device.details = {
|
|
123
|
+
filter_life: {
|
|
124
|
+
percent: 8,
|
|
125
|
+
replace_indicator: true
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
expect(device.filterLife).toBe(8);
|
|
129
|
+
});
|
|
130
|
+
it('should handle new filter (100%)', () => {
|
|
131
|
+
device.details = {
|
|
132
|
+
filter_life: {
|
|
133
|
+
percent: 100,
|
|
134
|
+
replace_indicator: false
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
expect(device.filterLife).toBe(100);
|
|
138
|
+
});
|
|
139
|
+
it('should handle filter needs replacement (0%)', () => {
|
|
140
|
+
device.details = {
|
|
141
|
+
filter_life: {
|
|
142
|
+
percent: 0,
|
|
143
|
+
replace_indicator: true
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
expect(device.filterLife).toBe(0);
|
|
147
|
+
});
|
|
148
|
+
it('should handle missing percent in filter object', () => {
|
|
149
|
+
device.details = {
|
|
150
|
+
filter_life: {
|
|
151
|
+
replace_indicator: true
|
|
152
|
+
// percent missing
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
expect(device.filterLife).toBe(0);
|
|
156
|
+
});
|
|
157
|
+
it('should handle null filter life object', () => {
|
|
158
|
+
device.details = {
|
|
159
|
+
filter_life: null
|
|
160
|
+
};
|
|
161
|
+
expect(device.filterLife).toBe(0);
|
|
162
|
+
});
|
|
163
|
+
it('should handle empty filter life object', () => {
|
|
164
|
+
device.details = {
|
|
165
|
+
filter_life: {}
|
|
166
|
+
};
|
|
167
|
+
expect(device.filterLife).toBe(0);
|
|
168
|
+
});
|
|
169
|
+
it('should handle malformed filter life object', () => {
|
|
170
|
+
device.details = {
|
|
171
|
+
filter_life: {
|
|
172
|
+
percent: 'not_a_number',
|
|
173
|
+
replace_indicator: 'not_a_boolean'
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
expect(device.filterLife).toBe(0);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe('Device Configuration and Features', () => {
|
|
180
|
+
it('should support air quality features for LV-PUR131S', () => {
|
|
181
|
+
expect(device.hasFeature('air_quality')).toBe(true);
|
|
182
|
+
expect(device.hasFeature('display')).toBe(true);
|
|
183
|
+
expect(device.hasFeature('child_lock')).toBe(true);
|
|
184
|
+
expect(device.hasFeature('night_light')).toBe(true);
|
|
185
|
+
expect(device.hasFeature('timer')).toBe(true);
|
|
186
|
+
expect(device.hasFeature('fan_speed')).toBe(true);
|
|
187
|
+
expect(device.hasFeature('auto_mode')).toBe(true);
|
|
188
|
+
});
|
|
189
|
+
it('should return correct maximum speed for LV-PUR131S', () => {
|
|
190
|
+
expect(device.getMaxFanSpeed()).toBe(3); // LV series has 3 speeds
|
|
191
|
+
});
|
|
192
|
+
it('should handle LV-RH131S without air quality', () => {
|
|
193
|
+
const rh131s = new air131_1.VeSyncAir131({
|
|
194
|
+
deviceName: 'LV-RH131S Test',
|
|
195
|
+
deviceType: 'LV-RH131S',
|
|
196
|
+
cid: 'test-cid',
|
|
197
|
+
uuid: 'test-uuid'
|
|
198
|
+
}, mockManager);
|
|
199
|
+
expect(rh131s.hasFeature('air_quality')).toBe(false);
|
|
200
|
+
expect(rh131s.hasFeature('display')).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe('Mode-specific Behavior', () => {
|
|
204
|
+
it('should support air quality in auto mode', () => {
|
|
205
|
+
device.details.mode = 'auto';
|
|
206
|
+
expect(device.isFeatureSupportedInCurrentMode('air_quality')).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
it('should support air quality in manual mode', () => {
|
|
209
|
+
device.details.mode = 'manual';
|
|
210
|
+
expect(device.isFeatureSupportedInCurrentMode('air_quality')).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
it('should restrict fan speed control to manual mode', () => {
|
|
213
|
+
device.details.mode = 'manual';
|
|
214
|
+
expect(device.isFeatureSupportedInCurrentMode('fan_speed')).toBe(true);
|
|
215
|
+
device.details.mode = 'auto';
|
|
216
|
+
expect(device.isFeatureSupportedInCurrentMode('fan_speed')).toBe(false);
|
|
217
|
+
});
|
|
218
|
+
it('should disable display and child lock in sleep mode', () => {
|
|
219
|
+
device.details.mode = 'sleep';
|
|
220
|
+
expect(device.isFeatureSupportedInCurrentMode('display')).toBe(false);
|
|
221
|
+
expect(device.isFeatureSupportedInCurrentMode('child_lock')).toBe(false);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
describe('State Tracking and Updates', () => {
|
|
225
|
+
it('should track complete device state', () => {
|
|
226
|
+
device.details = {
|
|
227
|
+
enabled: true,
|
|
228
|
+
mode: 'auto',
|
|
229
|
+
level: 2,
|
|
230
|
+
speed: 2,
|
|
231
|
+
display: true,
|
|
232
|
+
child_lock: false,
|
|
233
|
+
night_light: 'dim',
|
|
234
|
+
air_quality: 2,
|
|
235
|
+
air_quality_value: 32,
|
|
236
|
+
filter_life: {
|
|
237
|
+
percent: 55,
|
|
238
|
+
replace_indicator: false
|
|
239
|
+
},
|
|
240
|
+
timer_remain: 1800
|
|
241
|
+
};
|
|
242
|
+
expect(device.deviceStatus).toBe('on');
|
|
243
|
+
expect(device.mode).toBe('auto');
|
|
244
|
+
expect(device.speed).toBe(2);
|
|
245
|
+
expect(device.screenStatus).toBe('on'); // display: true maps to 'on'
|
|
246
|
+
expect(device.childLock).toBe(false);
|
|
247
|
+
expect(device.airQualityLevel).toBe(2);
|
|
248
|
+
expect(device.airQualityValue).toBe(32);
|
|
249
|
+
expect(device.filterLife).toBe(55);
|
|
250
|
+
});
|
|
251
|
+
it('should handle boolean display values correctly', () => {
|
|
252
|
+
device.details = { display: true };
|
|
253
|
+
expect(device.screenStatus).toBe('on');
|
|
254
|
+
device.details = { display: false };
|
|
255
|
+
expect(device.screenStatus).toBe('off');
|
|
256
|
+
});
|
|
257
|
+
it('should track filter replacement warnings', () => {
|
|
258
|
+
// Filter life getting low
|
|
259
|
+
device.details = {
|
|
260
|
+
filter_life: {
|
|
261
|
+
percent: 15,
|
|
262
|
+
replace_indicator: false
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
expect(device.filterLife).toBe(15);
|
|
266
|
+
// Filter replacement needed
|
|
267
|
+
device.details = {
|
|
268
|
+
filter_life: {
|
|
269
|
+
percent: 5,
|
|
270
|
+
replace_indicator: true
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
expect(device.filterLife).toBe(5);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
describe('Air Quality History and Trends', () => {
|
|
277
|
+
it('should handle air quality improvements', () => {
|
|
278
|
+
// Poor air quality initially
|
|
279
|
+
device.details = {
|
|
280
|
+
air_quality: 4,
|
|
281
|
+
air_quality_value: 85,
|
|
282
|
+
filter_life: { percent: 40 }
|
|
283
|
+
};
|
|
284
|
+
expect(device.airQualityLevel).toBe(4);
|
|
285
|
+
// Air quality improves
|
|
286
|
+
device.details.air_quality = 2;
|
|
287
|
+
device.details.air_quality_value = 25;
|
|
288
|
+
expect(device.airQualityLevel).toBe(2);
|
|
289
|
+
expect(device.airQualityValue).toBe(25);
|
|
290
|
+
});
|
|
291
|
+
it('should handle air quality deterioration', () => {
|
|
292
|
+
// Good air quality initially
|
|
293
|
+
device.details = {
|
|
294
|
+
air_quality: 2,
|
|
295
|
+
air_quality_value: 20,
|
|
296
|
+
filter_life: { percent: 60 }
|
|
297
|
+
};
|
|
298
|
+
expect(device.airQualityLevel).toBe(2);
|
|
299
|
+
// Air quality worsens
|
|
300
|
+
device.details.air_quality = 4;
|
|
301
|
+
device.details.air_quality_value = 90;
|
|
302
|
+
expect(device.airQualityLevel).toBe(4);
|
|
303
|
+
expect(device.airQualityValue).toBe(90);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
describe('Error Handling and Edge Cases', () => {
|
|
307
|
+
it('should handle completely missing details', () => {
|
|
308
|
+
device.details = undefined;
|
|
309
|
+
expect(device.airQualityLevel).toBe('');
|
|
310
|
+
expect(device.airQualityValue).toBe(0);
|
|
311
|
+
expect(device.filterLife).toBe(0);
|
|
312
|
+
});
|
|
313
|
+
it('should handle partial API responses', () => {
|
|
314
|
+
device.details = {
|
|
315
|
+
mode: 'auto',
|
|
316
|
+
speed: 1
|
|
317
|
+
// All other fields missing
|
|
318
|
+
};
|
|
319
|
+
expect(device.mode).toBe('auto');
|
|
320
|
+
expect(device.speed).toBe(1);
|
|
321
|
+
expect(device.airQualityLevel).toBe('');
|
|
322
|
+
expect(device.airQualityValue).toBe(0);
|
|
323
|
+
expect(device.filterLife).toBe(0);
|
|
324
|
+
});
|
|
325
|
+
it('should handle network errors gracefully', () => {
|
|
326
|
+
// Simulate network error - details remain from last update
|
|
327
|
+
device.details = {
|
|
328
|
+
air_quality: 2,
|
|
329
|
+
air_quality_value: 30,
|
|
330
|
+
filter_life: { percent: 70 }
|
|
331
|
+
};
|
|
332
|
+
// Values preserved from last successful update
|
|
333
|
+
expect(device.airQualityLevel).toBe(2);
|
|
334
|
+
expect(device.airQualityValue).toBe(30);
|
|
335
|
+
expect(device.filterLife).toBe(70);
|
|
336
|
+
});
|
|
337
|
+
it('should handle invalid air quality levels', () => {
|
|
338
|
+
device.details = {
|
|
339
|
+
air_quality: 10, // Invalid level (should be 1-5)
|
|
340
|
+
air_quality_value: 50,
|
|
341
|
+
filter_life: { percent: 50 }
|
|
342
|
+
};
|
|
343
|
+
expect(device.airQualityLevel).toBe(10); // Return raw value for debugging
|
|
344
|
+
expect(device.airQualityValue).toBe(50);
|
|
345
|
+
});
|
|
346
|
+
it('should handle negative filter life percent', () => {
|
|
347
|
+
device.details = {
|
|
348
|
+
filter_life: {
|
|
349
|
+
percent: -10,
|
|
350
|
+
replace_indicator: true
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
expect(device.filterLife).toBe(-10); // Allow for error detection
|
|
354
|
+
});
|
|
355
|
+
it('should handle filter life percent over 100', () => {
|
|
356
|
+
device.details = {
|
|
357
|
+
filter_life: {
|
|
358
|
+
percent: 150,
|
|
359
|
+
replace_indicator: false
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
expect(device.filterLife).toBe(150); // Allow for error detection
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
});
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for VeSyncAirBaseV2 air quality data parsing
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const airBaseV2_1 = require("../../lib/fans/airBaseV2");
|
|
7
|
+
// Mock VeSync manager
|
|
8
|
+
const mockManager = {
|
|
9
|
+
username: 'test@example.com',
|
|
10
|
+
password: 'testpass',
|
|
11
|
+
token: 'testtoken',
|
|
12
|
+
accountID: 'testaccount',
|
|
13
|
+
apiKey: 'testapikey',
|
|
14
|
+
apiBase: 'testbase',
|
|
15
|
+
timezone: 'UTC'
|
|
16
|
+
};
|
|
17
|
+
describe('VeSyncAirBaseV2 Air Quality Features', () => {
|
|
18
|
+
let device;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
const deviceDetails = {
|
|
21
|
+
deviceName: 'LAP-V102S Test',
|
|
22
|
+
deviceType: 'LAP-V102S-AASR',
|
|
23
|
+
cid: 'test-cid',
|
|
24
|
+
uuid: 'test-uuid',
|
|
25
|
+
deviceStatus: 'on',
|
|
26
|
+
deviceRegion: 'US',
|
|
27
|
+
configModule: 'VeSyncAirBaseV2',
|
|
28
|
+
macId: '00:11:22:33:44:55',
|
|
29
|
+
deviceCategory: 'fan',
|
|
30
|
+
connectionStatus: 'online'
|
|
31
|
+
};
|
|
32
|
+
device = new airBaseV2_1.VeSyncAirBaseV2(deviceDetails, mockManager);
|
|
33
|
+
});
|
|
34
|
+
describe('Air Quality Data Parsing', () => {
|
|
35
|
+
it('should parse comprehensive air quality data', () => {
|
|
36
|
+
const mockResponse = {
|
|
37
|
+
result: {
|
|
38
|
+
code: 0,
|
|
39
|
+
result: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
mode: 'auto',
|
|
42
|
+
level: 2,
|
|
43
|
+
speed: 2,
|
|
44
|
+
display: 'on',
|
|
45
|
+
child_lock: false,
|
|
46
|
+
night_light: 'off',
|
|
47
|
+
air_quality: 'Good',
|
|
48
|
+
air_quality_value: 18,
|
|
49
|
+
pm1: 12,
|
|
50
|
+
pm10: 22,
|
|
51
|
+
aq_percent: 85,
|
|
52
|
+
filter_life: 78,
|
|
53
|
+
timer_remain: 3600
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
device.details = mockResponse.result.result;
|
|
58
|
+
expect(device.airQualityLevel).toBe('Good');
|
|
59
|
+
expect(device.airQualityValue).toBe(18);
|
|
60
|
+
expect(device.pm1).toBe(12);
|
|
61
|
+
expect(device.pm10).toBe(22);
|
|
62
|
+
expect(device.aqPercent).toBe(85);
|
|
63
|
+
expect(device.filterLife).toBe(78);
|
|
64
|
+
});
|
|
65
|
+
it('should handle numeric air quality levels', () => {
|
|
66
|
+
device.details = {
|
|
67
|
+
air_quality: 3,
|
|
68
|
+
air_quality_value: 45,
|
|
69
|
+
pm1: 25,
|
|
70
|
+
pm10: 65,
|
|
71
|
+
filter_life: 45
|
|
72
|
+
};
|
|
73
|
+
expect(device.airQualityLevel).toBe(3);
|
|
74
|
+
expect(device.airQualityValue).toBe(45);
|
|
75
|
+
expect(device.pm1).toBe(25);
|
|
76
|
+
expect(device.pm10).toBe(65);
|
|
77
|
+
});
|
|
78
|
+
it('should handle string air quality levels', () => {
|
|
79
|
+
const qualityLevels = ['Excellent', 'Good', 'Fair', 'Poor', 'Very Poor'];
|
|
80
|
+
qualityLevels.forEach((level, index) => {
|
|
81
|
+
device.details = {
|
|
82
|
+
air_quality: level,
|
|
83
|
+
air_quality_value: (index + 1) * 20
|
|
84
|
+
};
|
|
85
|
+
expect(device.airQualityLevel).toBe(level);
|
|
86
|
+
expect(device.airQualityValue).toBe((index + 1) * 20);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
it('should handle missing PM data gracefully', () => {
|
|
90
|
+
device.details = {
|
|
91
|
+
air_quality: 'Good',
|
|
92
|
+
air_quality_value: 20,
|
|
93
|
+
filter_life: 60
|
|
94
|
+
// pm1 and pm10 missing
|
|
95
|
+
};
|
|
96
|
+
expect(device.airQualityLevel).toBe('Good');
|
|
97
|
+
expect(device.airQualityValue).toBe(20);
|
|
98
|
+
expect(device.pm1).toBe(0);
|
|
99
|
+
expect(device.pm10).toBe(0);
|
|
100
|
+
expect(device.aqPercent).toBe(0);
|
|
101
|
+
});
|
|
102
|
+
it('should handle all PM values at zero', () => {
|
|
103
|
+
device.details = {
|
|
104
|
+
air_quality: 'Excellent',
|
|
105
|
+
air_quality_value: 0,
|
|
106
|
+
pm1: 0,
|
|
107
|
+
pm10: 0,
|
|
108
|
+
aq_percent: 100
|
|
109
|
+
};
|
|
110
|
+
expect(device.airQualityValue).toBe(0);
|
|
111
|
+
expect(device.pm1).toBe(0);
|
|
112
|
+
expect(device.pm10).toBe(0);
|
|
113
|
+
expect(device.aqPercent).toBe(100);
|
|
114
|
+
});
|
|
115
|
+
it('should handle extreme air quality values', () => {
|
|
116
|
+
device.details = {
|
|
117
|
+
air_quality: 'Hazardous',
|
|
118
|
+
air_quality_value: 300,
|
|
119
|
+
pm1: 200,
|
|
120
|
+
pm10: 400,
|
|
121
|
+
aq_percent: 5,
|
|
122
|
+
filter_life: 15
|
|
123
|
+
};
|
|
124
|
+
expect(device.airQualityLevel).toBe('Hazardous');
|
|
125
|
+
expect(device.airQualityValue).toBe(300);
|
|
126
|
+
expect(device.pm1).toBe(200);
|
|
127
|
+
expect(device.pm10).toBe(400);
|
|
128
|
+
expect(device.aqPercent).toBe(5);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('Filter Life Management', () => {
|
|
132
|
+
it('should handle normal filter life values', () => {
|
|
133
|
+
const testValues = [0, 25, 50, 75, 100];
|
|
134
|
+
testValues.forEach(value => {
|
|
135
|
+
device.details = { filter_life: value };
|
|
136
|
+
expect(device.filterLife).toBe(value);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
it('should handle low filter life warning threshold', () => {
|
|
140
|
+
device.details = { filter_life: 5 };
|
|
141
|
+
expect(device.filterLife).toBe(5);
|
|
142
|
+
});
|
|
143
|
+
it('should handle filter replacement needed (0%)', () => {
|
|
144
|
+
device.details = { filter_life: 0 };
|
|
145
|
+
expect(device.filterLife).toBe(0);
|
|
146
|
+
});
|
|
147
|
+
it('should handle new filter (100%)', () => {
|
|
148
|
+
device.details = { filter_life: 100 };
|
|
149
|
+
expect(device.filterLife).toBe(100);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe('Device Configuration', () => {
|
|
153
|
+
it('should support air quality features for LAP-V102S', () => {
|
|
154
|
+
expect(device.hasFeature('air_quality')).toBe(true);
|
|
155
|
+
expect(device.hasFeature('display')).toBe(true);
|
|
156
|
+
expect(device.hasFeature('child_lock')).toBe(true);
|
|
157
|
+
expect(device.hasFeature('night_light')).toBe(true);
|
|
158
|
+
expect(device.hasFeature('timer')).toBe(true);
|
|
159
|
+
expect(device.hasFeature('fan_speed')).toBe(true);
|
|
160
|
+
expect(device.hasFeature('auto_mode')).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
it('should return correct maximum speed', () => {
|
|
163
|
+
expect(device.getMaxFanSpeed()).toBe(4); // LAP-V102S has 4 speeds
|
|
164
|
+
});
|
|
165
|
+
it('should support air quality in all modes', () => {
|
|
166
|
+
const modes = ['auto', 'manual', 'sleep', 'turbo'];
|
|
167
|
+
modes.forEach(mode => {
|
|
168
|
+
device.details.mode = mode;
|
|
169
|
+
expect(device.isFeatureSupportedInCurrentMode('air_quality')).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
describe('Real-time Data Updates', () => {
|
|
174
|
+
it('should handle real-time air quality updates', () => {
|
|
175
|
+
// Initial state
|
|
176
|
+
device.details = {
|
|
177
|
+
air_quality: 'Good',
|
|
178
|
+
air_quality_value: 25,
|
|
179
|
+
pm1: 15,
|
|
180
|
+
pm10: 35
|
|
181
|
+
};
|
|
182
|
+
expect(device.airQualityValue).toBe(25);
|
|
183
|
+
// Simulate air quality change
|
|
184
|
+
device.details.air_quality = 'Fair';
|
|
185
|
+
device.details.air_quality_value = 40;
|
|
186
|
+
device.details.pm1 = 28;
|
|
187
|
+
device.details.pm10 = 55;
|
|
188
|
+
expect(device.airQualityLevel).toBe('Fair');
|
|
189
|
+
expect(device.airQualityValue).toBe(40);
|
|
190
|
+
expect(device.pm1).toBe(28);
|
|
191
|
+
expect(device.pm10).toBe(55);
|
|
192
|
+
});
|
|
193
|
+
it('should handle rapid air quality fluctuations', () => {
|
|
194
|
+
const rapidUpdates = [
|
|
195
|
+
{ quality: 'Good', value: 20 },
|
|
196
|
+
{ quality: 'Fair', value: 35 },
|
|
197
|
+
{ quality: 'Good', value: 22 },
|
|
198
|
+
{ quality: 'Poor', value: 75 },
|
|
199
|
+
{ quality: 'Fair', value: 38 }
|
|
200
|
+
];
|
|
201
|
+
rapidUpdates.forEach(update => {
|
|
202
|
+
device.details.air_quality = update.quality;
|
|
203
|
+
device.details.air_quality_value = update.value;
|
|
204
|
+
expect(device.airQualityLevel).toBe(update.quality);
|
|
205
|
+
expect(device.airQualityValue).toBe(update.value);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
describe('Device State Synchronization', () => {
|
|
210
|
+
it('should maintain consistent state across updates', () => {
|
|
211
|
+
device.details = {
|
|
212
|
+
enabled: true,
|
|
213
|
+
mode: 'auto',
|
|
214
|
+
level: 3,
|
|
215
|
+
speed: 3,
|
|
216
|
+
display: 'on',
|
|
217
|
+
child_lock: true,
|
|
218
|
+
air_quality: 'Good',
|
|
219
|
+
air_quality_value: 22,
|
|
220
|
+
filter_life: 65
|
|
221
|
+
};
|
|
222
|
+
expect(device.deviceStatus).toBe('on');
|
|
223
|
+
expect(device.mode).toBe('auto');
|
|
224
|
+
expect(device.speed).toBe(3);
|
|
225
|
+
expect(device.screenStatus).toBe('on');
|
|
226
|
+
expect(device.childLock).toBe(true);
|
|
227
|
+
expect(device.airQualityLevel).toBe('Good');
|
|
228
|
+
expect(device.filterLife).toBe(65);
|
|
229
|
+
});
|
|
230
|
+
it('should handle partial state updates', () => {
|
|
231
|
+
// Initial state
|
|
232
|
+
device.details = {
|
|
233
|
+
mode: 'auto',
|
|
234
|
+
speed: 2,
|
|
235
|
+
air_quality_value: 30,
|
|
236
|
+
filter_life: 70
|
|
237
|
+
};
|
|
238
|
+
// Partial update (only some fields changed)
|
|
239
|
+
device.details.air_quality_value = 35;
|
|
240
|
+
device.details.speed = 3;
|
|
241
|
+
expect(device.mode).toBe('auto'); // Unchanged
|
|
242
|
+
expect(device.speed).toBe(3); // Changed
|
|
243
|
+
expect(device.airQualityValue).toBe(35); // Changed
|
|
244
|
+
expect(device.filterLife).toBe(70); // Unchanged
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
describe('Error Conditions', () => {
|
|
248
|
+
it('should handle API errors gracefully', () => {
|
|
249
|
+
device.details = undefined;
|
|
250
|
+
expect(device.airQualityValue).toBe(0);
|
|
251
|
+
expect(device.pm1).toBe(0);
|
|
252
|
+
expect(device.pm10).toBe(0);
|
|
253
|
+
expect(device.aqPercent).toBe(0);
|
|
254
|
+
expect(device.filterLife).toBe(0);
|
|
255
|
+
});
|
|
256
|
+
it('should handle corrupted data fields', () => {
|
|
257
|
+
device.details = {
|
|
258
|
+
air_quality: { invalid: 'object' },
|
|
259
|
+
air_quality_value: 'not_a_number',
|
|
260
|
+
pm1: null,
|
|
261
|
+
pm10: undefined,
|
|
262
|
+
filter_life: []
|
|
263
|
+
};
|
|
264
|
+
expect(device.airQualityLevel).toEqual({ invalid: 'object' });
|
|
265
|
+
expect(device.airQualityValue).toBe(0);
|
|
266
|
+
expect(device.pm1).toBe(0);
|
|
267
|
+
expect(device.pm10).toBe(0);
|
|
268
|
+
expect(device.filterLife).toBe(0);
|
|
269
|
+
});
|
|
270
|
+
it('should handle network timeout scenarios', () => {
|
|
271
|
+
// Simulate stale data from last successful update
|
|
272
|
+
device.details = {
|
|
273
|
+
air_quality: 'Good',
|
|
274
|
+
air_quality_value: 25,
|
|
275
|
+
filter_life: 60
|
|
276
|
+
};
|
|
277
|
+
// Values should remain from last update
|
|
278
|
+
expect(device.airQualityLevel).toBe('Good');
|
|
279
|
+
expect(device.airQualityValue).toBe(25);
|
|
280
|
+
expect(device.filterLife).toBe(60);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|