BREAKING CHANGE(devicemanager): migrate tests to new UniversalDevice/feature-based API, add device factories, SNMP protocol/feature and IP helper utilities
This commit is contained in:
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-01-10 - 3.0.0 - BREAKING CHANGE(devicemanager)
|
||||||
|
migrate tests to new UniversalDevice/feature-based API, add device factories, SNMP protocol/feature and IP helper utilities
|
||||||
|
|
||||||
|
- Replace protocol-specific device classes (Scanner, Printer) with UniversalDevice and feature objects (ScanFeature, PrintFeature, PlaybackFeature, VolumeFeature, PowerFeature, SnmpFeature)
|
||||||
|
- Add device factory functions: createScanner, createPrinter, createSpeaker, createUpsDevice
|
||||||
|
- Add DeviceManager.getDevices selector and updated selectDevice behavior (throws when no match)
|
||||||
|
- Expose SnmpProtocol and other protocol implementations
|
||||||
|
- Introduce IP helper utilities: isValidIp, cidrToIps, getLocalSubnet
|
||||||
|
- Update tests and logging to use feature-based APIs and factories (selectFeature/getFeature, hasFeature, featureCount)
|
||||||
|
|
||||||
## 2026-01-09 - 2.3.1 - fix(readme)
|
## 2026-01-09 - 2.3.1 - fix(readme)
|
||||||
update README to comprehensive, TypeScript-first documentation covering installation, quick start, examples, API usage, events, error handling, requirements, credits, and legal/company information
|
update README to comprehensive, TypeScript-first documentation covering installation, quick start, examples, API usage, events, error handling, requirements, credits, and legal/company information
|
||||||
|
|
||||||
|
|||||||
136
test/test.ts
136
test/test.ts
@@ -1,26 +1,40 @@
|
|||||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||||
import * as devicemanager from '../ts/index.js';
|
import * as devicemanager from '../ts/index.js';
|
||||||
|
|
||||||
// Test imports
|
// Test core exports
|
||||||
tap.test('should export DeviceManager', async () => {
|
tap.test('should export DeviceManager', async () => {
|
||||||
expect(devicemanager.DeviceManager).toBeDefined();
|
expect(devicemanager.DeviceManager).toBeDefined();
|
||||||
expect(typeof devicemanager.DeviceManager).toEqual('function');
|
expect(typeof devicemanager.DeviceManager).toEqual('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should export Scanner', async () => {
|
tap.test('should export UniversalDevice', async () => {
|
||||||
expect(devicemanager.Scanner).toBeDefined();
|
expect(devicemanager.UniversalDevice).toBeDefined();
|
||||||
expect(typeof devicemanager.Scanner).toEqual('function');
|
expect(typeof devicemanager.UniversalDevice).toEqual('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should export Printer', async () => {
|
tap.test('should export Features', async () => {
|
||||||
expect(devicemanager.Printer).toBeDefined();
|
expect(devicemanager.ScanFeature).toBeDefined();
|
||||||
expect(typeof devicemanager.Printer).toEqual('function');
|
expect(devicemanager.PrintFeature).toBeDefined();
|
||||||
|
expect(devicemanager.PlaybackFeature).toBeDefined();
|
||||||
|
expect(devicemanager.VolumeFeature).toBeDefined();
|
||||||
|
expect(devicemanager.PowerFeature).toBeDefined();
|
||||||
|
expect(devicemanager.SnmpFeature).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should export device factories', async () => {
|
||||||
|
expect(devicemanager.createScanner).toBeDefined();
|
||||||
|
expect(devicemanager.createPrinter).toBeDefined();
|
||||||
|
expect(devicemanager.createSpeaker).toBeDefined();
|
||||||
|
expect(devicemanager.createUpsDevice).toBeDefined();
|
||||||
|
expect(typeof devicemanager.createScanner).toEqual('function');
|
||||||
|
expect(typeof devicemanager.createPrinter).toEqual('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should export protocol implementations', async () => {
|
tap.test('should export protocol implementations', async () => {
|
||||||
expect(devicemanager.EsclProtocol).toBeDefined();
|
expect(devicemanager.EsclProtocol).toBeDefined();
|
||||||
expect(devicemanager.SaneProtocol).toBeDefined();
|
expect(devicemanager.SaneProtocol).toBeDefined();
|
||||||
expect(devicemanager.IppProtocol).toBeDefined();
|
expect(devicemanager.IppProtocol).toBeDefined();
|
||||||
|
expect(devicemanager.SnmpProtocol).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should export retry helpers', async () => {
|
tap.test('should export retry helpers', async () => {
|
||||||
@@ -39,6 +53,28 @@ tap.test('should create DeviceManager instance', async () => {
|
|||||||
expect(dm.isDiscovering).toEqual(false);
|
expect(dm.isDiscovering).toEqual(false);
|
||||||
expect(dm.getScanners()).toEqual([]);
|
expect(dm.getScanners()).toEqual([]);
|
||||||
expect(dm.getPrinters()).toEqual([]);
|
expect(dm.getPrinters()).toEqual([]);
|
||||||
|
expect(dm.getDevices()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test device selector
|
||||||
|
tap.test('should support device selection with IDeviceSelector', async () => {
|
||||||
|
const dm = new devicemanager.DeviceManager({
|
||||||
|
autoDiscovery: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Empty manager should return empty array
|
||||||
|
expect(dm.getDevices({ hasFeature: 'scan' })).toEqual([]);
|
||||||
|
expect(dm.getDevices({ name: 'Test' })).toEqual([]);
|
||||||
|
|
||||||
|
// selectDevice should throw when no devices match
|
||||||
|
let error: Error | null = null;
|
||||||
|
try {
|
||||||
|
dm.selectDevice({ address: '192.168.1.100' });
|
||||||
|
} catch (e) {
|
||||||
|
error = e as Error;
|
||||||
|
}
|
||||||
|
expect(error).not.toBeNull();
|
||||||
|
expect(error?.message).toContain('No device found');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test retry helper
|
// Test retry helper
|
||||||
@@ -114,17 +150,18 @@ tap.test('should start and stop discovery', async () => {
|
|||||||
// Wait a bit for potential device discovery
|
// Wait a bit for potential device discovery
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
|
||||||
// Log discovered devices
|
// Log discovered devices using new API
|
||||||
const scanners = dm.getScanners();
|
const scanners = dm.getDevices({ hasFeature: 'scan' });
|
||||||
const printers = dm.getPrinters();
|
const printers = dm.getDevices({ hasFeature: 'print' });
|
||||||
console.log(`Discovered ${scanners.length} scanner(s) and ${printers.length} printer(s)`);
|
console.log(`Discovered ${scanners.length} scanner(s) and ${printers.length} printer(s)`);
|
||||||
|
|
||||||
for (const scanner of scanners) {
|
for (const scanner of scanners) {
|
||||||
console.log(` Scanner: ${scanner.name} (${scanner.address}:${scanner.port}) - ${scanner.protocol}`);
|
const scanFeature = scanner.getFeature<devicemanager.ScanFeature>('scan');
|
||||||
|
console.log(` Scanner: ${scanner.name} (${scanner.address}) - ${scanFeature?.protocol || 'unknown'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const printer of printers) {
|
for (const printer of printers) {
|
||||||
console.log(` Printer: ${printer.name} (${printer.address}:${printer.port})`);
|
console.log(` Printer: ${printer.name} (${printer.address})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await dm.stopDiscovery();
|
await dm.stopDiscovery();
|
||||||
@@ -134,9 +171,9 @@ tap.test('should start and stop discovery', async () => {
|
|||||||
await dm.shutdown();
|
await dm.shutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test Scanner creation from discovery info
|
// Test Scanner creation using factory
|
||||||
tap.test('should create Scanner from discovery info', async () => {
|
tap.test('should create Scanner device using factory', async () => {
|
||||||
const scanner = devicemanager.Scanner.fromDiscovery({
|
const scanner = devicemanager.createScanner({
|
||||||
id: 'test:scanner:1',
|
id: 'test:scanner:1',
|
||||||
name: 'Test Scanner',
|
name: 'Test Scanner',
|
||||||
address: '192.168.1.100',
|
address: '192.168.1.100',
|
||||||
@@ -150,40 +187,75 @@ tap.test('should create Scanner from discovery info', async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(scanner).toBeInstanceOf(devicemanager.UniversalDevice);
|
||||||
expect(scanner.name).toEqual('Test Scanner');
|
expect(scanner.name).toEqual('Test Scanner');
|
||||||
expect(scanner.address).toEqual('192.168.1.100');
|
expect(scanner.address).toEqual('192.168.1.100');
|
||||||
expect(scanner.port).toEqual(443);
|
expect(scanner.hasFeature('scan')).toEqual(true);
|
||||||
expect(scanner.protocol).toEqual('escl');
|
|
||||||
expect(scanner.supportedFormats).toContain('jpeg');
|
const scanFeature = scanner.selectFeature<devicemanager.ScanFeature>('scan');
|
||||||
expect(scanner.supportedFormats).toContain('pdf');
|
expect(scanFeature).toBeInstanceOf(devicemanager.ScanFeature);
|
||||||
expect(scanner.supportedColorModes).toContain('color');
|
expect(scanFeature.protocol).toEqual('escl');
|
||||||
expect(scanner.supportedColorModes).toContain('grayscale');
|
|
||||||
expect(scanner.supportedSources).toContain('flatbed');
|
|
||||||
expect(scanner.supportedSources).toContain('adf');
|
|
||||||
expect(scanner.hasAdf).toEqual(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test Printer creation from discovery info
|
// Test Printer creation using factory
|
||||||
tap.test('should create Printer from discovery info', async () => {
|
tap.test('should create Printer device using factory', async () => {
|
||||||
const printer = devicemanager.Printer.fromDiscovery({
|
const printer = devicemanager.createPrinter({
|
||||||
id: 'test:printer:1',
|
id: 'test:printer:1',
|
||||||
name: 'Test Printer',
|
name: 'Test Printer',
|
||||||
address: '192.168.1.101',
|
address: '192.168.1.101',
|
||||||
port: 631,
|
port: 631,
|
||||||
|
ippPath: '/ipp/print',
|
||||||
txtRecords: {
|
txtRecords: {
|
||||||
'ty': 'Brother HL-L2350DW',
|
'ty': 'Brother HL-L2350DW',
|
||||||
'rp': 'ipp/print',
|
|
||||||
'Color': 'T',
|
'Color': 'T',
|
||||||
'Duplex': 'T',
|
'Duplex': 'T',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(printer).toBeInstanceOf(devicemanager.UniversalDevice);
|
||||||
expect(printer.name).toEqual('Test Printer');
|
expect(printer.name).toEqual('Test Printer');
|
||||||
expect(printer.address).toEqual('192.168.1.101');
|
expect(printer.address).toEqual('192.168.1.101');
|
||||||
expect(printer.port).toEqual(631);
|
expect(printer.hasFeature('print')).toEqual(true);
|
||||||
expect(printer.supportsColor).toEqual(true);
|
|
||||||
expect(printer.supportsDuplex).toEqual(true);
|
const printFeature = printer.selectFeature<devicemanager.PrintFeature>('print');
|
||||||
expect(printer.uri).toContain('ipp://');
|
expect(printFeature).toBeInstanceOf(devicemanager.PrintFeature);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test UniversalDevice feature management
|
||||||
|
tap.test('should manage features on UniversalDevice', async () => {
|
||||||
|
const device = new devicemanager.UniversalDevice('192.168.1.50', 80, {
|
||||||
|
name: 'Test Device',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(device.name).toEqual('Test Device');
|
||||||
|
expect(device.address).toEqual('192.168.1.50');
|
||||||
|
expect(device.featureCount).toEqual(0);
|
||||||
|
expect(device.hasFeature('scan')).toEqual(false);
|
||||||
|
|
||||||
|
// selectFeature should throw when feature doesn't exist
|
||||||
|
let error: Error | null = null;
|
||||||
|
try {
|
||||||
|
device.selectFeature('scan');
|
||||||
|
} catch (e) {
|
||||||
|
error = e as Error;
|
||||||
|
}
|
||||||
|
expect(error).not.toBeNull();
|
||||||
|
expect(error?.message).toContain("does not have feature 'scan'");
|
||||||
|
|
||||||
|
// getFeature should return undefined
|
||||||
|
expect(device.getFeature('scan')).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test IP helpers
|
||||||
|
tap.test('should export IP helper utilities', async () => {
|
||||||
|
expect(devicemanager.isValidIp).toBeDefined();
|
||||||
|
expect(devicemanager.cidrToIps).toBeDefined();
|
||||||
|
expect(devicemanager.getLocalSubnet).toBeDefined();
|
||||||
|
|
||||||
|
// Test isValidIp
|
||||||
|
expect(devicemanager.isValidIp('192.168.1.1')).toEqual(true);
|
||||||
|
expect(devicemanager.isValidIp('invalid')).toEqual(false);
|
||||||
|
expect(devicemanager.isValidIp('256.1.1.1')).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default tap.start();
|
export default tap.start();
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@ecobridge.xyz/devicemanager',
|
name: '@ecobridge.xyz/devicemanager',
|
||||||
version: '2.3.1',
|
version: '3.0.0',
|
||||||
description: 'a device manager for talking to devices on network and over usb'
|
description: 'a device manager for talking to devices on network and over usb'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user