UNPKG

homebridge-tsvesync

Version:

Homebridge plugin for VeSync devices including Levoit air purifiers, humidifiers, and Etekcity smart outlets

302 lines (264 loc) 10.3 kB
// Set up mocks before imports jest.mock('../../utils/device-factory'); jest.mock('tsvesync'); jest.mock('../../utils/retry'); import { API, Logger, PlatformAccessory, Service as ServiceType, Characteristic as CharacteristicType, CharacteristicValue } from 'homebridge'; import { VeSync } from 'tsvesync'; import { TSVESyncPlatform } from '../../platform'; import { TEST_CONFIG } from '../setup'; import { createMockLogger, createMockSwitch } from '../utils/test-helpers'; import { DeviceFactory } from '../../utils/device-factory'; import { BaseAccessory } from '../../accessories/base.accessory'; import { VeSyncSwitch } from '../../types/device.types'; import { RetryManager } from '../../utils/retry'; const mockDeviceFactory = jest.mocked(DeviceFactory); const mockRetryManager = jest.mocked(RetryManager); describe('Switch Device Tests', () => { let platform: TSVESyncPlatform; let mockLogger: jest.Mocked<Logger>; let mockAPI: jest.Mocked<API>; let mockVeSync: jest.Mocked<VeSync>; let mockSetHandler: jest.Mock; let mockGetHandler: jest.Mock; const defaultConfig = { platform: 'TSVESync', name: 'TSVESync', username: TEST_CONFIG.username || 'test@example.com', password: TEST_CONFIG.password || 'password123', apiUrl: TEST_CONFIG.apiUrl, debug: true, }; beforeEach(() => { jest.clearAllMocks(); // Mock RetryManager to execute operations immediately mockRetryManager.prototype.execute.mockImplementation(async (operation) => { return operation(); }); // Setup handlers mockSetHandler = jest.fn(); mockGetHandler = jest.fn(); // Setup logger mock mockLogger = createMockLogger(); // Setup API mock mockAPI = { version: 2.0, serverVersion: '1.0.0', user: { configPath: jest.fn(), storagePath: jest.fn(), persistPath: jest.fn(), }, hapLegacyTypes: {}, platformAccessory: jest.fn().mockImplementation((name, uuid) => ({ UUID: uuid, displayName: name, context: {}, services: new Map(), addService: jest.fn(), removeService: jest.fn(), getService: jest.fn().mockReturnValue({ getCharacteristic: jest.fn().mockReturnValue({ onSet: jest.fn().mockImplementation((fn) => { mockSetHandler = fn; return { onGet: jest.fn() }; }), onGet: jest.fn().mockImplementation((fn) => { mockGetHandler = fn; return { updateValue: jest.fn() }; }), updateValue: jest.fn(), }), setCharacteristic: jest.fn().mockReturnThis(), }), getServiceById: jest.fn(), })), versionGreaterOrEqual: jest.fn(), registerAccessory: jest.fn(), registerPlatform: jest.fn(), publishCameraAccessories: jest.fn(), registerPlatformAccessories: jest.fn(), unregisterPlatformAccessories: jest.fn(), publishExternalAccessories: jest.fn(), updatePlatformAccessories: jest.fn(), registerPlatformAccessory: jest.fn(), on: jest.fn(), emit: jest.fn(), hap: { Service: { Switch: jest.fn().mockImplementation(() => ({ getCharacteristic: jest.fn().mockReturnValue({ onSet: jest.fn().mockImplementation((fn) => { mockSetHandler = fn; return { onGet: jest.fn() }; }), onGet: jest.fn().mockImplementation((fn) => { mockGetHandler = fn; return { updateValue: jest.fn() }; }), updateValue: jest.fn(), }), setCharacteristic: jest.fn().mockReturnThis(), })), AccessoryInformation: jest.fn(), } as unknown as typeof ServiceType, Characteristic: { On: 'On', Name: 'Name', Model: 'Model', Manufacturer: 'Manufacturer', SerialNumber: 'SerialNumber', FirmwareRevision: 'FirmwareRevision', } as unknown as typeof CharacteristicType, uuid: { generate: jest.fn().mockImplementation((id) => `test-uuid-${id}`), }, }, } as unknown as jest.Mocked<API>; // Create platform instance platform = new TSVESyncPlatform(mockLogger, defaultConfig, mockAPI); platform.isReady = jest.fn().mockResolvedValue(true); platform.discoverDevices = jest.fn().mockResolvedValue(undefined); // Setup VeSync mock mockVeSync = { login: jest.fn().mockResolvedValue(true), getDevices: jest.fn().mockResolvedValue(true), switches: [], } as unknown as jest.Mocked<VeSync>; // Replace VeSync client (platform as any).client = mockVeSync; // Mock DeviceFactory mockDeviceFactory.getAccessoryCategory.mockReturnValue(8); // 8 is the category for switches mockDeviceFactory.createAccessory.mockImplementation((platform, accessory, device) => { const baseAccessory = { service: new mockAPI.hap.Service.Switch(), platform, accessory, device, initialize: jest.fn().mockResolvedValue(undefined), updateCharacteristicValue: jest.fn(), } as unknown as BaseAccessory; return baseAccessory; }); }); afterEach(() => { jest.clearAllMocks(); }); describe('switch state management', () => { let mockSwitch: jest.Mocked<VeSyncSwitch>; let switchAccessory: BaseAccessory; let onCharacteristic: any; beforeEach(async () => { // Create a mock switch with immediate responses mockSwitch = createMockSwitch({ deviceName: 'Test Switch', deviceType: 'ESW01-EU', cid: 'test-cid-123', uuid: 'test-uuid-123', power: false, }); // Mock successful details retrieval mockSwitch.getDetails.mockResolvedValue(true); mockSwitch.deviceStatus = 'off'; // Create a mock accessory const mockAccessory = new mockAPI.platformAccessory(mockSwitch.deviceName, mockAPI.hap.uuid.generate(mockSwitch.cid)); mockAccessory.context.device = mockSwitch; // Create the switch accessory switchAccessory = mockDeviceFactory.createAccessory(platform, mockAccessory, mockSwitch); // Set up the characteristic handlers onCharacteristic = { onSet: jest.fn().mockImplementation(async (value: boolean) => { if (value) { await mockSwitch.turnOn(); } else { await mockSwitch.turnOff(); } }), onGet: jest.fn().mockImplementation(async () => { return mockSwitch.deviceStatus === 'on'; }), }; // Mock the service to use our handlers const mockService = { getCharacteristic: jest.fn().mockReturnValue(onCharacteristic), setCharacteristic: jest.fn().mockReturnThis(), }; (mockAccessory.getService as jest.Mock).mockReturnValue(mockService); await switchAccessory.initialize(); }); it('should handle power state changes', async () => { // Test turning on await onCharacteristic.onSet(true); expect(mockSwitch.turnOn).toHaveBeenCalled(); // Test turning off await onCharacteristic.onSet(false); expect(mockSwitch.turnOff).toHaveBeenCalled(); }); it('should handle device errors', async () => { // Mock error responses mockSwitch.turnOn.mockRejectedValueOnce(new Error('Failed to turn on')); mockSwitch.turnOff.mockRejectedValueOnce(new Error('Failed to turn off')); // Test error handling for turn on await expect(onCharacteristic.onSet(true)).rejects.toThrow('Failed to turn on'); // Test error handling for turn off await expect(onCharacteristic.onSet(false)).rejects.toThrow('Failed to turn off'); }); }); describe('switch device types', () => { beforeEach(async () => { // Ensure platform is ready before each test await platform.isReady(); }); it('should support ESW01-EU devices', async () => { // Create a mock switch with immediate responses const mockSwitch = createMockSwitch({ deviceName: 'Test Switch', deviceType: 'ESW01-EU', cid: 'test-cid-123', uuid: 'test-uuid-123', }); // Mock successful details retrieval mockSwitch.getDetails.mockResolvedValue(true); mockSwitch.deviceStatus = 'off'; // Create a mock accessory const mockAccessory = new mockAPI.platformAccessory(mockSwitch.deviceName, mockAPI.hap.uuid.generate(mockSwitch.cid)); mockAccessory.context.device = mockSwitch; // Create the switch accessory const switchAccessory = mockDeviceFactory.createAccessory(platform, mockAccessory, mockSwitch); await switchAccessory.initialize(); // Get the switch service const switchService = mockAccessory.getService(platform.Service.Switch); expect(switchService).toBeDefined(); expect(mockSwitch.deviceType).toBe('ESW01-EU'); }); it('should support other switch device types', async () => { // Create a mock switch with immediate responses const mockSwitch = createMockSwitch({ deviceName: 'Test Switch', deviceType: 'other-switch-type', cid: 'test-cid-123', uuid: 'test-uuid-123', }); // Mock successful details retrieval mockSwitch.getDetails.mockResolvedValue(true); mockSwitch.deviceStatus = 'off'; // Create a mock accessory const mockAccessory = new mockAPI.platformAccessory(mockSwitch.deviceName, mockAPI.hap.uuid.generate(mockSwitch.cid)); mockAccessory.context.device = mockSwitch; // Create the switch accessory const switchAccessory = mockDeviceFactory.createAccessory(platform, mockAccessory, mockSwitch); await switchAccessory.initialize(); // Get the switch service const switchService = mockAccessory.getService(platform.Service.Switch); expect(switchService).toBeDefined(); expect(mockSwitch.deviceType).toBe('other-switch-type'); }); }); });