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