Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot mock relative URLs in v3.0.4 #46

Closed
jwarby opened this issue Dec 23, 2015 · 28 comments
Closed

Cannot mock relative URLs in v3.0.4 #46

jwarby opened this issue Dec 23, 2015 · 28 comments

Comments

@jwarby
Copy link

jwarby commented Dec 23, 2015

As of v3.0.4, mocking a relative URL results in an Error being thrown from the dependency module node-fetch:

Error: only absolute urls are supported
    at new Request (node_modules/fetch-mock/node_modules/node-fetch/lib/request.js:34:9)
    at toRequest (node_modules/fetch-mock/src/fetch-mock.js:65:13)
    at Object.route.matcher (node_modules/fetch-mock/src/fetch-mock.js:111:15)
    at node_modules/fetch-mock/src/fetch-mock.js:233:15
    at Array.some (native)
    at routes.forEach.routes.forEach.routes.some (node_modules/fetch-mock/src/fetch-mock.js:231:11)
    at mock (node_modules/fetch-mock/src/fetch-mock.js:329:21)
    ....

In my use case, I am writing tests for a Redux application which uses fetch to grab some i18n data defined in a JSON file. The code I am testing looks like this:

fetch(`public/i18n/${locale}.json`)
  .then(/* ...snip... */)

The tests are written in mocha and are executed through Node.js, so I use isomorphic-fetch assigned to the global object inside the test, to fill in for the browser's fetch function, and use fetch-mock to mock the response:

import fetchMock from 'fetch-mock'
import fetch from 'isomorphic-fetch'

global.fetch = fetch
/* ...snip... */
fetchMock.mock(`public/i18n/${locale}.json`, { /* fixture i18n data */ })

This code worked fine in v3.0.3 as fetch-mock wasn't using Request from node-fetch, which sets the absolute URL limitation (LIMITS.md in node-fetch repository).

Related issue in node-fetch: node-fetch/node-fetch#43

@wheresrhys
Copy link
Owner

I see. So I need to use whatwg-fetch's Request implementation probably. Will have a look in the new year

@wheresrhys
Copy link
Owner

Or here https://github.com/wheresrhys/fetch-mock/blob/master/src/fetch-mock.js#L65 I can most likely just return a Request-like object as I'm only interested in comparing a few properties, not the internals

@wheresrhys
Copy link
Owner

fixed in 3.1.1. Cheers for raising the issue

@jwarby
Copy link
Author

jwarby commented Dec 27, 2015

Thanks for this!

@ericat
Copy link

ericat commented Feb 2, 2016

Sorry to open this old issue, but I came across the same error (Error: only absolute urls are supported : Request failed) and I was wondering if you could take a look. I constructed the test exactly as outlined above by jwarby, which is more or less:

import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import fetchMock from 'fetch-mock';
import fetch from 'isomorphic-fetch';

/* ... */

global.fetch = fetch;
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

describe('actions', () => {
    afterEach(() => {
      fetchMock.restore();
    });

   fetchMock
      .mock('<url>');
});

This is the stack trace:

at Object.assert [as default] (node_modules/expect/lib/assert.js:20:9)
      at Expectation.toEqual (node_modules/expect/lib/Expectation.js:69:26)
      at dispatch (node_modules/redux-mock-store/lib/index.js:55:48)
      at node_modules/redux-thunk/lib/index.js:9:74
      at dispatch (node_modules/redux/lib/applyMiddleware.js:46:18)

I get the same error with nock. I am using fetch-mock 4.1.0 and isomorphic-fetch 2.2.0. The bizarre thing is that I have been advised to use fetch-nock to get around this problem. Am I missing anything obvious?

@wheresrhys
Copy link
Owner

try importing isomorphic fetch before fetch mock. And you don't need to assign it to a local variable - isomorphic fetch will already set itself as a global

@ericat
Copy link

ericat commented Feb 3, 2016

No luck unfortunately :( Thanks for your reply, will probably try to mock the whole thing.

@wheresrhys
Copy link
Owner

If your code is public on github I don't mind having a look to see what the problem is. There's probably something in the way you're requiring your js that I haven't considered, and which is causing node-fetch to be included instead of whatwg-fetch and I'd like to either fix it or add to the troubleshooting guide.

@ericat
Copy link

ericat commented Feb 4, 2016

I'm sure I am. It's not public, but this is basically it:

require('isomorphic-fetch');
import fetchMock from 'fetch-mock';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import * as actions from '../actions/users';
import {
  LOAD_USERS
} from '../actions/users';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

describe('user actions', () => {
  var user;
  beforeEach(function() {
    contract = {
      name: "Ericat",
      email: "erica@example.com"
    };
  });

  afterEach(() => {
    fetchMock.restore();
  });

  it('creates LOAD_USERS', (done) => {
    fetchMock
      .mock('http://127.0.0.1:8080/v1/users', {users: [user]});

    const expectedActions = [
      { type: LOAD_USERS },
      { type: LOAD_USERS, body: { users: [user]  } }
    ];

    const store = mockStore({ users: [] },
      expectedActions,
      done);

      store.dispatch(actions.loadUsers())
  });
});

@nanoamp
Copy link

nanoamp commented Feb 17, 2016

I had exactly this problem, using fetch-mock 4.1.1 and isomorphic-fetch 2.2.1. To resolve it I had to do all of the following:

  • don't import isomorphic-fetch in the action code (under test). If I did, I always got the only absolute urls error.
  • to polyfill for older browsers, import whatwg-fetch; in the action instead.
  • in the test class, import isomorphic-fetch; alongside import fetch-mock;

(edited following further simplification)

Hope this helps.

@ericat
Copy link

ericat commented Feb 19, 2016

@dr-nick sorry but I am very confused:

  1. by "action code under test", do you mean in the test itself? Because in the last bullet point you seem to be saying the opposite;
  2. Do you suggest using whatwg-fetch instead of isomorphic-fetch and calling global.fetch() in my actual code?

Thank you very much for your time

@nanoamp
Copy link

nanoamp commented Feb 19, 2016

@ericat my bad, I'd been through so many permutations trying to make it work that I lost the power of writing!

  1. I mean the code being tested - i.e. the redux action creator.
  2. I did, although by tweaking the import syntax I've since dropped the need to mess around with explicitly namespacing global, so now only the first part holds true. I've revised the comment to simplify (and lose the confusing last bullet!).

Here's some barebones working code:

fetch.js

import 'whatwg-fetch'; // don't specify `as` or `from` as this polyfills into global namespace anyway
...
export function saveQuery(query) {
  return dispatch => {
    dispatch(storeQuery(query));
    fetch(`SAVE_URL${query}`))
      .then(
        resp => dispatch(receiveResults(resp)),
        error => dispatch(logError(error))
      );
  };
}

FetchTest.js

import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import fetchMock from 'fetch-mock';
import 'isomorphic-fetch'; // don't specify `as` or `from` as this polyfills into global namespace anyway
import * as actions from 'actions/fetch';
...
describe('fetch async creators', () => {
  const middlewares = [ thunk ];
  const mockStore = configureMockStore(middlewares);

  afterEach(() => {
    fetchMock.restore();
  });

  it('should dispatch expected actions for saving a query', (done) => {
    fetchMock.mock(/SAVE_URL.+/, {
      savedTerm: true,
    });

    const priorState = {};
    const expectedActions = [
      {
        type: STORE_QUERY,
        query: 'some query',
      }
    ];

    const store = mockStore(priorState, expectedActions, done);
    store.dispatch(actions.saveQuery('some query'));
  });
});

tl;dr: replace isomorphic-fetch with whatwg-fetch in the action creator module alone, make sure you don't assign it to a variable, and you should be golden.

@sallakarppinen
Copy link

@ericat, did you ever manage to resolve this issue? I am running into it now with my test suite... @dr-nick's suggestion didn't solve the problem but it did bring about a different error - after reorganising my imports in tests and swapping to whatwg-fetch in my action creators I then got TypeError: this.realFetch is not a function. Does anyone have any further suggestions?

@ghost
Copy link

ghost commented Apr 13, 2016

Getting TypeError: this.realFetch is not a function as well whenever I try to make a POST request in non-testing code

fetch(myApiRoute, {method: 'POST'})

Edit:
Just realized this will work, it needs to have a fetch passed into its context, not the global context.

fetchMock.useNonGlobalFetch(fetch)
fetchMock.mock(myApiRoute, 'POST', payload)

@wheresrhys
Copy link
Owner

I plan to make the behaviour a bit better anyway. A colleague of mine raised this issue a few days ago too

@giacomorebonato
Copy link

The useNonGlobalFetch did the trick for me too, although if in the documentation it is marked as deprecated.

@sstrong-pica9
Copy link

@wheresrhys it seems like some people have resolved this issue but I'm doing everything the same and still getting the error from node-fetch:

Error: only absolute urls are supported

The only thing I've noticed about the example from @dr-nick that's different than mine (and I understand it is just a snippet and not full working code) is that it's not testing the response from the async action (using a then statement). Once I try to return the response from the async request, I still get the error, and I don't see a way that error wouldn't be thrown judging by the stack trace and the construct of the Request object.

Sorry to reopen this but I just wanted to know if this was considered resolved or if there was still being more done to solve it.

Here's a snippet of my test that's failing:

describe("async", function() {
    beforeEach(function() {
        fetchMock.useNonGlobalFetch(fetch);
        fetchMock.mock(API_URL, { body: { response: ['value'] }});
    })

    afterEach(function() {
        fetchMock.restore();
    })

    it("runs fetch and collects response", function() {
        const store = mockStore({entities: {}});
        return store.dispatch(actions.action())
            .then(() => {
                expect(true).to.equal(true);
            })
    })
})

@seethroughdev
Copy link

@sstrong-pica9 - did you ever figure this out? I'm still getting the error as well.

@wheresrhys wheresrhys reopened this Jun 11, 2016
@wheresrhys
Copy link
Owner

wheresrhys commented Jun 11, 2016

Reopening as it's obviously still an ongoing problem. But to investigate I need somebody to post a minimal test case i.e. a repo I can clone, install and attempt to run a test on. It's not an issue I've ever been able to recreate. I have recently (v4.5.4) fixed a bug whereby mocking fetch was not properly scoped to a test/test-group, which may have some bearing on this issue

@seethroughdev
Copy link

Thanks @wheresrhys . I'll see if I can extract to a repo this weekend... I am using 4.5.4, and will test with an older version as well. Let you know how it goes.

@seethroughdev
Copy link

I think there's too many outside dependencies on my side to confirm this is a fetch-mock issue. (I switched to another fetch mocking utility and got the same result).

Going to dig further, there's definitely an issue, just not sure where its coming from.
Fine to close in the meantime, will comment if I can pinpoint it. Thanks!

@dijs
Copy link

dijs commented Jul 10, 2016

I am having the same issues...

"[Error: only absolute urls are supported]"

@dr-nick 's solution worked at first, but then I pulled in https://github.com/redux-effects/redux-effects-fetch and my tests broke with this error again...

@ericat
Copy link

ericat commented Jul 12, 2016

@sallakarppinen sorry for the late reply! I am using nock at the moment, everything appears to be working fine. I have put the test in a gist, in case it's useful to someone https://gist.github.com/Ericat/84151d8abdde2e1f228f39bea58b3f04

@killthrush
Copy link

I ran into this in v5.0 as well. As far as I can tell, the issue seems to be related to use of a non-global fetch. fetch-mock does not seem to apply to those - is it supposed to? I was able to work around the problem by using global.fetch everywhere. This stack trace helped me find the problem, gathered by using e.stack in my promise's .catch():

 Error: only absolute urls are supported
    at ./node_modules/node-fetch/index.js:54:10
    at new Fetch (./node_modules/node-fetch/index.js:49:9)
    at Fetch (./node_modules/node-fetch/index.js:37:10)
    at module.exports (./node_modules/isomorphic-fetch/fetch-npm-node.js:8:19)
    at callAPI (my-code-under-test.js:37:10)

Note that fetch-mock is nowhere to be found in the trace - my code under test is going straight into the 'real' fetch implementation.

Using mocha 2.4.5, isomorphic-fetch 2.2.1, and fetch-mock 5.0.1.

@wheresrhys
Copy link
Owner

wheresrhys commented Jul 20, 2016

There are lots of notes on use with non-global fetch here http://www.wheresrhys.co.uk/fetch-mock/installation. If they don't address your problem please provide a minimal test case of your problem (ideally as a repo I can clone, install and attempt to run a test in)

@killthrush
Copy link

killthrush commented Jul 20, 2016

Oh, I don't actually have a problem anymore, using global everywhere solved it. Was just hoping to help others that ran into the same problem. It's just weird to be using globals again after so long, but that's the fetch standard for you =)

I don't really think it's a bug if fetch is supposed to be global. If however your intent is to make this work even if someone uses an import, then I can probably help. I just need to finish my real work first and I can make a repo.

@wahengchang
Copy link

same error, does anyone figure it out ?

I am using NOCK as the API mock tool, and using thunk as async middleware action

@kellyrmilligan
Copy link

I can confirm that for me at least, in my entry point to my app,

import 'isomorphic-fetch', and only in the tests where code is involved that calls fetch,

import 'isomorphic-fetch'
import fetchMock from 'fetch-mock'

totally works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests