'use strict'; var expect = require('expect.js'); var EventEmitter = require('events').EventEmitter; var progress = require('../'); function normalizeStates(states) { states.forEach(function (state) { state.time.elapsed = Math.round(state.time.elapsed), state.time.remaining = state.time.remaining != null ? Math.round(state.time.remaining) : null; state.speed = state.speed != null ? Math.round(state.speed) : null; }); } describe('request-progress', function () { var request; var states; var response; beforeEach(function () { states = []; request = new EventEmitter(); response = new EventEmitter(); request.on('progress', function (state) { states.push(JSON.parse(JSON.stringify(state))); }); }); it('should emit the progress event with the correct state information', function (done) { progress(request, { throttle: 0 }) .on('end', function () { normalizeStates(states); expect(states).to.eql([{ percent: 0.5, speed: null, size: { total: 10, transferred: 5, }, time: { elapsed: 0, remaining: null } }, { percent: 0.8, speed: 7, size: { total: 10, transferred: 8 }, time: { elapsed: 1, remaining: 0 } }, { percent: 1, speed: 8, size: { total: 10, transferred: 10 }, time: { elapsed: 1, remaining: 0 } }]); done(); }); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 10 } })); setTimeout(function () { response.emit('data', new Buffer('aaaaa')); }, 25); setTimeout(function () { response.emit('data', new Buffer('bbb')); }, 1150); setTimeout(function () { response.emit('data', new Buffer('cc')); request.emit('end'); }, 1250); }); it('should provide request.progressState (and request.progressContext)', function (done) { progress(request, { throttle: 0 }) .on('end', function () { expect(request.progressState).to.be(null); expect(request.progressContext).to.be(null); done(); }); expect(request.progressContext).to.be.an('object'); expect(request.progressState).to.be(undefined); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 2 } })); expect(request.progressContext).to.be.an('object'); expect(request.progressState).to.be.an('object'); setTimeout(function () { response.emit('data', new Buffer('a')); expect(request.progressContext).to.be.an('object'); expect(request.progressState).to.be.an('object'); expect(request.progressState.percent).to.be(0.5); }, 25); setTimeout(function () { response.emit('data', new Buffer('b')); expect(request.progressContext).to.be.an('object'); expect(request.progressState).to.be.an('object'); expect(request.progressState.percent).to.be(1); request.emit('end'); }, 100); }); it('should have a option.delay default of 0', function () { progress(request); expect(request.progressContext.options.delay).to.be(0); }); it('should respect the passed option.delay', function (done) { progress(request, { throttle: 0, delay: 250 }) .on('end', function () { expect(states).to.have.length(2); expect(states[0].percent).to.be(0.6); expect(states[1].percent).to.be(1); done(); }); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 10 } })); setTimeout(function () { response.emit('data', new Buffer('aa')); }, 25); setTimeout(function () { response.emit('data', new Buffer('bb')); }, 200); setTimeout(function () { response.emit('data', new Buffer('cc')); }, 300); setTimeout(function () { response.emit('data', new Buffer('dddd')); request.emit('end'); }, 400); }); it('should have a option.throttle default of 1000', function () { progress(request); expect(request.progressContext.options.throttle).to.be(1000); }); it('should respect the passed option.throttle', function (done) { progress(request, { throttle: 300, delay: 0 }) .on('end', function () { expect(states).to.have.length(3); expect(states[0].percent).to.be(0.2); expect(states[1].percent).to.be(0.6); expect(states[2].percent).to.be(0.9); done(); }); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 10 } })); setTimeout(function () { response.emit('data', new Buffer('aa')); }, 25); setTimeout(function () { response.emit('data', new Buffer('bb')); }, 100); setTimeout(function () { response.emit('data', new Buffer('cc')); }, 300); setTimeout(function () { response.emit('data', new Buffer('dd')); }, 400); setTimeout(function () { response.emit('data', new Buffer('e')); }, 500); setTimeout(function () { response.emit('data', new Buffer('bf')); request.emit('end'); }, 700); }); it('should have a option.lengthHeader default of "content-length"', function () { progress(request); expect(request.progressContext.options.lengthHeader).to.be('content-length'); }); it('should use option.lengthHeader', function (done) { progress(request, { throttle: 0, lengthHeader: 'x-transfer-length' }) .on('end', function () { expect(states).to.have.length(2); expect(states[0].percent).to.be(0.5); expect(states[0].size.total).to.be(10); expect(states[1].percent).to.be(1); expect(states[1].size.total).to.be(10); done(); }); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'x-transfer-length': 10, 'content-length': 5 } })); setTimeout(function () { response.emit('data', new Buffer('aaaaa')); }, 25); setTimeout(function () { response.emit('data', new Buffer('bbbbb')); request.emit('end'); }, 200); }); it('should fail if response is already set', function () { request.response = { headers: { 'content-length': 10 } }; expect(function () { progress(request); }).to.throwException(/too late/); }); it('should deal with unknown content length', function (done) { progress(request, { throttle: 0 }) .on('end', function () { normalizeStates(states); expect(states).to.eql([{ percent: null, speed: null, size: { total: null, transferred: 5, }, time: { elapsed: 0, remaining: null } }, { percent: null, speed: 10, size: { total: null, transferred: 12, }, time: { elapsed: 1, remaining: null } }]); done(); }); request.emit('request'); request.emit('response', Object.assign(response, { headers: {} })); setTimeout(function () { response.emit('data', new Buffer('aaaaa')); }, 25); setTimeout(function () { response.emit('data', new Buffer('bbbbbbb')); request.emit('end'); }, 1150); }); it('should deal with payloads higher than the content length', function (done) { progress(request, { throttle: 0 }) .on('end', function () { normalizeStates(states); expect(states).to.eql([{ percent: 0.5, speed: null, size: { total: 10, transferred: 5, }, time: { elapsed: 0, remaining: null } }, { percent: 1, speed: 10, size: { total: 10, transferred: 12 }, time: { elapsed: 1, remaining: 0 } }]); done(); }); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 10 } })); setTimeout(function () { response.emit('data', new Buffer('aaaaa')); }, 25); setTimeout(function () { response.emit('data', new Buffer('bbbbbbb')); request.emit('end'); }, 1150); }); it('should not report after the request ends', function (done) { progress(request, { throttle: 100 }); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 10 } })); setTimeout(function () { response.emit('data', new Buffer('aa')); }, 25); setTimeout(function () { response.emit('data', new Buffer('bbbbbbbb')); request.emit('end'); }, 50); setTimeout(function () { normalizeStates(states); expect(states).to.have.length(1); expect(states[0].percent).to.be(0.2); done(); }, 500); }); it('should not generate duplicate progress events if called twice', function (done) { progress(request, { throttle: 0 }); progress(request, { throttle: 0 }) .on('end', function () { expect(states).to.have.length(1); expect(states[0].percent).to.be(1); done(); }); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 2 } })); setTimeout(function () { response.emit('data', new Buffer('aa')); request.emit('end'); }, 25); }); it('should reset stuff on "request" event', function () { progress(request, { throttle: 0 }); expect(request.progressContext).to.be.an('object'); expect(request.progressState).to.be(undefined); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 2 } })); expect(request.progressContext).to.be.an('object'); expect(request.progressState).to.be.an('object'); request.emit('request'); expect(request.progressContext).to.be.an('object'); expect(request.progressState).to.be(null); }); it('should hook into "data" event from the response and not the request', function (done) { // See: https://github.com/IndigoUnited/node-request-progress/issues/20 progress(request, { throttle: 0 }) .on('end', function () { expect(states).to.have.length(2); expect(states[0].percent).to.be(0.5); expect(states[1].percent).to.be(1); done(); }); request.emit('request'); request.emit('response', Object.assign(response, { headers: { 'content-length': 4 } })); setTimeout(function () { response.emit('data', new Buffer('aa')); }, 25); setTimeout(function () { request.emit('data', new Buffer('aa')); }, 50); setTimeout(function () { response.emit('data', new Buffer('aa')); request.emit('end'); }, 100); }); });