test.js
1 'use strict' 2 3 const test = require('tape') 4 const split = require('./') 5 const callback = require('callback-stream') 6 const strcb = callback.bind(null, { decodeStrings: false }) 7 const objcb = callback.bind(null, { objectMode: true }) 8 9 test('split two lines on end', function (t) { 10 t.plan(2) 11 12 const input = split() 13 14 input.pipe(strcb(function (err, list) { 15 t.error(err) 16 t.deepEqual(list, ['hello', 'world']) 17 })) 18 19 input.end('hello\nworld') 20 }) 21 22 test('split two lines on two writes', function (t) { 23 t.plan(2) 24 25 const input = split() 26 27 input.pipe(strcb(function (err, list) { 28 t.error(err) 29 t.deepEqual(list, ['hello', 'world']) 30 })) 31 32 input.write('hello') 33 input.write('\nworld') 34 input.end() 35 }) 36 37 test('split four lines on three writes', function (t) { 38 t.plan(2) 39 40 const input = split() 41 42 input.pipe(strcb(function (err, list) { 43 t.error(err) 44 t.deepEqual(list, ['hello', 'world', 'bye', 'world']) 45 })) 46 47 input.write('hello\nwor') 48 input.write('ld\nbye\nwo') 49 input.write('rld') 50 input.end() 51 }) 52 53 test('accumulate multiple writes', function (t) { 54 t.plan(2) 55 56 const input = split() 57 58 input.pipe(strcb(function (err, list) { 59 t.error(err) 60 t.deepEqual(list, ['helloworld']) 61 })) 62 63 input.write('hello') 64 input.write('world') 65 input.end() 66 }) 67 68 test('split using a custom string matcher', function (t) { 69 t.plan(2) 70 71 const input = split('~') 72 73 input.pipe(strcb(function (err, list) { 74 t.error(err) 75 t.deepEqual(list, ['hello', 'world']) 76 })) 77 78 input.end('hello~world') 79 }) 80 81 test('split using a custom regexp matcher', function (t) { 82 t.plan(2) 83 84 const input = split(/~/) 85 86 input.pipe(strcb(function (err, list) { 87 t.error(err) 88 t.deepEqual(list, ['hello', 'world']) 89 })) 90 91 input.end('hello~world') 92 }) 93 94 test('support an option argument', function (t) { 95 t.plan(2) 96 97 const input = split({ highWaterMark: 2 }) 98 99 input.pipe(strcb(function (err, list) { 100 t.error(err) 101 t.deepEqual(list, ['hello', 'world']) 102 })) 103 104 input.end('hello\nworld') 105 }) 106 107 test('support a mapper function', function (t) { 108 t.plan(2) 109 110 const a = { a: '42' } 111 const b = { b: '24' } 112 113 const input = split(JSON.parse) 114 115 input.pipe(objcb(function (err, list) { 116 t.error(err) 117 t.deepEqual(list, [a, b]) 118 })) 119 120 input.write(JSON.stringify(a)) 121 input.write('\n') 122 input.end(JSON.stringify(b)) 123 }) 124 125 test('split lines windows-style', function (t) { 126 t.plan(2) 127 128 const input = split() 129 130 input.pipe(strcb(function (err, list) { 131 t.error(err) 132 t.deepEqual(list, ['hello', 'world']) 133 })) 134 135 input.end('hello\r\nworld') 136 }) 137 138 test('splits a buffer', function (t) { 139 t.plan(2) 140 141 const input = split() 142 143 input.pipe(strcb(function (err, list) { 144 t.error(err) 145 t.deepEqual(list, ['hello', 'world']) 146 })) 147 148 input.end(Buffer.from('hello\nworld')) 149 }) 150 151 test('do not end on undefined', function (t) { 152 t.plan(2) 153 154 const input = split(function (line) { }) 155 156 input.pipe(strcb(function (err, list) { 157 t.error(err) 158 t.deepEqual(list, []) 159 })) 160 161 input.end(Buffer.from('hello\nworld')) 162 }) 163 164 test('has destroy method', function (t) { 165 t.plan(1) 166 167 const input = split(function (line) { }) 168 169 input.on('close', function () { 170 t.ok(true, 'close emitted') 171 t.end() 172 }) 173 174 input.destroy() 175 }) 176 177 test('support custom matcher and mapper', function (t) { 178 t.plan(4) 179 180 const a = { a: '42' } 181 const b = { b: '24' } 182 const input = split('~', JSON.parse) 183 184 t.equal(input.matcher, '~') 185 t.equal(typeof input.mapper, 'function') 186 187 input.pipe(objcb(function (err, list) { 188 t.notOk(err, 'no errors') 189 t.deepEqual(list, [a, b]) 190 })) 191 192 input.write(JSON.stringify(a)) 193 input.write('~') 194 input.end(JSON.stringify(b)) 195 }) 196 197 test('support custom matcher and options', function (t) { 198 t.plan(6) 199 200 const input = split('~', { highWaterMark: 1024 }) 201 202 t.equal(input.matcher, '~') 203 t.equal(typeof input.mapper, 'function') 204 t.equal(input._readableState.highWaterMark, 1024) 205 t.equal(input._writableState.highWaterMark, 1024) 206 207 input.pipe(strcb(function (err, list) { 208 t.error(err) 209 t.deepEqual(list, ['hello', 'world']) 210 })) 211 212 input.end('hello~world') 213 }) 214 215 test('support mapper and options', function (t) { 216 t.plan(6) 217 218 const a = { a: '42' } 219 const b = { b: '24' } 220 const input = split(JSON.parse, { highWaterMark: 1024 }) 221 222 t.ok(input.matcher instanceof RegExp, 'matcher is RegExp') 223 t.equal(typeof input.mapper, 'function') 224 t.equal(input._readableState.highWaterMark, 1024) 225 t.equal(input._writableState.highWaterMark, 1024) 226 227 input.pipe(objcb(function (err, list) { 228 t.error(err) 229 t.deepEqual(list, [a, b]) 230 })) 231 232 input.write(JSON.stringify(a)) 233 input.write('\n') 234 input.end(JSON.stringify(b)) 235 }) 236 237 test('split utf8 chars', function (t) { 238 t.plan(2) 239 240 const input = split() 241 242 input.pipe(strcb(function (err, list) { 243 t.error(err) 244 t.deepEqual(list, ['烫烫烫', '锟斤拷']) 245 })) 246 247 const buf = Buffer.from('烫烫烫\r\n锟斤拷', 'utf8') 248 for (let i = 0; i < buf.length; ++i) { 249 input.write(buf.slice(i, i + 1)) 250 } 251 input.end() 252 }) 253 254 test('split utf8 chars 2by2', function (t) { 255 t.plan(2) 256 257 const input = split() 258 259 input.pipe(strcb(function (err, list) { 260 t.error(err) 261 t.deepEqual(list, ['烫烫烫', '烫烫烫']) 262 })) 263 264 const str = '烫烫烫\r\n烫烫烫' 265 const buf = Buffer.from(str, 'utf8') 266 for (let i = 0; i < buf.length; i += 2) { 267 input.write(buf.slice(i, i + 2)) 268 } 269 input.end() 270 }) 271 272 test('split lines when the \n comes at the end of a chunk', function (t) { 273 t.plan(2) 274 275 const input = split() 276 277 input.pipe(strcb(function (err, list) { 278 t.error(err) 279 t.deepEqual(list, ['hello', 'world']) 280 })) 281 282 input.write('hello\n') 283 input.end('world') 284 }) 285 286 test('truncated utf-8 char', function (t) { 287 t.plan(2) 288 289 const input = split() 290 291 input.pipe(strcb(function (err, list) { 292 t.error(err) 293 t.deepEqual(list, ['烫' + Buffer.from('e7', 'hex').toString()]) 294 })) 295 296 const str = '烫烫' 297 const buf = Buffer.from(str, 'utf8') 298 299 input.write(buf.slice(0, 3)) 300 input.end(buf.slice(3, 4)) 301 }) 302 303 test('maximum buffer limit', function (t) { 304 t.plan(1) 305 306 const input = split({ maxLength: 2 }) 307 input.on('error', function (err) { 308 t.ok(err) 309 }) 310 311 input.resume() 312 313 input.write('hey') 314 }) 315 316 test('readable highWaterMark', function (t) { 317 const input = split() 318 t.equal(input._readableState.highWaterMark, 16) 319 t.end() 320 }) 321 322 test('maxLength < chunk size', function (t) { 323 t.plan(2) 324 325 const input = split({ maxLength: 2 }) 326 327 input.pipe(strcb(function (err, list) { 328 t.error(err) 329 t.deepEqual(list, ['a', 'b']) 330 })) 331 332 input.end('a\nb') 333 }) 334 335 test('maximum buffer limit w/skip', function (t) { 336 t.plan(2) 337 338 const input = split({ maxLength: 2, skipOverflow: true }) 339 340 input.pipe(strcb(function (err, list) { 341 t.error(err) 342 t.deepEqual(list, ['a', 'b', 'c']) 343 })) 344 345 input.write('a\n123') 346 input.write('456') 347 input.write('789\nb\nc') 348 input.end() 349 }) 350 351 test("don't modify the options object", function (t) { 352 t.plan(2) 353 354 const options = {} 355 const input = split(options) 356 357 input.pipe(strcb(function (err, list) { 358 t.error(err) 359 t.same(options, {}) 360 })) 361 362 input.end() 363 }) 364 365 test('mapper throws flush', function (t) { 366 t.plan(1) 367 const error = new Error() 368 const input = split(function () { 369 throw error 370 }) 371 372 input.on('error', (err, list) => { 373 t.same(err, error) 374 }) 375 input.end('hello') 376 }) 377 378 test('mapper throws on transform', function (t) { 379 t.plan(1) 380 381 const error = new Error() 382 const input = split(function (l) { 383 throw error 384 }) 385 386 input.on('error', (err) => { 387 t.same(err, error) 388 }) 389 input.write('a') 390 input.write('\n') 391 input.end('b') 392 }) 393 394 test('supports Symbol.split', function (t) { 395 t.plan(2) 396 397 const input = split({ 398 [Symbol.split] (str) { 399 return str.split('~') 400 } 401 }) 402 403 input.pipe(strcb(function (err, list) { 404 t.error(err) 405 t.deepEqual(list, ['hello', 'world']) 406 })) 407 408 input.end('hello~world') 409 })