jefflau / jest-fetch-mock

Jest mock for fetch
MIT License
883 stars 116 forks source link

Add support for streams #113

Open vivekratnavel opened 5 years ago

vivekratnavel commented 5 years ago

I am trying to test ReadableStream as response to fetch, but unable to. I think jest-fetch-mock only supports string responses. It would be great to have support for streams.

jefflau commented 5 years ago

Did you try adding a stream in the body parameter? We use the Response object so I assume you should be able to add a stream.

https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Parameters

fredex42 commented 4 years ago

Hi there,

I am having the same issue.

In my test, I create a stream and try to use it as a Mock value:

        const fakeStream = new ReadableStream({
            start(controller) {
                return pump();
                function pump(){
                    controller.enqueue(JSON.stringify( {id: 1, collectionId: 1234, siteId: "VX", title:"Keith"}));
                    controller.enqueue(JSON.stringify( {id: 2, collectionId: 2345, siteId: "VX", title:"Jen"}));
                    controller.enqueue(JSON.stringify( {id: 3, collectionId: 3456, siteId: "VX", title:"Sarah"}));
                }
            }
        });

        fetch.once(fakeStream);

and in my main code I use console.log() to see what I get out:

            fetch("/some/url", {
                    method: "PUT",
                    body: JSON.stringify(searchDoc),
                    headers: {
                        "Content-Type": "application/json"
                    }
                })
                .then(response =>{
                    console.log(response);
                    .
                    .
                })

and the result that I see is:

    Response {
      size: 0,
      timeout: 0,
      [Symbol(Body internals)]: { body: '[object Object]', disturbed: false, error: null },
      [Symbol(Response internals)]: {
        url: undefined,
        status: 200,
        statusText: 'OK',
        headers: Headers { [Symbol(map)]: [Object: null prototype] {} }
      }
    }

i.e. that the body was set to the string [object Object] rather than my mock ReadableStream.

I am running in Jest 24.8.0 with jest-fetch-mock version 2.1.2 under Node 12.13.0. ReadableStreams are provided by web-streams-polyfill/es6 which is brought in via my jestSetup.jsx file.

vlovich commented 2 years ago

I think I'm hitting this too:

    } else if (body instanceof Stream) ; else {
        // none of the above
        // coerce to string then buffer
        body = Buffer.from(String(body));
    }

It's unclear why because I create the readable I initialize the body with via

const { readable, writable } = new require('stream/web').TransformStream()
const r = new Request('PUT', { body: readable } )
// r.body here is `readable` coerced to a string. 

The bug is in the node-fetch dependency.

vlovich commented 2 years ago

https://github.com/node-fetch/node-fetch/issues/1096

AdrianBannister commented 1 year ago

For anyone wanting a workaround for this we added in our jest setup file:

// TODO: When this issued is resolved remove this polyfill: https://github.com/node-fetch/node-fetch/issues/1096
import { Readable } from 'stream';
class TempResponse extends Response {
  constructor(...args) {
    if (args[0] instanceof ReadableStream) {
      args[0] = Readable.from(args[0]);
    }
    super(...args);
  }
}
global.Response = TempResponse;
franciscop commented 1 year ago

Thanks Adrian, that almost worked for me! Had to do a small change but otherwise it's perfect!

// setup.js
// Allow for fetch() mock to handle streams
// https://github.com/jefflau/jest-fetch-mock/issues/113#issuecomment-1418504168
import { Readable } from "stream";
class TempResponse extends Response {
  constructor(...args) {
    if (args[0] instanceof ReadableStream) {
      args[0] = Readable.from(args[0]);
    }
    super(...args);
  }
}
Object.defineProperty(global, "Response", {
  value: TempResponse,
});
franciscop commented 1 year ago

Note also that this broke some of my unrelated text in subtle ways (I'm making a fetch() library so had a lot of tests):

expect(res.body).toEqual("hello");
expect(res.status).toEqual(200);
expect(res.statusText).toEqual("OK");     // <= Expected "OK"; Received ""
expect(res.headers.hello).toEqual("world");
expect(fetch.mock.calls.length).toEqual(1);