Client.abort is not a function - reactjs

I am getting below error while run test cases by using jest with react testing library
import React from 'react';
import { render} from '#testing-library/react';
import Somecomponent from './Somecomponent';
import { Provider } from 'react-redux';
import store from './store';
describe('<Somecomponent /> spec', () => {
it('renders the component', () => {
const container = render(
<Provider store={store}>
<Somecomponent />
</Provider>
);
expect(container.firstChild).toMatchSnapshot();
});
});
Actual Result:
Test suite failed to run
TypeError: client.abort is not a function
at XMLHttpRequest.abort (node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:379:16)
at Object.abort (node_modules/jsdom/lib/jsdom/living/xhr-utils.js:425:13)
at RequestManager.close (node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:133:21)
at Window.close (node_modules/jsdom/lib/jsdom/browser/Window.js:513:29)

I might be a little late to the party, but I found my solution here (in my case the error also occurred in Angular application). Adding the HttpClientTestingModule ensures it's not doing actual http requests in test cases.

Related

Uncaught Error: useNavigate() may be used only in the context of a <Router> component in cypress unit testcases

I am trying to write "unit-test" for components in react with cypress.
Followed the link cypress docs on component testing
when I wrote testcase for a component and tried to run with "npx cypress open-ct"
then getting the above error (question title)...
Note: for login oAuth2 is implemented !!!
My index.spec.js file is
import * as React from 'react';
import { mount } from '#cypress/react';
import User from './index';
describe('User component', ()=>{
before('loginApi', ()=>{
cy.login();
//login() has login-logic and setting localStorage (placed in "commands.js" file)
//even if cy.login() is commented, error is same (so guessing this not reason of error)
})
it('Mount user', () => {
mount(<User />);
});
})
Observation1: on cypress browser under TEST BODY "mount" value is <Unknown.../>
Observation2: [Network tab] User component makes api-call and failing with 401
(#known token issue, even fails with right token)
Find attached error screenshot.
After some beating around I found out...
mount <Unknown... /> basically thrown in the case, if you have not added all the dependencies which are used in respective index.jsx file.
useNavigation() may be used only in context is thrown in the case, if you are missing BrowserRouter dependency the way you have used in your routing page (either one)
import {BrowserRouter as Router} from 'react-router-dom';
import {BrowserRouter} from 'react-router-dom';
import * as React from 'react';
import { mount } from '#cypress/react';
import User from './index';
import {BrowserRouter as Router} from 'react-router-dom';
describe('User component', ()=>{
it('Mount user', () => {
const onSubmit= cy.stub();
//or any other function which is probably used by ur component
mount(
<Router>
<User onSumbit={onSubmit}/>
</Router>
);
});
});

Unit testing Chakra UI with Jest

Currently I am trying to unit test my application that is built with Create-React-App with typescript, and it is styled with chakraui. Chakrui includes a component ThemeProvider that must wrap the entire application as such.
This is my index.tsx file
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { ThemeProvider, CSSReset } from "#chakra-ui/core/dist";
import { theme } from "#chakra-ui/core/dist";
ReactDOM.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<CSSReset />
<App />
</ThemeProvider>
</React.StrictMode>,
document.getElementById("root")
For every unit test that I write, I am having to wrap the component with ThemeProvider for the test to pass:
import React from "react";
import { render } from "#testing-library/react";
import { ThemeProvider } from "#chakra-ui/core/dist";
import App from "./App";
describe("<App />", () => {
test("smoke test", () => {
render(
<ThemeProvider>
<App />
</ThemeProvider>
);
});
});
But this is very verbose, and must be done for every test that I write. Is there a way to do this just once in each .test.tsx file?
You could create your own theme wrapper function
import React from "react";
import { ThemeProvider } from "#chakra-ui/core/dist";
export const ThemeWrapper = ({ children }) => (
<ThemeProvider>{children}</ThemeProvider>
);
And then specify the wrapper in the test
import React from "react";
import { render } from "#testing-library/react";
import { ThemeWrapper } from "../testUtils";
import App from "./App";
describe("<App />", () => {
test("smoke test", () => {
render(<App />, { wrapper: ThemeWrapper });
});
});
This marginally reduces the code for testing. You may be able to also go the route of creating a custom render function (following the steps for redux).
It could look something like
import React from "react";
import { render } from "#testing-library/react";
import { ThemeProvider } from "#chakra-ui/core/dist";
export const renderWithTheme = ui => {
const Wrapper = ({ children }) => (
<ThemeProvider>{children}</ThemeProvider>
);
return render(ui, { wrapper: Wrapper });
};
Basically the same as the wrapper above, but more integrated into a test render function. You can adjust the function signature a bit as well if you need to pass in a theme object, or other render options, this is just a simple example.
Now the test looks like
import React from "react";
import { renderWithTheme } from "../testUtils";
import App from "./App";
describe("<App />", () => {
test("smoke test", () => {
renderWithTheme(<App />);
});
It might be the case that Jest might be mocking your imports from #chakra-ui/core/dist (depending on your jest configuration) which might be resulting your imported chakra-ui components to be undefined.
Importing the Theme Provider and wrapping it everytime with your renders might be one way to do it. The problem might arise when you have multiple components in your index.tsx. So, you might not want to import each and every component.
In that case, you will want to import the actual components from #chakra-ui/core.
The best way (according to me) to do so in Jest is:
jest.mock("#chakra-ui/core", () => {
const ui = jest.requireActual("#chakra-ui/core");
return {
...ui,
customKey: 'customValue',
};
})
This way you can even add custom function and key-values to the imported module.

Cannot read property 'then' of undefined | When i try to import a component with babel-dynamic-import

I am trying to import a react component with #babel/plugin-syntax-dynamic-import but i can not make it work.
Here is my code in my main componant :
import React from 'react';
import Loadable from 'react-loadable';
const Test = Loadable({
loader: () => import('./Space').default,
loading: <div>loading</div>
});
const App = () => <Test />;
export default App;
And my code in my Space component :
import React from 'react';
const Space = () => <h1>hello from space</h1>;
export default Space;
And with this code I get this error : Uncaught TypeError: Cannot read property 'then' of undefined
And this which I put here without knowing if it can help : The above error occurred in the <LoadableComponent> component: react-dom.development.js:17117
Yes i have the #babel/plugin-syntax-dynamic-import installed and added in my babel config file.
Ok I solved it by using React.lazy (see #Mario Subotic comment).
Here is my final code:
import React, { lazy, Suspense } from 'react';
import Loadable from 'react-loadable';
const Test = lazy(() => import('./Space'));
const App = () => (
<Suspense fallback={<div>Loading...</div>}>
<Test />
</Suspense>
);
export default App;
Note: apprently have to use Suspense around the component you're lazy loading.
Source: https://github.com/reactjs/reactjs.org/issues/1810

mount() fails because target container is not dom element

I'm getting error message Target container is not a DOM element. Using webpack.
Full Error:
FAIL src/App.test.js
● Test suite failed to run
Invariant Violation: Target container is not a DOM element.
at invariant (node_modules/fbjs/lib/invariant.js:42:15)
at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:17238:34)
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:17317:12)
at Object.<anonymous> (src/index.js:32:20)
at Object.<anonymous> (src/App.test.js:5:14)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:169:7)
App.js
import React, { Component } from 'react';
import './App.scss';
import Tables from './containers/Tables/Tables'
class App extends Component {
render() {
return (
<Tables />
);
}
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import aaaApi from '#aaa/aaajs';
import './index.css';
import App from './App';
import tableBuilderReducer from './store/reducers/tableBuilder'
import registerServiceWorker from './registerServiceWorker';
const aaa = new aaaApi('xxx');
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const reducers = {
platforms: tableBuilderReducer('platforms'),
regions: tableBuilderReducer('regions'),
playback: tableBuilderReducer('playback')}
export const store = createStore(
combineReducers(reducers),
composeEnhancers(
applyMiddleware(thunk.withExtraArgument(aaa)),
),
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
registerServiceWorker();
App.test.js
import React from 'react'
import Enzyme, { mount, shallow } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import App from './App'
import { store } from './index.js'
Enzyme.configure({ adapter: new Adapter() })
function setup() {
const props = {
sortColumnHandler: jest.fn()
}
const enzymeWraper = mount(<App />)
return {
props,
enzymeWraper
}
}
describe('components', ()=> {
describe('Tables', () => {
it('should render self and sub-components', () => {
const { enzymeWraper } = setup()
})
})
})
The issue is being caused by index.js being included in the test and is failing at ReactDOM.render() when root is not found in the document created by the jsdom environment in Jest.
import { store } from './index.js' is not needed in App.test.js so removing that will fix the initial error.
Using mount from enzyme does a full DOM rendering and requires that everything be set up for a full render. It should only be used to unit test components that use lifecycle methods, interact with the DOM, etc. It will fully render the component being tested and all children components. In this case the children of App use the redux store so a store would need to be provided in a wrapping Provider in order to do a full render using mount.
In most cases, React components should be unit tested using shallow. It does a shallow rendering of just the component being tested. In this case App is a simple stateless component that contains Tables so using shallow is the right approach.
bonus tip:
Install enzyme-to-json as a dev dependency, add it to Jest as a snapshot serializer, and App.test.js gets simplified to this:
import { shallow } from 'enzyme';
import * as React from 'react';
describe("App", () => {
it('renders as expected', () => {
const component = shallow(<App />);
expect(component).toMatchSnapshot();
});
});

Invalid Chai property: toMatchSnapshot -- React.js Jest testing

I'm getting an error: Invalid Chai property: toMatchSnapshot when I try to use Jest + Enzyme's snapshot testing. I've updated my React version to 16.2 and I use enzyme-to-json library with Enzyme 3.
Code is below:
import React from 'react';
import ReactDOM from 'react-dom';
import ConnectedApp, { App } from './App';
import { ConnectedRouter } from 'react-router-redux';
import { Provider } from 'react-redux';
import { expect } from 'chai';
import { mount, shallow } from 'enzyme';
import createHistory from 'history/createMemoryHistory'
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import toJson from 'enzyme-to-json';
describe('App tests', () => {
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
let store, container, history, wrapper;
const initialState = {
output: true
}
beforeEach(() => {
store = mockStore(initialState);
history = createHistory();
wrapper = mount(
<Provider store={store}>
<ConnectedRouter history={history}>
<ConnectedApp />
</ConnectedRouter>
</Provider>
)
});
it('+++capturing Snapshot of App', () => {
expect(toJson(wrapper)).toMatchSnapshot();
});
})
I've also tried this with Jest's render like so:
import renderer from 'react-test-renderer';
it('renders correctly', () => {
var component = <Provider store={store}>
<ConnectedRouter history={history}>
<ConnectedApp />
</ConnectedRouter>
</Provider>
const tree = renderer
.create(component)
.toJSON();
expect(tree).toMatchSnapshot();
});
But I still get the Invalid Chai property: toMatchSnapshot error. Anyone know what's up?
This isn't an issue with the renderer you are using. The problem is that you are using chai expectations instead of the expectation library that ships with jest. The chai API has no toMatchSnapshot method. To fix it you can do the following:
Stop using chai and use the jest expectations exclusively. This may simply be a matter of removing line 6: import { expect } from 'chai'
However, if you need to continue to use chai (i.e. you have a lot of chai tests already written and you don't want to do a major overhaul all at once) you can do two things:
Alias either the the chai or jest expect functions in your test setup file e.g. global.chaiExpect = chai.expect
Monkey-patch the global expect function so that you can use both the chai and the jest API like in this blog post: https://medium.com/#RubenOostinga/combining-chai-and-jest-matchers-d12d1ffd0303
The relevant bit is this:
// Make sure chai and jasmine ".not" play nice together
const originalNot = Object.getOwnPropertyDescriptor(chai.Assertion.prototype, 'not').get;
Object.defineProperty(chai.Assertion.prototype, 'not', {
get() {
Object.assign(this, this.assignedNot);
return originalNot.apply(this);
},
set(newNot) {
this.assignedNot = newNot;
return newNot;
},
});
// Combine both jest and chai matchers on expect
const originalExpect = global.expect;
global.expect = (actual) => {
const originalMatchers = originalExpect(actual);
const chaiMatchers = chai.expect(actual);
const combinedMatchers = Object.assign(chaiMatchers, originalMatchers);
return combinedMatchers;
};
For those transitioning away from chai (which would be hijacking expect() from jest in the top level setupTests.js file) the simpler solution is to load jest's expect() again on top of the current test file like so:
import { expect } from "#jest/globals";
That is, until you can't fully do away with any
global.expect = chai.expect;
somewhere in the code, setupTests.js for example.
Its Simple.Just write your test scripts (something.spec.js) in to another file without importing 'chai' . It will work like a charm. No need for messy stuffs.Keep it Simple !
This is partially related to this post & root cause given by other authors are quite accurate and very informative.
I also faced same problem as discussed in this post, when I was trying to use expect(container).toHaveLength(1);
I solved this issue by changing my way to write assertion in Jest way like,
expect(container).to.have.length(1);
So basically we need to find way to change our assertion to write in Jest way, if we are using Jest.
Hope it may help someone.

Resources