angular-ui-calendar
Version:
A complete AngularJS directive for the Arshaw FullCalendar.
419 lines (361 loc) • 19.4 kB
JavaScript
/*global beforeEach, afterEach, describe, it, inject, expect, module, spyOn, fullcalendar, angular, $*/
describe('uiCalendar', function () {
'use strict';
var scope, $compile, $locale, $controller, config, element, elementScope, fullCalendar;
//Date Objects needed for event
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
beforeEach(module('ui.calendar'));
beforeEach(inject(function (_$rootScope_, _$compile_, _$locale_,_$controller_,uiCalendarConfig) {
scope = _$rootScope_.$new();
$compile = _$compile_;
$locale = _$locale_;
$controller = _$controller_;
config = uiCalendarConfig;
// create an array of events, to pass into the directive.
scope.events = [
{title: 'All Day Event',start: new Date(y, m, 1),url: 'http://www.angularjs.org'},
{title: 'Long Event',start: new Date(y, m, d - 5),end: new Date(y, m, d - 2)},
{id: 999,title: 'Repeating Event',start: new Date(y, m, d - 3, 16, 0),allDay: false},
{id: 999,title: 'Repeating Event',start: new Date(y, m, d + 4, 16, 0),allDay: true}];
// create an array of events, to pass into the directive.
scope.events2 = [
{title: 'All Day Event 2',start: new Date(y, m, 1),url: 'http://www.atlantacarlocksmith.com'},
{title: 'Long Event 2',start: new Date(y, m, d - 5),end: new Date(y, m, d - 2)},
{id: 998,title: 'Repeating Event 2',start: new Date(y, m, d - 3, 16, 0),allDay: false},
{id: 998,title: 'Repeating Event 2',start: new Date(y, m, d + 4, 16, 0),allDay: true}];
//array to test equals occurance
scope.events3 = [
{title: 'All Day Event 3',start: new Date(y, m, 1),url: 'http://www.atlantacarlocksmith.com'},
{title: 'Long Event 3',start: new Date(y, m, d - 5),end: new Date(y, m, d - 2)},
{id: 998,title: 'Repeating Event 3',start: new Date(y, m, d - 3, 16, 0),allDay: false},
{id: 998,title: 'Repeating Event 3',start: new Date(y, m, d + 4, 16, 0),allDay: true}];
scope.calEventsExt = {
color: '#f00',
textColor: 'yellow',
events: [
{type:'party',title: 'Lunch',start: new Date(y, m, d, 12, 0),end: new Date(y, m, d, 14, 0),allDay: false},
{type:'party',title: 'Lunch 2',start: new Date(y, m, d, 12, 0),end: new Date(y, m, d, 14, 0),allDay: false},
{type:'party',title: 'Click for Google',start: new Date(y, m, 28),end: new Date(y, m, 29),url: 'http://google.com/'}
]
};
// create an array of events, to pass into the directive.
scope.events4 = [{title: 'All Day Event 3',start: new Date(y, m, 1),url: 'http://www.yoyoyo.com'}];
//event Sources array
scope.eventSources = [scope.events,scope.events2]; //End of Events Array
scope.addSource = function(source) {
scope.eventSources.push(source);
};
scope.addChild = function(array) {
array.push({
title: 'Click for Google ' + scope.events.length,
start: new Date(y, m, 28),
end: new Date(y, m, 29),
url: 'http://google.com/'
});
};
scope.remove = function(array,index) {
array.splice(index,1);
};
scope.uiConfig = {
calendar:{
height: 200,
weekends: false,
defaultView: 'month'
}
};
}));
describe('compiling this directive and checking for events inside the calendar', function () {
beforeEach(function(){
fullCalendar = spyOn($.fn, 'fullCalendar');
element = $compile('<div ui-calendar="{{uiConfig.calendar}}" ng-model="eventSources"></div>')(scope);
scope.$apply();
elementScope = element.scope();
elementScope.$digest();
});
/* test the calendar's initial setup */
it('should set up the calendar with the correct options and events', function () {
expect($.fn.fullCalendar.calls.mostRecent().args[0].eventSources[0].length).toBe(4);
expect($.fn.fullCalendar.calls.mostRecent().args[0].eventSources[0][0].title).toBe('All Day Event');
expect($.fn.fullCalendar.calls.mostRecent().args[0].eventSources[0][0].url).toBe('http://www.angularjs.org');
expect($.fn.fullCalendar.calls.mostRecent().args[0].eventSources[1][0].url).toBe('http://www.atlantacarlocksmith.com');
expect($.fn.fullCalendar.calls.mostRecent().args[0].eventSources[0][3].allDay).not.toBe(false);
expect($.fn.fullCalendar.calls.mostRecent().args[0].height).toEqual(200);
expect($.fn.fullCalendar.calls.mostRecent().args[0].weekends).toEqual(false);
});
/* Test to make sure that when an event is added to the calendar everything is updated with the new event. */
it('should call its renderEvent method', function () {
expect($.fn.fullCalendar.calls.mostRecent().args[0].eventSources[0].length).toEqual(4);
expect($.fn.fullCalendar.calls.count()).toEqual(1);
scope.addChild(scope.events);
scope.$apply();
expect($.fn.fullCalendar.calls.count()).toEqual(2);
expect($.fn.fullCalendar.calls.mostRecent().args[0]).toEqual('renderEvent');
scope.addChild(scope.events);
scope.$apply();
expect($.fn.fullCalendar.calls.count()).toEqual(3);
expect($.fn.fullCalendar.calls.mostRecent().args[0]).toEqual('renderEvent');
});
/* Test to see if calendar is calling removeEvents when an event is removed */
it('should remove the correct event from the event source.', function () {
//remove an event from the scope.
scope.remove(scope.events2,0);
scope.$apply();
//events should auto update inside the calendar.
var fullCalendarParam = $.fn.fullCalendar.calls.mostRecent().args[0];
var callCount = $.fn.fullCalendar.calls.count();
expect(fullCalendarParam).toEqual('removeEvents');
expect(callCount).toEqual(2);
scope.remove(scope.events,0);
scope.$apply();
fullCalendarParam = $.fn.fullCalendar.calls.mostRecent().args[0];
callCount = $.fn.fullCalendar.calls.count();
expect(fullCalendarParam).toEqual('removeEvents');
expect(callCount).toEqual(3);
});
/* Test to see if calendar is updating when a new eventSource is added. */
it('should update the calendar if an eventSource is Added', function () {
scope.addSource(scope.events4);
scope.$apply();
//eventSources should auto update inside the calendar.
var fullCalendarParam = $.fn.fullCalendar.calls.mostRecent().args[0];
expect(fullCalendarParam).toEqual('addEventSource');
});
/* Test to see if calendar is updating when an eventSource replaces another with an equal length. */
it('should update the calendar if an eventSource has same length as prior eventSource', function () {
//replace source with one that has the same length
scope.eventSources.splice(1,1,scope.events3);
scope.$apply();
//eventSources should update inside the calendar
var callCount = $.fn.fullCalendar.calls.count();
var fullCalendarParam = $.fn.fullCalendar.calls.mostRecent().args[0];
expect(fullCalendarParam).toEqual('addEventSource');
//fullcalendar has called 3 of its own events at this time. Remove, Add, and Rerender
expect(callCount).toEqual(4);
//remove an event to prove autobinding still works
scope.remove(scope.events,0);
scope.$apply();
fullCalendarParam = $.fn.fullCalendar.calls.mostRecent().args[0];
callCount = $.fn.fullCalendar.calls.count();
expect(fullCalendarParam).toEqual('removeEvents');
expect(callCount).toEqual(5);
});
it('should work with extended event sources', function () {
scope.eventSources.push(scope.calEventsExt);
scope.$apply();
var fullCalendarParam = $.fn.fullCalendar.calls.mostRecent().args[0];
expect(fullCalendarParam).toEqual('addEventSource');
});
it('shoud refetch the whole calendar when source events are replaced', function () {
scope.eventSources[0] = scope.events3;
scope.$apply();
var fullCalendarParam = $.fn.fullCalendar.calls.mostRecent().args[0];
expect(fullCalendarParam).toEqual('addEventSource');
});
it('should make sure that if we just change the title of the event that it updates itself', function () {
var originalEvent = angular.copy(scope.events[0]);
$.fn.fullCalendar.and.callFake(function(method) {
if (method === 'clientEvents') {
return [ originalEvent ];
}
});
scope.events[0].title = 'change title';
scope.$apply();
var fullCalendarParam = $.fn.fullCalendar.calls.mostRecent().args[0];
var fullCalendarParam1 = $.fn.fullCalendar.calls.mostRecent().args[1];
expect(fullCalendarParam).toEqual('updateEvent');
expect(fullCalendarParam1).toEqual(originalEvent);
// fullCalendar 'updateEvent' need an original Event Object
expect(fullCalendarParam1).toBe(originalEvent);
});
it('should make sure that if the calendars options change then the fullcalendar method is called with the new options', function () {
expect($.fn.fullCalendar.calls.mostRecent().args[0].weekends).toEqual(false);
scope.uiConfig.calendar.weekends = true;
scope.$apply();
//3 because we are destroying the calendar as well.
expect($.fn.fullCalendar.calls.count()).toEqual(3);
expect($.fn.fullCalendar.calls.mostRecent().args[0].weekends).toEqual(true);
});
});
describe('calendarCtrl changeWatcher functionality', function(){
var calendar,
calendarCtrl,
sourcesChanged;
function onFnAdd(source) {
sourcesChanged = 'added';
}
function onFnRemove(source) {
sourcesChanged = 'removed';
}
function onFnChanged(source) {
sourcesChanged = 'changed';
}
beforeEach(function(){
calendarCtrl = $controller('uiCalendarCtrl', {$scope: scope, $element: null});
sourcesChanged = false;
scope.$apply();
});
it('should make sure changeWatcher is initialized', function () {
expect(calendarCtrl.changeWatcher).not.toBe(undefined);
});
it('should call the correct function when an event source is added or removed', function () {
var sourceWatcher = calendarCtrl.changeWatcher(scope.eventSources,calendarCtrl.sourceFingerprint);
expect(sourcesChanged).toBe(false);
sourceWatcher.subscribe(scope);
sourceWatcher.onAdded = onFnAdd;
sourceWatcher.onRemoved = onFnRemove;
sourceWatcher.onChanged = onFnChanged;
scope.$apply();
scope.eventSources.push(scope.events3);
scope.$apply();
expect(sourcesChanged).toBe('added');
scope.eventSources[0] = scope.events4;
scope.$apply();
expect(sourcesChanged).toBe('added');
scope.eventSources.splice(0,1);
scope.$apply();
expect(sourcesChanged).toBe('removed');
});
it('should call the correct function when a single event is added or removed', function () {
var eventsWatcher = calendarCtrl.changeWatcher(calendarCtrl.allEvents,calendarCtrl.eventFingerprint);
expect(sourcesChanged).toBe(false);
eventsWatcher.subscribe(scope);
eventsWatcher.onAdded = onFnAdd;
eventsWatcher.onRemoved = onFnRemove;
eventsWatcher.onChanged = onFnChanged;
scope.$apply();
scope.events2.push({id: 8,title: 'Repeating Event 2',start: new Date(y, m, d - 3, 16, 0),allDay: false});
scope.$apply();
expect(sourcesChanged).toBe('added');
scope.events2.splice(0,1);
scope.$apply();
expect(sourcesChanged).toBe('removed');
scope.events2[0].title = 'tester :)';
scope.$apply();
expect(sourcesChanged).toBe('changed');
});
it('should make sure the correct function is called when the calendarWatchEvent function is return variable is altered', function () {
scope.testX = 0;
scope.calendarWatchEvent = function(){
return scope.testX;
};
var calendarCtrl2 = $controller('uiCalendarCtrl', {$scope: scope, $element: null});
scope.$apply();
var eventsWatcher = calendarCtrl2.changeWatcher(calendarCtrl2.allEvents,calendarCtrl2.eventFingerprint);
expect(sourcesChanged).toBe(false);
eventsWatcher.subscribe(scope);
eventsWatcher.onAdded = onFnAdd;
eventsWatcher.onRemoved = onFnRemove;
eventsWatcher.onChanged = onFnChanged;
scope.$apply();
scope.testX++;
scope.$apply();
expect(sourcesChanged).toBe('changed');
});
it('should make sure that eventSources in extended form are tracked properly', function () {
scope.testX = 0;
scope.eventSources.push(scope.calEventsExt);
var calendarCtrl2 = $controller('uiCalendarCtrl', {$scope: scope, $element: null});
scope.$apply();
var eventsWatcher = calendarCtrl2.changeWatcher(calendarCtrl2.allEvents,calendarCtrl2.eventFingerprint);
expect(sourcesChanged).toBe(false);
eventsWatcher.subscribe(scope);
eventsWatcher.onAdded = onFnAdd;
eventsWatcher.onRemoved = onFnRemove;
eventsWatcher.onChanged = onFnChanged;
scope.$apply();
scope.calEventsExt.events[0].title = 'Brunch';
scope.$apply();
expect(sourcesChanged).toBe('changed');
scope.calEventsExt.events[0].title = 'Brunch';
scope.calEventsExt.events.push({type:'party',title: 'Bunch 2',start: new Date(y, m, d, 12, 0),end: new Date(y, m, d, 14, 0),allDay: false});
scope.$apply();
expect(sourcesChanged).toBe('added');
scope.calEventsExt.events[0].title = 'Brunch';
scope.calEventsExt.events.splice(0,1);
scope.$apply();
expect(sourcesChanged).toBe('removed');
});
});
describe('Testing the ability to add calendars to the calendarConfig', function(){
beforeEach(function(){
spyOn($.fn, 'fullCalendar');
expect(config.calendars.myCalendar).toBe(undefined);
$compile('<div ui-calendar="{{uiConfig.calendar}}" calendar="myCalendar" ng-model="eventSources"></div>')(scope);
scope.$apply();
});
it('should make sure the calendar sets the myCalendar object to the calendarConfig', function () {
expect(config.calendars.myCalendar).not.toBe(undefined);
});
});
describe('Local variable config testing and option overriding', function(){
it('should set names for $locale by default', function() {
spyOn($.fn, 'fullCalendar');
$locale.DATETIME_FORMATS.MONTH[0] = 'enero';
$compile('<div ui-calendar="{{uiConfig.calendar}}" ng-model="eventSources"></div>')(scope);
scope.$apply();
expect($.fn.fullCalendar.calls.mostRecent().args[0].monthNames[0]).toBe('enero');
});
it('should allow overriding names for $locale', function() {
spyOn($.fn, 'fullCalendar');
scope.uiConfig.calendar.monthNames = $locale.DATETIME_FORMATS.MONTH.slice();
scope.uiConfig.calendar.monthNames[0] = 'custom name';
$compile('<div ui-calendar="{{uiConfig.calendar}}" ng-model="eventSources"></div>')(scope);
scope.$apply();
expect($.fn.fullCalendar.calls.mostRecent().args[0].monthNames[0]).toBe('custom name');
});
/* Test to make sure header options can be overwritten */
it('should overwrite default header options', function () {
spyOn($.fn, 'fullCalendar');
scope.uiConfig2 = {
calendar:{
header: {center: 'title'}
}
};
$compile('<div ui-calendar="{{uiConfig2.calendar}}" ng-model="eventSources"></div>')(scope);
scope.$apply();
expect($.fn.fullCalendar.calls.mostRecent().args[0].hasOwnProperty('header')).toEqual(true);
var header = $.fn.fullCalendar.calls.mostRecent().args[0].header;
expect(header).toEqual({center: 'title'});
});
});
describe('Describing calendarCtrl and its configurations functions', function(){
var calendarCtrl;
beforeEach(function(){
calendarCtrl = $controller('uiCalendarCtrl', {$scope: scope, $element: null});
scope.$apply();
});
it('should make sure that all config functions are called in an angular context', inject(function($rootScope){
scope.uiConfig = {
calendar:{
height: 200,
weekends: false,
defaultView: 'month'
}
};
var keys = ['dayClick', 'eventClick', 'eventDrop', 'eventResize', 'eventMouseover'];
angular.forEach(keys, function(key) {
scope.uiConfig.calendar[key] = jasmine.createSpy().and.callFake(function(){
return key;
});
});
var fullCalendarConfig = calendarCtrl.getFullCalendarConfig(scope.uiConfig.calendar, {});
spyOn($rootScope,'$apply').and.callThrough();
angular.forEach(keys, function(key){
$rootScope.$apply.calls.reset();
var fn = fullCalendarConfig[key];
expect(fn()).toBe(key);
expect($rootScope.$apply.calls.count()).toBe(1);
expect(scope.uiConfig.calendar[key].calls.count()).toBe(1);
expect($rootScope.$apply(function(){
expect($rootScope.$apply.calls.count()).toBe(2);
return fn();
})).toBe(key);
expect($rootScope.$apply.calls.count()).toBe(2);
expect(scope.uiConfig.calendar[key].calls.count()).toBe(2);
});
}));
});
});