I'm getting the following warnings in a Jest test:
console.error node_modules/fbjs/lib/warning.js:33
Warning: `NaN` is an invalid value for the `height` css style property.
in div (created by DayPicker)
in div (created by DayPicker)
in div (created by DayPicker)
in div (created by OutsideClickHandler)
in OutsideClickHandler (created by DayPicker)
in div (created by DayPicker)
in DayPicker (created by withStyles(DayPicker))
in withStyles(DayPicker) (created by DayPickerSingleDateController)
in DayPickerSingleDateController (at DateTimeSelector.js:131)
in div (created by DateTimeSelector__DayPickerWrapper)
in DateTimeSelector__DayPickerWrapper (at DateTimeSelector.js:130)
in div (created by DateTimeSelector__Container)
in DateTimeSelector__Container (at DateTimeSelector.js:126)
in DateTimeSelector (created by Connect(DateTimeSelector))
in Connect(DateTimeSelector) (at OrderPage.js:166)
in div (created by OrderPage__Container)
in OrderPage__Container (at OrderPage.js:165)
in OrderPage (created by Connect(OrderPage))
in Connect(OrderPage) (at index.js:9)
in Route (at index.js:5)
in ProtectedRoute (at PortalApp.js:36)
in Switch (at PortalApp.js:22)
in div (at PortalApp.js:20)
in Router (created by BrowserRouter)
in BrowserRouter (at PortalApp.js:19)
in Routes (created by Connect(Routes))
in Connect(Routes) (at PortalApp.js:85)
in Provider (at PortalApp.js:84)
in PortalApp (created by WrapperComponent)
in WrapperComponent
console.error node_modules/fbjs/lib/warning.js:33
Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.
Please check the code for the DayPicker component.
I solved that adding the following line to setupTests.js:
jest.spyOn(CalendarMonth.WrappedComponent.prototype, 'setMonthTitleHeight').mockImplementation();
I also get the following:
console.error node_modules/fbjs/lib/warning.js:33
Warning: `NaN` is an invalid value for the `width` css style property.
in div (created by DayPicker)
in DayPicker (created by withStyles(DayPicker))
in withStyles(DayPicker) (created by DayPickerSingleDateController)
in DayPickerSingleDateController (at DateTimeSelector.js:131)
in div (created by DateTimeSelector__DayPickerWrapper)
in DateTimeSelector__DayPickerWrapper (at DateTimeSelector.js:130)
in div (created by DateTimeSelector__Container)
in DateTimeSelector__Container (at DateTimeSelector.js:126)
in DateTimeSelector (created by Connect(DateTimeSelector))
in Connect(DateTimeSelector) (at OrderPage.js:166)
in div (created by OrderPage__Container)
in OrderPage__Container (at OrderPage.js:165)
in OrderPage (created by Connect(OrderPage))
in Connect(OrderPage) (at index.js:9)
in Route (at index.js:5)
in ProtectedRoute (at PortalApp.js:36)
in Switch (at PortalApp.js:22)
in div (at PortalApp.js:20)
in Router (created by BrowserRouter)
in BrowserRouter (at PortalApp.js:19)
in Routes (created by Connect(Routes))
in Connect(Routes) (at PortalApp.js:85)
in Provider (at PortalApp.js:84)
in PortalApp (created by WrapperComponent)
in WrapperComponent
Solved by:
const { componentDidMount } = DayPicker.WrappedComponent.prototype;
jest.spyOn(DayPicker.WrappedComponent.prototype, 'componentDidMount').mockImplementation(
function mock(...args) {
const { style } = this.calendarInfo;
if (!style.marginLeft) style.marginLeft = 0;
if (!style.marginRight) style.marginRight = 0;
componentDidMount.apply(this, args);
},
);
I think that both NaN are generated in calculateDimension
https://github.com/airbnb/react-dates/blob/83ec316167b499ce939058973da0aff30e70d178/src/utils/calculateDimension.js#L19
Would be nice to do something like:
if (withMargin) {
size +=
parseFloat(style[`margin${axisStart}`] || 0)
+ parseFloat(style[`margin${axisEnd}`] || 0);
}
Any updates on this? Or is the jest.spyOn() the only solution?
This doesn鈥檛 really seem like a bug - if you mock something, it is solely on you to provide a correct mock, and the contract for that function is that it always returns something that parseFloat can handle.
You can choose to spy or stub any function you want, but you can鈥檛 reliably avoid returning the proper kind of value.
I agree with OP, we shouldn't have to mock internal functions to get clean test coverage. This error handling should be done within the component as neither height or width are required props.
Rereading this, I鈥檓 a bit confused.
If the error appears without any mocks, then yes, it鈥檚 a real problem. However, it鈥檚 not clear how to reproduce it. @isi-gach can you provide a repro case?
I'm getting the following warnings in a Jest test
I was getting that error running Jest tests using the react-dates component
The exact jest test code would help :-)
import DateRangePicker, { DateRangePickerComponentProps } from './DateRangePicker';
import { DayPickerRangeController } from 'react-dates';
import React from 'react';
import { shallow, mount, ShallowWrapper } from 'enzyme';
import moment from 'moment';
export type Moment = moment.Moment;
const defaultProps: DateRangePickerComponentProps = {
downloadActivity: jest.fn(),
onRequestClose: jest.fn(),
translations: {
today: 'Today',
yesterday: 'Yesterday',
last7Days: 'Last 7 days',
last30Days: 'Last 30 days',
lastMonth: 'Last month',
thisMonth: 'This month',
customRange: 'Custom range',
rangeSelected: 'Range Selected',
download: 'Download',
calendarMessage: 'Narrow it down to just the information you need by applying a date range.',
downloadActivity: 'Download activity'
},
defaultOption: {
label: 'Today',
value: 'today'
}
};
describe('DateRangePicker', () => {
it('will render datepiicker with initial month as last month', () => {
const wrapper = shallow(
<DateRangePicker
{...defaultProps}
/>
) as ShallowWrapper;
const dateRangePicker = wrapper.find(DayPickerRangeController);
expect(dateRangePicker.exists).toBeTruthy();
});
});
is the test code and the usage is
<DayPickerRangeController
startDate={selectedStartDate}
endDate={selectedEndDate}
onDatesChange={({ startDate, endDate }) => {
const newStartDate = startDate || today;
const newEndDate = endDate || today;
setDates(newStartDate, newEndDate);
}}
focusedInput={
focusedInput ? focusedInput : DateRanges.START_DATE
}
onFocusChange={(input) => {
if (!customRange && input !== focusedInput) {
const custom = {
label: translations.customRange,
value: DateRanges.CUSTOM_RANGE
};
setCurrentSelectedOption(custom);
}
if (input !== focusedInput) {
onFocusChange(input);
}
}}
numberOfMonths={2}
calendarInfoPosition={'before'}
initialVisibleMonth={() => lastMonth}
navPrev={leftArrow()}
navNext={rightArrow()}
isOutsideRange={(day: moment.Moment) => day.isBefore(oneYearAgo)}
hideKeyboardShortcutsPanel={true}
/>
@kvieira90 expect(dateRangePicker.exists).toBeTruthy(); is nonsensical; an enzyme wrapper has an "exists" function. Perhaps you mean expect(dateRangePicker.length).not.toEqual(0), or similar?
Thanks for the tip, I'll make that change. Any clue about the console errors?
Not yet.
Any updates on this? The warning is annoying.
@isi-gach could you elaborate on your jest.spyOn() solution? Where/from which package do I import DayPicker and CalendarMonth? Can't seem to work
TypeError: Cannot read property 'style' of undefined
12 | const { componentDidMount } = DayPicker.WrappedComponent.prototype;
13 | jest.spyOn(DayPicker.WrappedComponent.prototype, "componentDidMount").mockImplementation(function mock(...args) {
> 14 | const { style } = this.calendarInfo;
| ^
15 | if (!style.marginLeft) style.marginLeft = 0;
16 | if (!style.marginRight) style.marginRight = 0;
17 | componentDidMount.apply(this, args);
@fushar no update yet.
Simpler repro code would be nice; a failing test case in a PR would be most ideal.
I'm also seeing the same things and debugged deeply to work it out. As pointed out above, the issue stems from within the calculateDimension util. Specifically the lines when it tries to access style properties.
style["margin|padding|border"] are all returning undefined. I'm fairly confident this traces back to jsdom not supporting window.getComputedStyle fully as it's only returning 2 values for the element: display and visibility.
I think providing the suggested fallback can work i.e. style["margin${axisStart}"] || 0
Seems reasonable (the priority, of course, is the browser, not jsdom). Care to make a PR?
Sure I'll take a stab at it
For those who want to remove this error from their tests right now:
Object.defineProperty(window, 'getComputedStyle', {
value: () => ({
paddingLeft: 0,
paddingRight: 0,
paddingTop: 0,
paddingBottom: 0,
marginLeft: 0,
marginRight: 0,
marginTop: 0,
marginBottom: 0,
borderBottomWidth: 0,
borderTopWidth: 0,
borderRightWidth: 0,
borderLeftWidth: 0
})
});
Thanks for the Solution @freshollie it works.
Can you please explain whats happening here.
@srinivasadnt3 The component is trying to calculate from the size of an element. For this, it uses the getComputedStyle function to find all the styles applied to the element. Because the JSDom has no concept of CSS, or at least it's not ever actually applied to elements, getComputedStyle fails to yield those properties which the calculation is looking for. This means that the calculation ends up creating NaN numbers from the calculation, which is then applied to the react style property, creating the JSDom error.
This solution makes the output of getComputedStyle always 0 for all those properties.
@freshollie workaround didn't work entire for me, since I was using the current getComputedStyle implementation of jsdom, that return a few stuffs (https://github.com/jsdom/jsdom/blob/master/lib/jsdom/browser/Window.js#L643-L671) and default return for getComputedStyle is CSSStyleDeclaration. That way I got the principle of his idea and arrived in this solution:
const originalGetComputedStyle = window.getComputedStyle
const getComputedStyle = (...args) => {
const cssStyleDeclaration = originalGetComputedStyle(...args)
cssStyleDeclaration.setProperty('padding-left', 0)
cssStyleDeclaration.setProperty('padding-right', 0)
cssStyleDeclaration.setProperty('padding-top', 0)
cssStyleDeclaration.setProperty('padding-bottom', 0)
cssStyleDeclaration.setProperty('margin-left', 0)
cssStyleDeclaration.setProperty('margin-right', 0)
cssStyleDeclaration.setProperty('margin-top', 0)
cssStyleDeclaration.setProperty('margin-bottom', 0)
cssStyleDeclaration.setProperty('border-left-width', 0)
cssStyleDeclaration.setProperty('border-right-width', 0)
cssStyleDeclaration.setProperty('border-top-width', 0)
cssStyleDeclaration.setProperty('border-bottom-width', 0)
return cssStyleDeclaration
}
Object.defineProperty(window, 'getComputedStyle', {
value: getComputedStyle,
})
Here's a typescript version of @renatoagds awesome contribution, using a jest spy that is properly cleaned up in between tests.
let windowSpy: jest.SpyInstance;
beforeEach(() => {
const originalGetComputedStyle = window.getComputedStyle;
const getComputedStyle = (...args: any[]) => {
const cssStyleDeclaration = originalGetComputedStyle(args[0], args[1]);
cssStyleDeclaration.setProperty('padding-left', '0');
cssStyleDeclaration.setProperty('padding-right', '0');
cssStyleDeclaration.setProperty('padding-top', '0');
cssStyleDeclaration.setProperty('padding-bottom', '0');
cssStyleDeclaration.setProperty('margin-left', '0');
cssStyleDeclaration.setProperty('margin-right', '0');
cssStyleDeclaration.setProperty('margin-top', '0');
cssStyleDeclaration.setProperty('margin-bottom', '0');
cssStyleDeclaration.setProperty('border-left-width', '0');
cssStyleDeclaration.setProperty('border-right-width', '0');
cssStyleDeclaration.setProperty('border-top-width', '0');
cssStyleDeclaration.setProperty('border-bottom-width', '0');
return cssStyleDeclaration;
};
windowSpy = jest.spyOn(window, 'getComputedStyle');
windowSpy.mockImplementation(getComputedStyle);
});
afterEach(() => {
windowSpy.mockRestore();
});
Adding this css to the page also solved this:
.CalendarMonth_caption {
margin-top: 0;
margin-bottom: 0;
}
Most helpful comment
For those who want to remove this error from their tests right now: