import React from 'react';
import { shallow, mount } from 'enzyme';
import { getMonthStart } from '@wojtekmaj/date-utils';
import Calendar from './Calendar';
const { format } = new Intl.DateTimeFormat('en-US', { day: 'numeric', month: 'long', year: 'numeric' });
const event = document.createEvent('MouseEvent');
event.initEvent('click', true, true);
event.persist = () => {};
describe('Calendar', () => {
it('applies className to its wrapper when given a string', () => {
const className = 'testClassName';
const component = shallow(
,
);
const wrapperClassName = component.find('.react-calendar').prop('className');
expect(wrapperClassName.includes(className)).toBe(true);
});
it('passes container element to inputRef properly', () => {
const inputRef = jest.fn();
mount(
,
);
expect(inputRef).toHaveBeenCalled();
expect(inputRef.mock.calls[0][0]).toBeInstanceOf(HTMLElement);
});
it('renders Navigation by default', () => {
const component = shallow(
,
);
const navigation = component.find('Navigation');
expect(navigation).toHaveLength(1);
});
it('does not render Navigation when showNavigation flag is set to false', () => {
const component = shallow(
,
);
const navigation = component.find('Navigation');
expect(navigation).toHaveLength(0);
});
it('uses given value when passed value using value prop', () => {
const component = shallow(
,
);
expect(component.instance().value).toEqual(new Date(2019, 0, 1));
});
it('uses given value when passed value using defaultValue prop', () => {
const component = shallow(
,
);
expect(component.instance().value).toEqual(new Date(2019, 0, 1));
});
it('renders given view when passed view using view prop', () => {
const component = shallow(
,
);
expect(component.instance().view).toBe('century');
});
it('renders given view when passed view using defaultView prop', () => {
const component = shallow(
,
);
expect(component.instance().view).toBe('century');
});
it('renders given active start date when passed active start date using activeStartDate prop', () => {
const component = shallow(
,
);
expect(component.instance().activeStartDate).toEqual(new Date(2019, 0, 1));
});
it('renders given active start date when passed active start date using activeStartDate prop', () => {
const component = shallow(
,
);
expect(component.instance().activeStartDate).toEqual(new Date(2019, 0, 1));
});
it('changes activeStartDate when updating value via props change', () => {
const value = new Date(2018, 1, 15);
const newValue = new Date(2018, 0, 15);
const newActiveStartDate = new Date(2018, 0, 1);
const component = shallow(
,
);
component.setProps({ value: newValue });
expect(component.instance().activeStartDate).toEqual(newActiveStartDate);
});
it('changes activeStartDate when updating value via onChange', () => {
const value = new Date(2018, 1, 15);
const newValue = new Date(2018, 0, 15);
const newActiveStartDate = new Date(2018, 0, 1);
const component = shallow(
,
);
component.instance().onChange(newValue, event);
expect(component.instance().activeStartDate).toEqual(newActiveStartDate);
});
it('changes Calendar view given new activeStartDate value', () => {
const activeStartDate = new Date(2017, 0, 1);
const newActiveStartDate = new Date(2018, 0, 1);
const component = shallow(
,
);
component.setProps({ activeStartDate: newActiveStartDate });
expect(component.instance().activeStartDate).toEqual(newActiveStartDate);
});
describe('renders views properly', () => {
it('renders MonthView by default', () => {
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView).toHaveLength(1);
});
it('renders MonthView when given view = "month"', () => {
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView).toHaveLength(1);
});
it('renders YearView when given view = "year"', () => {
const component = shallow(
,
);
const yearView = component.find('YearView');
expect(yearView).toHaveLength(1);
});
it('renders DecadeView when given view = "decade"', () => {
const component = shallow(
,
);
const decadeView = component.find('DecadeView');
expect(decadeView).toHaveLength(1);
});
it('renders CenturyView when given view = "century"', () => {
const component = shallow(
,
);
const centuryView = component.find('CenturyView');
expect(centuryView).toHaveLength(1);
});
it('renders maximum allowed view when given maxDetail', () => {
const component = shallow(
,
);
const yearView = component.find('YearView');
expect(yearView).toHaveLength(1);
});
it('renders maximum allowed view when given view that is not allowed', () => {
const component = shallow(
,
);
const yearView = component.find('YearView');
expect(yearView).toHaveLength(1);
});
it('renders maximum allowed view when attempting to externally switch to a view that is not allowed', () => {
const component = shallow(
,
);
component.setProps({ view: 'month' });
const yearView = component.find('YearView');
expect(yearView).toHaveLength(1);
});
it('renders maximum allowed view when given changed maxDetail', () => {
const component = shallow(
,
);
component.setProps({ maxDetail: 'year' });
component.update();
const yearView = component.find('YearView');
expect(yearView).toHaveLength(1);
});
});
it('does not pass showWeekNumbers flag to MonthView component by default', () => {
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('showWeekNumbers')).toBeFalsy();
});
it('passes showWeekNumbers flag to MonthView component given showWeekNumbers flag', () => {
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('showWeekNumbers')).toBeTruthy();
});
it('passes showNeighboringMonth flag to MonthView component given showNeighboringMonth flag', () => {
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('showNeighboringMonth')).toBeTruthy();
});
describe('displays initial view properly', () => {
it('displays a view with a given value when defaultValue is given', () => {
const defaultValue = new Date(2017, 0, 15);
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('activeStartDate')).toEqual(new Date(2017, 0, 1));
});
it('displays a view with a given value when value is given', () => {
const value = new Date(2017, 0, 15);
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('activeStartDate')).toEqual(new Date(2017, 0, 1));
});
it('displays a view with defaultActiveStartDate when value is given and defaultActiveStartDate is given', () => {
const defaultActiveStartDate = new Date(2017, 0, 1);
const value = new Date(2018, 0, 15);
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('activeStartDate')).toEqual(defaultActiveStartDate);
});
it('displays a view with defaultActiveStartDate when no value is given and defaultActiveStartDate is given', () => {
const defaultActiveStartDate = new Date(2017, 0, 1);
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('activeStartDate')).toEqual(defaultActiveStartDate);
});
it('displays a view with activeStartDate when no value is given and activeStartDate is given', () => {
const activeStartDate = new Date(2017, 0, 1);
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('activeStartDate')).toEqual(activeStartDate);
});
it('displays a view with today\'s date when no value and no activeStartDate is given', () => {
const today = new Date();
const beginOfCurrentMonth = getMonthStart(today);
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('activeStartDate')).toEqual(beginOfCurrentMonth);
});
it('displays days on the correct weekdays when given a defaultActiveStartDate', () => {
const defaultActiveStartDate = new Date(2012, 5, 6);
const component = mount(
,
);
const firstDayTile = component.find('.react-calendar__tile').first();
const firstDayTileTimeAbbr = firstDayTile.find('abbr').prop('aria-label');
// The date of the first Monday that this calendar should show is May 28, 2012.
expect(firstDayTileTimeAbbr).toBe(format(new Date(2012, 4, 28)));
});
it('displays days on the correct weekdays when given an activeStartDate', () => {
const activeStartDate = new Date(2012, 5, 6);
const component = mount(
,
);
const firstDayTile = component.find('.react-calendar__tile').first();
const firstDayTileTimeAbbr = firstDayTile.find('abbr').prop('aria-label');
// The date of the first Monday that this calendar should show is May 28, 2012.
expect(firstDayTileTimeAbbr).toBe(format(new Date(2012, 4, 28)));
});
});
describe('handles drill up properly', () => {
it('drills up when allowed', () => {
const component = shallow(
,
);
component.setState({ view: 'month' });
component.instance().drillUp();
expect(component.instance().view).toBe('year');
});
it('calls onDrillUp on drill up properly given view prop', () => {
const onDrillUp = jest.fn();
const component = shallow(
,
);
component.instance().drillUp();
expect(onDrillUp).toHaveBeenCalledWith({
activeStartDate: new Date(2017, 0, 1),
view: 'year',
});
});
it('calls onDrillUp on drill up properly when not given view prop', () => {
const onDrillUp = jest.fn();
const component = shallow(
,
);
component.setState({ view: 'month' });
component.instance().drillUp();
expect(onDrillUp).toHaveBeenCalledWith({
activeStartDate: new Date(2017, 0, 1),
view: 'year',
});
});
it('refuses to drill up when already on minimum allowed detail', () => {
const onDrillUp = jest.fn();
const component = shallow(
,
);
component.instance().drillUp();
expect(onDrillUp).not.toHaveBeenCalled();
});
});
describe('handles drill down properly', () => {
it('drills down when allowed', () => {
const component = shallow(
,
);
component.setState({ view: 'century' });
component.instance().drillDown(new Date(2011, 0, 1));
expect(component.instance().view).toBe('decade');
});
it('calls onDrillDown on drill down given view prop', () => {
const onDrillDown = jest.fn();
const component = shallow(
,
);
component.instance().drillDown(new Date(2011, 0, 1));
expect(onDrillDown).toHaveBeenCalledWith({
activeStartDate: new Date(2011, 0, 1),
view: 'decade',
});
});
it('calls onDrillDown on drill down when not given view prop', () => {
const onDrillDown = jest.fn();
const component = shallow(
,
);
component.setState({ view: 'century' });
component.instance().drillDown(new Date(2011, 0, 1));
expect(onDrillDown).toHaveBeenCalledWith({
activeStartDate: new Date(2011, 0, 1),
view: 'decade',
});
});
it('refuses to drill down when already on minimum allowed detail', () => {
const onDrillDown = jest.fn();
const component = shallow(
,
);
component.instance().drillUp();
expect(onDrillDown).not.toHaveBeenCalled();
});
});
describe('handles active start date change properly', () => {
it('changes active start date when allowed', () => {
const component = shallow(
,
);
component.instance().setActiveStartDate(new Date(2019, 0, 1));
expect(component.instance().activeStartDate).toEqual(new Date(2019, 0, 1));
});
it('calls onActiveStartDateChange on activeStartDate initial set', () => {
const value = new Date(2019, 0, 15);
const newActiveStartDate = new Date(2018, 0, 1);
const onActiveStartDateChange = jest.fn();
const component = shallow(
,
);
component.instance().setActiveStartDate(newActiveStartDate);
expect(onActiveStartDateChange).toHaveBeenCalledWith({
activeStartDate: newActiveStartDate,
value,
view: 'year',
});
});
it('calls onActiveStartDateChange on activeStartDate change', () => {
const value = new Date(2019, 0, 15);
const activeStartDate = new Date(2017, 0, 1);
const newActiveStartDate = new Date(2018, 0, 1);
const onActiveStartDateChange = jest.fn();
const component = shallow(
,
);
component.instance().setActiveStartDate(newActiveStartDate);
expect(onActiveStartDateChange).toHaveBeenCalledWith({
activeStartDate: newActiveStartDate,
value,
view: 'year',
});
});
it('does not call onActiveStartDateChange on activeStartDate change if value is the same as before', () => {
const activeStartDate = new Date(2017, 0, 1);
const newActiveStartDate = new Date(2017, 0, 1);
const onActiveStartDateChange = jest.fn();
const component = shallow(
,
);
component.instance().setActiveStartDate(newActiveStartDate);
expect(onActiveStartDateChange).not.toHaveBeenCalled();
});
it('does not call onActiveStartDateChange on activeStartDate change if value is the same as previously inherited', () => {
const value = new Date(2017, 0, 1);
const newActiveStartDate = new Date(2017, 0, 1);
const onActiveStartDateChange = jest.fn();
const component = shallow(
,
);
component.instance().setActiveStartDate(newActiveStartDate);
expect(onActiveStartDateChange).not.toHaveBeenCalled();
});
});
describe('handles view change properly', () => {
it('calls onViewChange on view initial set', () => {
const value = new Date(2019, 0, 15);
const activeStartDate = new Date(2017, 0, 1);
const newView = 'year';
const onViewChange = jest.fn();
const component = shallow(
,
);
component.instance().setStateAndCallCallbacks({ view: newView });
expect(onViewChange).toHaveBeenCalledWith({
activeStartDate,
value,
view: newView,
});
});
it('calls onViewChange on view change', () => {
const value = new Date(2019, 0, 15);
const activeStartDate = new Date(2017, 0, 1);
const view = 'year';
const newView = 'month';
const onViewChange = jest.fn();
const component = shallow(
,
);
component.instance().setStateAndCallCallbacks({ view: newView });
expect(onViewChange).toHaveBeenCalledWith({
activeStartDate,
value,
view: newView,
});
});
it('does not call onViewChange on view change if value is the same as before', () => {
const view = 'year';
const newView = 'year';
const onViewChange = jest.fn();
const component = shallow(
,
);
component.instance().setStateAndCallCallbacks({ view: newView });
expect(onViewChange).not.toHaveBeenCalled();
});
});
describe('calls onChange properly', () => {
it('calls onChange function returning the beginning of selected period by default', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2017, 0, 1), event);
expect(onChange).toHaveBeenCalledWith(new Date(2017, 0, 1), event);
});
it('calls onChange function returning the beginning of the selected period when returnValue is set to "start"', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2017, 0, 1), event);
expect(onChange).toHaveBeenCalledWith(new Date(2017, 0, 1), event);
});
it('calls onChange function returning the end of the selected period when returnValue is set to "end"', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2017, 0, 1), event);
expect(onChange).toHaveBeenCalledWith(new Date(2017, 0, 1, 23, 59, 59, 999), event);
});
it('calls onChange function returning the beginning of selected period when returnValue is set to "range"', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2017, 0, 1), event);
expect(onChange).toHaveBeenCalledWith([
new Date(2017, 0, 1),
new Date(2017, 0, 1, 23, 59, 59, 999),
], event);
});
it('calls onChange function returning the beginning of selected period, but no earlier than minDate', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2017, 0, 1), event);
expect(onChange).toHaveBeenCalledWith(new Date(2017, 0, 1, 12), event);
});
it('calls onChange function returning the beginning of selected period, but no later than maxDate', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2017, 0, 2), event);
expect(onChange).toHaveBeenCalledWith(new Date(2017, 0, 1, 12), event);
});
it('calls onChange function returning the end of selected period, but no earlier than minDate', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2017, 0, 1), event);
expect(onChange).toHaveBeenCalledWith(new Date(2017, 0, 2, 12), event);
});
it('calls onChange function returning the end of selected period, but no later than maxDate', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2017, 0, 2), event);
expect(onChange).toHaveBeenCalledWith(new Date(2017, 0, 1, 12), event);
});
it('does not call onChange function returning a range when selected one piece of a range by default', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2018, 0, 1), event);
expect(onChange).not.toHaveBeenCalled();
});
it('does not call onChange function returning a range when selected one piece of a range given allowPartialRange = false', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2018, 0, 1), event);
expect(onChange).not.toHaveBeenCalled();
});
it('calls onChange function returning a partial range when selected one piece of a range given allowPartialRange = true', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2018, 0, 1), event);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith([
new Date(2018, 0, 1),
], event);
});
it('calls onChange function returning a range when selected two pieces of a range', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2018, 0, 1), event);
component.instance().onChange(new Date(2018, 6, 1), event);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith([
new Date(2018, 0, 1),
new Date(2018, 6, 1, 23, 59, 59, 999),
], event);
});
it('calls onChange function returning a range when selected reversed two pieces of a range', () => {
const onChange = jest.fn();
const component = shallow(
,
);
component.instance().onChange(new Date(2018, 6, 1), event);
component.instance().onChange(new Date(2018, 0, 1), event);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith([
new Date(2018, 0, 1),
new Date(2018, 6, 1, 23, 59, 59, 999),
], event);
});
});
it('passes formatMonthYear to Navigation component', () => {
const formatMonthYear = () => 'Month year';
const component = shallow(
,
);
const navigation = component.find('Navigation');
expect(navigation.prop('formatMonthYear')).toBe(formatMonthYear);
});
it('passes formatYear to Navigation component', () => {
const formatYear = () => 'Year';
const component = shallow(
,
);
const navigation = component.find('Navigation');
expect(navigation.prop('formatYear')).toBe(formatYear);
});
it('passes formatLongDate to MonthView component', () => {
const formatLongDate = () => 'Long date';
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('formatLongDate')).toBe(formatLongDate);
});
it('passes formatShortWeekday to MonthView component', () => {
const formatShortWeekday = () => 'Weekday';
const component = shallow(
,
);
const monthView = component.find('MonthView');
expect(monthView.prop('formatShortWeekday')).toBe(formatShortWeekday);
});
it('passes formatMonth to YearView component', () => {
const formatMonth = () => 'Month';
const component = shallow(
,
);
const yearView = component.find('YearView');
expect(yearView.prop('formatMonth')).toBe(formatMonth);
});
it('passes formatYear to DecadeView component', () => {
const formatYear = () => 'Year';
const component = shallow(
,
);
const decadeView = component.find('DecadeView');
expect(decadeView.prop('formatYear')).toBe(formatYear);
});
it('passes formatYear to CenturyView component', () => {
const formatYear = () => 'Year';
const component = shallow(
,
);
const centuryView = component.find('CenturyView');
expect(centuryView.prop('formatYear')).toBe(formatYear);
});
});