18d307f52SEvan Baconimport nock from 'nock'; 28d307f52SEvan Baconimport fetch from 'node-fetch'; 38d307f52SEvan Baconimport path from 'path'; 48d307f52SEvan Baconimport { Stream } from 'stream'; 58d307f52SEvan Baconimport { promisify } from 'util'; 68d307f52SEvan Bacon 7*fa5bc561SWill Schurmanimport { asMock } from '../../../__tests__/asMock'; 88d307f52SEvan Baconimport * as Log from '../../../log'; 98d307f52SEvan Baconimport { wrapFetchWithProgress } from '../wrapFetchWithProgress'; 108d307f52SEvan Bacon 118d307f52SEvan Baconconst fs = jest.requireActual('fs') as typeof import('fs'); 128d307f52SEvan Baconconst pipeline = promisify(Stream.pipeline); 138d307f52SEvan Bacon 148d307f52SEvan Baconjest.mock(`../../../log`); 158d307f52SEvan Bacon 168d307f52SEvan Bacondescribe(wrapFetchWithProgress, () => { 178d307f52SEvan Bacon beforeEach(() => { 188d307f52SEvan Bacon asMock(Log.warn).mockClear(); 198d307f52SEvan Bacon }); 208d307f52SEvan Bacon it('should call the progress callback', async () => { 218d307f52SEvan Bacon const url = 'https://example.com'; 228d307f52SEvan Bacon 238d307f52SEvan Bacon const scope = nock(url) 248d307f52SEvan Bacon .get('/asset') 258d307f52SEvan Bacon .reply(() => { 268d307f52SEvan Bacon const fixturePath = path.join(__dirname, './fixtures/panda.png'); 278d307f52SEvan Bacon return [ 288d307f52SEvan Bacon // Status 298d307f52SEvan Bacon 200, 308d307f52SEvan Bacon // Data 318d307f52SEvan Bacon fs.createReadStream(fixturePath), 328d307f52SEvan Bacon { 338d307f52SEvan Bacon // Headers for progress 348d307f52SEvan Bacon 'Content-Length': fs.statSync(fixturePath).size, 358d307f52SEvan Bacon }, 368d307f52SEvan Bacon ]; 378d307f52SEvan Bacon }); 388d307f52SEvan Bacon 398d307f52SEvan Bacon const onProgress = jest.fn(); 408d307f52SEvan Bacon 418d307f52SEvan Bacon await pipeline( 428d307f52SEvan Bacon ( 438d307f52SEvan Bacon await wrapFetchWithProgress(fetch)(url + '/asset', { 448d307f52SEvan Bacon onProgress, 458d307f52SEvan Bacon }) 468d307f52SEvan Bacon ).body, 478d307f52SEvan Bacon require('fs').createWriteStream('/foobar') 488d307f52SEvan Bacon ); 498d307f52SEvan Bacon // Ensure this example is called more than once. 508d307f52SEvan Bacon expect(onProgress).toHaveBeenCalledTimes(4); 518d307f52SEvan Bacon expect(onProgress).toHaveBeenNthCalledWith(1, { 528d307f52SEvan Bacon loaded: 65536, 538d307f52SEvan Bacon progress: 0.43515444476906323, 548d307f52SEvan Bacon total: 150604, 558d307f52SEvan Bacon }); 568d307f52SEvan Bacon 578d307f52SEvan Bacon // Ensure progress ends on 1.0 588d307f52SEvan Bacon expect(onProgress).toHaveBeenLastCalledWith({ 598d307f52SEvan Bacon loaded: 150604, 608d307f52SEvan Bacon progress: 1, 618d307f52SEvan Bacon total: 150604, 628d307f52SEvan Bacon }); 638d307f52SEvan Bacon expect(scope.isDone()).toBe(true); 648d307f52SEvan Bacon }); 658d307f52SEvan Bacon 668d307f52SEvan Bacon it('should warn that a request is missing the content length header', async () => { 678d307f52SEvan Bacon const url = 'https://example.com'; 688d307f52SEvan Bacon 698d307f52SEvan Bacon // Return no Content-Length header. 708d307f52SEvan Bacon const scope = nock(url).get('/asset').reply(200, ''); 718d307f52SEvan Bacon 728d307f52SEvan Bacon const onProgress = jest.fn(); 738d307f52SEvan Bacon 748d307f52SEvan Bacon await wrapFetchWithProgress(fetch)(url + '/asset', { 758d307f52SEvan Bacon onProgress, 768d307f52SEvan Bacon }); 778d307f52SEvan Bacon expect(Log.warn).toHaveBeenCalledWith( 788d307f52SEvan Bacon 'Progress callback not supported for network request because "Content-Length" header missing or invalid in response from URL:', 798d307f52SEvan Bacon 'https://example.com/asset' 808d307f52SEvan Bacon ); 818d307f52SEvan Bacon 828d307f52SEvan Bacon expect(scope.isDone()).toBe(true); 838d307f52SEvan Bacon }); 848d307f52SEvan Bacon}); 85