@risleylima/escpos 0.0.13 → 0.1.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/CHANGELOG.md +60 -0
- package/README.md +798 -8
- package/docs/COVERAGE_ANALYSIS.md +98 -0
- package/docs/DEPENDENCIES_REVIEW.md +127 -0
- package/docs/JSDOC_REVIEW.md +122 -0
- package/docs/LIBRARY_OVERVIEW.md +383 -0
- package/docs/PRE_PUBLISH_CHECKLIST.md +331 -0
- package/docs/PUBLIC_API_ANALYSIS.md +224 -0
- package/docs/README.md +34 -0
- package/docs/SERIALPORT_V13_MIGRATION_COMPLETE.md +127 -0
- package/docs/TESTS_IMPLEMENTED.md +129 -0
- package/docs/USB_V2_REVIEW.md +148 -0
- package/docs/VERIFICATION_RESULTS.md +172 -0
- package/jest.config.js +16 -0
- package/package.json +12 -7
- package/src/adapter/index.js +37 -0
- package/src/printer/commands.js +6 -4
- package/src/printer/image.js +28 -7
- package/src/printer/index.js +7 -2
- package/src/printer/utils.js +21 -14
- package/src/serial-adapter/index.js +133 -84
- package/src/usb-adapter/index.js +157 -43
- package/tests/README.md +67 -0
- package/tests/integration/printer-flow.test.js +128 -0
- package/tests/unit/adapters/adapter.test.js +49 -0
- package/tests/unit/adapters/serial-adapter.test.js +224 -0
- package/tests/unit/adapters/usb-adapter.test.js +319 -0
- package/tests/unit/image/image.test.js +157 -0
- package/tests/unit/printer/buffer.test.js +60 -0
- package/tests/unit/printer/commands.test.js +109 -0
- package/tests/unit/printer/printer.test.js +405 -0
- package/tests/unit/utils/utils.test.js +96 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Printer = require('../../../src/printer');
|
|
4
|
+
|
|
5
|
+
describe('Printer', () => {
|
|
6
|
+
let printer;
|
|
7
|
+
let mockAdapter;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockAdapter = {
|
|
11
|
+
write: jest.fn().mockResolvedValue(true),
|
|
12
|
+
close: jest.fn().mockResolvedValue(true)
|
|
13
|
+
};
|
|
14
|
+
printer = new Printer(mockAdapter);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('Constructor', () => {
|
|
18
|
+
it('should initialize with adapter', () => {
|
|
19
|
+
expect(printer.adapter).toBeDefined();
|
|
20
|
+
expect(printer.buffer).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should set default encoding', () => {
|
|
24
|
+
expect(printer.encoding).toBe('GB18030');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should set default width', () => {
|
|
28
|
+
expect(printer.width).toBe(48);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should accept custom options', () => {
|
|
32
|
+
const customPrinter = new Printer(mockAdapter, {
|
|
33
|
+
encoding: 'UTF-8',
|
|
34
|
+
width: 42
|
|
35
|
+
});
|
|
36
|
+
expect(customPrinter.encoding).toBe('UTF-8');
|
|
37
|
+
expect(customPrinter.width).toBe(42);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('Text Operations', () => {
|
|
42
|
+
it('should print text without encoding', () => {
|
|
43
|
+
printer.print('Hello');
|
|
44
|
+
const buffer = printer.buffer.flush();
|
|
45
|
+
expect(buffer.toString('ascii')).toBe('Hello');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should print text with line break', () => {
|
|
49
|
+
printer.println('Hello');
|
|
50
|
+
const buffer = printer.buffer.flush();
|
|
51
|
+
expect(buffer.toString('ascii')).toContain('Hello');
|
|
52
|
+
expect(buffer.toString('hex')).toContain('0a'); // LF
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should print text with encoding', () => {
|
|
56
|
+
printer.text('Hello');
|
|
57
|
+
const buffer = printer.buffer.flush();
|
|
58
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should print text with line break and encoding', () => {
|
|
62
|
+
printer.textln('Hello');
|
|
63
|
+
const buffer = printer.buffer.flush();
|
|
64
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should add new line', () => {
|
|
68
|
+
printer.newLine();
|
|
69
|
+
const buffer = printer.buffer.flush();
|
|
70
|
+
expect(buffer.toString('hex')).toContain('0a');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('Alignment', () => {
|
|
75
|
+
it('should set left alignment', () => {
|
|
76
|
+
printer.align('lt');
|
|
77
|
+
const buffer = printer.buffer.flush();
|
|
78
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B6100');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should set center alignment', () => {
|
|
82
|
+
printer.align('ct');
|
|
83
|
+
const buffer = printer.buffer.flush();
|
|
84
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B6101');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should set right alignment', () => {
|
|
88
|
+
printer.align('rt');
|
|
89
|
+
const buffer = printer.buffer.flush();
|
|
90
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B6102');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('Text Formatting', () => {
|
|
95
|
+
it('should set text size', () => {
|
|
96
|
+
printer.size(2, 2);
|
|
97
|
+
const buffer = printer.buffer.flush();
|
|
98
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should clamp width to 8 when width > 8', () => {
|
|
102
|
+
printer.size(10, 2);
|
|
103
|
+
const buffer = printer.buffer.flush();
|
|
104
|
+
// Should use clamped width (8) instead of 10
|
|
105
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
106
|
+
// Verify the command uses clamped value
|
|
107
|
+
const hex = buffer.toString('hex').toUpperCase();
|
|
108
|
+
expect(hex).toContain('1D21'); // TXT_CUSTOM_SIZE command
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should clamp width to 1 when width < 1', () => {
|
|
112
|
+
printer.size(0, 2);
|
|
113
|
+
const buffer = printer.buffer.flush();
|
|
114
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
115
|
+
const hex = buffer.toString('hex').toUpperCase();
|
|
116
|
+
expect(hex).toContain('1D21'); // TXT_CUSTOM_SIZE command
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should clamp height to 8 when height > 8', () => {
|
|
120
|
+
printer.size(2, 10);
|
|
121
|
+
const buffer = printer.buffer.flush();
|
|
122
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
123
|
+
const hex = buffer.toString('hex').toUpperCase();
|
|
124
|
+
expect(hex).toContain('1D21'); // TXT_CUSTOM_SIZE command
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should clamp height to 1 when height < 1', () => {
|
|
128
|
+
printer.size(2, 0);
|
|
129
|
+
const buffer = printer.buffer.flush();
|
|
130
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
131
|
+
const hex = buffer.toString('hex').toUpperCase();
|
|
132
|
+
expect(hex).toContain('1D21'); // TXT_CUSTOM_SIZE command
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should clamp both width and height when both are out of range', () => {
|
|
136
|
+
printer.size(10, 0);
|
|
137
|
+
const buffer = printer.buffer.flush();
|
|
138
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
139
|
+
const hex = buffer.toString('hex').toUpperCase();
|
|
140
|
+
expect(hex).toContain('1D21'); // TXT_CUSTOM_SIZE command
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should set encoding', () => {
|
|
144
|
+
printer.encode('UTF-8');
|
|
145
|
+
expect(printer.encoding).toBe('UTF-8');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should set font family', () => {
|
|
149
|
+
printer.font('A');
|
|
150
|
+
const buffer = printer.buffer.flush();
|
|
151
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B4D00');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should set bold style', () => {
|
|
155
|
+
printer.style('B');
|
|
156
|
+
const buffer = printer.buffer.flush();
|
|
157
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B4501');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should set italic style', () => {
|
|
161
|
+
printer.style('I');
|
|
162
|
+
const buffer = printer.buffer.flush();
|
|
163
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B34');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should set underline style', () => {
|
|
167
|
+
printer.style('U');
|
|
168
|
+
const buffer = printer.buffer.flush();
|
|
169
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B2D01');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should set normal style', () => {
|
|
173
|
+
printer.style('NORMAL');
|
|
174
|
+
const buffer = printer.buffer.flush();
|
|
175
|
+
// NORMAL style sets bold off, italic off, underline off
|
|
176
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B4500'); // Bold off
|
|
177
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B35'); // Italic off
|
|
178
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B2D00'); // Underline off
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('Paper Control', () => {
|
|
183
|
+
it('should feed paper', () => {
|
|
184
|
+
printer.feed(3);
|
|
185
|
+
const buffer = printer.buffer.flush();
|
|
186
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should feed default 1 line', () => {
|
|
190
|
+
printer.feed();
|
|
191
|
+
const buffer = printer.buffer.flush();
|
|
192
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should draw line', () => {
|
|
196
|
+
printer.drawLine();
|
|
197
|
+
const buffer = printer.buffer.flush();
|
|
198
|
+
const text = buffer.toString('ascii');
|
|
199
|
+
expect(text).toContain('-');
|
|
200
|
+
expect(text).toContain('\n');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should draw line with custom character', () => {
|
|
204
|
+
printer.drawLine('=');
|
|
205
|
+
const buffer = printer.buffer.flush();
|
|
206
|
+
const text = buffer.toString('ascii');
|
|
207
|
+
expect(text).toContain('=');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should cut paper partially', () => {
|
|
211
|
+
printer.cut(true);
|
|
212
|
+
const buffer = printer.buffer.flush();
|
|
213
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1D5601');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should cut paper fully', () => {
|
|
217
|
+
printer.cut(false);
|
|
218
|
+
const buffer = printer.buffer.flush();
|
|
219
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1D5600');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should cut with feed lines', () => {
|
|
223
|
+
printer.cut(true, 5);
|
|
224
|
+
const buffer = printer.buffer.flush();
|
|
225
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe('Hardware Commands', () => {
|
|
230
|
+
it('should initialize hardware', () => {
|
|
231
|
+
printer.hardware('init');
|
|
232
|
+
const buffer = printer.buffer.flush();
|
|
233
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B40');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should beep', () => {
|
|
237
|
+
printer.beep(2, 1);
|
|
238
|
+
const buffer = printer.buffer.flush();
|
|
239
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B42');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should open cash drawer', () => {
|
|
243
|
+
printer.cashdraw(2);
|
|
244
|
+
const buffer = printer.buffer.flush();
|
|
245
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B70');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('Barcode', () => {
|
|
250
|
+
it('should print EAN13 barcode', () => {
|
|
251
|
+
printer.barcode('123456789012', 'EAN13');
|
|
252
|
+
const buffer = printer.buffer.flush();
|
|
253
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should throw error for invalid EAN13 length', () => {
|
|
257
|
+
expect(() => {
|
|
258
|
+
printer.barcode('12345678901', 'EAN13');
|
|
259
|
+
}).toThrow('EAN13 Barcode type requires code length 12');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should throw error for invalid EAN8 length', () => {
|
|
263
|
+
expect(() => {
|
|
264
|
+
printer.barcode('123456', 'EAN8');
|
|
265
|
+
}).toThrow('EAN8 Barcode type requires code length 7');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should print barcode with options', () => {
|
|
269
|
+
printer.barcode('123456789012', 'EAN13', {
|
|
270
|
+
width: 2,
|
|
271
|
+
height: 50,
|
|
272
|
+
position: 'BLW',
|
|
273
|
+
font: 'A'
|
|
274
|
+
});
|
|
275
|
+
const buffer = printer.buffer.flush();
|
|
276
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
describe('Raw Commands', () => {
|
|
281
|
+
it('should write raw hex string', () => {
|
|
282
|
+
printer.raw('1B40');
|
|
283
|
+
const buffer = printer.buffer.flush();
|
|
284
|
+
expect(buffer.toString('hex').toUpperCase()).toBe('1B40');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should write raw hex string with spaces', () => {
|
|
288
|
+
printer.raw('1B 40');
|
|
289
|
+
const buffer = printer.buffer.flush();
|
|
290
|
+
expect(buffer.toString('hex').toUpperCase()).toBe('1B40');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should write raw hex string with colons', () => {
|
|
294
|
+
printer.raw('1B:40');
|
|
295
|
+
const buffer = printer.buffer.flush();
|
|
296
|
+
expect(buffer.toString('hex').toUpperCase()).toBe('1B40');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should write raw Buffer', () => {
|
|
300
|
+
const testBuffer = Buffer.from('1B40', 'hex');
|
|
301
|
+
printer.raw(testBuffer);
|
|
302
|
+
const buffer = printer.buffer.flush();
|
|
303
|
+
expect(buffer.toString('hex').toUpperCase()).toBe('1B40');
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should throw error for invalid raw data', () => {
|
|
307
|
+
expect(() => {
|
|
308
|
+
printer.raw(123);
|
|
309
|
+
}).toThrow('Data is Invalid!');
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
describe('Method Chaining', () => {
|
|
314
|
+
it('should support method chaining', () => {
|
|
315
|
+
const result = printer
|
|
316
|
+
.align('ct')
|
|
317
|
+
.size(2, 2)
|
|
318
|
+
.textln('Test')
|
|
319
|
+
.align('lt');
|
|
320
|
+
|
|
321
|
+
expect(result).toBe(printer);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('should chain multiple operations', () => {
|
|
325
|
+
printer
|
|
326
|
+
.hardware('init')
|
|
327
|
+
.align('ct')
|
|
328
|
+
.textln('Title')
|
|
329
|
+
.align('lt')
|
|
330
|
+
.textln('Content')
|
|
331
|
+
.cut(true);
|
|
332
|
+
|
|
333
|
+
const buffer = printer.buffer.flush();
|
|
334
|
+
expect(buffer.length).toBeGreaterThan(0);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe('Flush', () => {
|
|
339
|
+
it('should flush buffer to adapter', async () => {
|
|
340
|
+
printer.text('Test');
|
|
341
|
+
await printer.flush();
|
|
342
|
+
|
|
343
|
+
expect(mockAdapter.write).toHaveBeenCalled();
|
|
344
|
+
const writtenBuffer = mockAdapter.write.mock.calls[0][0];
|
|
345
|
+
expect(writtenBuffer.toString('ascii')).toContain('Test');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should clear buffer after flush', async () => {
|
|
349
|
+
printer.text('Test');
|
|
350
|
+
await printer.flush();
|
|
351
|
+
const buffer = printer.buffer.flush();
|
|
352
|
+
expect(buffer.length).toBe(0);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should not write if buffer is empty', async () => {
|
|
356
|
+
await printer.flush();
|
|
357
|
+
expect(mockAdapter.write).not.toHaveBeenCalled();
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe('Close', () => {
|
|
362
|
+
it('should flush and close adapter', async () => {
|
|
363
|
+
printer.text('Test');
|
|
364
|
+
await printer.close();
|
|
365
|
+
|
|
366
|
+
expect(mockAdapter.write).toHaveBeenCalled();
|
|
367
|
+
expect(mockAdapter.close).toHaveBeenCalled();
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
describe('Color', () => {
|
|
372
|
+
it('should set primary color (black)', () => {
|
|
373
|
+
printer.color(0);
|
|
374
|
+
const buffer = printer.buffer.flush();
|
|
375
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B7200');
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should set secondary color (red)', () => {
|
|
379
|
+
printer.color(1);
|
|
380
|
+
const buffer = printer.buffer.flush();
|
|
381
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B7201');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should default to black for invalid color', () => {
|
|
385
|
+
printer.color(99);
|
|
386
|
+
const buffer = printer.buffer.flush();
|
|
387
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1B7200');
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
describe('Reverse Colors', () => {
|
|
392
|
+
it('should set reverse colors', () => {
|
|
393
|
+
printer.setReverseColors(true);
|
|
394
|
+
const buffer = printer.buffer.flush();
|
|
395
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1DB1');
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should unset reverse colors', () => {
|
|
399
|
+
printer.setReverseColors(false);
|
|
400
|
+
const buffer = printer.buffer.flush();
|
|
401
|
+
expect(buffer.toString('hex').toUpperCase()).toContain('1DB0');
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const utils = require('../../../src/printer/utils');
|
|
4
|
+
|
|
5
|
+
describe('Utils', () => {
|
|
6
|
+
describe('getParityBit', () => {
|
|
7
|
+
it('should calculate parity bit for EAN13', () => {
|
|
8
|
+
const code = '123456789012';
|
|
9
|
+
const parity = utils.getParityBit(code);
|
|
10
|
+
expect(parity).toMatch(/^\d$/);
|
|
11
|
+
expect(parseInt(parity)).toBeGreaterThanOrEqual(0);
|
|
12
|
+
expect(parseInt(parity)).toBeLessThanOrEqual(9);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should calculate parity bit for EAN8', () => {
|
|
16
|
+
const code = '1234567';
|
|
17
|
+
const parity = utils.getParityBit(code);
|
|
18
|
+
expect(parity).toMatch(/^\d$/);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return valid parity for different codes', () => {
|
|
22
|
+
const codes = ['123456789012', '987654321098', '111111111111'];
|
|
23
|
+
codes.forEach(code => {
|
|
24
|
+
const parity = utils.getParityBit(code);
|
|
25
|
+
expect(parity).toMatch(/^\d$/);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('codeLength', () => {
|
|
31
|
+
it('should return length as hex string', () => {
|
|
32
|
+
const result = utils.codeLength('12345');
|
|
33
|
+
expect(typeof result).toBe('string');
|
|
34
|
+
// codeLength converts number to hex, then to Buffer, then to string
|
|
35
|
+
// For length 5, hex is '5', which becomes a single byte Buffer
|
|
36
|
+
expect(result).toBeDefined();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should handle empty string', () => {
|
|
40
|
+
const result = utils.codeLength('');
|
|
41
|
+
expect(typeof result).toBe('string');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle different string lengths', () => {
|
|
45
|
+
expect(utils.codeLength('A')).toBeDefined();
|
|
46
|
+
expect(utils.codeLength('AB')).toBeDefined();
|
|
47
|
+
expect(utils.codeLength('ABC')).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('textLength', () => {
|
|
52
|
+
it('should count ASCII characters as 1', () => {
|
|
53
|
+
expect(utils.textLength('Hello')).toBe(5);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should count multi-byte characters as 2', () => {
|
|
57
|
+
const chinese = '你好';
|
|
58
|
+
const length = utils.textLength(chinese);
|
|
59
|
+
expect(length).toBeGreaterThan(2);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should handle mixed ASCII and multi-byte', () => {
|
|
63
|
+
const mixed = 'Hello 世界';
|
|
64
|
+
const length = utils.textLength(mixed);
|
|
65
|
+
expect(length).toBeGreaterThan(7);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should handle empty string', () => {
|
|
69
|
+
expect(utils.textLength('')).toBe(0);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('textSubstring', () => {
|
|
74
|
+
it('should extract substring correctly', () => {
|
|
75
|
+
const result = utils.textSubstring('Hello World', 0, 5);
|
|
76
|
+
expect(result).toBe('Hello');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle start and end positions', () => {
|
|
80
|
+
const result = utils.textSubstring('Hello World', 6, 11);
|
|
81
|
+
expect(result).toBe('World');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should handle multi-byte characters', () => {
|
|
85
|
+
const chinese = '你好世界';
|
|
86
|
+
const result = utils.textSubstring(chinese, 0, 2);
|
|
87
|
+
expect(result.length).toBeGreaterThan(0);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should handle end undefined', () => {
|
|
91
|
+
const result = utils.textSubstring('Hello World', 6);
|
|
92
|
+
expect(result).toBe('World');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|