Adam Shaw 9 år sedan
förälder
incheckning
69f0c53351

+ 164 - 0
tests/automated-better/globals.js

@@ -0,0 +1,164 @@
+
+// Setup / Teardown
+// ---------------------------------------------------------------------------------------------------------------------
+
+var optionsStack = null;
+var currentCalendar = null;
+
+
+beforeEach(function() {
+	optionsStack = [];
+});
+
+afterEach(function() {
+	optionsStack = null;
+	if (currentCalendar) {
+		currentCalendar.destroy();
+		currentCalendar = null;
+	}
+	$('#calendar').remove();
+});
+
+
+// Calendar Options and Initialization
+// ---------------------------------------------------------------------------------------------------------------------
+
+function pushOptions(options) {
+	beforeEach(function() {
+		return optionsStack.push(options);
+	});
+}
+
+function initCalendar(options, el) {
+	var Calendar = $.fullCalendar.Calendar;
+	var $el;
+
+	if (options) {
+		optionsStack.push(options);
+	}
+
+	if (el) {
+		$el = $(el);
+	}
+	else {
+		$el = $('<div id="calendar">').appendTo('body');
+	}
+
+	currentCalendar = new Calendar($el, getCurrentOptions()); // set the global
+
+	return currentCalendar.render();
+}
+
+function getCurrentOptions() {
+	return $.extend.apply($, [ {} ].concat(optionsStack));
+}
+
+
+// Categorizing Tests
+// ---------------------------------------------------------------------------------------------------------------------
+
+/*
+describeOptions(optionName, descriptionAndValueHash, callback)
+describeOptions(descriptionAndOptionsHash, callback)
+ */
+function describeOptions(optName, hash, callback) {
+	if ($.type(optName) === 'object') {
+		callback = hash;
+		hash = optName;
+		optName = null;
+	}
+
+	$.each(hash, function(desc, val) {
+		var opts;
+
+		if (optName) {
+			opts = {};
+			opts[optName] = val;
+		}
+		else {
+			opts = val;
+		}
+		opts = $.extend(true, {}, opts);
+
+		describe(desc, function() {
+			pushOptions(opts);
+			callback(val);
+		});
+	});
+}
+
+function describeValues(hash, callback) {
+	$.each(hash, function(desc, val) {
+		describe(desc, function() {
+			callback(val);
+		});
+	});
+}
+
+
+// Timezone Tests (needed?)
+// ---------------------------------------------------------------------------------------------------------------------
+
+var timezoneScenarios = {
+	none: {
+		description: 'when no timezone',
+		value: null,
+		moment: function(str) {
+			return $.fullCalendar.moment.parseZone(str);
+		}
+	},
+	local: {
+		description: 'when local timezone',
+		value: 'local',
+		moment: function(str) {
+			return moment(str);
+		}
+	},
+	UTC: {
+		description: 'when UTC timezone',
+		moment: function(str) {
+			return moment.utc(str);
+		}
+	}
+};
+
+function describeTimezones(callback) {
+	$.each(timezoneScenarios, function(name, scenario) {
+		describe(scenario.description, function() {
+			pushOptions({
+				timezone: name
+			});
+			callback(scenario);
+		});
+	});
+}
+
+function describeTimezone(name, callback) {
+	var scenario = timezoneScenarios[name];
+
+	describe(scenario.description, function() {
+		pushOptions({
+			timezone: name
+		});
+		callback(scenario);
+	});
+}
+
+
+// Misc
+// ---------------------------------------------------------------------------------------------------------------------
+
+function isElWithinRtl(el) {
+	return el.closest('.fc').hasClass('fc-rtl');
+}
+
+function oneCall(func) {
+	var called;
+	called = false;
+	return function() {
+		if (!called) {
+			called = true;
+			return func.apply(this, arguments);
+		}
+	};
+}

+ 10 - 0
tests/automated-better/toolbar/ToolbarUtils.js

@@ -0,0 +1,10 @@
+
+var ToolbarUtils = {
+
+	expectButtonEnabled: function(name, bool) {
+		var el = $('.fc-' + name + '-button');
+		expect(el.length).toBe(1);
+		expect(el.hasClass('fc-enabled')).toBe(bool);
+	}
+
+};

+ 52 - 0
tests/automated-better/toolbar/next-button.js

@@ -0,0 +1,52 @@
+/*
+TODO:
+- quick test for when button is clicked
+
+SEE ALSO:
+- rangeComputation, dateAlignment, dateIncrement
+*/
+describe('next button', function() {
+	pushOptions({
+		header: { left: 'next' },
+		defaultView: 'week',
+		defaultDate: '2017-06-08',
+		dateIncrement: { years: 1 } // next range is 2018-06-03 - 2018-06-10
+	});
+
+	describe('when there is no maxDate', function() {
+		xit('is enabled', function() {
+			initCalendar();
+			ToolbarUtils.expecButtonEnabled('next', true);
+		});
+	});
+
+	describe('when next date range is completely within maxDate', function() {
+		pushOptions({
+			maxDate: '2018-06-10'
+		});
+		xit('is enabled', function() {
+			initCalendar();
+			ToolbarUtils.expecButtonEnabled('next', true);
+		});
+	});
+
+	describe('when next date range is partially outside maxDate', function() {
+		pushOptions({
+			maxDate: '2018-06-05'
+		})
+		xit('is enabled', function() {
+			initCalendar();
+			ToolbarUtils.expecButtonEnabled('next', true);
+		});
+	});
+
+	describe('when next date range is completely beyond maxDate', function() {
+		pushOptions({
+			maxDate: '2018-06-03'
+		});
+		xit('is enabled', function() {
+			initCalendar();
+			ToolbarUtils.expecButtonEnabled('next', false);
+		});
+	});
+});

+ 32 - 0
tests/automated-better/toolbar/prev-button.js

@@ -0,0 +1,32 @@
+/*
+TODO:
+- quick test for when button is clicked
+
+SEE ALSO:
+- other range intersection tests handled by next-button
+*/
+describe('prev button', function() {
+	pushOptions({
+		header: { left: 'prev' },
+		defaultView: 'week',
+		defaultDate: '2017-06-08',
+		dateIncrement: { years: 1 } // prev range is 2016-06-05 - 2016-06-12
+	});
+
+	describe('when there is no minDate', function() {
+		xit('is enabled', function() {
+			initCalendar();
+			ToolbarUtils.expectButtonEnabled('next', true);
+		});
+	});
+
+	describe('when prev date range is completely before minDate', function() {
+		pushOptions({
+			minDate: '2018-06-12'
+		});
+		xit('is disabled', function() {
+			initCalendar();
+			ToolbarUtils.expectButtonEnabled('next', false);
+		});
+	});
+});

+ 57 - 0
tests/automated-better/toolbar/today-button.js

@@ -0,0 +1,57 @@
+/*
+TODO:
+- quick test for when button is clicked
+
+SEE ALSO:
+- other range intersection tests handled by next-button
+*/
+describe('today button', function() {
+	pushOptions({
+		header: { left: 'today' },
+		defaultView: 'month',
+		now: '2017-06-30'
+	});
+
+	describe('when now is in current month', function() {
+		pushOptions({
+			defaultDate: '2017-06-01'
+		});
+		xit('is disabled', function() {
+			initCalendar();
+			ToolbarUtils.expectButtonEnabled('today', false);
+		});
+	})
+
+	describe('when now is not current month, but still visible', function() {
+		pushOptions({
+			defaultDate: '2017-07-01'
+		});
+		xit('is disabled', function() {
+			initCalendar();
+			ToolbarUtils.expectButtonEnabled('today', false);
+		});
+	});
+
+	describe('when now is out of view', function() {
+		pushOptions({
+			defaultDate: '2017-08-01'
+		});
+
+		describe('when no minDate', function() {
+			xit('is enabled', function() {
+				initCalendar();
+				ToolbarUtils.expectButtonEnabled('today', true);
+			});
+		});
+
+		describe('when now\'s month is entirely before minDate', function() {
+			pushOptions({
+				minDate: '2017-07-02' // previous day is visible in the June
+			});
+			xit('is disabled', function() {
+				initCalendar();
+				ToolbarUtils.expectButtonEnabled('today', false);
+			});
+		});
+	});
+});

+ 10 - 0
tests/automated-better/view-dates/ViewUtils.js

@@ -0,0 +1,10 @@
+
+var ViewUtils = {
+
+	expectRange: function(start, end) {
+		var currentView = currentCalendar.getView();
+		expect(currentView.start).toEqualMoment(start);
+		expect(currentView.end).toEqualMoment(end);
+	}
+
+};

+ 15 - 0
tests/automated-better/view-dates/dateAlignment.js

@@ -0,0 +1,15 @@
+
+describe('dateAlignment', function() {
+	pushOptions({
+		defaultView: 'agenda',
+		duration: { days: 3 },
+		dateAlignment: 'week'
+	});
+
+	xit('aligns with the week', function() {
+		initCalendar({
+			defaultDate: '2017-06-15'
+		});
+		ViewUtils.expectRange('2017-06-11', '2017-06-14');
+	});
+});

+ 117 - 0
tests/automated-better/view-dates/defaultDate.js

@@ -0,0 +1,117 @@
+/*
+How a given defaultDate results in a rendered date range.
+
+SEE ALSO:
+- dateAlignment (how a custom view can force alignment)
+- rangeComputation (how is interprets defaultDate differently)
+*/
+describe('defaultDate', function() {
+	pushOptions({
+		defaultDate: '2017-06-08'
+	});
+
+	describe('when one week view', function() { // a view that has date-alignment by default
+		pushOptions({
+			defaultView: 'agendaWeek'
+		});
+
+		// when there is no shifting
+
+		describeOptions({
+			'when no minDate/maxDate': {},
+			'when range is partially before of minDate': { minDate: '2017-06-06' },
+			'when range is partially after of maxDate': { maxDate: '2017-06-05' }
+		}, function() {
+
+			xit('displays the whole range', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-04', '2017-06-11');
+			});
+		});
+
+		// when minDate/maxDate shifts range
+
+		describe('when range is completely before of minDate (which is week start)', function() {
+			pushOptions({
+				minDate: '2017-06-11'
+			});
+			xit('displays the first week on or after minDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-11', '2017-06-18');
+			});
+		});
+
+		describe('when range is completely before of minDate (which is a Wednesday)', function() {
+			pushOptions({
+				minDate: '2017-06-14'
+			});
+			xit('displays the first week on or after minDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-11', '2017-06-18');
+			});
+		});
+
+		describe('when range is completely after of maxDate (which is week start)', function() {
+			pushOptions({
+				maxDate: '2017-06-04'
+			});
+			xit('displays the last week on or before maxDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-05-28', '2017-06-04');
+			});
+		});
+
+		describe('when range is completely after of maxDate (which is a Wednesday)', function() {
+			pushOptions({
+				maxDate: '2017-05-31'
+			});
+			xit('displays the last week on or before maxDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-05-28', '2017-06-04');
+			});
+		});
+	});
+
+	describe('when a three-day view', function() { // a view with no alignment
+		pushOptions({
+			defaultView: 'agenda',
+			duration: { days: 3 }
+		});
+
+		// when there is no shifting
+
+		describeOptions({
+			'when no minDate/maxDate': {},
+			'when range is partially before of minDate': { minDate: '2017-06-09' },
+			'when range is partially after of maxDate': { maxDate: '2017-06-10' }
+		}, function() {
+
+			xit('displays the whole range', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-08', '2017-06-11');
+			});
+		});
+
+		// when minDate/maxDate shifts range
+
+		describe('when range is completely before of minDate (which is a Wednesday)', function() {
+			pushOptions({
+				minDate: '2017-06-14'
+			});
+			xit('displays the last full day as minDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-14', '2017-06-17');
+			});
+		});
+
+		describe('when range is completely after of maxDate (which is a Wednesday)', function() {
+			pushOptions({
+				maxDate: '2017-05-31'
+			});
+			xit('displays the first full date as maxDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-05-31', '2017-06-03');
+			});
+		});
+	});
+});

+ 206 - 0
tests/automated-better/view-dates/defaultRange.js

@@ -0,0 +1,206 @@
+
+describe('defaultRange', function() {
+
+	describe('when custom view with a flexible range', function() {
+		pushOptions({
+			defaultView: 'agenda'
+		});
+
+		describe('when given moment objects', function() {
+			pushOptions({
+				defaultRange: {
+					start: $.fullCalendar.moment('2017-06-26'),
+					end: $.fullCalendar.moment('2017-06-29')
+				}
+			});
+			xit('displays the range', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-26', '2017-06-29');
+			});
+		});
+
+		describe('when given strings', function() {
+			pushOptions({
+				defaultRange: {
+					start: '2017-06-26',
+					end: '2017-06-29'
+				}
+			});
+
+			xit('displays the range', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-26', '2017-06-29');
+			});
+		});
+
+		describe('when given an invalid range', function() {
+			pushOptions({
+				defaultRange: {
+					start: '2017-06-18',
+					end: '2017-06-15'
+				}
+			});
+
+			xit('reports an error and defaults to the now date', function() {
+				initCalendar({
+					now: '2017-08-01'
+				})
+				ViewUtils.expectRange('2017-08-01', '2017-08-02');
+				// TODO: detect error reporting
+			});
+		});
+
+		xit('causes rangeComputation to be ignored', function() {
+			var rangeComputationCalled = false;
+
+			initCalendar({
+				defaultRange: {
+					start: '2017-06-26',
+					end: '2017-06-29'
+				},
+				rangeComputation: function() {
+					rangeComputationCalled = true;
+				}
+			});
+			expect(rangeComputationCalled).toBe(false);
+		});
+
+		describe('when later switching to a one-day view', function() {
+			xit('shows the view at the range\'s start', function() {
+				initCalendar({
+					defaultRange: {
+						start: '2017-06-26',
+						end: '2017-06-29'
+					}
+				});
+				calendar.changeView('agendaDay');
+				ViewUtils.expectRange('2017-06-26', '2017-06-27');
+			});
+		});
+
+		describe('when range is partially before minDate', function() {
+			pushOptions({
+				defaultRange: {
+					start: '2017-05-30',
+					end: '2017-06-03'
+				},
+				minDate: '2017-06-01'
+			});
+
+			xit('navigates to specified range', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-05-30', '2017-06-03');
+			});
+
+			describe('when later switching to a one-day view', function() {
+				xit('shows the view at minDate', function() {
+					initCalendar();
+					calendar.changeView('agendaDay');
+					ViewUtils.expectRange('2017-06-01', '2017-06-02');
+				});
+			});
+		});
+
+		describe('when range is partially after maxDate', function() {
+			pushOptions({
+				defaultRange: {
+					start: '2017-06-29',
+					end: '2017-07-04',
+				},
+				maxDate: '2017-07-01'
+			});
+
+			xit('navigates to specified range', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-29', '2017-07-04');
+			});
+
+			describe('when later switching to a one-day view', function() {
+				xit('shows the view at the ms before maxDate', function() {
+					initCalendar();
+					calendar.changeView('agendaDay');
+					ViewUtils.expectRange('2017-06-30', '2017-07-01');
+				});
+			});
+		});
+
+		describe('when range is completely before minDate', function() {
+			pushOptions({
+				defaultRange: {
+					start: '2017-05-25',
+					end: '2017-05-28',
+				},
+				maxDate: '2017-07-01'
+			});
+
+			xit('navigates to minDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-01', '2017-06-02');
+			});
+
+			describe('when later switching to a one-day view', function() {
+				xit('shows the view at minDate', function() {
+					initCalendar();
+					calendar.changeView('agendaDay');
+					ViewUtils.expectRange('2017-06-01', '2017-06-02');
+				});
+			});
+		});
+
+		describe('when range is completely after minDate', function() {
+			pushOptions({
+				defaultRange: {
+					start: '2017-07-01',
+					end: '2017-07-04'
+				},
+				minDate: '2017-06-01'
+			});
+
+			xit('navigates to maxDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-30', '2017-07-01');
+			});
+
+			describe('when later switching to a one-day view', function() {
+				xit('shows the view at maxDate', function() {
+					initCalendar();
+					calendar.changeView('agendaDay');
+					ViewUtils.expectRange('2017-06-30', '2017-07-01');
+				});
+			});
+		});
+	});
+
+	describe('when custom view with fixed duration', function() {
+		pushOptions({
+			defaultView: 'agenda',
+			duration: { days: 3 }
+		});
+
+		xit('uses the start date but does not respect the range', function() {
+			initCalendar({
+				defaultRange: {
+					start: '2017-06-29',
+					end: '2017-07-04'
+				}
+			});
+			ViewUtils.expectRange('2017-06-29', '2017-07-01');
+		});
+	});
+
+	describe('when standard view', function() {
+		pushOptions({
+			defaultView: 'agendaWeek'
+		});
+
+		xit('uses the start date but does not respect the range', function() {
+			initCalendar({
+				defaultRange: {
+					start: '2017-06-29',
+					end: '2017-07-04'
+				}
+			});
+			ViewUtils.expectRange('2017-06-29', '2017-06-30');
+		});
+	});
+});

+ 90 - 0
tests/automated-better/view-dates/gotoDate.js

@@ -0,0 +1,90 @@
+/*
+SEE ALSO:
+- defaultDate (for core of date setting and contraining)
+*/
+describe('gotoDate', function() {
+	pushOptions({
+		defaultView: 'month',
+		defaultDate: '2017-06-01'
+	});
+
+	var hasRerendered;
+	beforeEach(function() {
+		hasRerendered = false;
+	});
+	pushOptions({
+		viewRender: function() { // TODO: opportunity to spy
+			hasRerendered = true;
+		}
+	});
+
+	describeValues({
+		'when forceFreshRange is undefined': undefined,
+		'when forceFreshRange is false': false
+	}, function(forceFreshRange) {
+
+		describe('when given date is within current range', function() {
+			var dateVal = '2017-06-05';
+
+			xit('should not navigate or rerender', function() {
+				initCalendar();
+				currentCalendar.gotoDate(dateVal, forceFreshRange);
+				expect(hasRerendered).toBe(false);
+			});
+		});
+
+		describe('when given date is outside current range, but still in view', function() {
+			var dateVal = '2017-07-01';
+
+			xit('should not navigate or rerender', function() {
+				initCalendar();
+				currentCalendar.gotoDate(dateVal, forceFreshRange);
+				expect(hasRerendered).toBe(false);
+			});
+		});
+	});
+
+	describe('when forceFreshRange is true', function() {
+
+		describe('when given date is within current range', function() {
+			var dateVal = '2017-06-05';
+
+			xit('should not navigate or rerender', function() {
+				initCalendar();
+				currentCalendar.gotoDate(dateVal, true);
+				expect(hasRerendered).toBe(false);
+			});
+		});
+
+		describe('when given date is outside current range, but still in view', function() {
+			var dateVal = '2017-07-01';
+
+			xit('should navigate to a new range', function() {
+				initCalendar();
+				currentCalendar.gotoDate(dateVal, true);
+				expect(hasRerendered).toBe(true);
+				expectViewRange('2017-06-25', '2017-08-06');
+			});
+		});
+	})
+
+	describe('when rangeComputation\'s range is same as current', function() {
+
+		pushOptions({
+			defaultView: 'agenda',
+			defaultDate: '2017-07-01',
+			rangeComputation: function() {
+				return {
+					start: '2017-07-01',
+					end: '2017-07-05'
+				};
+			}
+		})
+
+		xit('does not rerender', function() {
+			initCalendar();
+			currentCalendar.gotoDate('2017-12-01'); // rangeComputation won't consider this
+			expect(hasRerendered).toBe(false);
+		});
+	});
+});

+ 52 - 0
tests/automated-better/view-dates/gotoRange.js

@@ -0,0 +1,52 @@
+/*
+SEE ALSO:
+- defaultRange (for core of range setting and constraining)
+*/
+describe('gotoRange', function() {
+	pushOptions({
+		defaultView: 'agenda',
+		defaultRange: {
+			start: '2017-06-15',
+			end: '2017-06-18'
+		}
+	});
+
+	xit('navigates to a given range', function() {
+		initCalendar();
+		currentCalendar.gotoRange({
+			start: '2017-07-01',
+			end: '2017-07-05'
+		});
+		ViewUtil.expectRange('2017-07-01', '2017-07-05');
+	});
+
+	describe('when navigating to an invalid range', function() {
+		var startVal = '2017-06-05';
+		var endVal = '2017-06-04';
+
+		xit('reports an error and stays at current range', function() {
+			initCalendar();
+			currentCalendar.gotoRange({ start: startVal, end: endVal });
+			ViewUtil.expectRange('2017-06-15', '2017-06-18');
+			// TODO: detect error reporting
+		});
+	});
+
+	describe('when new range is same as old', function() {
+		var startVal = '2017-06-15';
+		var endVal = '2017-06-18';
+
+		xit('does not rerender', function() {
+			var viewRenderCalled = false;
+
+			initCalendar({
+				viewRender: function() {
+					viewRenderCalled = true;
+				}
+			});
+			currentCalendar.gotoRange({ start: startVal, end: endVal });
+			ViewUtil.expectRange('2017-06-15', '2017-06-18');
+			expect(viewRenderCalled).toBe(false);
+		});
+	});
+});

+ 5 - 0
tests/automated-better/view-dates/maxDate.js

@@ -0,0 +1,5 @@
+/*
+SEE:
+- minDate
+- rangeComputation
+*/

+ 7 - 0
tests/automated-better/view-dates/minDate.js

@@ -0,0 +1,7 @@
+/*
+SEE:
+- defaultDate
+- defaultRange
+- rangeComputation
+- toolbar/next-button
+*/

+ 147 - 0
tests/automated-better/view-dates/next.js

@@ -0,0 +1,147 @@
+
+describe('next', function() {
+	pushOptions({
+		defaultDate: '2017-06-08'
+	});
+
+	describe('when in a week view', function() {
+		pushOptions({
+			defaultView: 'agendaWeek'
+		});
+
+		describe('when dateIncrement not specified', function() {
+
+			xit('moves back by one week', function() {
+				initCalendar();
+				currentCalendar.prev();
+				ViewUtil.expectRange('2017-05-28', '2017-06-04');
+			});
+
+			xit('moves forward by one week', function() {
+				initCalendar();
+				currentCalendar.next();
+				var view = currentCalendar.getView();
+				ViewUtil.expectRange('2017-06-11', '2017-06-24');
+			});
+		})
+
+		describeOptions('dateIncrement', {
+			'when two week dateIncrement specified as a plain object': { weeks: 2 },
+			'when two week dateIncrement specified as a Duration object': moment.duration({ weeks: 2 }),
+			'when two week dateIncrement specified as a string': '14.00:00:00'
+		}, function() {
+
+			xit('moves back by two weeks', function() {
+				initCalendar();
+				currentCalendar.prev();
+				ViewUtil.expectRange('2017-05-21', '2017-05-28');
+			});
+
+			xit('moves forward by two weeks', function() {
+				initCalendar();
+				currentCalendar.next();
+				ViewUtil.expectRange('2017-06-18', '2017-07-02');
+			});
+		});
+	});
+
+	describe('when in a month view', function() {
+		pushOptions({
+			defaultView: 'month'
+		});
+
+		describe('when dateIncrement not specified', function() {
+
+			xit('moves back by one month', function() {
+				initCalendar();
+				currentCalendar.next();
+				ViewUtil.expectRange('2017-05-30', '2017-06-04');
+			});
+
+			xit('moves forward by one month', function() {
+				initCalendar();
+				currentCalendar.next();
+				ViewUtil.expectRange('2017-06-25', '2017-08-06');
+			});
+		});
+
+		describe('when two month dateIncrement is specified', function() {
+			pushOptions({
+				dateIncrement: { months: 2 }
+			});
+
+			xit('moves back by two months', function() {
+				initCalendar();
+				currentCalendar.next();
+				ViewUtil.expectRange('2017-03-26', '2017-05-07');
+			});
+
+			xit('moves forward by two months', function() {
+				initCalendar();
+				currentCalendar.next();
+				ViewUtil.expectRange('2017-07-30', '2017-09-03');
+			});
+		});
+	});
+
+	describe('when in custom three day view', function() {
+		pushOptions({
+			defaultView: 'basic',
+			duration: { days: 3 }
+		});
+
+		describe('when no dateAlignment is specified', function() {
+
+			describe('when dateIncrement not specified', function() {
+				xit('moves forward three days', function() {
+					initCalendar();
+					calendar.next();
+					ViewUtil.expectRange('2017-06-11', '2017-06-14');
+				});
+			});
+
+			describe('when two day dateIncrement is specified', function() {
+				pushOptions({
+					dateIncrement: { days: 2 }
+				})
+				xit('moves forward two days', function() {
+					initCalendar();
+					calendar.next();
+					ViewUtil.expectRange('2017-06-10', '2017-06-13');
+				});
+			});
+		})
+
+		describe('when week dateAlignment is specified', function() {
+			pushOptions({
+				dateAlignment: 'week'
+			});
+
+			describe('when dateIncrement not specified', function() {
+				xit('moves back one week', function() {
+					initCalendar();
+					calendar.next();
+					ViewUtil.expectRange('2017-06-11', '2017-06-14');
+				});
+			});
+
+			describe('when two day dateIncrement is specified', function() {
+				pushOptions({
+					dateIncrement: { days: 2 }
+				});
+
+				xit('does not navigate nor rerender', function() {
+					viewRenderCalled = false
+					initCalendar({
+						viewRender: function() {
+							viewRenderCalled = true
+						}
+					});
+					calendar.next();
+					ViewUtil.expectRange('2017-06-04', '2017-06-07'); // the same as how it started
+					expect(viewRenderCalled).toBe(false)
+				});
+			});
+		});
+	});
+});

+ 33 - 0
tests/automated-better/view-dates/prev.js

@@ -0,0 +1,33 @@
+/*
+SEE ALSO:
+- next (does core of date switching)
+*/
+describe('prev', function() {
+	pushOptions({
+		defaultDate: '2017-06-08'
+	});
+
+	describe('when in a week view', function() {
+		pushOptions({
+			defaultView: 'agendaWeek'
+		});
+
+		xit('moves back by one week', function() {
+			initCalendar();
+			currentCalendar.prev();
+			ViewUtil.expectRange('2017-05-28', '2017-06-04');
+		});
+
+		describe('when two week dateIncrement', function() {
+			pushOptions({
+				dateIncrement: { weeks: 2 }
+			});
+
+			xit('moves back by two weeks', function() {
+				initCalendar();
+				currentCalendar.prev();
+				ViewUtil.expectRange('2017-05-21', '2017-05-28');
+			});
+		});
+	});
+});

+ 153 - 0
tests/automated-better/view-dates/rangeComputation.js

@@ -0,0 +1,153 @@
+
+describe('rangeComputation', function() {
+	pushOptions({
+		defaultDate: '2017-06-08'
+	});
+
+	describe('for 3-day centered view', function() {
+
+		var receivedDate;
+		beforeEach(function() {
+			receivedDate = null;
+		});
+
+		pushOptions({
+			rangeComputation: function(date) {
+				receivedDate = date.clone();
+				return {
+					start: date.clone().subtract(1, 'days'),
+					end: date.clone().add(2, 'days')
+				};
+			}
+		});
+
+		xit('renders the correct range', function() {
+			initCalendar();
+			ViewUtils.expectRange('2017-06-07', '2017-06-10');
+		});
+
+		describe('when defaultRange is specified', function() {
+			pushOptions({
+				defaultRange: {
+					start: '2018-06-08',
+					end: '2018-06-11'
+				}
+			});
+			xit('does not get called', function() {
+				initCalendar();
+				expect(receivedDate).toBe(null);
+			});
+		});
+
+		describe('when defaultDate is before minDate', function() {
+			pushOptions({
+				minDate: '2017-07-01'
+			});
+			xit('receives minDate', function() {
+				initCalendar();
+				expect(receivedDate).toEqualMoment('2017-07-01');
+			});
+		});
+
+		describe('when defaultDate is after maxDate', function() {
+			pushOptions({
+				minDate: '2017-06-02'
+			});
+			xit('receives the millisecond before maxDate', function() {
+				initCalendar();
+				expect(receivedDate).toEqualMoment(
+					$.fullCalendar.moment('2017-07-01T00:00:00').subtract(1)
+				);
+			});
+		});
+	});
+
+	describe('for far-future view', function() {
+		pushOptions({
+			rangeComputation: function(date) {
+				return {
+					start: date.clone().add(1, 'years'),
+					end: date.clone().add(2, 'years')
+				};
+			}
+		});
+
+		describe('when returned range is beyond maxDate', function() {
+			pushOptions({
+				maxDate: '2017-07-01'
+			});
+			xit('renders the day before maxDate', function() {
+				initCalendar();
+				ViewUtils.expectRange('2017-06-30', '2017-07-01');
+			});
+		});
+	});
+
+	describe('when it returns a string start and end', function() {
+		var initialStartVal = '2017-03-01';
+		var initialEndVal = '2017-03-05';
+
+		pushOptions({
+			defaultView: 'agenda',
+			rangeComputation: function() {
+				return {
+					start: initialStartVal,
+					end: initialEndVal
+				};
+			}
+		})
+
+		xit('is respected by initial render', function() {
+			initCalendar();
+			ViewUtils.expectRange(initialStartVal, initialEndVal);
+		})
+	});
+
+	// invalid inputs
+
+	describeValues({
+		'when it has no start': { end: '2017-06-02' },
+		'when it has no end': { start: '2017-06-09' },
+		'when it has end <= start': { start: '2017-06-09', end: '2017-06-02' }
+	}, function(badRange) {
+
+		describe('defaultDate', function() {
+			pushOptions({
+				defaultView: 'agenda',
+				defaultDate: '2017-06-29',
+				rangeComputation: function() {
+					return badRange;
+				}
+			})
+
+			xit('reports a warning and renders single day at defaultDate', function() {
+				initCalendar()
+				ViewUtils.expectRange('2017-06-29', '2017-06-30');
+				// TODO: detect console.warn
+			});
+		});
+
+		describe('gotoDate', function() {
+			var initialStartVal = '2017-03-01';
+			var initialEndVal = '2017-03-05';
+
+			pushOptions({
+				defaultView: 'agenda',
+				defaultRange: {
+					start: initialStartVal,
+					end: initialEndVal
+				},
+				rangeComputation: function() {
+					return badRange;
+				}
+			});
+
+			xit('resports a warning and does not navigate', function() {
+				initCalendar()
+				currentCalendar.gotoDate('2017-06-01');
+				ViewUtils.expectRange(initialStartVal, initialEndVal);
+				// TODO: detect console.warn
+			});
+		});
+	});
+});