matterbridge-roborock-vacuum-plugin
Version:
Matterbridge Roborock Vacuum Plugin
560 lines • 29.9 kB
JavaScript
import { MatterbridgeIdentifyServer, MatterbridgeServiceAreaServer } from 'matterbridge';
import { MatterbridgeRvcOperationalStateServer } from 'matterbridge/devices';
import { RvcCleanMode, ServiceArea } from 'matterbridge/matter/clusters';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { MapInfo } from '../core/application/models/MapInfo.js';
import { RoomMap } from '../core/application/models/RoomMap.js';
import { HomeEntity } from '../core/domain/entities/Home.js';
import { PlatformConfigManager } from '../platform/platformConfigManager.js';
import { RoborockVacuumCleaner } from '../types/roborockVacuumCleaner.js';
import { asPartial, asType, setReadOnlyProperty } from './testUtils.js';
function createMockLogger() {
return asType({
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
notice: vi.fn(),
logLevel: 'info',
});
}
describe('RoborockVacuumCleaner', () => {
let device;
let homeInfo;
let logger;
let vacuum;
let configManager;
let roborockService;
beforeEach(() => {
device = {
duid: 'duid-123',
specs: { model: 'roborock.s5', firmwareVersion: '1.0.0' },
serialNumber: 'serial-123',
deviceName: 'TestVac',
name: 'TestVac',
scenes: [],
};
const roomMap = new RoomMap([]);
const mapInfo = MapInfo.empty();
homeInfo = new HomeEntity(1, 'Test Home', roomMap, mapInfo, 0);
configManager = PlatformConfigManager.create(asPartial({
pluginConfiguration: asPartial({
enableMultipleMap: false,
enableServerMode: false,
}),
}), createMockLogger());
logger = createMockLogger();
roborockService = asPartial({
setSupportedRoutines: vi.fn(),
setSupportedAreas: vi.fn(),
setSupportedAreaIndexMap: vi.fn(),
getSupportedAreas: vi.fn().mockReturnValue([]),
switchMap: vi.fn().mockResolvedValue(undefined),
});
vacuum = new RoborockVacuumCleaner(device, homeInfo, configManager, roborockService, logger);
vi.spyOn(vacuum.log, 'info').mockImplementation(() => { });
vi.spyOn(vacuum.log, 'warn').mockImplementation(() => { });
vi.spyOn(vacuum.log, 'debug').mockImplementation(() => { });
vi.spyOn(vacuum.log, 'error').mockImplementation(() => { });
vi.spyOn(vacuum, 'stateOf').mockReturnValue({});
});
it('should construct with correct properties', () => {
expect(vacuum).toBeInstanceOf(RoborockVacuumCleaner);
expect(vacuum.device).toBe(device);
});
describe('resolveAllRoomsForActiveMap', () => {
it('should return rooms for active map when single map exists', () => {
const mockSupportedAreas = [
{ areaId: 1, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 2, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
];
roborockService.getSupportedAreas = vi.fn().mockReturnValue(mockSupportedAreas);
vi.spyOn(vacuum, 'getAttribute').mockReturnValue([]);
const result = vacuum.resolveAllRoomsForActiveMap();
expect(result).toEqual([1, 2]);
});
it('should infer active map from current selectedAreas attribute', () => {
const mockSupportedAreas = [
{ areaId: 1, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 2, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 3, mapId: 1, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 4, mapId: 1, areaInfo: { locationInfo: null, landmarkInfo: null } },
];
roborockService.getSupportedAreas = vi.fn().mockReturnValue(mockSupportedAreas);
vi.spyOn(vacuum, 'getAttribute').mockReturnValue([3]);
const result = vacuum.resolveAllRoomsForActiveMap();
expect(result).toEqual([3, 4]);
});
it('should use homeInFo.activeMapId when no selectedAreas hint exists', () => {
const mockSupportedAreas = [
{ areaId: 1, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 2, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 3, mapId: 1, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 4, mapId: 1, areaInfo: { locationInfo: null, landmarkInfo: null } },
];
roborockService.getSupportedAreas = vi.fn().mockReturnValue(mockSupportedAreas);
vi.spyOn(vacuum, 'getAttribute').mockReturnValue([]);
homeInfo = new HomeEntity(1, 'Test Home', new RoomMap([]), MapInfo.empty(), 0);
setReadOnlyProperty(homeInfo, 'activeMapId', 1);
const newVacuum = new RoborockVacuumCleaner(device, homeInfo, configManager, roborockService, logger);
vi.spyOn(newVacuum, 'getAttribute').mockReturnValue([]);
vi.spyOn(newVacuum.log, 'info').mockImplementation(() => { });
vi.spyOn(newVacuum.log, 'warn').mockImplementation(() => { });
vi.spyOn(newVacuum.log, 'debug').mockImplementation(() => { });
vi.spyOn(newVacuum.log, 'error').mockImplementation(() => { });
vi.spyOn(newVacuum, 'stateOf').mockReturnValue({});
const result = newVacuum.resolveAllRoomsForActiveMap();
expect(result).toEqual([3, 4]);
});
it('should fall back to first map rooms when no hints exist', () => {
const mockSupportedAreas = [
{ areaId: 1, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 2, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 3, mapId: 1, areaInfo: { locationInfo: null, landmarkInfo: null } },
];
roborockService.getSupportedAreas = vi.fn().mockReturnValue(mockSupportedAreas);
vi.spyOn(vacuum, 'getAttribute').mockReturnValue([]);
// homeInfo.activeMapId is -1 by default
const result = vacuum.resolveAllRoomsForActiveMap();
expect(result).toEqual([1, 2]);
});
it('should return empty list when no rooms exist', () => {
roborockService.getSupportedAreas = vi.fn().mockReturnValue([]);
vi.spyOn(vacuum, 'getAttribute').mockReturnValue([]);
const result = vacuum.resolveAllRoomsForActiveMap();
expect(result).toEqual([]);
});
});
it('should call behaviorHandler for identify command', async () => {
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
await vacuum.executeCommandHandler('identify', { identifyTime: 5 }, 'identify', vacuum.stateOf(MatterbridgeIdentifyServer), vacuum);
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('identify', 5);
});
describe('SELECT_AREAS command with non-empty input (regression tests)', () => {
it('should execute non-empty selectAreas without regressing to empty behavior', async () => {
const mockSupportedAreas = [
{ areaId: 1, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 2, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
];
vi.mocked(roborockService.getSupportedAreas).mockReturnValue(mockSupportedAreas);
vi.spyOn(vacuum, 'getAttribute').mockReturnValue([]);
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
await vacuum.executeCommandHandler('selectAreas', { newAreas: [1, 2] }, 'serviceArea', vacuum.stateOf(MatterbridgeServiceAreaServer), vacuum);
// Most importantly: non-empty input should NOT be populated; should pass through unchanged
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('selectAreas', [1, 2]);
});
it('should not populate all rooms when non-empty areas explicitly provided (regression guard)', async () => {
const mockSupportedAreas = [
{ areaId: 1, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 2, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 3, mapId: 1, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 4, mapId: 1, areaInfo: { locationInfo: null, landmarkInfo: null } },
];
vi.mocked(roborockService.getSupportedAreas).mockReturnValue(mockSupportedAreas);
vi.spyOn(vacuum, 'getAttribute').mockReturnValue([]);
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
// Explicitly select only areas [1, 2] (not all areas, not all of any specific map)
await vacuum.executeCommandHandler('selectAreas', { newAreas: [1, 2] }, 'serviceArea', vacuum.stateOf(MatterbridgeServiceAreaServer), vacuum);
// Should pass through exactly as provided, not populate with all map rooms
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('selectAreas', [1, 2]);
// Verify it wasn't expanded to other areas
expect(behaviorHandler.executeCommand).not.toHaveBeenCalledWith('selectAreas', [1, 2, 3, 4]);
});
it('should NOT call updateAttribute for selectedAreas when explicit rooms are provided (regression guard)', async () => {
const mockSupportedAreas = [
{ areaId: 1, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
{ areaId: 2, mapId: 0, areaInfo: { locationInfo: null, landmarkInfo: null } },
];
vi.mocked(roborockService.getSupportedAreas).mockReturnValue(mockSupportedAreas);
vi.spyOn(vacuum, 'getAttribute').mockReturnValue([]);
const updateAttributeSpy = vi.spyOn(vacuum, 'updateAttribute').mockImplementation(() => Promise.resolve(true));
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
await vacuum.executeCommandHandler('selectAreas', { newAreas: [1, 2] }, 'serviceArea', vacuum.stateOf(MatterbridgeServiceAreaServer), vacuum);
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('selectAreas', [1, 2]);
// Explicit-rooms branch must NOT call updateAttribute for selectedAreas (user-approved scope restriction)
expect(updateAttributeSpy).not.toHaveBeenCalledWith(ServiceArea.id, 'selectedAreas', expect.anything(), expect.anything());
});
});
it('should call behaviorHandler for selectAreas command', async () => {
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
await vacuum.executeCommandHandler('selectAreas', { newAreas: [1, 2] }, 'serviceArea', vacuum.stateOf(MatterbridgeServiceAreaServer), vacuum);
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('selectAreas', [1, 2]);
});
it('should call behaviorHandler for changeToMode command', async () => {
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
const request = { newMode: 42 };
await vacuum.executeCommandHandler('changeToMode', request, 'modeSelect', vacuum.stateOf(MatterbridgeRvcOperationalStateServer), vacuum);
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('changeToMode', 42);
});
it('should call behaviorHandler for pause command', async () => {
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
await vacuum.executeCommandHandler('pause', {}, 'operationalState', vacuum.stateOf(MatterbridgeRvcOperationalStateServer), vacuum);
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('pause');
});
it('should call behaviorHandler for resume command', async () => {
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
await vacuum.executeCommandHandler('resume', {}, 'operationalState', vacuum.stateOf(MatterbridgeRvcOperationalStateServer), vacuum);
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('resume');
});
it('should call behaviorHandler for goHome command', async () => {
const behaviorHandler = {
executeCommand: vi.fn(),
setCommandHandler: vi.fn(),
log: logger,
commands: {},
};
vacuum.configureHandler(behaviorHandler);
await vacuum.executeCommandHandler('goHome', {}, 'rvcOperationalState', vacuum.stateOf(MatterbridgeRvcOperationalStateServer), vacuum);
expect(behaviorHandler.executeCommand).toHaveBeenCalledWith('goHome');
});
it('should cover initializeDeviceConfiguration with experimental features', () => {
const expLogger = createMockLogger();
const expConfig = asPartial({
authentication: {
username: 'user',
region: 'US',
forceAuthentication: false,
authenticationMethod: 'Password',
},
pluginConfiguration: {
whiteList: [],
enableServerMode: true,
enableMultipleMap: true,
sanitizeSensitiveLogs: false,
refreshInterval: 60,
debug: false,
unregisterOnShutdown: false,
},
advancedFeature: {
enableAdvancedFeature: true,
settings: {
clearStorageOnStartup: false,
enableLiveMapUpdates: false,
showRoutinesAsRoom: false,
includeDockStationStatus: false,
includeVacuumErrorStatus: false,
forceRunAtDefault: true,
useVacationModeToSendVacuumToDock: false,
enableCleanModeMapping: false,
cleanModeSettings: {},
overrideMatterConfiguration: false,
matterOverrideSettings: {
matterVendorName: 'xxx',
matterVendorId: 123,
matterProductName: 'yy',
matterProductId: 456,
},
enableEmailNotification: false,
emailNotificationSettings: {},
},
},
});
const configManager = PlatformConfigManager.create(expConfig, expLogger);
const dev = { ...device, data: { model: 'roborock.s7', firmwareVersion: '2.0.0' } };
const testHomeInfo = new HomeEntity(1, 'Test', new RoomMap([]), MapInfo.empty(), 0);
const result = RoborockVacuumCleaner['initializeDeviceConfiguration'](dev, testHomeInfo, configManager, roborockService, expLogger);
expect(result.cleanModes).toBeDefined();
expect(result.supportedAreas).toBeDefined();
expect(result.supportedMaps).toBeDefined();
expect(result.supportedAreaAndRoutines).toBeDefined();
expect(result.deviceName).toContain(dev.name);
expect(result.bridgeMode).toBe('server');
});
it('should cover initializeDeviceConfiguration with minimal config', () => {
const minLogger = createMockLogger();
const minConfig = asPartial({
authentication: {
username: 'user',
region: 'US',
forceAuthentication: false,
authenticationMethod: 'Password',
},
pluginConfiguration: {
whiteList: [],
enableServerMode: false,
enableMultipleMap: false,
sanitizeSensitiveLogs: false,
refreshInterval: 60,
debug: false,
unregisterOnShutdown: false,
},
advancedFeature: {
enableAdvancedFeature: false,
settings: {
clearStorageOnStartup: false,
enableLiveMapUpdates: false,
showRoutinesAsRoom: false,
includeDockStationStatus: false,
includeVacuumErrorStatus: false,
forceRunAtDefault: false,
useVacationModeToSendVacuumToDock: false,
enableCleanModeMapping: false,
cleanModeSettings: {},
overrideMatterConfiguration: false,
matterOverrideSettings: {
matterVendorName: 'xxx',
matterVendorId: 123,
matterProductName: 'yy',
matterProductId: 456,
},
enableEmailNotification: false,
emailNotificationSettings: {},
},
},
});
const configManager = PlatformConfigManager.create(minConfig, minLogger);
const testHomeInfo = new HomeEntity(1, 'Test', new RoomMap([]), MapInfo.empty(), 0);
const result = RoborockVacuumCleaner['initializeDeviceConfiguration'](device, testHomeInfo, configManager, roborockService, minLogger);
expect(result.cleanModes).toBeDefined();
expect(result.supportedAreas).toBeDefined();
expect(result.supportedMaps).toBeDefined();
expect(result.supportedAreaAndRoutines).toBeDefined();
expect(result.deviceName).toContain(device.name);
expect(result.bridgeMode).toBe('matter');
});
describe('ServiceArea Feature Configuration', () => {
it('should have ServiceArea cluster server configured', () => {
// Verify the cluster server exists
const serviceAreaServer = vacuum.stateOf(MatterbridgeServiceAreaServer);
expect(serviceAreaServer).toBeDefined();
});
});
describe('initializeDeviceConfiguration with resolved areas and maps', () => {
it('should use resolved areas instead of empty array when passed', () => {
const mockDevice = {
duid: 'test-duid',
specs: { model: 'roborock.s5', firmwareVersion: '1.0.0' },
serialNumber: 'test-serial',
name: 'TestVac',
scenes: [],
};
const mockHomeInfo = new HomeEntity(1, 'Test Home', RoomMap.empty(), MapInfo.empty(), 0);
const resolvedAreas = [
{ areaId: 1, mapId: 1, name: 'Living Room' },
{ areaId: 2, mapId: 1, name: 'Kitchen' },
];
const mockConfigManager = PlatformConfigManager.create(asPartial({
pluginConfiguration: asPartial({
enableMultipleMap: false,
enableServerMode: false,
}),
advancedFeature: asPartial({
enableAdvancedFeature: false,
settings: asPartial({ showRoutinesAsRoom: false }),
}),
}), logger);
const config = RoborockVacuumCleaner.initializeDeviceConfiguration(mockDevice, mockHomeInfo, mockConfigManager, roborockService, logger, resolvedAreas, []);
expect(config.supportedAreas).toEqual(resolvedAreas);
expect(config.supportedAreaAndRoutines).toEqual(resolvedAreas);
});
it('should include resolved maps when passed', () => {
const mockDevice = {
duid: 'test-duid',
specs: { model: 'roborock.s5', firmwareVersion: '1.0.0' },
serialNumber: 'test-serial',
name: 'TestVac',
scenes: [],
};
const mockHomeInfo = new HomeEntity(1, 'Test Home', RoomMap.empty(), MapInfo.empty(), 0);
const resolvedMaps = [{ mapId: 1, name: 'Map 1' }];
const mockConfigManager = PlatformConfigManager.create(asPartial({
pluginConfiguration: asPartial({
enableMultipleMap: false,
enableServerMode: false,
}),
advancedFeature: asPartial({
enableAdvancedFeature: false,
settings: asPartial({ showRoutinesAsRoom: false }),
}),
}), logger);
const config = RoborockVacuumCleaner.initializeDeviceConfiguration(mockDevice, mockHomeInfo, mockConfigManager, roborockService, logger, [], resolvedMaps);
// Should include the resolved map
expect(config.supportedMaps).toHaveLength(1);
expect(config.supportedMaps[0]).toEqual(resolvedMaps[0]);
});
it('should support routine maps when showRoutinesAsRoom is enabled', () => {
const mockDevice = {
duid: 'test-duid',
specs: { model: 'roborock.s5', firmwareVersion: '1.0.0' },
serialNumber: 'test-serial',
name: 'TestVac',
scenes: [],
};
const mockHomeInfo = new HomeEntity(1, 'Test Home', RoomMap.empty(), MapInfo.empty(), 0);
const resolvedMaps = [{ mapId: 1, name: 'Map 1' }];
const mockConfigManager = PlatformConfigManager.create(asPartial({
pluginConfiguration: asPartial({
enableMultipleMap: false,
enableServerMode: false,
}),
advancedFeature: asPartial({
enableAdvancedFeature: false,
settings: asPartial({ showRoutinesAsRoom: true }),
}),
}), logger);
const config = RoborockVacuumCleaner.initializeDeviceConfiguration(mockDevice, mockHomeInfo, mockConfigManager, roborockService, logger, [], resolvedMaps);
// Should include the resolved map
expect(config.supportedMaps).toContainEqual(resolvedMaps[0]);
});
it('should place resolved areas before any routine areas when both exist', () => {
const mockDevice = {
duid: 'test-duid',
specs: { model: 'roborock.s5', firmwareVersion: '1.0.0' },
serialNumber: 'test-serial',
name: 'TestVac',
scenes: [],
};
const mockHomeInfo = new HomeEntity(1, 'Test Home', RoomMap.empty(), MapInfo.empty(), 0);
const resolvedAreas = [{ areaId: 1, mapId: 1 }, { areaId: 2, mapId: 1 }];
const mockConfigManager = PlatformConfigManager.create(asPartial({
pluginConfiguration: asPartial({
enableMultipleMap: false,
enableServerMode: false,
}),
advancedFeature: asPartial({
enableAdvancedFeature: false,
settings: asPartial({ showRoutinesAsRoom: false }),
}),
}), logger);
const config = RoborockVacuumCleaner.initializeDeviceConfiguration(mockDevice, mockHomeInfo, mockConfigManager, roborockService, logger, resolvedAreas, []);
// Resolved areas should come before routine areas in the combined list
expect(config.supportedAreaAndRoutines.slice(0, resolvedAreas.length)).toEqual(resolvedAreas);
});
it('should use empty arrays when resolved areas and maps are empty', () => {
const mockDevice = {
duid: 'test-duid',
specs: { model: 'roborock.s5', firmwareVersion: '1.0.0' },
serialNumber: 'test-serial',
name: 'TestVac',
scenes: [],
};
const mockHomeInfo = new HomeEntity(1, 'Test Home', RoomMap.empty(), MapInfo.empty(), 0);
const mockConfigManager = PlatformConfigManager.create(asPartial({
pluginConfiguration: asPartial({
enableMultipleMap: false,
enableServerMode: false,
}),
advancedFeature: asPartial({
enableAdvancedFeature: false,
settings: asPartial({ showRoutinesAsRoom: false }),
}),
}), logger);
const config = RoborockVacuumCleaner.initializeDeviceConfiguration(mockDevice, mockHomeInfo, mockConfigManager, roborockService, logger, [], []);
expect(config.supportedAreas).toEqual([]);
expect(config.supportedMaps).toEqual([]);
expect(config.supportedAreaAndRoutines).toEqual([]);
});
});
describe('RoborockVacuumCleaner constructor with resolved areas', () => {
it('should construct successfully with resolved areas', () => {
const resolvedAreas = [{ areaId: 1, mapId: 1 }];
const resolvedMaps = [{ mapId: 1, name: 'Map 1' }];
const vac = new RoborockVacuumCleaner(device, homeInfo, configManager, roborockService, logger, resolvedAreas, resolvedMaps);
expect(vac).toBeInstanceOf(RoborockVacuumCleaner);
expect(vac.device).toBe(device);
});
it('should construct successfully with empty resolved areas', () => {
const vac = new RoborockVacuumCleaner(device, homeInfo, configManager, roborockService, logger, [], []);
expect(vac).toBeInstanceOf(RoborockVacuumCleaner);
});
it('should construct with default empty arrays when parameters not provided', () => {
const vac = new RoborockVacuumCleaner(device, homeInfo, configManager, roborockService, logger);
expect(vac).toBeInstanceOf(RoborockVacuumCleaner);
});
});
describe('createDefaultRvcCleanModeClusterServer', () => {
it('should create Clean Mode cluster with DirectModeChange feature enabled', () => {
// Call the method to initialize the cluster
vacuum.createDefaultRvcCleanModeClusterServer();
// Verify behaviors.require was called with MatterbridgeRvcCleanModeServer
// and DirectModeChange feature (cannot directly inspect .with() but can verify the method was called)
expect(vacuum).toBeInstanceOf(RoborockVacuumCleaner);
});
it('should use default supportedModes when no args provided', () => {
const result = vacuum.createDefaultRvcCleanModeClusterServer();
// Verify method returns this for chaining
expect(result).toBe(vacuum);
});
it('should use default currentMode=1 when no args provided', () => {
const result = vacuum.createDefaultRvcCleanModeClusterServer();
// Verify method returns this for chaining
expect(result).toBe(vacuum);
});
it('should accept custom currentMode and supportedModes parameters', () => {
const customModes = [
{ label: 'Custom Vacuum', mode: 1, modeTags: [{ value: RvcCleanMode.ModeTag.Vacuum }] },
{ label: 'Custom Mop', mode: 2, modeTags: [{ value: RvcCleanMode.ModeTag.Mop }] },
];
vacuum.createDefaultRvcCleanModeClusterServer(2, customModes);
// Verify the instance is still valid and chainable
expect(vacuum).toBeInstanceOf(RoborockVacuumCleaner);
});
it('should return this for method chaining', () => {
const result = vacuum.createDefaultRvcCleanModeClusterServer();
expect(result).toBe(vacuum);
});
it('should include all three default modes (Vacuum, Mop, DeepClean)', () => {
vacuum.createDefaultRvcCleanModeClusterServer();
// Verify instance is created successfully with defaults
expect(vacuum).toBeInstanceOf(RoborockVacuumCleaner);
});
it('should allow mode change during active cleaning (DirectModeChange feature)', () => {
// Set operational state to Running
vacuum.createDefaultRvcCleanModeClusterServer();
// The feature DirectModeChange enables mode changes without requiring Idle state
// Verification: this is tested by the feature flag declaration, which is
// read by controllers to know they can call CHANGE_TO_MODE at any time.
// No explicit test needed here beyond verifying the method completes.
expect(vacuum).toBeInstanceOf(RoborockVacuumCleaner);
});
});
});
//# sourceMappingURL=roborockVacuumCleaner.test.js.map