@risleylima/escpos 0.2.1 → 1.0.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/README.md +94 -766
- package/dist/adapter/index.d.ts +29 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +25 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/network-adapter/index.d.ts +24 -0
- package/dist/network-adapter/index.d.ts.map +1 -0
- package/dist/network-adapter/index.js +263 -0
- package/dist/network-adapter/index.js.map +1 -0
- package/dist/printer/commands-types.d.ts +37 -0
- package/dist/printer/commands-types.d.ts.map +1 -0
- package/dist/printer/commands-types.js +63 -0
- package/dist/printer/commands-types.js.map +1 -0
- package/dist/printer/commands.d.ts +169 -0
- package/dist/printer/commands.d.ts.map +1 -0
- package/dist/printer/commands.js +192 -0
- package/dist/printer/commands.js.map +1 -0
- package/dist/printer/image-loader.d.ts +17 -0
- package/dist/printer/image-loader.d.ts.map +1 -0
- package/dist/printer/image-loader.js +462 -0
- package/dist/printer/image-loader.js.map +1 -0
- package/dist/printer/image.d.ts +43 -0
- package/dist/printer/image.d.ts.map +1 -0
- package/dist/printer/image.js +132 -0
- package/dist/printer/image.js.map +1 -0
- package/dist/printer/index.d.ts +158 -0
- package/dist/printer/index.d.ts.map +1 -0
- package/dist/printer/index.js +703 -0
- package/dist/printer/index.js.map +1 -0
- package/dist/printer/profiles/bematech/mp4200th.d.ts +13 -0
- package/dist/printer/profiles/bematech/mp4200th.d.ts.map +1 -0
- package/dist/printer/profiles/bematech/mp4200th.js +29 -0
- package/dist/printer/profiles/bematech/mp4200th.js.map +1 -0
- package/dist/printer/profiles/custom/bematech-mp4200th.d.ts +13 -0
- package/dist/printer/profiles/custom/bematech-mp4200th.d.ts.map +1 -0
- package/dist/printer/profiles/custom/bematech-mp4200th.js +21 -0
- package/dist/printer/profiles/custom/bematech-mp4200th.js.map +1 -0
- package/dist/printer/profiles/custom/vkp80iii.d.ts +19 -0
- package/dist/printer/profiles/custom/vkp80iii.d.ts.map +1 -0
- package/dist/printer/profiles/custom/vkp80iii.js +87 -0
- package/dist/printer/profiles/custom/vkp80iii.js.map +1 -0
- package/dist/printer/profiles/default.d.ts +7 -0
- package/dist/printer/profiles/default.d.ts.map +1 -0
- package/dist/printer/profiles/default.js +15 -0
- package/dist/printer/profiles/default.js.map +1 -0
- package/dist/printer/profiles/index.d.ts +41 -0
- package/dist/printer/profiles/index.d.ts.map +1 -0
- package/dist/printer/profiles/index.js +98 -0
- package/dist/printer/profiles/index.js.map +1 -0
- package/dist/printer/profiles/merge.d.ts +7 -0
- package/dist/printer/profiles/merge.d.ts.map +1 -0
- package/dist/printer/profiles/merge.js +58 -0
- package/dist/printer/profiles/merge.js.map +1 -0
- package/dist/printer/profiles/types.d.ts +99 -0
- package/dist/printer/profiles/types.d.ts.map +1 -0
- package/dist/printer/profiles/types.js +8 -0
- package/dist/printer/profiles/types.js.map +1 -0
- package/dist/printer/utils.d.ts +9 -0
- package/dist/printer/utils.d.ts.map +1 -0
- package/dist/printer/utils.js +54 -0
- package/dist/printer/utils.js.map +1 -0
- package/dist/serial-adapter/index.d.ts +17 -0
- package/dist/serial-adapter/index.d.ts.map +1 -0
- package/dist/serial-adapter/index.js +172 -0
- package/dist/serial-adapter/index.js.map +1 -0
- package/dist/usb-adapter/index.d.ts +20 -0
- package/dist/usb-adapter/index.d.ts.map +1 -0
- package/dist/usb-adapter/index.js +264 -0
- package/dist/usb-adapter/index.js.map +1 -0
- package/package.json +42 -15
- package/CHANGELOG.md +0 -74
- package/docs/COVERAGE_ANALYSIS.md +0 -98
- package/docs/DEPENDENCIES_REVIEW.md +0 -127
- package/docs/JSDOC_REVIEW.md +0 -122
- package/docs/LIBRARY_OVERVIEW.md +0 -383
- package/docs/PRE_PUBLISH_CHECKLIST.md +0 -331
- package/docs/PUBLIC_API_ANALYSIS.md +0 -223
- package/docs/README.md +0 -37
- package/docs/SERIALPORT_V13_MIGRATION_COMPLETE.md +0 -127
- package/docs/TESTS_IMPLEMENTED.md +0 -129
- package/docs/USB_V2_REVIEW.md +0 -148
- package/docs/VERIFICATION_RESULTS.md +0 -172
- package/docs/VERSIONING.md +0 -102
- package/examples/printTest.js +0 -59
- package/index.js +0 -7
- package/jest.config.js +0 -16
- package/src/adapter/index.js +0 -75
- package/src/printer/commands.js +0 -199
- package/src/printer/image.js +0 -159
- package/src/printer/index.js +0 -621
- package/src/printer/utils.js +0 -58
- package/src/serial-adapter/index.js +0 -198
- package/src/usb-adapter/index.js +0 -283
- package/tests/README.md +0 -67
- package/tests/integration/printer-flow.test.js +0 -128
- package/tests/unit/adapters/adapter.test.js +0 -49
- package/tests/unit/adapters/serial-adapter.test.js +0 -238
- package/tests/unit/adapters/usb-adapter.test.js +0 -319
- package/tests/unit/image/image.test.js +0 -157
- package/tests/unit/printer/buffer.test.js +0 -60
- package/tests/unit/printer/commands.test.js +0 -109
- package/tests/unit/printer/printer.test.js +0 -405
- package/tests/unit/utils/utils.test.js +0 -96
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const Adapter = require('../../../src/adapter');
|
|
4
|
-
|
|
5
|
-
describe('Adapter', () => {
|
|
6
|
-
let adapter;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
adapter = new Adapter();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('should be an instance of EventEmitter', () => {
|
|
13
|
-
const EventEmitter = require('events');
|
|
14
|
-
expect(adapter).toBeInstanceOf(EventEmitter);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should throw NotImplementedException for connect', () => {
|
|
18
|
-
expect(() => adapter.connect()).toThrow(Error);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should throw NotImplementedException for open', () => {
|
|
22
|
-
expect(() => adapter.open()).toThrow(Error);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should throw NotImplementedException for write', () => {
|
|
26
|
-
expect(() => adapter.write()).toThrow(Error);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should throw NotImplementedException for close', () => {
|
|
30
|
-
expect(() => adapter.close()).toThrow(Error);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should throw NotImplementedException for read', () => {
|
|
34
|
-
expect(() => adapter.read()).toThrow(Error);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should copy properties from provided adapter', () => {
|
|
38
|
-
const mockAdapter = {
|
|
39
|
-
connect: jest.fn(),
|
|
40
|
-
write: jest.fn(),
|
|
41
|
-
customProp: 'test'
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const adapter = new Adapter(mockAdapter);
|
|
45
|
-
expect(adapter.customProp).toBe('test');
|
|
46
|
-
expect(adapter.connect).toBe(mockAdapter.connect);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const Serial = require('../../../src/serial-adapter');
|
|
4
|
-
const { SerialPort } = require('serialport');
|
|
5
|
-
|
|
6
|
-
// Mock serialport (v13 API - uses Promises)
|
|
7
|
-
jest.mock('serialport', () => {
|
|
8
|
-
const mockPort = {
|
|
9
|
-
isOpen: false,
|
|
10
|
-
open: jest.fn().mockResolvedValue(undefined),
|
|
11
|
-
close: jest.fn().mockResolvedValue(undefined),
|
|
12
|
-
write: jest.fn().mockResolvedValue(undefined),
|
|
13
|
-
flush: jest.fn().mockResolvedValue(undefined),
|
|
14
|
-
drain: jest.fn().mockResolvedValue(undefined),
|
|
15
|
-
on: jest.fn(),
|
|
16
|
-
removeListener: jest.fn()
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// Update isOpen when open/close are called
|
|
20
|
-
const originalOpen = mockPort.open;
|
|
21
|
-
mockPort.open = jest.fn().mockImplementation(async () => {
|
|
22
|
-
mockPort.isOpen = true;
|
|
23
|
-
return originalOpen();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const originalClose = mockPort.close;
|
|
27
|
-
mockPort.close = jest.fn().mockImplementation(async () => {
|
|
28
|
-
mockPort.isOpen = false;
|
|
29
|
-
return originalClose();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const SerialPortConstructor = jest.fn().mockImplementation((options) => {
|
|
33
|
-
return mockPort;
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// Attach list as static method to SerialPort constructor
|
|
37
|
-
SerialPortConstructor.list = jest.fn().mockResolvedValue([
|
|
38
|
-
{ path: '/dev/ttyUSB0' },
|
|
39
|
-
{ path: '/dev/ttyUSB1' }
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
SerialPort: SerialPortConstructor
|
|
44
|
-
};
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('Serial Adapter', () => {
|
|
48
|
-
beforeEach(() => {
|
|
49
|
-
jest.clearAllMocks();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe('listSerial', () => {
|
|
53
|
-
it('should list all available serial ports', async () => {
|
|
54
|
-
const ports = await Serial.listSerial();
|
|
55
|
-
expect(Array.isArray(ports)).toBe(true);
|
|
56
|
-
expect(SerialPort.list).toHaveBeenCalled();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should return port objects with path property', async () => {
|
|
60
|
-
const ports = await Serial.listSerial();
|
|
61
|
-
expect(ports.length).toBeGreaterThan(0);
|
|
62
|
-
expect(ports[0]).toHaveProperty('path');
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('connect', () => {
|
|
67
|
-
it('should connect to serial port', async () => {
|
|
68
|
-
const result = await Serial.connect('/dev/ttyUSB0');
|
|
69
|
-
expect(result).toBe(true);
|
|
70
|
-
expect(SerialPort).toHaveBeenCalled();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should verify port exists', async () => {
|
|
74
|
-
await Serial.connect('/dev/ttyUSB0');
|
|
75
|
-
expect(SerialPort.list).toHaveBeenCalled();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should throw error if port does not exist', async () => {
|
|
79
|
-
await expect(Serial.connect('/dev/invalid')).rejects.toThrow('The specified port does not exist!');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should emit connect event', (done) => {
|
|
83
|
-
Serial.once('connect', () => {
|
|
84
|
-
done();
|
|
85
|
-
});
|
|
86
|
-
Serial.connect('/dev/ttyUSB0');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('open', () => {
|
|
91
|
-
beforeEach(async () => {
|
|
92
|
-
await Serial.connect('/dev/ttyUSB0');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should open port if closed', async () => {
|
|
96
|
-
const result = await Serial.open();
|
|
97
|
-
expect(result).toBe(true);
|
|
98
|
-
|
|
99
|
-
// Verify port.open was called
|
|
100
|
-
const mockPort = SerialPort.mock.results[0].value;
|
|
101
|
-
expect(mockPort.open).toHaveBeenCalled();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should return true if already open', async () => {
|
|
105
|
-
// Get the mock port and set it as already open
|
|
106
|
-
const mockPort = SerialPort.mock.results[0].value;
|
|
107
|
-
const previousCallCount = mockPort.open.mock.calls.length;
|
|
108
|
-
mockPort.isOpen = true;
|
|
109
|
-
|
|
110
|
-
const result = await Serial.open();
|
|
111
|
-
expect(result).toBe(true);
|
|
112
|
-
|
|
113
|
-
// Verify port.open was NOT called again (call count should be the same)
|
|
114
|
-
// Since isOpen is true, the code should return early without calling open()
|
|
115
|
-
expect(mockPort.open.mock.calls.length).toBe(previousCallCount);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe('write', () => {
|
|
120
|
-
beforeEach(async () => {
|
|
121
|
-
await Serial.connect('/dev/ttyUSB0');
|
|
122
|
-
await Serial.open();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should write data to port', async () => {
|
|
126
|
-
const testData = Buffer.from('test', 'ascii');
|
|
127
|
-
const result = await Serial.write(testData);
|
|
128
|
-
expect(result).toBe(true);
|
|
129
|
-
|
|
130
|
-
// Verify port.write was called with correct data
|
|
131
|
-
const mockPort = SerialPort.mock.results[0].value;
|
|
132
|
-
expect(mockPort.write).toHaveBeenCalledWith(testData);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('should drain port after write', async () => {
|
|
136
|
-
const testData = Buffer.from('test', 'ascii');
|
|
137
|
-
await Serial.write(testData);
|
|
138
|
-
|
|
139
|
-
const mockPort = SerialPort.mock.results[0].value;
|
|
140
|
-
expect(mockPort.write).toHaveBeenCalled();
|
|
141
|
-
expect(mockPort.drain).toHaveBeenCalled();
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe('close', () => {
|
|
146
|
-
beforeEach(async () => {
|
|
147
|
-
await Serial.connect('/dev/ttyUSB0');
|
|
148
|
-
await Serial.open();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('should close port', async () => {
|
|
152
|
-
const result = await Serial.close();
|
|
153
|
-
expect(result).toBe(true);
|
|
154
|
-
|
|
155
|
-
// Verify close sequence was called
|
|
156
|
-
const mockPort = SerialPort.mock.results[0].value;
|
|
157
|
-
expect(mockPort.flush).toHaveBeenCalled();
|
|
158
|
-
expect(mockPort.drain).toHaveBeenCalled();
|
|
159
|
-
expect(mockPort.close).toHaveBeenCalled();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should flush before closing', async () => {
|
|
163
|
-
await Serial.close();
|
|
164
|
-
|
|
165
|
-
const mockPort = SerialPort.mock.results[0].value;
|
|
166
|
-
expect(mockPort.flush).toHaveBeenCalled();
|
|
167
|
-
expect(mockPort.drain).toHaveBeenCalled();
|
|
168
|
-
expect(mockPort.close).toHaveBeenCalled();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('should use default timeout of 50ms', async () => {
|
|
172
|
-
await Serial.close();
|
|
173
|
-
// Should complete without error
|
|
174
|
-
expect(true).toBe(true);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should use custom timeout', async () => {
|
|
178
|
-
await Serial.close(100);
|
|
179
|
-
// Should complete without error
|
|
180
|
-
expect(true).toBe(true);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should emit close event', async () => {
|
|
184
|
-
let eventReceived = false;
|
|
185
|
-
const promise = new Promise((resolve) => {
|
|
186
|
-
Serial.once('close', () => {
|
|
187
|
-
eventReceived = true;
|
|
188
|
-
resolve();
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
expect(Serial.listenerCount('close')).toBe(1);
|
|
193
|
-
|
|
194
|
-
await Serial.close();
|
|
195
|
-
|
|
196
|
-
await Promise.race([
|
|
197
|
-
promise,
|
|
198
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('Close event not emitted within timeout')), 1000))
|
|
199
|
-
]);
|
|
200
|
-
|
|
201
|
-
expect(eventReceived).toBe(true);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
describe('disconnect', () => {
|
|
206
|
-
beforeEach(async () => {
|
|
207
|
-
await Serial.connect('/dev/ttyUSB0');
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('should disconnect (calls close)', async () => {
|
|
211
|
-
const result = await Serial.disconnect();
|
|
212
|
-
expect(result).toBe(true);
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe('read', () => {
|
|
217
|
-
beforeEach(async () => {
|
|
218
|
-
await Serial.connect('/dev/ttyUSB0');
|
|
219
|
-
await Serial.open();
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('should read data from port', async () => {
|
|
223
|
-
const mockPort = SerialPort.mock.results[0].value;
|
|
224
|
-
const testData = Buffer.from('response', 'ascii');
|
|
225
|
-
|
|
226
|
-
// Mock data event
|
|
227
|
-
mockPort.on.mockImplementation((event, handler) => {
|
|
228
|
-
if (event === 'data') {
|
|
229
|
-
setTimeout(() => handler(testData), 10);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
const data = await Serial.read();
|
|
234
|
-
expect(Buffer.isBuffer(data)).toBe(true);
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
|
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const USB = require('../../../src/usb-adapter');
|
|
4
|
-
const usb = require('usb');
|
|
5
|
-
|
|
6
|
-
// Mock usb module (v2 API - uses Promises)
|
|
7
|
-
jest.mock('usb', () => {
|
|
8
|
-
const createMockDevice = () => {
|
|
9
|
-
const mockEndpoint = {
|
|
10
|
-
direction: 'out',
|
|
11
|
-
transfer: jest.fn().mockResolvedValue(undefined)
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const mockInterface = {
|
|
15
|
-
isKernelDriverActive: jest.fn().mockReturnValue(false),
|
|
16
|
-
claim: jest.fn().mockResolvedValue(undefined),
|
|
17
|
-
release: jest.fn().mockResolvedValue(undefined),
|
|
18
|
-
detachKernelDriver: jest.fn().mockResolvedValue(undefined),
|
|
19
|
-
descriptor: {
|
|
20
|
-
bInterfaceClass: 0x07, // PRINTER class
|
|
21
|
-
bInterfaceNumber: 0
|
|
22
|
-
},
|
|
23
|
-
endpoints: [mockEndpoint]
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Link endpoint to interface (v2 API structure)
|
|
27
|
-
mockEndpoint.interface = mockInterface;
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
open: jest.fn().mockResolvedValue(undefined),
|
|
31
|
-
close: jest.fn().mockResolvedValue(undefined),
|
|
32
|
-
interfaces: [mockInterface],
|
|
33
|
-
configDescriptor: {
|
|
34
|
-
interfaces: [
|
|
35
|
-
[
|
|
36
|
-
{
|
|
37
|
-
bInterfaceClass: 0x07 // PRINTER class
|
|
38
|
-
}
|
|
39
|
-
]
|
|
40
|
-
]
|
|
41
|
-
},
|
|
42
|
-
deviceDescriptor: {
|
|
43
|
-
iManufacturer: 1,
|
|
44
|
-
iProduct: 2
|
|
45
|
-
},
|
|
46
|
-
getStringDescriptor: jest.fn().mockResolvedValue('Device String')
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// Create a single shared mock device instance that persists across tests
|
|
51
|
-
// This ensures the same device is returned every time, maintaining state
|
|
52
|
-
const mockDeviceInstance = createMockDevice();
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
getDeviceList: jest.fn().mockReturnValue([mockDeviceInstance]),
|
|
56
|
-
findByIds: jest.fn().mockReturnValue(mockDeviceInstance),
|
|
57
|
-
on: jest.fn()
|
|
58
|
-
};
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('USB Adapter', () => {
|
|
62
|
-
beforeEach(() => {
|
|
63
|
-
// Don't clear mocks that break the device structure
|
|
64
|
-
// Only clear call history for methods that need it
|
|
65
|
-
const mockDevice = usb.findByIds(1046, 20497);
|
|
66
|
-
if (mockDevice) {
|
|
67
|
-
// Clear call history but preserve mock structure
|
|
68
|
-
if (mockDevice.open && typeof mockDevice.open.mockClear === 'function') {
|
|
69
|
-
mockDevice.open.mockClear();
|
|
70
|
-
}
|
|
71
|
-
if (mockDevice.close && typeof mockDevice.close.mockClear === 'function') {
|
|
72
|
-
mockDevice.close.mockClear();
|
|
73
|
-
}
|
|
74
|
-
if (mockDevice.interfaces && mockDevice.interfaces[0]) {
|
|
75
|
-
if (mockDevice.interfaces[0].claim && typeof mockDevice.interfaces[0].claim.mockClear === 'function') {
|
|
76
|
-
mockDevice.interfaces[0].claim.mockClear();
|
|
77
|
-
}
|
|
78
|
-
if (mockDevice.interfaces[0].release && typeof mockDevice.interfaces[0].release.mockClear === 'function') {
|
|
79
|
-
mockDevice.interfaces[0].release.mockClear();
|
|
80
|
-
}
|
|
81
|
-
if (mockDevice.interfaces[0].endpoints && mockDevice.interfaces[0].endpoints[0]) {
|
|
82
|
-
if (mockDevice.interfaces[0].endpoints[0].transfer && typeof mockDevice.interfaces[0].endpoints[0].transfer.mockClear === 'function') {
|
|
83
|
-
mockDevice.interfaces[0].endpoints[0].transfer.mockClear();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// Don't clear findByIds mock as it needs to return the device
|
|
89
|
-
// usb.findByIds.mockClear(); // Commented out to preserve mock return value
|
|
90
|
-
// usb.getDeviceList.mockClear(); // Commented out to preserve mock return value
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe('listUSB', () => {
|
|
94
|
-
it('should list USB printer devices', async () => {
|
|
95
|
-
const devices = await USB.listUSB();
|
|
96
|
-
expect(Array.isArray(devices)).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should filter only printer devices', async () => {
|
|
100
|
-
const devices = await USB.listUSB();
|
|
101
|
-
// Should only return devices with PRINTER interface class
|
|
102
|
-
expect(devices.length).toBeGreaterThanOrEqual(0);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should get device manufacturer and product strings', async () => {
|
|
106
|
-
const devices = await USB.listUSB();
|
|
107
|
-
if (devices.length > 0) {
|
|
108
|
-
const device = devices[0];
|
|
109
|
-
expect(device.manufacturer).toBeDefined();
|
|
110
|
-
expect(device.product).toBeDefined();
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should handle devices without descriptors gracefully', async () => {
|
|
115
|
-
// Mock a device that fails to get descriptor
|
|
116
|
-
const mockDeviceWithoutDescriptor = {
|
|
117
|
-
open: jest.fn().mockResolvedValue(undefined),
|
|
118
|
-
close: jest.fn().mockResolvedValue(undefined),
|
|
119
|
-
getStringDescriptor: jest.fn().mockRejectedValue(new Error('Descriptor error')),
|
|
120
|
-
configDescriptor: {
|
|
121
|
-
interfaces: [
|
|
122
|
-
[
|
|
123
|
-
{
|
|
124
|
-
bInterfaceClass: 0x07
|
|
125
|
-
}
|
|
126
|
-
]
|
|
127
|
-
]
|
|
128
|
-
},
|
|
129
|
-
deviceDescriptor: {
|
|
130
|
-
iManufacturer: 1,
|
|
131
|
-
iProduct: 2
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
usb.getDeviceList.mockReturnValueOnce([mockDeviceWithoutDescriptor]);
|
|
136
|
-
|
|
137
|
-
const devices = await USB.listUSB();
|
|
138
|
-
// Should not include devices that fail to get descriptors
|
|
139
|
-
expect(devices.length).toBe(0);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe('connect', () => {
|
|
144
|
-
it('should connect to device by VID/PID', async () => {
|
|
145
|
-
const result = await USB.connect(1046, 20497);
|
|
146
|
-
expect(result).toBe(true);
|
|
147
|
-
expect(usb.findByIds).toHaveBeenCalledWith(1046, 20497);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('should connect to first available device if no VID/PID', async () => {
|
|
151
|
-
const result = await USB.connect();
|
|
152
|
-
expect(result).toBe(true);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('should throw error if device not found', async () => {
|
|
156
|
-
usb.findByIds.mockReturnValueOnce(null);
|
|
157
|
-
usb.getDeviceList.mockReturnValueOnce([]);
|
|
158
|
-
|
|
159
|
-
await expect(USB.connect(9999, 9999)).rejects.toThrow('Cannot find printer!');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should emit connect event', (done) => {
|
|
163
|
-
USB.once('connect', () => {
|
|
164
|
-
done();
|
|
165
|
-
});
|
|
166
|
-
USB.connect(1046, 20497);
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe('open', () => {
|
|
171
|
-
beforeEach(async () => {
|
|
172
|
-
await USB.connect(1046, 20497);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('should open device connection', async () => {
|
|
176
|
-
const result = await USB.open();
|
|
177
|
-
expect(result).toBe(true);
|
|
178
|
-
|
|
179
|
-
// Verify device.open was called
|
|
180
|
-
const mockDevice = usb.findByIds(1046, 20497);
|
|
181
|
-
expect(mockDevice.open).toHaveBeenCalled();
|
|
182
|
-
|
|
183
|
-
// Verify interface was claimed
|
|
184
|
-
if (mockDevice.interfaces && mockDevice.interfaces[0]) {
|
|
185
|
-
expect(mockDevice.interfaces[0].claim).toHaveBeenCalled();
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should find output endpoint', async () => {
|
|
190
|
-
await USB.open();
|
|
191
|
-
// Endpoint should be found (mocked)
|
|
192
|
-
expect(USB.write).toBeDefined();
|
|
193
|
-
|
|
194
|
-
// Verify endpoint was found
|
|
195
|
-
const mockDevice = usb.findByIds(1046, 20497);
|
|
196
|
-
if (mockDevice.interfaces && mockDevice.interfaces[0] && mockDevice.interfaces[0].endpoints[0]) {
|
|
197
|
-
expect(mockDevice.interfaces[0].endpoints[0].direction).toBe('out');
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('should throw error if endpoint not found', async () => {
|
|
202
|
-
const mockDevice = usb.findByIds(1046, 20497);
|
|
203
|
-
// Save original interfaces to restore later
|
|
204
|
-
const originalInterfaces = mockDevice.interfaces;
|
|
205
|
-
|
|
206
|
-
const mockInterfaceWithoutEndpoints = {
|
|
207
|
-
isKernelDriverActive: jest.fn().mockReturnValue(false),
|
|
208
|
-
claim: jest.fn().mockResolvedValue(undefined),
|
|
209
|
-
release: jest.fn().mockResolvedValue(undefined),
|
|
210
|
-
detachKernelDriver: jest.fn().mockResolvedValue(undefined),
|
|
211
|
-
descriptor: {
|
|
212
|
-
bInterfaceClass: 0x07,
|
|
213
|
-
bInterfaceNumber: 0
|
|
214
|
-
},
|
|
215
|
-
endpoints: [] // No endpoints
|
|
216
|
-
};
|
|
217
|
-
mockDevice.interfaces = [mockInterfaceWithoutEndpoints];
|
|
218
|
-
|
|
219
|
-
try {
|
|
220
|
-
await expect(USB.open()).rejects.toThrow('Can not find endpoint from printer');
|
|
221
|
-
} finally {
|
|
222
|
-
// Restore original interfaces to prevent affecting other tests
|
|
223
|
-
mockDevice.interfaces = originalInterfaces;
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe('write', () => {
|
|
229
|
-
beforeEach(async () => {
|
|
230
|
-
await USB.connect(1046, 20497);
|
|
231
|
-
await USB.open();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it('should write data to endpoint', async () => {
|
|
235
|
-
const testData = Buffer.from('test', 'ascii');
|
|
236
|
-
const result = await USB.write(testData);
|
|
237
|
-
expect(result).toBe(true);
|
|
238
|
-
|
|
239
|
-
// Verify that endpoint.transfer was called with correct data
|
|
240
|
-
const mockDevice = usb.findByIds(1046, 20497);
|
|
241
|
-
if (mockDevice.interfaces && mockDevice.interfaces[0] && mockDevice.interfaces[0].endpoints[0]) {
|
|
242
|
-
expect(mockDevice.interfaces[0].endpoints[0].transfer).toHaveBeenCalledWith(testData);
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe('close', () => {
|
|
248
|
-
beforeEach(async () => {
|
|
249
|
-
await USB.connect(1046, 20497);
|
|
250
|
-
await USB.open();
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('should close device connection', async () => {
|
|
254
|
-
const result = await USB.close();
|
|
255
|
-
expect(result).toBe(true);
|
|
256
|
-
|
|
257
|
-
// Verify that interface release was called
|
|
258
|
-
const mockDevice = usb.findByIds(1046, 20497);
|
|
259
|
-
if (mockDevice.interfaces && mockDevice.interfaces[0]) {
|
|
260
|
-
expect(mockDevice.interfaces[0].release).toHaveBeenCalled();
|
|
261
|
-
}
|
|
262
|
-
expect(mockDevice.close).toHaveBeenCalled();
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('should emit close event', async () => {
|
|
266
|
-
let eventReceived = false;
|
|
267
|
-
const promise = new Promise((resolve) => {
|
|
268
|
-
USB.once('close', () => {
|
|
269
|
-
eventReceived = true;
|
|
270
|
-
resolve();
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
expect(USB.listenerCount('close')).toBe(1);
|
|
275
|
-
|
|
276
|
-
await USB.close();
|
|
277
|
-
|
|
278
|
-
await Promise.race([
|
|
279
|
-
promise,
|
|
280
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('Close event not emitted within timeout')), 1000))
|
|
281
|
-
]);
|
|
282
|
-
|
|
283
|
-
expect(eventReceived).toBe(true);
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
describe('disconnect', () => {
|
|
288
|
-
beforeEach(async () => {
|
|
289
|
-
await USB.connect(1046, 20497);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
it('should disconnect device', async () => {
|
|
293
|
-
const result = await USB.disconnect();
|
|
294
|
-
expect(result).toBe(true);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it('should emit disconnect event', async () => {
|
|
298
|
-
let eventReceived = false;
|
|
299
|
-
const promise = new Promise((resolve) => {
|
|
300
|
-
USB.once('disconnect', () => {
|
|
301
|
-
eventReceived = true;
|
|
302
|
-
resolve();
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
expect(USB.listenerCount('disconnect')).toBe(1);
|
|
307
|
-
|
|
308
|
-
await USB.disconnect();
|
|
309
|
-
|
|
310
|
-
await Promise.race([
|
|
311
|
-
promise,
|
|
312
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('Disconnect event not emitted within timeout')), 1000))
|
|
313
|
-
]);
|
|
314
|
-
|
|
315
|
-
expect(eventReceived).toBe(true);
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
});
|
|
319
|
-
|