Error: connect ECONNREFUSED 127.0.0.1:80 if an axios fetch gets initialized outside of a react component react-testing-library/msw
Asked Answered
L

1

0

I'm getting the ECONNREFUSED error and it looks like my msw server isn't catching my get request when I start a get outside of a React component (which shaves some time off of the response).

When axios.get is uses inside of the useEffect everything works as normal and the msw picks up the request with no errors. Is there a special way to setup react-testing-library with msw so that I can initialize an axios.get() outside of react components and then have the component pick up the promise once it's loaded.

Thanks, get tool, but I just couldn't find how to get this working.

const signinPromise = axios.get(`signin?ajax=true`);

const Index = () => {
    useEffect(() => {
        (async () => {
            try {
                const response = await signinPromise;
                // ... do something with data etc
            } catch (err) {
            } finally {
            }
        })();
    }, []);

    return <>{/* content here */}</>;
};

test that's failing

import { screen } from '@testing-library/dom';
import { render, waitFor } from 'jestTestUtils';
import Index from './Index';

test('Signed in -> 404 page renders when given a bad route.', async () => {
    render(<Index />, { route: '/imabadroute' });

    await waitFor(() => expect(screen.getByRole('heading', { name: /chloë: add 404 page here to show nothing is at this route/i })));
});

My setup is as follows:

server-handlers.ts

/**
 * these are the happy path responses for endpoints that we want to exist for tests so the pages load properly during tests
 */
import { rest } from 'msw';
import { getActivities, getAlertTypes, getDashboard, getSearch, getTab, getUserAccounts } from './data';

export const handlers = [
    rest.get('/signin', (req, res, ctx) => {
        return res(ctx.status(200), ctx.json(getUserAccounts({ amount: 2, overrides: [{ disabled: false }, { disabled: false }] })));
    }),
    rest.post('/signin', (req, res, ctx) => {
        return res(ctx.status(200), ctx.json(getUserAccounts({ amount: 2, overrides: [{ disabled: false }, { disabled: false }] })));
    }),
    rest.get(`/accounts/:accountId/tweet_event/types`, (req, res, ctx) => {
        return res(ctx.status(200), ctx.json(getAlertTypes()));
    }),
    rest.get(`/accounts/:accountId/event_dashboard`, (req, res, ctx) => {
        return res(ctx.status(200), ctx.json(getDashboard({ tabAmount: 1 })));
    }),
    rest.get(`/accounts/:accountId/event_dashboard/searches/:searchId`, (req, res, ctx) => {
        return res(ctx.status(200), ctx.json(getTab()));
    }),
    rest.get(`/accounts/:accountId/event_dashboard/searches/:searchId/search`, (req, res, ctx) => {
        return res(ctx.status(200), ctx.json(getSearch()));
    }),
    rest.get(`/accounts/:accountId/event_dashboard/searches/:searchId/activities`, (req, res, ctx) => {
        return res(ctx.status(200), ctx.json(getActivities({ amount: 2 })));
    }),
];

server.ts

/** this is just building a server with all the end points setup in server-handlers */
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { handlers } from './server-handlers';

const server = setupServer(...handlers);
export { server, rest };

server-env.ts

/** our mock api is setup before each test, so the happy path is available to all tests */
import { server } from './server';

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

jestTestUtils.tsx

import { CssBaseline, ThemeProvider } from '@mui/material';
import '@testing-library/jest-dom/extend-expect';
import { render, RenderOptions } from '@testing-library/react';
import { ReactElement } from 'react';
import { BrowserRouter } from 'react-router-dom';
import theme from '../theme';

interface RenderOptionsWithRoute extends RenderOptions {
    route: string;
}

interface Props {
    children: JSX.Element;
}

const AllTheProviders = ({ children }: Props) => (
    <ThemeProvider theme={theme}>
        <CssBaseline />
        <BrowserRouter>{children}</BrowserRouter>
    </ThemeProvider>
);

const customRender = (ui: ReactElement, options?: Omit<RenderOptionsWithRoute, 'wrapper'>) => {
    window.history.pushState({}, 'Test page', options?.route || '/');
    return render(ui, { wrapper: AllTheProviders, ...options });
};

export * from '@testing-library/react';
export { customRender as render };

jest.config.ts

export default {
    // Automatically clear mock calls, instances and results before every test
    clearMocks: true,

    // An array of glob patterns indicating a set of files for which coverage information should be collected
    collectCoverageFrom: ['./src/routes/**', './src/router/**', './src/utils/**'],

    // The directory where Jest should output its coverage files
    coverageDirectory: 'coverage',

    // An array of directory names to be searched recursively up from the requiring module's location
    moduleDirectories: [
        /** gives an absolute path to the root directory so jestTestUtils can be imported by an absolute path */
        'node_modules',
        'src/testing',
    ],

    /**
     * A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
     *
     * mirrors the resolve.alias object in webpack.config.js so the tests can import the same as in the code
     */
    moduleNameMapper: {
        '^@mui/styled-engine$': '@mui/styled-engine-sc',
        '^routes(.*)$': '<rootDir>/src/routes/$1',
        '^router(.*)$': '<rootDir>/src/router/$1',
        '^stores(.*)$': '<rootDir>/src/stores/$1',
        '^utils(.*)$': '<rootDir>/src/utils/$1',
        // svgr support
        '\\.svg': '<rootDir>/src/testing/__mocks__/svg.ts',
    },

    // A list of paths to modules that run some code to configure or set up the testing framework before each test
    setupFilesAfterEnv: ['<rootDir>/src/testing/__mocks__/setup-env.ts'],

    // The test environment that will be used for testing
    testEnvironment: 'jest-environment-jsdom',
};
Longtin answered 21/4, 2022 at 21:37 Comment(0)
B
0

My guess here is that since you're using msw and you're triggering the get request once you import the file, the server might still not be up to catch your request. In that case, I'll try using require or dynamic import to import the component within the specific test and it will work.

Something like this:

  it("should catch request", () => {
    const { App } = require("./App");
    render(<App />);
  });
Beesley answered 23/4, 2022 at 13:43 Comment(5)
No luck with that. Same thing. The promise is created, but it throws the ECONNREFUSED error in the useEffect when it tries to resolve it. It's unhappy either way with this pattern of starting a fetch outside of a component.Canova
You're on to something, this issue is definitely some kind of race condition, but using require inside the test doesn't get it catching the msw endpoints.Canova
@ChloëRice can you please edit the question and add your msw server registration? I did reproduce this one locally and the fix I suggested worked. Either attach a github repo with this issue or add the missing pieces so we'll be able to help :)Beesley
I added a bunch of my msw server setup for you. Probably some other stuff that isn't important too. Thanks again for your help!Canova
Sorry it took me some time. I tried reproducing that and it works in my repo, you can have a look here: github.com/MatanBobi/stackoverflow-71961138Beesley

© 2022 - 2024 — McMap. All rights reserved.