Make react-router not break Jest (reactJs) tests - javascript

I'm currently trying to add Jest tests to a React application (found here).
However, when I run the following test,
/** #jsx React.DOM */
jest.dontMock('jquery');
jest.dontMock('../js/components/CategoryPage.jsx');
describe('Category Page', function() {
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
var CategoryPage = require('../js/components/CategoryPage.jsx');
it('renders into the page correctly', function() {
// Render the CategoryPage into the document
var categoryPage = TestUtils.renderIntoDocument(
<CategoryPage params={{"category": "tests"}} />
);
expect(categoryPage).toBeDefined();
});
});
I get the following error:
● Category Page › it renders into the page correctly
- TypeError: Property 'makeHref' of object #<Object> is not a function
at Navigation.makeHref (/home/stephen/reps/node_modules/react- router/modules/mixins/Navigation.js:29:25)
at React.createClass.getHref (/home/stephen/reps/node_modules/react-router/modules/components/Link.js:76:17)
at React.createClass.render (/home/stephen/reps/node_modules/react-router/modules/components/Link.js:97:18)
at ReactCompositeComponentMixin._renderValidatedComponent (/home/stephen/reps/node_modules/react/lib/ReactCompositeComponent.js:1260:34)
at wrapper [as _renderValidatedComponent] (/home/stephen/reps/node_modules/react/lib/ReactPerf.js:50:21)
at ReactCompositeComponentMixin.mountComponent (/home/stephen/reps/node_modules/react/lib/ReactCompositeComponent.js:802:14)
at wrapper [as mountComponent] (/home/stephen/reps/node_modules/react/lib/ReactPerf.js:50:21)
at ReactDOMComponent.ReactMultiChild.Mixin.mountChildren (/home/stephen/reps/node_modules/react/lib/ReactMultiChild.js:195:42)
at ReactDOMComponent.Mixin._createContentMarkup (/home/stephen/reps/node_modules/react/lib/ReactDOMComponent.js:260:32)
at ReactDOMComponent.Mixin.mountComponent (/home/stephen/reps/node_modules/react/lib/ReactDOMComponent.js:182:14)
at ReactDOMComponent.wrapper [as mountComponent] (/home/stephen/reps/node_modules/react/lib/ReactPerf.js:50:21)
at ReactDOMComponent.ReactMultiChild.Mixin.mountChildren (/home/stephen/reps/node_modules/react/lib/ReactMultiChild.js:195:42)
at ReactDOMComponent.Mixin._createContentMarkup (/home/stephen/reps/node_modules/react/lib/ReactDOMComponent.js:260:32)
Both my app and the CategoryPage component specifically use react-router. The CategoryPage contains a mixin which uses react-router for authentication. Based on my own debugging, I have found that the error is occurring when Jest tries to call makeHref, one of react-router's built-in methods for Navigation.
To fix this, I first tried calling jest.dontMock('react-router'), but this did not have any effect. The problem seems to be that, by not mocking CategoryPage, jest will automatically and irreversibly include all of its dependecies, unmocked.
Part of the reason this issue is so difficult to solve is because most people using Jest with React seem to not be testing their components, either because they are not as test-focused or because they are using Flux and only testing Stores, Dispatchers, etc. We are not yet using Flux, so this is not an option for us, but may be something we have to transition to in the future.
EDIT 1: The test passes if I remove the jest.dontMock('../js/components/CategoryPage.jsx') but then it is impossible to actually test the functionality of that component.
EDIT 2: When I exclude jest.dontMock('jquery') I get another error related to the mixin I use to create Modals:
Category Page › it encountered a declaration exception
- TypeError:
/home/stephen/reps/js/components/CategoryPage.jsx:
/home/stephen/reps/js/components/Feed.jsx:
/home/stephen/reps/js/components/InvestmentButton.jsx:
/home/stephen/reps/js/components/Modal.jsx:
/home/stephen/reps/js/mixins/BootstrapModalMixin.jsx:
/home/stephen/reps/node_modules/bootstrap/dist/js/npm.js:
/home/stephen/reps/node_modules/bootstrap/js/alert.js: Cannot call method 'on' of undefined
EDIT 3: I have seemingly isolated the bug to react-router's Navigation mixin, where it calls this.context.makeHref. The React team has deprecated this.context since version .9 so I believe this may be the source of the problems. Thus, any work-around or fix for this.context is welcome.

I went ahead and put together a fix which allows you to still use Jest.
https://labs.chie.do/jest-testing-with-react-router/

I ended up figuring this out with some help from the creator of rackt-router after creating the issue found here: https://github.com/rackt/react-router/issues/465 .
I got around this by using Karma and Jasmine to test my application. I then used the stub function makeStubbedDescriptor found here: https://gist.github.com/rpflorence/1f72da0cd9e507ebec29.

Related

Getting "TypeError: undefined is not an object" in an Expo React Native project when trying to create a custom "react-hook-form" input component

I'm trying to create a reusable component and use it instead of the default TextInput component in all of our screens. Inside this new CustomTextInput.js component, I'm exporting the main configurable component as default which is wrapped in a Controller component provided by the react-hook-form library. I'm then supposed to pass the control config object from the parent component whenever I create an instance of the CustomTextInput component.
Besides the default export, I'm also exporting some pre-configured variations of that CustomTextInput component. I was successful with exporting and using the EmailInput and PasswordInput configurations, but I'm encountering ref and revalidation errors whenever I try to use my CalendarInput and LocationInput components. Though I'm able to create them manually by passing all the configurations through the main component from the default export of CustomTextInput.js.
To be more specific I'm getting this error message for when I try to display either CalendarInput or LocationInput:
TypeError: undefined is not an object (evaluating '_ref21$reValidateMode.isReValidateOnBlur')
I reproduced the error in an Expo Snack and the essential parts of the code can be found at this URL:
https://snack.expo.io/#sepsol/custom-text-input-error
How can I bypass this error and resolve this issue by successfully displaying CalendarInput and LocationInput exported from CustomTextInput.js component?
Thanks to #leapful I was able to solve the problem. It was a typo on my end:
The CalendarInput and LocationInput are currently using wrong props parameter. It should be:
export function CalendarInput({ control, ... }) {}
instead of
export function CalendarInput(control, ...)

How to hide componentWillMount warnings

I'm getting four big warnings that can not be minimized in my console. These warnings are from what I understand not because I have done anything wrong, but because react-router-dom and react-select use the deprecated componentWillMount function. How do I get rid of the warnings?
I have tried looking up the problem on this site, but the closest to a solution I have found is https://stackoverflow.com/a/49166225/12057512. Since the answer is from over a year ago I am wondering if this is still the case. Have these big/popular npm packages still not updated since then?
This is one of the warnings I get (the others are similar):
react-dom.development.js:11494 Warning: componentWillMount has been
renamed, and is not recommended for use. See https:// fb .
me/react-async-component-lifecycle-hooks for details.
Move code with side effects to componentDidMount, and set initial state in the constructor.
Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name
will work. To rename all deprecated lifecycles to their new names, you
can run npx react-codemod rename-unsafe-lifecycles in your project
source folder.
Please update the following components: BrowserRouter, Route, Router,
Switch
(I actually tried to run "npx react-codemod rename-unsafe-lifecycles" but it made no difference)
I have no control over how these npm packages work internally, so I find it frustrating that I constantly get these warnings that I can not fix or remove.
The best I found..
const warn = console.warn;
function logWarning(...warnings){
let showWarning = true;
warnings.forEach(warning => {
if (warning.includes("UNSAFE_")) showWarning = false;
else if (warning.includes("SourceMap")) showWarning = false;
else if (warning.includes("DevTools")) showWarning = false;
});
if(showWarning) warn(...warnings);
}
console.warn = logWarning;
The common way to fix this would be to update the affected libraries (as you say react-router and react-select). If these are being maintained, then they would release new versions that don't produce these warnings. If that is not an option for you, then I don't know, I don't think React has a way of suppressing specific warnings.
Note that the warnings are only shown in the dev build of React, they won't be shown in the production build of React (see DOCs).
JeanMGirard's answer causes Uncaught RangeError: Maximum call stack size exceeded in some instances like when you forget to add dependencies to the dependency array within the useEffect React Hook. This can make it very hard to debug certain bugs in your code.
Normally, React DevTools would handle this by displaying the warning cause and possible solutions.
Here's a solution which ensures React DevTools handles our warnings as normal, but hides the UNSAFE_ warnings:
const backup = console.warn;
console.warn = function filterWarnings(warning) {
// If the warning includes any of the following text, let's hide it.
const supressedWarnings = [
"Warning: componentWillMount has been renamed, and is not recommended for use.",
"Warning: componentWillReceiveProps has been renamed, and is not recommended for use.",
"Warning: componentWillUpdate has been renamed, and is not recommended for use.",
];
if (warning.length && !supressedWarnings.some(entry => warning.includes(entry))) {
backup.apply(console, arguments);
}
};
Use code below:
console.disableYellowBox = true;
In index.android.js, you can use:
console.disableYellowBox = true
console.warn = () => {}

Unable to import andThen in acceptance test - ember.js

I am quite new to ember.js project where i am trying to write my first acceptance test for testing my root path works. I am following the below tutorial . I was unable to import "module-for-acceptance" from the helpers as its deprecated. when i run the below test i am getting an error which says (0 , _testHelpers.andThen) is not a function. I had also gone through ember js discussion post and imported andThen. It does not seem to work . How can i import andThen and make my test work . Thank you.
Test case
import { module, test } from 'qunit';
import { visit, currentURL ,andThen } from '#ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
module('Acceptance | list rentals', function(hooks) {
setupApplicationTest(hooks);
test('should redirect to rentals route', function (assert) {
visit('/');
andThen(function() {
assert.equal(currentURL(), '/rentals', 'should redirect automatically');
});
});
});
Log
Died on test #1 at Object.<anonymous> (http://localhost:7357/assets/tests.js:8:21)
at processModule (http://localhost:7357/assets/test-support.js:3765:16)
at module$1 (http://localhost:7357/assets/test-support.js:3790:4)
at Module.callback (http://localhost:7357/assets/tests.js:6:21)
at Module.exports (http://localhost:7357/assets/vendor.js:111:32)
at requireModule (http://localhost:7357/assets/vendor.js:32:18)
at TestLoader.require (http://localhost:7357/assets/test-support.js:13736:9): (0 , _testHelpers.andThen) is not a function# 60 ms
Source:
TypeError: (0 , _testHelpers.andThen) is not a function
at Object.<anonymous> (http://localhost:7357/assets/tests.js:10:32)
at runTest (http://localhost:7357/assets/test-support.js:5618:30)
at Test.run (http://localhost:7357/assets/test-support.js:5604:6)
at http://localhost:7357/assets/test-support.js:5831:12
at processTaskQueue (http://localhost:7357/assets/test-support.js:5197:24)
at advanceTaskQueue (http://localhost:7357/assets/test-support.js:5182:4)
at Object.advance (http://localhost:7357/assets/test-support.js:5168:4)
at unblockAndAdvanceQueue (http://localhost:7357/assets/test-support.js:6944:20)
at begin (http://localhost:7357/assets/test-support.js:6978:5)
at http://localhost:7357/assets/test-support.js:6219:6
Tried to restart test while already started (test's semaphore was 0 already)# 61 ms
Source:
at resume (http://localhost:7357/assets/test-support.js:6171:5)
at done (http://localhost:7357/assets/test-support.js:6362:7)
at Class.asyncEnd (http://localhost:7357/assets/test-support.js:13822:9)
at asyncEnd (http://localhost:7357/assets/vendor.js:68040:15)
at http://localhost:7357/assets/vendor.js:67197:31
at invoke (http://localhost:7357/assets/vendor.js:65509:16)
at Queue.flush (http://localhost:7357/assets/vendor.js:65400:13)
at DeferredActionQueues.flush (http://localhost:7357/assets/vendor.js:65597:21)
Ember testing has moved to an async/await pattern instead of using andThen and other global test helpers. That tutorial is for a fairly old version of Ember, you'll have a lot more success with a more recent guide. Even if you are not ready to update to a newer version of ember I would still recommend following the new test patterns as they are significantly easier to read and write.
If you want to test it with andThen you wouldn't need to import it as it was provided as a global, but you need to make sure your testing dependencies are correct. I would start with comparing your current package.json with the default for ember apps at that time you may need to downgrade some packages in order to get access to the old imports and global test helpers.

Testing React Native FlatList with Enzyme

I'm trying to test with Enzyme a React Native FlatList. I want to check if the right function is called when the list reaches the end:
import { listIsAtTheEnd } from "./actions";
import { mount } from "enzyme";
jest.mock("./actions");
describe("Main page", () => {
if("Calls listIsAtTheEnd when FlatList reaches the end", () => {
var app = mount(<App />);
var container = app.find("FlatListContainer");
var fList = container.childAt(0);
fList.instance().scrollToEnd();
expect(listIsAtTheEnd.mock.calls).toHaveLength(1)
})
})
But this is what I'm getting:
TypeError: this._scrollRef.scrollTo is not a function
at VirtualizedList.scrollToEnd (node_modules/react-native/Libraries/Lists/VirtualizedList.js:219:17)
at FlatList.scrollToEnd (node_modules/react-native/Libraries/Lists/FlatList.js:544:141)
at Object.<anonymous> (src/components/forumPage/__tests__/forumPageTests.js:81:21)
at tryCallTwo (node_modules/promise/lib/core.js:45:5)
at doResolve (node_modules/promise/lib/core.js:200:13)
at new Promise (node_modules/promise/lib/core.js:66:3)
What am I doing wrong? What's the correct way to test this?
As it stands it doesn't seem possible/very nice to test React Native with enzyme as soon as you go beyond a very simple shallow render and snapshot combo.
I've found using Test Renderer far more reliable to render out something like a FlatList, traversing it and invoking actions
In addition testing the above is going to be tricky, so far I've been checking the correct APIs are invoked using spies rather than actually testing the functionality as above.
This error happens though because scrollTo hasn't been correctly mocked on the ScrollView mock which you can hack around with jest.mock for example. See: this issue

Unable to unit test an Angular directive that uses $window

I've got a bunch of working unit tests for various Angular (1.4.7) directives, and I'm using Karma, Jasmine and Sinon for testing.
I'm trying to add a unit test for a new directive, which is the only directive I currently have that uses $window but I'm seeing a cryptic error in the console output:
TypeError: 'undefined' is not an object (evaluating 'this.proxy.toString')
This error is coming from sinon.js at line 2372.
I'm doing all the 'normal' things in a directive unit test such as creating a fake element that has the directive as an attribute:
testElement = document.createElement('div');
testElement.setAttribute('data-my-directive');
document.body.appendChild(testElement);
And compiling the directive:
$compile(testElement)($scope);
I'm using $provide to try mock the $window object:
module('app', function ($provide) {
$provide.value('$window', { id: 'test' });
});
But as soon as I try to use $window in the file being tested, the error shown above is thrown.
As I say, I have a bunch of other unit tests for other directives, services and controllers working as expected, so everything appears to be setup correctly. It's just this particular test.
Any ideas?
I am not sure if this is the same bug, but just a couple of days ago a fix to similar issue was got solved on sinon github:
https://github.com/sinonjs/sinon/pull/833
Fix contains lines:
var callStr = this.proxy ? this.proxy.toString() + "(" : "";
where the null check is one thing and several other lines.
This fix is at file lib/sinon/call.js in commit 7a18eb5.
I am not sure if this is same, because file is different and so is line, too. Still, this was so interesting that I would try latest sinon version and see if this gets fixed. It may be though, that similar error is in several parts of sinon, if the coder is for example same in both files.

Categories