1import nock from 'nock'; 2import fetch from 'node-fetch'; 3import path from 'path'; 4import { Stream } from 'stream'; 5import { promisify } from 'util'; 6 7import * as Log from '../../../log'; 8import { wrapFetchWithProgress } from '../wrapFetchWithProgress'; 9 10const fs = jest.requireActual('fs') as typeof import('fs'); 11const pipeline = promisify(Stream.pipeline); 12 13const asMock = <T extends (...args: any[]) => any>(fn: T): jest.MockedFunction<T> => 14 fn as jest.MockedFunction<T>; 15 16jest.mock(`../../../log`); 17 18describe(wrapFetchWithProgress, () => { 19 beforeEach(() => { 20 asMock(Log.warn).mockClear(); 21 }); 22 it('should call the progress callback', async () => { 23 const url = 'https://example.com'; 24 25 const scope = nock(url) 26 .get('/asset') 27 .reply(() => { 28 const fixturePath = path.join(__dirname, './fixtures/panda.png'); 29 return [ 30 // Status 31 200, 32 // Data 33 fs.createReadStream(fixturePath), 34 { 35 // Headers for progress 36 'Content-Length': fs.statSync(fixturePath).size, 37 }, 38 ]; 39 }); 40 41 const onProgress = jest.fn(); 42 43 await pipeline( 44 ( 45 await wrapFetchWithProgress(fetch)(url + '/asset', { 46 onProgress, 47 }) 48 ).body, 49 require('fs').createWriteStream('/foobar') 50 ); 51 // Ensure this example is called more than once. 52 expect(onProgress).toHaveBeenCalledTimes(4); 53 expect(onProgress).toHaveBeenNthCalledWith(1, { 54 loaded: 65536, 55 progress: 0.43515444476906323, 56 total: 150604, 57 }); 58 59 // Ensure progress ends on 1.0 60 expect(onProgress).toHaveBeenLastCalledWith({ 61 loaded: 150604, 62 progress: 1, 63 total: 150604, 64 }); 65 expect(scope.isDone()).toBe(true); 66 }); 67 68 it('should warn that a request is missing the content length header', async () => { 69 const url = 'https://example.com'; 70 71 // Return no Content-Length header. 72 const scope = nock(url).get('/asset').reply(200, ''); 73 74 const onProgress = jest.fn(); 75 76 await wrapFetchWithProgress(fetch)(url + '/asset', { 77 onProgress, 78 }); 79 expect(Log.warn).toHaveBeenCalledWith( 80 'Progress callback not supported for network request because "Content-Length" header missing or invalid in response from URL:', 81 'https://example.com/asset' 82 ); 83 84 expect(scope.isDone()).toBe(true); 85 }); 86}); 87